// 零依赖本地热重载静态服务器 // 用法: node serve.mjs [port] 默认 5180 import http from 'node:http'; import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; const ROOT = path.dirname(fileURLToPath(import.meta.url)); const PORT = Number(process.argv[2]) || 5180; const MIME = { '.html': 'text/html; charset=utf-8', '.css': 'text/css; charset=utf-8', '.js': 'text/javascript; charset=utf-8', '.mjs': 'text/javascript; charset=utf-8', '.json': 'application/json; charset=utf-8', '.svg': 'image/svg+xml', '.png': 'image/png', '.jpg': 'image/jpeg', '.woff2': 'font/woff2', }; // 热重载改短轮询(2026-07-10):原 SSE 每个 tab 一条常驻连接,Chrome 同域 // HTTP/1.1 上限 6 条——开到第 7 个原型 tab 连接池耗尽整站卡死。轮询即用即断,无上限。 let version = 0; const RELOAD_SNIPPET = `\n\n`; const server = http.createServer((req, res) => { if (req.url === '/__version' || req.url === '/__reload') { res.writeHead(200, { 'Content-Type': 'application/json', 'Cache-Control': 'no-store' }); res.end(JSON.stringify({ v: version })); return; } let urlPath = decodeURIComponent(req.url.split('?')[0]); if (urlPath === '/') urlPath = '/index.html'; const filePath = path.join(ROOT, urlPath); if (!filePath.startsWith(ROOT)) { res.writeHead(403); res.end('forbidden'); return; } fs.readFile(filePath, (err, data) => { if (err) { res.writeHead(404, { 'Content-Type': 'text/html; charset=utf-8' }); res.end('