import { createServer } from "node:http"; import { readFile, stat } from "node:fs/promises"; import { watch } from "node:fs"; import { resolve, extname, sep } from "node:path"; import { fileURLToPath } from "node:url"; import { build, root } from "./build.mjs"; const mime = { ".json": "application/json; charset=utf-8", ".txt": "text/plain; charset=utf-8", ".html": "text/html; charset=utf-8", ".css": "text/css", ".js": "text/javascript", ".mjs": "text/javascript", ".svg": "image/svg+xml", ".webp": "image/webp", ".woff2": "font/woff2", ".png": "image/png", }; export async function startPreview({ port = 4174, rebuild = true } = {}) { if (rebuild) await build(); const directory = resolve(root, "dist"); const server = createServer(async (req, res) => { try { const path = decodeURIComponent( new URL(req.url, "http://localhost").pathname, ); let file = resolve(directory, "." + path); if (file !== directory && !file.startsWith(directory + sep)) { res.writeHead(403).end(); return; } if ((await stat(file)).isDirectory()) file = resolve(file, "index.html"); const body = await readFile(file); res .writeHead(200, { "Content-Type": mime[extname(file)] || "application/octet-stream", "Cache-Control": "no-store", }) .end(body); } catch (error) { const status = error.code === "ENOENT" ? 404 : error instanceof URIError ? 400 : 500; if (status === 500) console.error(error); res .writeHead(status) .end(status === 404 ? "Not found" : "Unable to serve this file"); } }); await new Promise((done, reject) => { server.once("error", reject); server.listen(port, "127.0.0.1", done); }); return { url: `http://127.0.0.1:${server.address().port}/`, close: () => new Promise((done, reject) => server.close((error) => (error ? reject(error) : done())), ), }; } if ( process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url) ) { const app = await startPreview({ port: Number(process.env.PORT || 4174) }); console.log(`Preview: ${app.url}`); let watcher, timer, building = Promise.resolve(); if (process.argv.includes("--watch")) watcher = watch(resolve(root, "src"), { recursive: true }, () => { clearTimeout(timer); timer = setTimeout(() => { building = building .then(() => build()) .then(() => console.log("Rebuilt. Refresh the browser.")) .catch(console.error); }, 100); }); async function close() { clearTimeout(timer); watcher?.close(); await building; await app.close(); } process.once("SIGINT", close); process.once("SIGTERM", close); }