Files
functional-programming-snake/scripts/serve.mjs
T
2026-10-28 23:34:21 +01:00

31 lines
1.5 KiB
JavaScript

// Tiny zero-dependency static file server for the repo root. Tooling, not course code.
// Usage: node scripts/serve.mjs [port] (default 8000)
import { createServer } from 'node:http'
import { readFile, stat } from 'node:fs/promises'
import { extname, join, normalize, resolve, sep } from 'node:path'
import { fileURLToPath } from 'node:url'
const root = resolve(fileURLToPath(new URL('..', import.meta.url)))
const port = Number(process.argv[2] ?? 8000)
const types = {
'.html': 'text/html; charset=utf-8', '.js': 'text/javascript; charset=utf-8',
'.mjs': 'text/javascript; charset=utf-8', '.json': 'application/json', '.css': 'text/css',
'.md': 'text/markdown; charset=utf-8', '.svg': 'image/svg+xml', '.png': 'image/png',
'.wasm': 'application/wasm', '.map': 'application/json',
}
createServer(async (req, res) => {
try {
const path = normalize(decodeURIComponent(new URL(req.url, 'http://x').pathname))
let file = join(root, path)
if (file !== root && !file.startsWith(root + sep)) throw Object.assign(new Error(), { code: 'EACCES' })
if ((await stat(file)).isDirectory()) file = join(file, 'index.html')
const body = await readFile(file)
res.writeHead(200, { 'content-type': types[extname(file)] ?? 'application/octet-stream' })
res.end(body)
} catch (e) {
res.writeHead(e.code === 'EACCES' ? 403 : 404, { 'content-type': 'text/plain' })
res.end(e.code === 'EACCES' ? 'forbidden' : 'not found')
}
}).listen(port, () => console.log(`Serving ${root} on http://localhost:${port}`))