// `npm run local` launcher: build pages + config, then run the standalone // mock API and `next dev` together, tearing both down on exit. Cross-platform // (no concurrently dependency). // // `npm run local:lan` binds the dev server to 0.0.0.0 and prints the LAN URLs so // a phone / foldable on the same Wi-Fi can load the site for real-device testing. import { spawn, spawnSync } from "node:child_process"; import os from "node:os"; const LAN = process.argv.includes("--lan"); const PORT = process.env.PORT || "3000"; // Every non-internal IPv4 the machine answers on. VPN adapters show up here too, // so print them all and let the tester pick the one their phone can reach. const lanAddresses = () => Object.entries(os.networkInterfaces()).flatMap(([name, addrs]) => (addrs || []) .filter((a) => a.family === "IPv4" && !a.internal) .map((a) => ({ name, address: a.address })), ); // Prep static outputs the dev server serves. for (const s of ["build-config.mjs", "build-pages.mjs"]) { const r = spawnSync(process.execPath, [`scripts/${s}`], { stdio: "inherit" }); if (r.status !== 0) process.exit(r.status || 1); } const children = []; const start = (cmd, args, opts = {}) => { const c = spawn(cmd, args, { stdio: "inherit", ...opts }); children.push(c); return c; }; // Mock API (127.0.0.1:4100) — next.config proxies /api/mock/* here. Spawn node // directly (no shell) so a spaced node path (e.g. Program Files) is safe. start(process.execPath, ["scripts/mock-server.mjs"]); // Next dev — resolved from node_modules/.bin via the shell (npm puts it on PATH). const devArgs = ["dev", "-p", PORT, ...(LAN ? ["-H", "0.0.0.0"] : [])]; const dev = start("next", devArgs, { shell: true }); if (LAN) { const ips = lanAddresses(); console.log("\n Open on a device on the same network:"); for (const { name, address } of ips) console.log(` http://${address}:${PORT}/en (${name})`); if (!ips.length) console.log(" no external IPv4 found — is the machine on a network?"); console.log( "\n If the phone can't connect: allow node.exe through Windows Firewall on the\n" + " private network, and disconnect any VPN (its adapter shadows the LAN route).\n", ); } const shutdown = () => { for (const c of children) { try { c.kill(); } catch {} } }; dev.on("exit", (code) => { shutdown(); process.exit(code ?? 0); }); process.on("SIGINT", () => { shutdown(); process.exit(0); }); process.on("SIGTERM", () => { shutdown(); process.exit(0); });