| 1 | /** |
| 2 | * Shared child-process ENTRY for framework production builds. |
| 3 | * |
| 4 | * Executed only as a spawned process by `runBuildChild` (`BuildChild.ts`) — |
| 5 | * never imported. `argv[2]` carries the JSON {@link BuildChildPayload}: the |
| 6 | * framework source module to load (a file URL), the config for its exported |
| 7 | * `buildInChild`, and the path to persist the resulting `BuildOutput` to. |
| 8 | * |
| 9 | * This is deliberately the ONLY module in the package with top-level |
| 10 | * execution: every importable module (including the framework source |
| 11 | * modules) stays pure — their `buildInChild` exports are plain functions |
| 12 | * returning Effects, callable in-process by tests. Process concerns (argv |
| 13 | * parsing, platform services, exit codes) live here, at the process |
| 14 | * boundary where they belong. |
| 15 | */ |
| 16 | import * as NodeServices from "@effect/platform-node/NodeServices"; |
| 17 | import * as Effect from "effect/Effect"; |
| 18 | import * as FileSystem from "effect/FileSystem"; |
| 19 | import type { BuildChildModule, BuildChildPayload } from "./BuildChild.ts"; |
| 20 | import { writeBuildOutput } from "./BuildOutput.ts"; |
| 21 | |
| 22 | const payload = JSON.parse(process.argv[2] ?? "{}") as BuildChildPayload; |
| 23 | |
| 24 | const program = Effect.gen(function* () { |
| 25 | const module = (yield* Effect.promise( |
| 26 | () => import(payload.module), |
| 27 | )) as Partial<BuildChildModule>; |
| 28 | if (typeof module.buildInChild !== "function") { |
| 29 | return yield* Effect.die( |
| 30 | new Error( |
| 31 | `Build child module ${payload.module} does not export a buildInChild function`, |
| 32 | ), |
| 33 | ); |
| 34 | } |
| 35 | const output = yield* module.buildInChild(payload.config as never).pipe( |
| 36 | // Hand the failure message back to `runBuildChild` for its error. |
| 37 | Effect.tapError((error) => |
| 38 | Effect.gen(function* () { |
| 39 | const fs = yield* FileSystem.FileSystem; |
| 40 | yield* fs |
| 41 | .writeFileString( |
| 42 | payload.errorPath, |
| 43 | error instanceof Error ? error.message : String(error), |
| 44 | ) |
| 45 | .pipe(Effect.ignore); |
| 46 | }), |
| 47 | ), |
| 48 | ); |
| 49 | yield* writeBuildOutput(payload.outputPath, output); |
| 50 | }).pipe(Effect.provide(NodeServices.layer)); |
| 51 | |
| 52 | void Effect.runPromise(program).then( |
| 53 | // The macrotask hop lets buffered stdout/stderr drain before exiting. |
| 54 | () => setTimeout(() => process.exit(0), 0), |
| 55 | (error) => { |
| 56 | console.error(error); |
| 57 | setTimeout(() => process.exit(1), 0); |
| 58 | }, |
| 59 | ); |