paperclip/scripts/serve-storybook-static.mjs

81 lines
2.5 KiB
JavaScript

#!/usr/bin/env node
// Tiny dependency-free static file server for the built Storybook
// (ui/storybook-static). Used by the visual snapshot suite's webServer so we
// don't add an http-server dependency.
import { createServer } from "node:http";
import { createReadStream, existsSync, statSync } from "node:fs";
import { extname, join, normalize, resolve } from "node:path";
import { fileURLToPath } from "node:url";
const root = resolve(
fileURLToPath(new URL(".", import.meta.url)),
"..",
"ui",
"storybook-static",
);
const portArgIndex = process.argv.indexOf("--port");
const explicitPort =
portArgIndex >= 0 &&
process.argv[portArgIndex + 1] &&
!process.argv[portArgIndex + 1].startsWith("--")
? process.argv[portArgIndex + 1]
: null;
const portSource = explicitPort ?? process.env.PORT ?? 6106;
const port = Number(portSource);
if (!Number.isInteger(port) || port <= 0 || port > 65535) {
console.error(`Invalid Storybook static server port: ${portSource}`);
process.exit(1);
}
if (!existsSync(join(root, "index.html"))) {
console.error(`No built Storybook at ${root}. Run \`pnpm build-storybook\` first.`);
process.exit(1);
}
const MIME = {
".html": "text/html; charset=utf-8",
".js": "text/javascript; charset=utf-8",
".mjs": "text/javascript; charset=utf-8",
".css": "text/css; charset=utf-8",
".json": "application/json; charset=utf-8",
".png": "image/png",
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".gif": "image/gif",
".svg": "image/svg+xml",
".ico": "image/x-icon",
".woff": "font/woff",
".woff2": "font/woff2",
".ttf": "font/ttf",
".map": "application/json; charset=utf-8",
".txt": "text/plain; charset=utf-8",
".webm": "video/webm",
".mp4": "video/mp4",
};
const server = createServer((req, res) => {
const url = new URL(req.url ?? "/", `http://localhost:${port}`);
let filePath = normalize(join(root, decodeURIComponent(url.pathname)));
if (!filePath.startsWith(root)) {
res.writeHead(403).end("forbidden");
return;
}
if (existsSync(filePath) && statSync(filePath).isDirectory()) {
filePath = join(filePath, "index.html");
}
if (!existsSync(filePath)) {
res.writeHead(404).end("not found");
return;
}
res.writeHead(200, {
"content-type": MIME[extname(filePath)] ?? "application/octet-stream",
"cache-control": "no-store",
});
createReadStream(filePath).pipe(res);
});
server.listen(port, () => {
console.log(`storybook-static served at http://localhost:${port}`);
});