1import * as Bundle from "@/Bundle/Bundle";
2import * as NodeServices from "@effect/platform-node/NodeServices";
3import { expect, layer } from "alchemy-test";
4import * as Effect from "effect/Effect";
5import * as FileSystem from "effect/FileSystem";
6import * as Result from "effect/Result";
7import {
8 isolatedProject,
9 materializeIsolatedProject,
10 removeIsolatedProject,
11} from "../IsolatedProject.ts";
12
13/** Bare (non-relative, non-builtin) `import` statements left in the chunks. */
14const bareImports = (result: Bundle.BundleOutput) =>
15 result.files
16 .filter((file) => typeof file.content === "string")
17 .flatMap((file) => (file.content as string).split("\n"))
18 .filter((line) => /^import\b/.test(line))
19 .filter((line) => !/["'](\.\/|node:)/.test(line));
20
21/** Every generated-entry bootstrap module (see src/Runtime/Bootstrap). */
22const BOOTSTRAP_MODULES = [
23 "Ecs",
24 "AppRunner",
25 "Batch",
26 "Ec2",
27 "Lambda",
28 "MicrovmBun",
29 "MicrovmNode",
30 "CloudflareContainerBun",
31 "CloudflareContainerNode",
32 "Docker",
33 "Fly",
34 "Hetzner",
35 "Prisma",
36 "Railway",
37] as const;
38
39layer(NodeServices.layer)("generated entry bootstraps", (it) => {
40 // The contract every platform's generated entry relies on: a consumer
41 // project that has `alchemy` installed and NOTHING else reachable (bun's
42 // isolated linker / pnpm) must be able to bundle
43 // `alchemy/Runtime/Bootstrap/<Platform>` — i.e. alchemy's own dependencies
44 // (`@distilled.cloud/*`, `@effect/platform-*`) resolve from alchemy's
45 // location, never from the consumer's. Before the bootstraps became real
46 // modules, the entry imported those directly and an isolated project left
47 // them external (the deployed process died with `Cannot find module`).
48 for (const name of BOOTSTRAP_MODULES) {
49 it.effect(
50 `alchemy/Runtime/Bootstrap/${name} bundles from an isolated project`,
51 () =>
52 Effect.gen(function* () {
53 const project = isolatedProject(
54 `bootstrap-${name.toLowerCase()}`,
55 import.meta.filename,
56 );
57 yield* materializeIsolatedProject(project);
58 const virtualEntryPlugin = yield* Bundle.virtualEntryPlugin;
59 // The exact production condition sets: bun modules resolve
60 // `alchemy/*` through the `bun` condition (`src/*.ts`); node
61 // modules (Lambda zip, the *Node container variants) resolve
62 // through `import` → `lib/*.js`, so this test requires a built
63 // lib (`pnpm build`), exactly like a deployed Node bundle.
64 const isNode = name.endsWith("Node") || name === "Lambda";
65
66 try {
67 const result = yield* Bundle.build(
68 {
69 cwd: project.dir,
70 input: project.main,
71 platform: "node",
72 external: ["bun", "bun:*"],
73 resolve: {
74 conditionNames: isNode
75 ? [...Bundle.NODE_CONDITION_NAMES]
76 : [...Bundle.BUN_CONDITION_NAMES],
77 },
78 plugins: [
79 virtualEntryPlugin(
80 () => `
81import * as bootstrap from ${JSON.stringify(`alchemy/Runtime/Bootstrap/${name}`)};
82export default bootstrap;
83`,
84 ),
85 ],
86 },
87 { format: "esm" },
88 );
89 expect(bareImports(result)).toEqual([]);
90 expect(
91 result.files.some(
92 (file) =>
93 typeof file.content === "string" &&
94 file.content.includes("bootstrap"),
95 ),
96 ).toBe(true);
97 } finally {
98 yield* removeIsolatedProject(project);
99 }
100 }),
101 { tags: ["unit", "local"], timeout: 120_000 },
102 );
103 }
104
105 // An unresolvable import in a generated entry is a BUILD error with the
106 // cause spelled out — never a warning that leaves the import external and
107 // the deployed process dead at boot. (Doubles as the negative control for
108 // the harness: without the `alchemy` link the project resolves nothing.)
109 it.effect(
110 "an unresolvable import in a generated entry fails the build",
111 () =>
112 Effect.gen(function* () {
113 const fs = yield* FileSystem.FileSystem;
114 const project = isolatedProject(
115 "bootstrap-control",
116 import.meta.filename,
117 );
118 yield* materializeIsolatedProject(project);
119 yield* fs.remove(`${project.dir}/node_modules`, { recursive: true });
120 const virtualEntryPlugin = yield* Bundle.virtualEntryPlugin;
121 try {
122 const result = yield* Effect.result(
123 Bundle.build(
124 {
125 cwd: project.dir,
126 input: project.main,
127 platform: "node",
128 plugins: [
129 virtualEntryPlugin(
130 () => `
131import * as bootstrap from "alchemy/Runtime/Bootstrap/Ecs";
132export default bootstrap;
133`,
134 ),
135 ],
136 },
137 { format: "esm" },
138 ),
139 );
140 expect(Result.isFailure(result)).toBe(true);
141 if (Result.isFailure(result)) {
142 expect(result.failure.message).toContain(
143 'imports "alchemy/Runtime/Bootstrap/Ecs", which cannot be resolved',
144 );
145 }
146 } finally {
147 yield* removeIsolatedProject(project);
148 }
149 }),
150 { tags: ["unit", "local"] },
151 );
152
153 // Deliberate externals (runtime-provided modules such as `bun:*`,
154 // `cloudflare:workers`) resolve to `{ external: true }`, not to nothing, so
155 // the guard leaves them alone.
156 it.effect(
157 "a declared external in a generated entry is not an error",
158 () =>
159 Effect.gen(function* () {
160 const project = isolatedProject(
161 "bootstrap-external",
162 import.meta.filename,
163 );
164 yield* materializeIsolatedProject(project);
165 const virtualEntryPlugin = yield* Bundle.virtualEntryPlugin;
166 try {
167 const result = yield* Bundle.build(
168 {
169 cwd: project.dir,
170 input: project.main,
171 platform: "node",
172 external: ["bun:sqlite"],
173 plugins: [
174 virtualEntryPlugin(
175 () => `
176import { Database } from "bun:sqlite";
177export default Database;
178`,
179 ),
180 ],
181 },
182 { format: "esm" },
183 );
184 expect(bareImports(result)).toEqual([
185 'import { Database } from "bun:sqlite";',
186 ]);
187 } finally {
188 yield* removeIsolatedProject(project);
189 }
190 }),
191 { tags: ["unit", "local"] },
192 );
193});