/** * The child process launched by the dev supervisor. * * bun run serve-entry.ts [hostname] * * It starts the dev server and prints the route table. Because it runs in its * own process, every restart re-imports all route modules fresh — that is how * the supervisor delivers live reload of edited server code. */ import { readFileSync } from "node:fs"; import { dirname } from "node:path"; import type { Mode } from "@wrnexus/core"; import { findStyleEntry, headToString, loadAppConfig, renderFontHead } from "@wrnexus/styles"; import { startServer } from "./index.ts"; const [appDir, portStr, modeStr, hostname, hmrStr, certFile, keyFile] = process.argv.slice(2); const mode = (modeStr as Mode) || "development"; const port = Number(portStr) || 3000; if (!appDir) { throw new Error("WRN-DEV-APP-DIR: app directory argument is required."); } // Load optional wrnexus.config.ts next to the app directory. const appRoot = dirname(appDir); const config = await loadAppConfig(appRoot); const styleEntry = findStyleEntry(appDir, appRoot, config.styles?.entry); const server = await startServer({ appDir: appDir!, appConfig: { ...config, }, port, hostname, tls: certFile && keyFile ? { cert: readFileSync(certFile, "utf8"), key: readFileSync(keyFile, "utf8") } : undefined, mode, hmr: hmrStr === undefined ? undefined : hmrStr === "true", styleEntry, stylesConfig: config.styles, head: [renderFontHead(config.fonts), headToString(config.head)].filter(Boolean).join("\n "), seo: config.seo, security: config.security, theme: config.theme, cookies: config.cookies, i18n: config.i18n, db: config.db, databases: config.databases, realtime: config.realtime, storage: config.storage, mobile: config.mobile, pwa: config.pwa, devToolbar: config.devToolbar, plugins: config.plugins, observability: config.observability, tenancy: config.tenancy, navigation: config.navigation, }); const router = server.router; interface PrintableRoute { raw: string; } function printRouteGroup(label: string, items: readonly PrintableRoute[]): void { const routes = Array.from(new Set(items.map((item) => item.raw.trim()).filter(Boolean))).sort( (left, right) => left.localeCompare(right), ); console.log(` ${label}: ${routes.length}`); if (routes.length === 0) { console.warn(` ⚠ No ${label.toLowerCase()} registered`); return; } for (const route of routes) { console.log(` ${route}`); } } console.log(`\n ⚡ WrNexus — ${server.url}\n`); printRouteGroup("Pages", router.pages); console.log(""); printRouteGroup("API Routes", router.api); if (router.realtime.length > 0) { console.log(""); printRouteGroup("Realtime Routes", router.realtime); } console.log(`\n Components: ${router.components.length}`); console.log("");