(port: number)
| 330 | }; |
| 331 | |
| 332 | const startServer = (port: number): void => { |
| 333 | const servableFiles = collectServableFiles(); |
| 334 | /* |
| 335 | * Re-resolved each request so the index page picked up after |
| 336 | * `generateIndex()` ran is reachable. Generation happens before |
| 337 | * `startServer`, so the initial enumeration already contains it — |
| 338 | * but keeping the read fresh-on-each-request makes the watch-mode |
| 339 | * story honest, with no measurable cost on a dev-only server. |
| 340 | */ |
| 341 | |
| 342 | const server = http.createServer((req, res) => { |
| 343 | /* |
| 344 | * Strip query / fragment, normalise the route. Empty / "/" → index. |
| 345 | * `req.url` is the untrusted KEY into `servableFiles`; its parsed |
| 346 | * value never feeds into any filesystem call. |
| 347 | */ |
| 348 | const rawUrl = req.url ?? ""; |
| 349 | const decoded = (() => { |
| 350 | try { |
| 351 | return decodeURIComponent(rawUrl.split("?")[0]?.split("#")[0] ?? ""); |
| 352 | } catch { |
| 353 | return ""; |
| 354 | } |
| 355 | })(); |
| 356 | const route = decoded === "" || decoded === "/" ? "/index.html" : decoded; |
| 357 | |
| 358 | /* |
| 359 | * Lookup-by-lookup: the candidate paths below are STRINGS WE |
| 360 | * AUTHORED ABOVE (literal "/index.html", or the request `route` |
| 361 | * augmented with a literal ".html"). The path handed to |
| 362 | * `fs.readFileSync` is always pulled FROM `servableFiles`, whose |
| 363 | * values are absolute paths discovered via `fs.readdirSync` of |
| 364 | * PREVIEW_DIR. There is no flow from `req.url` to `readFileSync`. |
| 365 | */ |
| 366 | const resolvedPath = |
| 367 | servableFiles.get(route) ?? servableFiles.get(`${route}.html`); |
| 368 | |
| 369 | if (resolvedPath === undefined) { |
| 370 | res.writeHead(404, { "Content-Type": "text/html" }); |
| 371 | res.end("<h1>404 Not Found</h1>"); |
| 372 | |
| 373 | return; |
| 374 | } |
| 375 | |
| 376 | const ext = path.extname(resolvedPath); |
| 377 | |
| 378 | try { |
| 379 | const content = fs.readFileSync(resolvedPath, "utf8"); |
| 380 | |
| 381 | res.writeHead(200, { |
| 382 | "Content-Type": CONTENT_TYPES[ext] ?? "text/plain", |
| 383 | }); |
| 384 | res.end(content); |
| 385 | } catch (error: unknown) { |
| 386 | res.writeHead(500); |
| 387 | res.end( |
| 388 | `Error: ${error instanceof Error ? error.message : String(error)}` |
| 389 | ); |
no test coverage detected