77 lines
2.7 KiB
JavaScript
77 lines
2.7 KiB
JavaScript
/**
|
|
* dev/server.mjs — Orbit's dev static server.
|
|
*
|
|
* node dev/server.mjs [port] (default 8080)
|
|
*
|
|
* Why not `python3 -m http.server`? That server sends `Last-Modified`
|
|
* with no `Cache-Control`, so browsers apply HEURISTIC caching to the
|
|
* ES modules. During iterative development the browser then keeps
|
|
* running an OLD js/*.js module graph (stale GameScene.js, stale
|
|
* BuildWindow.js, …) long after the files on disk changed — while
|
|
* re-fetched JSON data is fresh. New data + old JS is exactly how you
|
|
* get "the build console behaves like an older version" no matter how
|
|
* often you clear the JSON cache or hard-reset the game.
|
|
*
|
|
* This server sends `Cache-Control: no-store` on every response, so a
|
|
* normal reload always runs what's on disk. Zero dependencies (node).
|
|
*/
|
|
import http from 'node:http';
|
|
import { promises as fs } from 'node:fs';
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
const PORT = Number(process.argv[2] ?? 8080);
|
|
|
|
const MIME = {
|
|
'.html': 'text/html; charset=utf-8',
|
|
'.js': 'text/javascript; charset=utf-8',
|
|
'.mjs': 'text/javascript; charset=utf-8',
|
|
'.json': 'application/json; charset=utf-8',
|
|
'.css': 'text/css; charset=utf-8',
|
|
'.mp4': 'video/mp4',
|
|
'.webm': 'video/webm',
|
|
'.png': 'image/png',
|
|
'.jpg': 'image/jpeg',
|
|
'.jpeg': 'image/jpeg',
|
|
'.gif': 'image/gif',
|
|
'.webp': 'image/webp',
|
|
'.svg': 'image/svg+xml',
|
|
'.otf': 'font/otf',
|
|
'.ttf': 'font/ttf',
|
|
'.woff': 'font/woff',
|
|
'.woff2': 'font/woff2',
|
|
'.md': 'text/markdown; charset=utf-8',
|
|
'.txt': 'text/plain; charset=utf-8',
|
|
};
|
|
|
|
const server = http.createServer(async (req, res) => {
|
|
const url = new URL(req.url, `http://${req.headers.host ?? 'localhost'}`);
|
|
let p = decodeURIComponent(url.pathname);
|
|
if (p.endsWith('/')) p += 'index.html';
|
|
const file = path.normalize(path.join(ROOT, p));
|
|
if (!file.startsWith(ROOT + path.sep) && file !== ROOT) {
|
|
res.writeHead(403).end('forbidden');
|
|
return;
|
|
}
|
|
try {
|
|
const data = await fs.readFile(file);
|
|
res.writeHead(200, {
|
|
'Content-Type': MIME[path.extname(file).toLowerCase()] ?? 'application/octet-stream',
|
|
'Content-Length': data.length,
|
|
// The whole point: browsers must never cache these.
|
|
'Cache-Control': 'no-store, no-cache, must-revalidate',
|
|
Pragma: 'no-cache',
|
|
Expires: '0',
|
|
});
|
|
res.end(data);
|
|
} catch {
|
|
res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8', 'Cache-Control': 'no-store' });
|
|
res.end(`404 — not found: ${url.pathname}\n(serving ${ROOT})`);
|
|
}
|
|
});
|
|
|
|
server.listen(PORT, '127.0.0.1', () => {
|
|
console.log(`orbit dev server → http://127.0.0.1:${PORT} (no-store caching; root ${ROOT})`);
|
|
});
|