import { spawnSync } from "node:child_process"; import { rm } from "node:fs/promises"; import process from "node:process"; import { INSTALLED_RUNTIME_CAPABILITIES } from "../src/features/installed-runtime-capabilities.ts"; /** * §17.2.2. Deterministic two-pass build. * * The Service Worker needs the exact hashed asset list at compile time, so the * order is fixed: * * 1. clean dist and .generated/frontend-runtime * 2. generate contractSet and build-info source * 3. Vite app build (emptyOutDir = true) * 4. materialize dist/config.json from the declared APP_PROFILE * 5. scan app dist and generate the static asset source * 6. ACTIVE only: Vite Service Worker build (emptyOutDir = false) * 7. generate the self-contained TechLog production serving boundary * 8. generate Release Manifest V2 and the build manifest * * Steps 5 and 6 are skipped for `REMOVE_REGISTRATION`, `PURGE_OWNED_RESOURCES` * and `null`: those modes never run an active worker build. * * Step 4 has to follow the Vite build and precede the asset scan. Vite copies * `public/` verbatim, so without it every build — including a production one — * ships the local runtime document; and the Service Worker hashes the emitted * `config.json`, so the profile must be in place before that inventory is * taken. */ const selection = INSTALLED_RUNTIME_CAPABILITIES.serviceWorker; const buildsActiveWorker = selection?.mode === "ACTIVE"; function run(command: string, args: readonly string[]): void { const result = spawnSync(command, [...args], { stdio: "inherit", env: process.env, }); if (result.status !== 0) { process.stderr.write(`build step failed: ${command} ${args.join(" ")}\n`); process.exit(result.status ?? 1); } } // 1. clean await rm("dist", { recursive: true, force: true }); await rm(".generated/frontend-runtime", { recursive: true, force: true }); // 2. contract set + build info run("node", ["scripts/generate-contract-set.ts"]); // 3. app build run("npx", ["vite", "build"]); // 4. runtime config for the declared profile run("node", ["scripts/generate-runtime-config.ts"]); if (buildsActiveWorker) { // 5. hashed asset inventory run("node", ["scripts/generate-service-worker-assets.ts", "dist"]); // 6. service worker build run("npx", ["vite", "build", "--config", "vite.service-worker.config.ts"]); } else { process.stdout.write( `service worker mode ${selection?.mode ?? "null"}: skipping worker build\n`, ); } // 7. production serving boundary run("node", ["scripts/generate-tech-log-serving-artifact.ts"]); // 8. release + build manifest run("node", ["scripts/generate-build-manifest.ts"]);