Files

40 lines
1.5 KiB
JavaScript

// The gates measure the EXPORT, so they need something that serves the export
// the way the Sites plane does: directory indexes, and a real 404 for a path
// with no file. Twenty lines of node beats a dependency.
import { createServer } from 'node:http'
import { readFile, stat } from 'node:fs/promises'
import { join, extname, normalize } from 'node:path'
const root = new URL('../out/', import.meta.url).pathname
const port = Number(process.env.PORT || 4321)
const TYPES = {
'.html': 'text/html; charset=utf-8',
'.js': 'text/javascript; charset=utf-8',
'.css': 'text/css; charset=utf-8',
'.txt': 'text/plain; charset=utf-8',
'.xml': 'application/xml; charset=utf-8',
'.json': 'application/json; charset=utf-8',
'.woff2': 'font/woff2',
'.svg': 'image/svg+xml',
}
createServer(async (req, res) => {
// normalize() collapses `..`, so a crafted path cannot climb out of out/.
const rel = normalize(decodeURIComponent(req.url.split('?')[0])).replace(/^(\.\.[/\\])+/, '')
let file = join(root, rel)
try {
if ((await stat(file)).isDirectory()) file = join(file, 'index.html')
} catch {
file = extname(file) ? file : `${file}/index.html`
}
try {
const body = await readFile(file)
res.writeHead(200, { 'content-type': TYPES[extname(file)] || 'application/octet-stream' })
res.end(body)
} catch {
res.writeHead(404, { 'content-type': 'text/plain' })
res.end('not found')
}
}).listen(port, () => console.log(`serving out/ on http://127.0.0.1:${port}`))