/** * `wrnexus dev` — the development supervisor. * * The child server process owns file watching and HMR now (see @wrnexus/dev-server): * - CSS and client-island edits update the live page over a WebSocket with no * process restart and no full reload. * - When a server module changes (it can't be re-imported in-process), the * child exits with RESTART_EXIT_CODE and this supervisor respawns it. The * browser reconnects and morphs in the new HTML — no visible refresh. * * The supervisor therefore only (re)launches the child; it does not watch files. */ import { spawn, type ChildProcess } from "node:child_process"; import { resolve, join } from "node:path"; import { fileURLToPath } from "node:url"; import { RESTART_EXIT_CODE } from "@wrnexus/dev-server"; // Resolve the dev server child entry through the package (not a source path) so // it works whether @wrnexus/dev-server is a workspace or an installed dependency. const SERVE_ENTRY = fileURLToPath(import.meta.resolve("@wrnexus/dev-server/serve-entry")); export function runDev(appRoot: string, port: number, hostname = "::"): void { const appDir = join(resolve(appRoot), "app"); let child: ChildProcess | null = null; let shuttingDown = false; const spawnChild = (): void => { child = spawn( process.execPath, // the Bun binary [SERVE_ENTRY, appDir, String(port), "development", hostname], { stdio: "inherit" }, ); child.on("exit", (code, signal) => { if (shuttingDown || signal) return; if (code === RESTART_EXIT_CODE) { spawnChild(); // requested restart — respawn immediately return; } if (code && code !== 0) { // Crash (e.g. a syntax error). Respawn after a short delay so the // watcher comes back and the server auto-recovers once it's fixed. console.error(`[wrnexus] server exited (code ${code}); retrying in 1.2s…`); setTimeout(() => { if (!shuttingDown) spawnChild(); }, 1200); } }); }; console.log(`\n ⚡ WrNexus dev (HMR) — ${appDir}`); // Regenerate typed DB queries + typed routes once before starting, then launch. // (Best effort; rerun `wrnexus db generate` after editing .sql.) void (async () => { try { const { loadAppConfig } = await import("@wrnexus/styles"); const { regenerateAllQueries } = await import("./db.ts"); const config = await loadAppConfig(resolve(appRoot)); await regenerateAllQueries(appDir, config); // default + every named database console.log(" ↻ db queries generated"); } catch { /* non-fatal */ } try { const { regenerateRoutes } = await import("./routes.ts"); const n = regenerateRoutes(appDir); console.log(` ↻ ${n} typed routes generated`); } catch { /* non-fatal */ } spawnChild(); })(); const shutdown = () => { shuttingDown = true; child?.kill(); process.exit(0); }; process.on("SIGINT", shutdown); process.on("SIGTERM", shutdown); }