1import { createMiniflareFromRolldown } from "../../../../cloudflare-test-tools/src/miniflare/miniflare.ts";
2import { assert, describe, expect, it } from "vitest";
3import { fileURLToPath } from "node:url";
4import { toPosixPath } from "../utils.ts";
5import cloudflare from "../plugin.ts";
6import { buildFixture } from "./utils/build-fixture.ts";
7
8describe("nodejs_compat", () => {
9 it("rewrites external CommonJS requires when compatibility enables Node by date", () => {
10 const plugins = cloudflare({ compatibilityDate: "2026-08-31" });
11
12 expect(plugins[0]?.name).toBe("builtin:esm-external-require");
13 });
14
15 it("can omit the external require rewrite for single-module internal workers", () => {
16 const plugins = cloudflare({
17 compatibilityDate: "2026-08-31",
18 externalRequire: false,
19 });
20
21 expect(plugins[0]?.name).not.toBe("builtin:esm-external-require");
22 });
23
24 it("runs node builtin imports with nodejs_compat enabled", async () => {
25 const built = await buildFixture({
26 fixture: "node-compat/index.ts",
27 pluginOptions: {
28 compatibilityDate: "2025-07-01",
29 compatibilityFlags: ["nodejs_compat"],
30 },
31 });
32
33 await using miniflare = await createMiniflareFromRolldown(built.output, {
34 compatibilityDate: "2025-07-01",
35 compatibilityFlags: ["nodejs_compat"],
36 });
37
38 expect(await miniflare.fetchText("/")).toBe("OK!");
39 });
40
41 it("creates and injects virtual globals for nodejs_compat entries", async () => {
42 const plugins = cloudflare({
43 compatibilityDate: "2025-07-01",
44 compatibilityFlags: ["nodejs_compat"],
45 }) as Array<{
46 name: string;
47 resolveId?: { handler?: (...args: Array<unknown>) => unknown };
48 load?: { handler?: (...args: Array<unknown>) => unknown };
49 transform?: (code: string, id: string) => string | undefined;
50 buildStart?: (options: { plugins: Array<unknown> }) => void;
51 }>;
52
53 const virtualModulesPlugin = plugins.find(
54 (plugin) => plugin?.name === "distilled-cloudflare:virtual-modules",
55 );
56
57 assert(virtualModulesPlugin, "virtualModulesPlugin is not defined");
58
59 virtualModulesPlugin.buildStart?.({ plugins: plugins.filter(Boolean) });
60
61 const resolved = virtualModulesPlugin.resolveId?.handler?.(
62 "\0distilled:inject:process",
63 );
64 expect(resolved).toEqual({ id: "\0distilled:inject:process" });
65
66 const loaded = virtualModulesPlugin.load?.handler?.(
67 "\0distilled:inject:process",
68 );
69 expect(loaded).toContain("globalThis.process = process;");
70
71 const transformed = virtualModulesPlugin?.load?.handler?.(
72 "\0distilled:worker-entry:test/fixtures/node-compat/index.ts",
73 );
74
75 expect(transformed).toContain(
76 `import "${toPosixPath(fileURLToPath(import.meta.resolve("@cloudflare/unenv-preset/polyfill/performance")))}";`,
77 );
78 expect(transformed).toContain('import "\0distilled:inject:process";');
79 });
80
81 it("warns when unresolved Node builtins are imported without nodejs_compat", async () => {
82 const plugins = cloudflare({
83 compatibilityDate: "2025-07-01",
84 });
85
86 const nodejsImportWarningPlugin = plugins.find(
87 (plugin) => plugin?.name === "distilled-cloudflare:nodejs-import-warning",
88 ) as unknown as {
89 buildStart: () => void;
90 resolveId: { handler: (...args: Array<any>) => unknown };
91 buildEnd: () => void;
92 };
93
94 const warnings: Array<string> = [];
95 nodejsImportWarningPlugin?.buildStart?.call({});
96
97 await nodejsImportWarningPlugin?.resolveId?.handler?.call(
98 {},
99 "node:fs",
100 "test/fixtures/example-a.ts",
101 {},
102 );
103 await nodejsImportWarningPlugin?.resolveId?.handler?.call(
104 {},
105 "node:fs",
106 "test/fixtures/example-b.ts",
107 {},
108 );
109
110 nodejsImportWarningPlugin?.buildEnd?.call({
111 warn(message: string) {
112 warnings.push(message);
113 },
114 getModuleInfo() {
115 return {};
116 },
117 });
118
119 expect(warnings).toHaveLength(1);
120 expect(warnings[0]).toContain("Unexpected Node.js imports.");
121 expect(warnings[0]).toContain(
122 'Do you need to enable the "nodejs_compat" compatibility flag?',
123 );
124 expect(warnings[0]).toContain(
125 "https://developers.cloudflare.com/workers/runtime-apis/nodejs/",
126 );
127 expect(warnings[0]).toContain(
128 '- "node:fs" imported from "test/fixtures/example-a.ts"',
129 );
130 expect(warnings[0]).toContain(
131 '- "node:fs" imported from "test/fixtures/example-b.ts"',
132 );
133 });
134});