31 lines
1.5 KiB
JavaScript
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}`))
|