Skip to content

Commit 1c46028

Browse files
Avoid blocking built-in node modules when using --no-bundle flag (#4303)
* Avoid blocking built-in node modules when using --no-bundle flag * fixup! Avoid blocking built-in node modules when using --no-bundle flag * fixup! Avoid blocking built-in node modules when using --no-bundle flag --------- Co-authored-by: Peter Bacon Darwin <[email protected]>
1 parent 73b20fa commit 1c46028

File tree

5 files changed

+158
-28
lines changed

5 files changed

+158
-28
lines changed

.changeset/brave-years-wave.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"wrangler": patch
3+
---
4+
5+
Allow page functions to import builtin modules, even when not bundling with wrangler

packages/wrangler/src/__tests__/pages/deploy.test.ts

Lines changed: 108 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2868,7 +2868,8 @@ async function onRequest() {
28682868
contents.includes("worker_default as default");
28692869

28702870
const simulateServer = (
2871-
generatedWorkerBundleCheck: (workerJsContent: string) => void
2871+
generatedWorkerBundleCheck: (workerJsContent: string) => void,
2872+
compatibility_flags?: string[]
28722873
) => {
28732874
mockGetUploadTokenRequest(
28742875
"<<funfetti-auth-jwt>>",
@@ -2929,7 +2930,10 @@ async function onRequest() {
29292930
errors: [],
29302931
messages: [],
29312932
result: {
2932-
deployment_configs: { production: {}, preview: {} },
2933+
deployment_configs: {
2934+
production: { compatibility_flags },
2935+
preview: { compatibility_flags },
2936+
},
29332937
},
29342938
})
29352939
)
@@ -2953,6 +2957,108 @@ async function onRequest() {
29532957
expect(std.out).toContain("✨ Uploading Worker bundle");
29542958
});
29552959

2960+
it("should not allow 3rd party imports when not bundling", async () => {
2961+
// Add in a 3rd party import to the bundle
2962+
writeFileSync(
2963+
"public/_worker.js",
2964+
`
2965+
import { Test } from "test-package";
2966+
2967+
export default {
2968+
async fetch() {
2969+
console.log(Test);
2970+
return new Response("Ok");
2971+
},
2972+
};`
2973+
);
2974+
2975+
simulateServer((generatedWorkerJS) =>
2976+
expect(workerIsBundled(generatedWorkerJS)).toBeFalsy()
2977+
);
2978+
let error = "Code did not throw!";
2979+
try {
2980+
await runWrangler("pages deploy public --project-name=foo --no-bundle");
2981+
} catch (e) {
2982+
error = `${e}`;
2983+
}
2984+
expect(error).toContain(
2985+
"ERROR: [plugin: block-worker-js-imports] _worker.js is not being bundled by Wrangler but it is importing from another file."
2986+
);
2987+
});
2988+
2989+
it("should allow `cloudflare:...` imports when not bundling", async () => {
2990+
// Add in a 3rd party import to the bundle
2991+
writeFileSync(
2992+
"public/_worker.js",
2993+
`
2994+
import { EmailMessage } from "cloudflare:email";
2995+
2996+
export default {
2997+
async fetch() {
2998+
console.log("EmailMessage", EmailMessage);
2999+
return new Response("Ok");
3000+
},
3001+
};`
3002+
);
3003+
3004+
simulateServer((generatedWorkerJS) =>
3005+
expect(workerIsBundled(generatedWorkerJS)).toBeFalsy()
3006+
);
3007+
await runWrangler("pages deploy public --project-name=foo --no-bundle");
3008+
expect(std.out).toContain("✨ Uploading Worker bundle");
3009+
});
3010+
3011+
it("should allow `node:...` imports when not bundling and marked with nodejs_compat", async () => {
3012+
// Add in a node built-in import to the bundle
3013+
writeFileSync(
3014+
"public/_worker.js",
3015+
`
3016+
import { Buffer } from "node:buffer";
3017+
3018+
export default {
3019+
async fetch() {
3020+
return new Response(Buffer.from("Ok", "utf8"));
3021+
},
3022+
};`
3023+
);
3024+
3025+
simulateServer(
3026+
(generatedWorkerJS) =>
3027+
expect(workerIsBundled(generatedWorkerJS)).toBeFalsy(),
3028+
["nodejs_compat"]
3029+
);
3030+
await runWrangler("pages deploy public --project-name=foo --no-bundle");
3031+
expect(std.out).toContain("✨ Uploading Worker bundle");
3032+
});
3033+
3034+
it("should not allow `node:...` imports when not bundling and not marked nodejs_compat", async () => {
3035+
// Add in a node built-in import to the bundle
3036+
writeFileSync(
3037+
"public/_worker.js",
3038+
`
3039+
import { Buffer } from "node:buffer";
3040+
3041+
export default {
3042+
async fetch() {
3043+
return new Response(Buffer.from("Ok", "utf8"));
3044+
},
3045+
};`
3046+
);
3047+
3048+
simulateServer((generatedWorkerJS) =>
3049+
expect(workerIsBundled(generatedWorkerJS)).toBeFalsy()
3050+
);
3051+
let error = "Code did not throw!";
3052+
try {
3053+
await runWrangler("pages deploy public --project-name=foo --no-bundle");
3054+
} catch (e) {
3055+
error = `${e}`;
3056+
}
3057+
expect(error).toContain(
3058+
"ERROR: [plugin: block-worker-js-imports] _worker.js is not being bundled by Wrangler but it is importing from another file."
3059+
);
3060+
});
3061+
29563062
it("should not bundle the _worker.js when `--bundle` is set to false", async () => {
29573063
simulateServer((generatedWorkerJS) =>
29583064
expect(workerIsBundled(generatedWorkerJS)).toBeFalsy()

packages/wrangler/src/api/pages/deploy.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ export async function deploy({
142142
const deploymentConfig =
143143
project.deployment_configs[isProduction ? "production" : "preview"];
144144
const nodejsCompat =
145-
deploymentConfig.compatibility_flags?.includes("nodejs_compat");
145+
deploymentConfig.compatibility_flags?.includes("nodejs_compat") ?? false;
146146

147147
const defineNavigatorUserAgent = isNavigatorDefined(
148148
deploymentConfig.compatibility_date,
@@ -280,7 +280,7 @@ export async function deploy({
280280
defineNavigatorUserAgent,
281281
});
282282
} else {
283-
await checkRawWorker(_workerPath, () => {});
283+
await checkRawWorker(_workerPath, nodejsCompat, () => {});
284284
// TODO: Let users configure this in the future.
285285
workerBundle = {
286286
modules: [],

packages/wrangler/src/pages/dev.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ export const Handler = async ({
316316

317317
let scriptPath = "";
318318

319-
const nodejsCompat = compatibilityFlags?.includes("nodejs_compat");
319+
const nodejsCompat = compatibilityFlags?.includes("nodejs_compat") ?? false;
320320
const defineNavigatorUserAgent = isNavigatorDefined(
321321
compatibilityDate,
322322
compatibilityFlags
@@ -354,7 +354,9 @@ export const Handler = async ({
354354
} else if (usingWorkerScript) {
355355
scriptPath = workerScriptPath;
356356
let runBuild = async () => {
357-
await checkRawWorker(workerScriptPath, () => scriptReadyResolve());
357+
await checkRawWorker(workerScriptPath, nodejsCompat, () =>
358+
scriptReadyResolve()
359+
);
358360
};
359361

360362
if (enableBundling) {

packages/wrangler/src/pages/functions/buildWorker.ts

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -351,33 +351,50 @@ export function buildNotifierPlugin(onEnd: () => void): Plugin {
351351
* This is useful when the user chooses not to bundle the `_worker.js` file by setting
352352
* `--no-bundle` at the command line.
353353
*/
354-
export async function checkRawWorker(scriptPath: string, onEnd: () => void) {
354+
export async function checkRawWorker(
355+
scriptPath: string,
356+
nodejsCompat: boolean,
357+
onEnd: () => void
358+
) {
355359
await esBuild({
356360
entryPoints: [scriptPath],
357361
write: false,
358362
// we need it to be bundled so that any imports that are used are affected by the blocker plugin
359363
bundle: true,
360-
plugins: [blockWorkerJsImports, buildNotifierPlugin(onEnd)],
364+
plugins: [blockWorkerJsImports(nodejsCompat), buildNotifierPlugin(onEnd)],
365+
logLevel: "silent",
361366
});
362367
}
363368

364-
const blockWorkerJsImports: Plugin = {
365-
name: "block-worker-js-imports",
366-
setup(build) {
367-
build.onResolve({ filter: /.*/g }, (args) => {
368-
// If it's the entrypoint, let it be as is
369-
if (args.kind === "entry-point") {
370-
return {
371-
path: args.path,
372-
};
373-
}
374-
// Otherwise, block any imports that the file is requesting
375-
throw new FatalError(
376-
"_worker.js is not being bundled by Wrangler but it is importing from another file.\n" +
377-
"This will throw an error if deployed.\n" +
378-
"You should bundle the Worker in a pre-build step, remove the import if it is unused, or ask Wrangler to bundle it by setting `--bundle`.",
379-
1
380-
);
381-
});
382-
},
383-
};
369+
function blockWorkerJsImports(nodejsCompat: boolean): Plugin {
370+
return {
371+
name: "block-worker-js-imports",
372+
setup(build) {
373+
build.onResolve({ filter: /.*/g }, (args) => {
374+
// If it's the entrypoint, let it be as is
375+
if (args.kind === "entry-point") {
376+
return {
377+
path: args.path,
378+
};
379+
}
380+
// If it's a node or cf built-in, mark it as external
381+
if (
382+
(nodejsCompat && args.path.startsWith("node:")) ||
383+
args.path.startsWith("cloudflare:")
384+
) {
385+
return {
386+
path: args.path,
387+
external: true,
388+
};
389+
}
390+
// Otherwise, block any other imports that the file is requesting
391+
throw new FatalError(
392+
"_worker.js is not being bundled by Wrangler but it is importing from another file.\n" +
393+
"This will throw an error if deployed.\n" +
394+
"You should bundle the Worker in a pre-build step, remove the import if it is unused, or ask Wrangler to bundle it by setting `--bundle`.",
395+
1
396+
);
397+
});
398+
},
399+
};
400+
}

0 commit comments

Comments
 (0)