1import * as Bundle from "@/Bundle/Bundle";
2import {
3 collectPureAnchors,
4 packageNameFromId,
5 purePlugin,
6 resolvePackageInfo,
7 type PackageInfo,
8} from "@/Bundle/PurePlugin";
9import * as NodeServices from "@effect/platform-node/NodeServices";
10import { describe, expect, it } from "alchemy-test";
11import * as Effect from "effect/Effect";
12import * as FileSystem from "effect/FileSystem";
13import * as Path from "effect/Path";
14import * as nodeFs from "node:fs";
15import * as nodePath from "node:path";
16import * as os from "node:os";
17import { rolldown, RolldownMagicString, type RolldownLog } from "rolldown";
18
19describe("packageNameFromId", { tags: ["unit", "local"] }, () => {
20 it("extracts a top-level package name", () => {
21 expect(packageNameFromId("/proj/node_modules/effect/dist/Effect.js")).toBe(
22 "effect",
23 );
24 });
25
26 it("extracts a scoped package name", () => {
27 expect(
28 packageNameFromId("/proj/node_modules/@effect/cluster/dist/index.js"),
29 ).toBe("@effect/cluster");
30 });
31
32 it("uses the LAST node_modules segment for nested deps", () => {
33 expect(
34 packageNameFromId(
35 "/proj/node_modules/foo/node_modules/effect/dist/Effect.js",
36 ),
37 ).toBe("effect");
38 });
39
40 it("returns null for ids outside node_modules", () => {
41 expect(packageNameFromId("/proj/src/Bundle.ts")).toBeNull();
42 });
43
44 it("normalizes Windows-style separators", () => {
45 expect(
46 packageNameFromId(
47 "C:\\proj\\node_modules\\@effect\\cluster\\dist\\index.js",
48 ),
49 ).toBe("@effect/cluster");
50 });
51});
52
53describe(
54 "resolvePackageInfo (filesystem walk)",
55 { tags: ["unit", "local"] },
56 () => {
57 it("walks up to the nearest package.json", async () => {
58 const root = nodeFs.mkdtempSync(
59 nodePath.join(os.tmpdir(), "alchemy-resolve-pkg-"),
60 );
61 try {
62 const pkgDir = nodePath.join(root, "packages", "fancy-pkg");
63 nodeFs.mkdirSync(nodePath.join(pkgDir, "src", "deep"), {
64 recursive: true,
65 });
66 nodeFs.writeFileSync(
67 nodePath.join(pkgDir, "package.json"),
68 JSON.stringify({ name: "@scope/fancy-pkg", sideEffects: false }),
69 );
70 const dir = nodePath.join(pkgDir, "src", "deep");
71 const cache = new Map<string, PackageInfo | null>();
72
73 const info = await resolvePackageInfo(dir, cache);
74 expect(info?.name).toBe("@scope/fancy-pkg");
75 expect(info?.sideEffects).toBe(false);
76 // Repeat hits the cache; result is identical and the visited dirs
77 // are populated.
78 expect((await resolvePackageInfo(dir, cache))?.name).toBe(
79 "@scope/fancy-pkg",
80 );
81 expect(cache.size).toBeGreaterThan(0);
82 } finally {
83 nodeFs.rmSync(root, { recursive: true, force: true });
84 }
85 });
86
87 it("caches results for sibling directories independently", async () => {
88 const root = nodeFs.mkdtempSync(
89 nodePath.join(os.tmpdir(), "alchemy-resolve-pkg-"),
90 );
91 try {
92 for (const [dir, name] of [
93 ["a", "pkg-a"],
94 ["b", "pkg-b"],
95 ] as const) {
96 nodeFs.mkdirSync(nodePath.join(root, dir, "src"), {
97 recursive: true,
98 });
99 nodeFs.writeFileSync(
100 nodePath.join(root, dir, "package.json"),
101 JSON.stringify({ name }),
102 );
103 }
104 const cache = new Map<string, PackageInfo | null>();
105 expect(
106 (await resolvePackageInfo(nodePath.join(root, "a", "src"), cache))
107 ?.name,
108 ).toBe("pkg-a");
109 expect(
110 (await resolvePackageInfo(nodePath.join(root, "b", "src"), cache))
111 ?.name,
112 ).toBe("pkg-b");
113 } finally {
114 nodeFs.rmSync(root, { recursive: true, force: true });
115 }
116 });
117
118 it("returns null when no package.json is found upward", async () => {
119 const root = nodeFs.mkdtempSync(
120 nodePath.join(os.tmpdir(), "alchemy-resolve-pkg-"),
121 );
122 try {
123 expect(await resolvePackageInfo(root, new Map())).toBeNull();
124 } finally {
125 nodeFs.rmSync(root, { recursive: true, force: true });
126 }
127 });
128
129 it("never walks above a node_modules boundary", async () => {
130 const root = nodeFs.mkdtempSync(
131 nodePath.join(os.tmpdir(), "alchemy-resolve-pkg-"),
132 );
133 try {
134 // A stray package.json ABOVE node_modules must never be latched onto.
135 nodeFs.writeFileSync(
136 nodePath.join(root, "package.json"),
137 JSON.stringify({ name: "stray-parent" }),
138 );
139 const dir = nodePath.join(root, "node_modules", "no-manifest", "dist");
140 nodeFs.mkdirSync(dir, { recursive: true });
141 expect(await resolvePackageInfo(dir, new Map())).toBeNull();
142 } finally {
143 nodeFs.rmSync(root, { recursive: true, force: true });
144 }
145 });
146 },
147);
148
149describe("collectPureAnchors", { tags: ["unit", "local"] }, () => {
150 /** Applies the returned anchors to `code` with a plain string splice. */
151 const apply = (code: string, anchors: number[]): string => {
152 let out = code;
153 for (const anchor of [...anchors].sort((a, b) => b - a)) {
154 out = `${out.slice(0, anchor)}/*#__PURE__*/ ${out.slice(anchor)}`;
155 }
156 return out;
157 };
158 // Applies BOTH bound and discarded anchors — the full-annotation view a
159 // sideEffects-free package receives.
160 const annotate = async (code: string): Promise<string | null> => {
161 const anchors = await collectPureAnchors(code, "/n/effect/dist/x.js");
162 return anchors === null
163 ? null
164 : apply(code, [...anchors.bound, ...anchors.discarded]);
165 };
166
167 it("annotates calls in TypeScript source (worker/bun condition path)", async () => {
168 const code = `import { make } from "./util";\nexport const x: number = make();\n`;
169 const anchors = await collectPureAnchors(
170 code,
171 "/proj/packages/alchemy/src/Util.ts",
172 );
173 expect(anchors).not.toBeNull();
174 expect(apply(code, anchors!.bound)).toContain("/*#__PURE__*/ make()");
175 });
176
177 it("classifies variable-initializer calls as bound", async () => {
178 const anchors = await collectPureAnchors(`const x = create();`, "/n/x.js");
179 expect(anchors!.bound.length).toBe(1);
180 expect(anchors!.discarded.length).toBe(0);
181 });
182
183 it("classifies bare expression-statement calls as discarded", async () => {
184 const anchors = await collectPureAnchors(
185 `app.get("/", handler);`,
186 "/n/x.js",
187 );
188 expect(anchors!.bound.length).toBe(0);
189 expect(anchors!.discarded.length).toBe(1);
190 });
191
192 it("classifies assignment right-hand sides as bound, even in statement position", async () => {
193 const anchors = await collectPureAnchors(`exports.x = make();`, "/n/x.js");
194 expect(anchors!.bound.length).toBe(1);
195 expect(anchors!.discarded.length).toBe(0);
196 });
197
198 it("classifies non-final sequence expressions as discarded even in initializers", async () => {
199 const anchors = await collectPureAnchors(
200 `const x = (register(), make());`,
201 "/n/x.js",
202 );
203 expect(anchors!.bound.length).toBe(1);
204 expect(anchors!.discarded.length).toBe(1);
205 });
206
207 it("classifies optional-chained statement calls as discarded", async () => {
208 const anchors = await collectPureAnchors(`app?.get("/", h);`, "/n/x.js");
209 expect(anchors!.bound.length).toBe(0);
210 expect(anchors!.discarded.length).toBe(1);
211 });
212
213 it("annotates top-level call expressions", async () => {
214 expect(await annotate(`const x = create();`)).toBe(
215 `const x = /*#__PURE__*/ create();`,
216 );
217 });
218
219 it("annotates top-level new expressions", async () => {
220 expect(await annotate(`const x = new Klass();`)).toBe(
221 `const x = /*#__PURE__*/ new Klass();`,
222 );
223 });
224
225 it("annotates calls inside named exports", async () => {
226 expect(await annotate(`export const x = make();`)).toBe(
227 `export const x = /*#__PURE__*/ make();`,
228 );
229 });
230
231 it("annotates calls inside default exports", async () => {
232 expect(await annotate(`export default make();`)).toBe(
233 `export default /*#__PURE__*/ make();`,
234 );
235 });
236
237 it("does NOT annotate calls inside function bodies", async () => {
238 expect(await annotate(`function f() { return inner(); }`)).toBeNull();
239 });
240
241 it("does NOT annotate IIFEs", async () => {
242 expect(await annotate(`(function () { sideEffect(); })();`)).toBeNull();
243 });
244
245 it("does NOT annotate arrow IIFEs", async () => {
246 expect(await annotate(`(() => sideEffect())();`)).toBeNull();
247 });
248
249 it("skips already-annotated calls", async () => {
250 expect(await annotate(`const x = /*#__PURE__*/ create();`)).toBeNull();
251 });
252
253 it("annotates calls through ts-as expressions", async () => {
254 expect(await annotate(`const x = make() as Foo;`)).toContain(
255 "/*#__PURE__*/ make()",
256 );
257 });
258
259 it("annotates both branches of ternary initializers", async () => {
260 const result = await annotate(`const x = cond ? a() : b();`);
261 expect(result).toContain("/*#__PURE__*/ a()");
262 expect(result).toContain("/*#__PURE__*/ b()");
263 });
264
265 it("preserves line numbers", async () => {
266 const input = `const a = first();\nconst b = second();\n`;
267 const result = await annotate(input);
268 expect(result!.split("\n").length).toBe(input.split("\n").length);
269 });
270
271 it("returns null on unparseable input", async () => {
272 expect(await collectPureAnchors(`const x = {;`, "/n/x.js")).toBeNull();
273 });
274});
275
276interface TransformOutput {
277 code?: unknown;
278 moduleSideEffects?: boolean | string | null;
279}
280
281/**
282 * Invokes a plugin's `transform` hook directly, regardless of whether it
283 * was defined as a plain function or a `{ filter, handler }` object. The
284 * plugin under test only reads `code`, `id` and `meta.magicString`, so we
285 * stub the context and meta with minimal shapes.
286 */
287async function callTransform(
288 plugin: ReturnType<typeof purePlugin>,
289 code: string,
290 id: string,
291): Promise<TransformOutput | string | null> {
292 const transform = plugin.transform;
293 if (transform === undefined) throw new Error("plugin has no transform hook");
294 const handler =
295 typeof transform === "function" ? transform : transform.handler;
296 return (await (handler as (...args: any[]) => unknown).call(
297 {} as any,
298 code,
299 id,
300 { moduleType: "js" },
301 )) as TransformOutput | string | null;
302}
303
304/** Stringifies a transform result's `code` (RolldownMagicString or string). */
305const codeOf = (result: TransformOutput | string | null): string => {
306 if (result === null) throw new Error("expected a transform result");
307 const code = typeof result === "string" ? result : result.code;
308 if (code === undefined) throw new Error("expected transformed code");
309 return String(code);
310};
311
312/**
313 * Invokes a plugin's `options` hook with stubbed plugin context. Awaits
314 * any returned promise so async hooks complete before we proceed.
315 */
316async function callOptions(
317 plugin: ReturnType<typeof purePlugin>,
318 opts: { input: string; cwd?: string },
319): Promise<void> {
320 const optsHook = plugin.options;
321 if (typeof optsHook !== "function") return;
322 await (optsHook as (...args: any[]) => unknown).call({} as any, opts);
323}
324
325describe("purePlugin", { tags: ["unit", "local"] }, () => {
326 it("transforms only modules from matched packages", async () => {
327 const plugin = purePlugin();
328 const userCode = `const x = doThing();`;
329
330 const matched = await callTransform(
331 plugin,
332 userCode,
333 "/proj/node_modules/effect/dist/Effect.js",
334 );
335 expect(matched).not.toBeNull();
336 expect(codeOf(matched)).toContain("/*#__PURE__*/");
337
338 const unmatched = await callTransform(
339 plugin,
340 userCode,
341 "/proj/node_modules/lodash/index.js",
342 );
343 expect(unmatched).toBeNull();
344
345 const userland = await callTransform(
346 plugin,
347 userCode,
348 "/proj/src/MyModule.ts",
349 );
350 expect(userland).toBeNull();
351 });
352
353 it("returns a RolldownMagicString so rolldown generates the sourcemap natively", async () => {
354 const plugin = purePlugin();
355 const result = await callTransform(
356 plugin,
357 `const x = doThing();`,
358 "/proj/node_modules/effect/dist/Effect.js",
359 );
360 expect(result).not.toBeNull();
361 expect((result as TransformOutput).code).toBeInstanceOf(
362 RolldownMagicString,
363 );
364 });
365
366 it("strips ?query and #hash suffixes before resolving", async () => {
367 const root = nodeFs.mkdtempSync(
368 nodePath.join(os.tmpdir(), "alchemy-pure-query-"),
369 );
370 try {
371 const pkgDir = nodePath.join(root, "pkg");
372 nodeFs.mkdirSync(pkgDir, { recursive: true });
373 nodeFs.writeFileSync(
374 nodePath.join(pkgDir, "package.json"),
375 JSON.stringify({ name: "with-query" }),
376 );
377 const plugin = purePlugin({
378 packages: ["with-query"],
379 replaceDefaults: true,
380 });
381 const result = await callTransform(
382 plugin,
383 `const x = doThing();`,
384 `${nodePath.join(pkgDir, "Mod.ts")}?v=123`,
385 );
386 expect(result).not.toBeNull();
387 expect(codeOf(result)).toContain("/*#__PURE__*/");
388 } finally {
389 nodeFs.rmSync(root, { recursive: true, force: true });
390 }
391 });
392
393 it("respects user-extended package list", async () => {
394 const plugin = purePlugin({ packages: ["my-lib"] });
395
396 const result = await callTransform(
397 plugin,
398 `const x = doThing();`,
399 "/proj/node_modules/my-lib/index.js",
400 );
401 expect(result).not.toBeNull();
402 expect(codeOf(result)).toContain("/*#__PURE__*/");
403
404 const effectResult = await callTransform(
405 plugin,
406 `const x = doThing();`,
407 "/proj/node_modules/effect/dist/Effect.js",
408 );
409 expect(effectResult).not.toBeNull();
410 });
411
412 it("replaceDefaults overrides the default package list", async () => {
413 const plugin = purePlugin({
414 packages: ["my-lib"],
415 replaceDefaults: true,
416 });
417
418 const effectResult = await callTransform(
419 plugin,
420 `const x = doThing();`,
421 "/proj/node_modules/effect/dist/Effect.js",
422 );
423 expect(effectResult).toBeNull();
424 });
425
426 it("does NOT override moduleSideEffects when the package's sideEffects field is unknown", async () => {
427 // Virtual id — there is no real `package.json` to read, so the
428 // plugin must conservatively leave `moduleSideEffects` alone.
429 const plugin = purePlugin();
430 const result = await callTransform(
431 plugin,
432 `function f() { return 1; }`,
433 "/proj/node_modules/effect/dist/Effect.js",
434 );
435 // No annotatable top-level calls AND no disk-backed sideEffects:false
436 // declaration → plugin returns null (no rewrite, no hint).
437 expect(result).toBeNull();
438 });
439});
440
441describe("explicitly listed user package", { tags: ["unit", "local"] }, () => {
442 it.effect(
443 "overrides moduleSideEffects when a listed package declares sideEffects: false",
444 () =>
445 Effect.gen(function* () {
446 const fs = yield* FileSystem.FileSystem;
447 const path = yield* Path.Path;
448 const root = yield* fs.makeTempDirectory({
449 prefix: "alchemy-pure-listed-sef-",
450 });
451 yield* fs.writeFileString(
452 path.join(root, "package.json"),
453 JSON.stringify({
454 name: "my-pure-app",
455 type: "module",
456 sideEffects: false,
457 }),
458 );
459
460 const plugin = purePlugin({ packages: ["my-pure-app"] });
461
462 const result = yield* Effect.promise(() =>
463 callTransform(
464 plugin,
465 `export const v = make();\nfunction make() { return 1; }`,
466 path.join(root, "lib.ts"),
467 ),
468 );
469 expect(result).not.toBeNull();
470 expect((result as TransformOutput).moduleSideEffects).toBe(false);
471
472 yield* fs.remove(root, { recursive: true });
473 }).pipe(Effect.provide(NodeServices.layer)),
474 );
475
476 it.effect(
477 "returns a metadata-only result (no code) for side-effect-free modules with nothing to annotate",
478 () =>
479 Effect.gen(function* () {
480 const fs = yield* FileSystem.FileSystem;
481 const path = yield* Path.Path;
482 const root = yield* fs.makeTempDirectory({
483 prefix: "alchemy-pure-metaonly-",
484 });
485 yield* fs.writeFileString(
486 path.join(root, "package.json"),
487 JSON.stringify({
488 name: "my-pure-app",
489 type: "module",
490 sideEffects: false,
491 }),
492 );
493
494 const plugin = purePlugin({ packages: ["my-pure-app"] });
495
496 // Only function declarations — nothing to annotate. The plugin
497 // must set moduleSideEffects WITHOUT returning `code` (returning
498 // untransformed code with no map is what caused rolldown's
499 // SOURCEMAP_BROKEN warning).
500 const result = yield* Effect.promise(() =>
501 callTransform(
502 plugin,
503 `export function f() { return 1; }`,
504 path.join(root, "lib.ts"),
505 ),
506 );
507 expect(result).not.toBeNull();
508 expect((result as TransformOutput).code).toBeUndefined();
509 expect((result as TransformOutput).moduleSideEffects).toBe(false);
510
511 yield* fs.remove(root, { recursive: true });
512 }).pipe(Effect.provide(NodeServices.layer)),
513 );
514
515 it.effect(
516 "does NOT mark entries as moduleSideEffects: false, even in matched packages",
517 () =>
518 Effect.gen(function* () {
519 const fs = yield* FileSystem.FileSystem;
520 const path = yield* Path.Path;
521 const root = yield* fs.makeTempDirectory({
522 prefix: "alchemy-pure-entry-preserve-",
523 });
524 yield* fs.writeFileString(
525 path.join(root, "package.json"),
526 JSON.stringify({
527 name: "my-app",
528 type: "module",
529 sideEffects: false,
530 }),
531 );
532 const entryPath = path.join(root, "entry.ts");
533
534 const plugin = purePlugin({ packages: ["my-app"] });
535 yield* Effect.promise(() =>
536 callOptions(plugin, { input: entryPath, cwd: root }),
537 );
538
539 // A bound call so the transform produces a non-null result — a
540 // discarded-only entry returns null and would skip the assertion.
541 const entryResult = yield* Effect.promise(() =>
542 callTransform(
543 plugin,
544 `const x = make();\nconsole.log(x);`,
545 entryPath,
546 ),
547 );
548 // The entry's moduleSideEffects flag must NOT be false regardless
549 // of what its package declares.
550 expect(entryResult).not.toBeNull();
551 expect((entryResult as TransformOutput).moduleSideEffects).not.toBe(
552 false,
553 );
554
555 yield* fs.remove(root, { recursive: true });
556 }).pipe(Effect.provide(NodeServices.layer)),
557 );
558});
559
560describe(
561 "discarded-result calls (issue #949)",
562 { tags: ["unit", "local"] },
563 () => {
564 it("does NOT annotate entry-module expression-statement calls (route registrations)", async () => {
565 const root = nodeFs.mkdtempSync(
566 nodePath.join(os.tmpdir(), "alchemy-pure-949-entry-"),
567 );
568 try {
569 nodeFs.writeFileSync(
570 nodePath.join(root, "package.json"),
571 JSON.stringify({ name: "my-app", type: "module" }),
572 );
573 const entryPath = nodePath.join(root, "entry.ts");
574 const plugin = purePlugin({ packages: ["my-app"] });
575 await callOptions(plugin, { input: entryPath, cwd: root });
576
577 const code = [
578 `const app = new Hono();`,
579 `app.get("/", (c) => c.json({ code: "OK" }));`,
580 `export default { fetch: app.fetch };`,
581 ].join("\n");
582 const result = await callTransform(plugin, code, entryPath);
583 // `new Hono()` (bound to `app`) may be annotated; the discarded
584 // `app.get(...)` registration must NOT be.
585 if (result !== null) {
586 expect(codeOf(result)).not.toContain("/*#__PURE__*/ app.get");
587 }
588 } finally {
589 nodeFs.rmSync(root, { recursive: true, force: true });
590 }
591 });
592
593 it("does NOT annotate expression-statement calls in packages without an explicit sideEffects opt-in", async () => {
594 const root = nodeFs.mkdtempSync(
595 nodePath.join(os.tmpdir(), "alchemy-pure-949-noopt-"),
596 );
597 try {
598 // No `sideEffects` field — the author never claimed purity.
599 nodeFs.writeFileSync(
600 nodePath.join(root, "package.json"),
601 JSON.stringify({ name: "my-app", type: "module" }),
602 );
603 const plugin = purePlugin({ packages: ["my-app"] });
604
605 // A NON-entry module of the user's package, imported for its side
606 // effects (`import "./routes.ts"`).
607 const code = [
608 `import { app } from "./app.ts";`,
609 `app.get("/r", () => "ok");`,
610 `export const x = makeX();`,
611 ].join("\n");
612 const result = await callTransform(
613 plugin,
614 code,
615 nodePath.join(root, "routes.ts"),
616 );
617 expect(result).not.toBeNull();
618 const out = codeOf(result);
619 // Bound calls stay tree-shakeable...
620 expect(out).toContain("/*#__PURE__*/ makeX()");
621 // ...but the discarded-result registration is preserved.
622 expect(out).not.toContain("/*#__PURE__*/ app.get");
623 } finally {
624 nodeFs.rmSync(root, { recursive: true, force: true });
625 }
626 });
627
628 it("leaves the entry app's package completely untouched when it is not listed, even with sideEffects: false", async () => {
629 const root = nodeFs.mkdtempSync(
630 nodePath.join(os.tmpdir(), "alchemy-pure-949-unlisted-"),
631 );
632 try {
633 // The app declares `sideEffects: false` (commonly cargo-culted for
634 // downstream module pruning). Without an explicit `packages` entry
635 // this must not opt the app into any annotation — deleting its own
636 // top-level registrations under minification least of all.
637 nodeFs.writeFileSync(
638 nodePath.join(root, "package.json"),
639 JSON.stringify({
640 name: "my-app",
641 type: "module",
642 sideEffects: false,
643 }),
644 );
645 const plugin = purePlugin();
646 await callOptions(plugin, {
647 input: nodePath.join(root, "entry.ts"),
648 cwd: root,
649 });
650
651 // A NON-entry module of the app (the entry-module guard does not
652 // apply here) with a discarded-result route registration.
653 const code = [
654 `import { app } from "./app.ts";`,
655 `app.get("/r", () => "ok");`,
656 `export const x = makeX();`,
657 ].join("\n");
658 const result = await callTransform(
659 plugin,
660 code,
661 nodePath.join(root, "routes.ts"),
662 );
663 expect(result).toBeNull();
664 } finally {
665 nodeFs.rmSync(root, { recursive: true, force: true });
666 }
667 });
668
669 it("leaves the entry app's package untouched when it is not listed and declares no sideEffects field", async () => {
670 const root = nodeFs.mkdtempSync(
671 nodePath.join(os.tmpdir(), "alchemy-pure-949-unlisted-nosef-"),
672 );
673 try {
674 nodeFs.writeFileSync(
675 nodePath.join(root, "package.json"),
676 JSON.stringify({ name: "my-app", type: "module" }),
677 );
678 const plugin = purePlugin();
679 await callOptions(plugin, {
680 input: nodePath.join(root, "entry.ts"),
681 cwd: root,
682 });
683
684 const result = await callTransform(
685 plugin,
686 `import { app } from "./app.ts";\napp.get("/r", () => "ok");\nexport const x = makeX();`,
687 nodePath.join(root, "routes.ts"),
688 );
689 expect(result).toBeNull();
690 } finally {
691 nodeFs.rmSync(root, { recursive: true, force: true });
692 }
693 });
694
695 it("STILL annotates expression-statement calls in packages that declare sideEffects: false", async () => {
696 // effect ships `sideEffects: []` on purpose — full annotation there is
697 // intentional and must be preserved. Use a fake on-disk package to
698 // avoid depending on the real effect layout.
699 const root = nodeFs.mkdtempSync(
700 nodePath.join(os.tmpdir(), "alchemy-pure-949-optin-"),
701 );
702 try {
703 const pkgDir = nodePath.join(root, "node_modules", "fake-effect");
704 nodeFs.mkdirSync(pkgDir, { recursive: true });
705 nodeFs.writeFileSync(
706 nodePath.join(pkgDir, "package.json"),
707 JSON.stringify({ name: "fake-effect", sideEffects: false }),
708 );
709 const optIn = purePlugin({
710 packages: ["fake-effect"],
711 replaceDefaults: true,
712 });
713 const result = await callTransform(
714 optIn,
715 `registerGlobal();`,
716 nodePath.join(pkgDir, "dist", "index.js"),
717 );
718 expect(result).not.toBeNull();
719 expect(codeOf(result)).toContain("/*#__PURE__*/ registerGlobal()");
720 } finally {
721 nodeFs.rmSync(root, { recursive: true, force: true });
722 }
723 });
724
725 it.effect(
726 "route registrations in the entry survive minify: true (end-to-end)",
727 () =>
728 Effect.gen(function* () {
729 const fs = yield* FileSystem.FileSystem;
730 const path = yield* Path.Path;
731 const root = yield* fs.makeTempDirectory({
732 prefix: "alchemy-pure-949-e2e-",
733 });
734
735 // A user app package with NO sideEffects declaration — the exact
736 // shape from the issue (Hono-style discarded-return registration).
737 yield* fs.writeFileString(
738 path.join(root, "package.json"),
739 JSON.stringify({ name: "my-app", type: "module" }),
740 );
741 const entry = path.join(root, "entry.ts");
742 yield* fs.writeFileString(
743 entry,
744 [
745 `import { app } from "./app.ts";`,
746 `import "./routes.ts";`,
747 `app.get("/", () => "ENTRY_ROUTE_MARKER");`,
748 `export default { fetch: (p: string) => app.handle(p) };`,
749 ].join("\n"),
750 );
751 yield* fs.writeFileString(
752 path.join(root, "app.ts"),
753 [
754 `type Handler = () => string;`,
755 `class Router {`,
756 ` routes: Record<string, Handler> = {};`,
757 ` get(p: string, h: Handler) { this.routes[p] = h; }`,
758 ` handle(p: string) { return this.routes[p]?.() ?? "404"; }`,
759 `}`,
760 `export const app = new Router();`,
761 ].join("\n"),
762 );
763 // Side-effect-imported module registering more routes — the same
764 // failure class beyond the entry file itself.
765 yield* fs.writeFileString(
766 path.join(root, "routes.ts"),
767 [
768 `import { app } from "./app.ts";`,
769 `app.get("/side", () => "SIDE_EFFECT_ROUTE_MARKER");`,
770 ].join("\n"),
771 );
772
773 const bundle = yield* Effect.tryPromise({
774 try: () =>
775 rolldown({
776 input: entry,
777 cwd: root,
778 plugins: [purePlugin()],
779 treeshake: true,
780 }),
781 catch: (cause) => cause,
782 });
783 const { output } = yield* Effect.tryPromise({
784 try: () => bundle.generate({ format: "esm", minify: true }),
785 catch: (cause) => cause,
786 });
787 yield* Effect.tryPromise({
788 try: () => bundle.close(),
789 catch: (cause) => cause,
790 });
791
792 const code = output
793 .filter((c) => c.type === "chunk")
794 .map((c) => c.code)
795 .join("\n");
796 expect(code).toContain("ENTRY_ROUTE_MARKER");
797 expect(code).toContain("SIDE_EFFECT_ROUTE_MARKER");
798
799 yield* fs.remove(root, { recursive: true });
800 }).pipe(Effect.provide(NodeServices.layer)),
801 );
802
803 it.effect(
804 "registrations in a REACHABLE module of an unlisted sideEffects:false app survive minify: true (issue #1020 end-to-end)",
805 () =>
806 Effect.gen(function* () {
807 const fs = yield* FileSystem.FileSystem;
808 const path = yield* Path.Path;
809 const root = yield* fs.makeTempDirectory({
810 prefix: "alchemy-pure-1020-e2e-",
811 });
812
813 // The exact production shape from issue #1020: the app declares
814 // `sideEffects: false`, and route registrations live in a
815 // non-entry module whose EXPORT is used — so the module is
816 // reachable (rolldown's native package-sideEffects pruning does
817 // not apply) and only statement deletion could lose the routes.
818 // Note a side-effect-ONLY import (`import "./routes.ts"`) under
819 // `sideEffects: false` is legitimately pruned by rolldown itself;
820 // that is the field's documented module-level meaning and out of
821 // this plugin's hands.
822 yield* fs.writeFileString(
823 path.join(root, "package.json"),
824 JSON.stringify({
825 name: "my-app",
826 type: "module",
827 sideEffects: false,
828 }),
829 );
830 const entry = path.join(root, "entry.ts");
831 yield* fs.writeFileString(
832 entry,
833 [
834 `import { findRoute } from "./routes.ts";`,
835 `export default { fetch: (p: string) => findRoute(p) };`,
836 ].join("\n"),
837 );
838 yield* fs.writeFileString(
839 path.join(root, "routes.ts"),
840 [
841 `type Handler = () => string;`,
842 `class Router {`,
843 ` routes: Record<string, Handler> = {};`,
844 ` on(p: string, h: Handler) { this.routes[p] = h; }`,
845 ` find(p: string) { return this.routes[p]?.() ?? "404"; }`,
846 `}`,
847 `const matcher = new Router();`,
848 `matcher.on("/gw", () => "GATEWAY_ROUTE_MARKER");`,
849 `export const findRoute = (p: string) => matcher.find(p);`,
850 ].join("\n"),
851 );
852
853 const bundle = yield* Effect.tryPromise({
854 try: () =>
855 rolldown({
856 input: entry,
857 cwd: root,
858 plugins: [purePlugin()],
859 treeshake: true,
860 }),
861 catch: (cause) => cause,
862 });
863 const { output } = yield* Effect.tryPromise({
864 try: () => bundle.generate({ format: "esm", minify: true }),
865 catch: (cause) => cause,
866 });
867 yield* Effect.tryPromise({
868 try: () => bundle.close(),
869 catch: (cause) => cause,
870 });
871
872 const code = output
873 .filter((c) => c.type === "chunk")
874 .map((c) => c.code)
875 .join("\n");
876 expect(code).toContain("GATEWAY_ROUTE_MARKER");
877
878 yield* fs.remove(root, { recursive: true });
879 }).pipe(Effect.provide(NodeServices.layer)),
880 );
881 },
882);
883
884describe("Bundle.build with purePlugin", { tags: ["unit", "local"] }, () => {
885 it.effect(
886 "drops unused exports from a workspace-linked TS package (no node_modules)",
887 () =>
888 Effect.gen(function* () {
889 const fs = yield* FileSystem.FileSystem;
890 const path = yield* Path.Path;
891 const root = yield* fs.makeTempDirectory({
892 prefix: "alchemy-pure-plugin-ts-",
893 });
894
895 // Mimic a monorepo workspace symlink: the package lives at
896 // packages/fake-ws/, with `src/index.ts` directly importable.
897 const pkgDir = path.join(root, "packages", "fake-ws");
898 yield* fs.makeDirectory(path.join(pkgDir, "src"), { recursive: true });
899 yield* fs.writeFileString(
900 path.join(pkgDir, "package.json"),
901 JSON.stringify({
902 name: "fake-ws",
903 version: "0.0.0",
904 type: "module",
905 sideEffects: false,
906 exports: { ".": "./src/index.ts" },
907 }),
908 );
909 yield* fs.writeFileString(
910 path.join(pkgDir, "src", "index.ts"),
911 [
912 `export const used: string = makeUsed();`,
913 `export const unused: string = makeUnused();`,
914 `function makeUsed(): string { return "USED_TS_MARKER"; }`,
915 `function makeUnused(): string { return "UNUSED_TS_MARKER"; }`,
916 ].join("\n"),
917 );
918
919 // Symlink the package under node_modules so rolldown can find it.
920 // Windows requires elevation for "dir" symlinks; junctions don't.
921 const nm = path.join(root, "node_modules");
922 yield* fs.makeDirectory(nm, { recursive: true });
923 nodeFs.symlinkSync(
924 pkgDir,
925 path.join(nm, "fake-ws"),
926 process.platform === "win32" ? "junction" : "dir",
927 );
928
929 const entry = path.join(root, "entry.ts");
930 yield* fs.writeFileString(
931 entry,
932 `import { used } from "fake-ws";\nconsole.log(used);`,
933 );
934
935 const result = yield* Effect.tryPromise({
936 try: () =>
937 rolldown({
938 input: entry,
939 cwd: root,
940 plugins: [
941 purePlugin({
942 packages: ["fake-ws"],
943 replaceDefaults: true,
944 }),
945 ],
946 treeshake: true,
947 }),
948 catch: (cause) => cause,
949 });
950 const { output } = yield* Effect.tryPromise({
951 try: () => result.generate({ format: "esm" }),
952 catch: (cause) => cause,
953 });
954 yield* Effect.tryPromise({
955 try: () => result.close(),
956 catch: (cause) => cause,
957 });
958
959 const code = output
960 .filter((c) => c.type === "chunk")
961 .map((c) => c.code)
962 .join("\n");
963 expect(code).toContain("USED_TS_MARKER");
964 expect(code).not.toContain("UNUSED_TS_MARKER");
965
966 yield* fs.remove(root, { recursive: true });
967 }).pipe(Effect.provide(NodeServices.layer)),
968 );
969
970 it.effect("drops unused exports from a side-effect-free fake package", () =>
971 Effect.gen(function* () {
972 const fs = yield* FileSystem.FileSystem;
973 const path = yield* Path.Path;
974 const root = yield* fs.makeTempDirectory({
975 prefix: "alchemy-pure-plugin-",
976 });
977
978 const fakePkgDir = path.join(root, "node_modules", "fake-effect");
979 yield* fs.makeDirectory(fakePkgDir, { recursive: true });
980 yield* fs.writeFileString(
981 path.join(fakePkgDir, "package.json"),
982 JSON.stringify({
983 name: "fake-effect",
984 version: "0.0.0",
985 type: "module",
986 main: "./index.js",
987 exports: { ".": "./index.js" },
988 }),
989 );
990 // Module-top-level call without annotation: bundlers normally
991 // assume this could have side-effects and keep it. With pure
992 // annotations + sideEffects:false we expect it dropped.
993 yield* fs.writeFileString(
994 path.join(fakePkgDir, "index.js"),
995 [
996 `export const used = makeUsed();`,
997 `export const unused = makeUnused();`,
998 `function makeUsed() { return "USED_MARKER"; }`,
999 `function makeUnused() { return "UNUSED_MARKER"; }`,
1000 ].join("\n"),
1001 );
1002
1003 const entry = path.join(root, "entry.js");
1004 yield* fs.writeFileString(
1005 entry,
1006 `import { used } from "fake-effect"; console.log(used);`,
1007 );
1008
1009 const result = yield* Effect.tryPromise({
1010 try: () =>
1011 rolldown({
1012 input: entry,
1013 cwd: root,
1014 plugins: [
1015 purePlugin({
1016 packages: ["fake-effect"],
1017 replaceDefaults: true,
1018 }),
1019 ],
1020 treeshake: true,
1021 }),
1022 catch: (cause) => cause,
1023 });
1024 const { output } = yield* Effect.tryPromise({
1025 try: () => result.generate({ format: "esm" }),
1026 catch: (cause) => cause,
1027 });
1028 yield* Effect.tryPromise({
1029 try: () => result.close(),
1030 catch: (cause) => cause,
1031 });
1032
1033 const code = output
1034 .filter((c) => c.type === "chunk")
1035 .map((c) => c.code)
1036 .join("\n");
1037 expect(code).toContain("USED_MARKER");
1038 expect(code).not.toContain("UNUSED_MARKER");
1039
1040 yield* fs.remove(root, { recursive: true });
1041 }).pipe(Effect.provide(NodeServices.layer)),
1042 );
1043
1044 it.effect("emits valid sourcemaps with no SOURCEMAP_BROKEN warning", () =>
1045 Effect.gen(function* () {
1046 const fs = yield* FileSystem.FileSystem;
1047 const path = yield* Path.Path;
1048 const root = yield* fs.makeTempDirectory({
1049 prefix: "alchemy-pure-sourcemap-",
1050 });
1051
1052 const fakePkgDir = path.join(root, "node_modules", "fake-effect");
1053 yield* fs.makeDirectory(fakePkgDir, { recursive: true });
1054 yield* fs.writeFileString(
1055 path.join(fakePkgDir, "package.json"),
1056 JSON.stringify({
1057 name: "fake-effect",
1058 version: "0.0.0",
1059 type: "module",
1060 sideEffects: false,
1061 exports: { ".": "./index.js", "./decls": "./decls.js" },
1062 }),
1063 );
1064 // One module with annotatable top-level calls (transformed) and
1065 // one with only declarations (metadata-only result). Under the
1066 // old implementation the latter returned untransformed code with
1067 // no map, flooding the build with SOURCEMAP_BROKEN warnings.
1068 yield* fs.writeFileString(
1069 path.join(fakePkgDir, "index.js"),
1070 [
1071 `export * from "./decls.js";`,
1072 `export const used = makeUsed();`,
1073 `function makeUsed() { return "USED_MARKER"; }`,
1074 ].join("\n"),
1075 );
1076 yield* fs.writeFileString(
1077 path.join(fakePkgDir, "decls.js"),
1078 `export function helper() { return "HELPER_MARKER"; }`,
1079 );
1080
1081 const entry = path.join(root, "entry.js");
1082 yield* fs.writeFileString(
1083 entry,
1084 [
1085 `import { used, helper } from "fake-effect";`,
1086 `console.log(used, helper());`,
1087 ].join("\n"),
1088 );
1089
1090 const logs: RolldownLog[] = [];
1091 const bundle = yield* Effect.tryPromise({
1092 try: () =>
1093 rolldown({
1094 input: entry,
1095 cwd: root,
1096 plugins: [
1097 purePlugin({
1098 packages: ["fake-effect"],
1099 replaceDefaults: true,
1100 }),
1101 ],
1102 treeshake: true,
1103 onLog: (_level, log) => {
1104 logs.push(log);
1105 },
1106 }),
1107 catch: (cause) => cause,
1108 });
1109 const { output } = yield* Effect.tryPromise({
1110 try: () => bundle.generate({ format: "esm", sourcemap: true }),
1111 catch: (cause) => cause,
1112 });
1113 yield* Effect.tryPromise({
1114 try: () => bundle.close(),
1115 catch: (cause) => cause,
1116 });
1117
1118 expect(logs.map((log) => log.code)).not.toContain("SOURCEMAP_BROKEN");
1119
1120 const chunk = output.find((c) => c.type === "chunk");
1121 expect(chunk).toBeDefined();
1122 expect(chunk!.map).toBeTruthy();
1123 expect(chunk!.map!.mappings.length).toBeGreaterThan(0);
1124
1125 yield* fs.remove(root, { recursive: true });
1126 }).pipe(Effect.provide(NodeServices.layer)),
1127 );
1128});
1129
1130// Touch Bundle to ensure tree-shaking re-export ordering during type-check.
1131void Bundle;