import { Command } from "commander"; import path from "path"; import grabDirNames from "../../utils/grab-dir-names"; import writeErrorFile from "../../functions/write-error-file"; import { existsSync, rmSync } from "fs"; const { BUNDLER_CTX_MAP_JSON, SSR_BUNDLER_CTX_MAP_JSON, BUNX_BUNDLER_ERROR_EXIT_FILE, } = grabDirNames(); let retries = 0; let timeout; const MAX_RETRIES = 5; export default function () { return new Command("dev") .description("Run development server") .action(async () => { try { rmSync(SSR_BUNDLER_CTX_MAP_JSON, { recursive: true }); rmSync(BUNDLER_CTX_MAP_JSON, { recursive: true }); // A stale error file from a previous session makes the bundler // run in "silent" mode and pages fail with misleading // "Application Error" messages until the first rebuild. rmSync(BUNX_BUNDLER_ERROR_EXIT_FILE, { force: true }); } catch (error) { } await dev(); }); } async function dev() { clearTimeout(timeout); if (retries >= MAX_RETRIES) { console.error(`Dev server crashed ${MAX_RETRIES} times. Exiting.`); process.exit(1); } const dev_spawn_file = path.resolve(__dirname, "dev-spawn.ts"); const dev_spawn_js_file = path.resolve(__dirname, "dev-spawn.js"); const final_spawn_file = existsSync(dev_spawn_js_file) ? dev_spawn_js_file : dev_spawn_file; const spawn_options = { cmd: ["bun", final_spawn_file], stdio: ["inherit", "inherit", "inherit"], async onExit(subprocess, exitCode, signalCode, error) { writeErrorFile({ exitCode, error }); }, env: { ...process.env, NODE_ENV: "development", }, }; let dev_process; try { dev_process = Bun.spawn(spawn_options); } catch (error) { console.error(`Failed to start dev process:`, error); retries++; timeout = setTimeout(() => { retries = 0; }, 10000); return await dev(); } const exited = await dev_process.exited; // Restart on any numeric exit (0 included: the server should never exit // cleanly). A null exit means the child was killed by a signal (e.g. // Ctrl+C in the shared terminal), which we must not restart. if (exited != null) { retries++; timeout = setTimeout(() => { retries = 0; }, 10000); return await dev(); } timeout = setTimeout(() => { retries = 0; }, 10000); }