// Lightweight static file server for playwright tests. import http from "node:http"; import fs from "node:fs"; import fsp from "node:fs/promises"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { MIME, isAssetPath } from "../build-support/assetPaths.js"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const root = path.resolve(__dirname, "..", process.env.BUILD_DIR || "build"); const port = parseInt(process.env.PORT || "8081", 10); let indexHtml = null; function getIndexHtml() { if (indexHtml === null) { indexHtml = fs.readFileSync(path.join(root, "index.html"), "utf-8"); } return indexHtml; } function send(res, status, body, type) { if (res.headersSent || res.writableEnded) return; res.writeHead(status, { "Content-Type": type }); res.end(body); } function safeJoin(urlPath) { const decoded = decodeURIComponent(urlPath.split("?")[0]); const target = path.normalize(path.join(root, decoded)); if (!target.startsWith(root)) return null; return target; } function createStaticServer() { return http.createServer(async (req, res) => { const target = safeJoin(req.url); const ext = target ? path.extname(target) : ""; if (target) { try { const stat = await fsp.stat(target); if (stat.isFile()) { res.writeHead(200, { "Content-Type": MIME[ext] || "application/octet-stream", }); const stream = fs.createReadStream(target); stream.on("error", (err) => { console.error(`Stream error for ${req.url}:`, err); // Headers are already sent; destroy the response to surface the // failure to the client rather than leaving it hanging. res.destroy(err); }); stream.pipe(res); return; } } catch (err) { if (err.code !== "ENOENT" && err.code !== "ENOTDIR") { console.error(`Stat error for ${req.url}:`, err); send(res, 500, "Internal Server Error", MIME[".html"]); return; } // Fall through to the not-found / SPA-fallback logic below. } } if (isAssetPath(req.url.split("?")[0])) { send(res, 404, `Not found: ${req.url}`, "text/plain; charset=utf-8"); } else { send(res, 200, getIndexHtml(), MIME[".html"]); } }); } if ( process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url) ) { createStaticServer().listen(port, () => { console.info(`Static server listening on http://localhost:${port}/`); }); }