// 零依赖静态服务器:把 dist/ 以正确的 MIME 暴露出来,供无头 Edge 与人工本地预览使用。 // 用法:node tools/serve.mjs [port] [--root ] // // 它同时承担"测试台"的角色,且完全不改动 dist/: // ?nojs=1 剥掉所有 `; async function html(body, query) { let out = body; if (query.get("nojs") === "1") out = out.replace(/]*><\/script>/g, ""); else if (query.has("__props") || query.has("__freeze")) { out = out.replace(/(]*type="module"[^>]*>)/, DRIVER(query) + "$1"); } return out; } const server = createServer(async (req, res) => { try { const url = new URL(req.url, "http://127.0.0.1"); let rel = decodeURIComponent(url.pathname); if (rel.endsWith("/")) rel += "index.html"; const abs = normalize(join(root, rel)); if (abs !== root && !abs.startsWith(root + sep)) { res.writeHead(403).end("forbidden"); return; } const info = await stat(abs).catch(() => null); if (!info || !info.isFile()) { res.writeHead(404, { "content-type": "text/plain; charset=utf-8" }).end(`404 ${rel}`); console.log(`404 ${rel}`); return; } if (extname(abs).toLowerCase() === ".html") { const body = await html(await readFile(abs, "utf8"), url.searchParams); res.writeHead(200, { "content-type": "text/html; charset=utf-8", "content-length": Buffer.byteLength(body), "cache-control": "no-store", }); res.end(body); return; } res.writeHead(200, { "content-type": MIME[extname(abs).toLowerCase()] ?? "application/octet-stream", "content-length": info.size, "cache-control": "no-store", }); createReadStream(abs).pipe(res); } catch (err) { res.writeHead(500, { "content-type": "text/plain; charset=utf-8" }).end(String(err)); } }); server.listen(port, "127.0.0.1", () => { console.log(`serving ${root}`); console.log(` http://127.0.0.1:${port}/`); });