Skip to content

Commit 1966c9d

Browse files
committed
WIP: proper no-bundle in Pages
1 parent 0a77990 commit 1966c9d

File tree

9 files changed

+199
-27
lines changed

9 files changed

+199
-27
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "pages-workerjs-directory",
3+
"version": "0.0.0",
4+
"private": true,
5+
"sideEffects": false,
6+
"scripts": {
7+
"check:type": "tsc",
8+
"dev": "npx wrangler pages dev public --port 8794",
9+
"test": "npx vitest",
10+
"test:ci": "npx vitest"
11+
},
12+
"devDependencies": {
13+
"undici": "^5.9.1"
14+
},
15+
"engines": {
16+
"node": ">=16.13"
17+
}
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export default {
2+
async fetch(request, env) {
3+
switch (new URL(request.url).pathname) {
4+
case "/worker": {
5+
return new Response(require("./other-script").default);
6+
}
7+
}
8+
9+
return env.ASSETS.fetch(request);
10+
},
11+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default "test";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<h1>Hello, world!</h1>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { resolve } from "node:path";
2+
import { fetch } from "undici";
3+
import { describe, it } from "vitest";
4+
import { runWranglerPagesDev } from "../../shared/src/run-wrangler-long-lived";
5+
6+
describe.concurrent("Pages _worker.js/ directory", () => {
7+
it("should support non-bundling with 'dev'", async ({ expect }) => {
8+
const { ip, port, stop } = await runWranglerPagesDev(
9+
resolve(__dirname, ".."),
10+
"public",
11+
["--port=0"]
12+
);
13+
await expect(
14+
fetch(`http://${ip}:${port}/`).then((resp) => resp.text())
15+
).resolves.toContain("Hello, world!");
16+
await expect(
17+
fetch(`http://${ip}:${port}/worker`).then((resp) => resp.text())
18+
).resolves.toContain("test");
19+
await stop();
20+
});
21+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ES2020",
4+
"esModuleInterop": true,
5+
"module": "CommonJS",
6+
"lib": ["ES2020"],
7+
"types": ["node"],
8+
"moduleResolution": "node",
9+
"noEmit": true
10+
},
11+
"include": ["tests", "../../node-types.d.ts"]
12+
}

packages/wrangler/src/__tests__/pages/functions-build.test.ts

+63
Original file line numberDiff line numberDiff line change
@@ -449,4 +449,67 @@ export default {
449449
hello.js:2:36: ERROR: Could not resolve \\"node:async_hooks\\""
450450
`);
451451
});
452+
453+
it("should compile a _worker.js/ directory", async () => {
454+
mkdirSync("public");
455+
mkdirSync("public/_worker.js");
456+
writeFileSync(
457+
"public/_worker.js/index.js",
458+
`
459+
export default {
460+
async fetch(request, env) {
461+
return new Response("Hello from _worker.js/index.js");
462+
},
463+
};`
464+
);
465+
writeFileSync(
466+
"public/_worker.js/cat.js",
467+
`
468+
export const cat = "cat";`
469+
);
470+
471+
await runWrangler(
472+
`pages functions build --outfile=public/_worker.bundle --compatibility-flag=nodejs_compat`
473+
);
474+
475+
expect(existsSync("public/_worker.bundle")).toBe(true);
476+
expect(std.out).toMatchInlineSnapshot(
477+
`"🚧 'wrangler pages <command>' is a beta command. Please report any issues to https://github.com/cloudflare/workers-sdk/issues/new/choose"`
478+
);
479+
480+
const workerBundleContents = readFileSync("public/_worker.bundle", "utf-8");
481+
const workerBundleWithConstantData = replaceRandomWithConstantData(
482+
workerBundleContents,
483+
[
484+
[/------formdata-undici-0.[0-9]*/g, "------formdata-undici-0.test"],
485+
[/functionsWorker-0.[0-9]*.js/g, "functionsWorker-0.test.js"],
486+
]
487+
);
488+
489+
expect(workerBundleWithConstantData).toMatchInlineSnapshot(`
490+
"------formdata-undici-0.test
491+
Content-Disposition: form-data; name=\\"metadata\\"
492+
493+
{\\"main_module\\":\\"index.js\\"}
494+
------formdata-undici-0.test
495+
Content-Disposition: form-data; name=\\"index.js\\"; filename=\\"index.js\\"
496+
Content-Type: application/javascript+module
497+
498+
499+
export default {
500+
async fetch(request, env) {
501+
return new Response(\\"Hello from _worker.js/index.js\\");
502+
},
503+
};
504+
------formdata-undici-0.test
505+
Content-Disposition: form-data; name=\\"cat.js\\"; filename=\\"cat.js\\"
506+
Content-Type: application/javascript+module
507+
508+
509+
export const cat = \\"cat\\";
510+
------formdata-undici-0.test--"
511+
`);
512+
513+
expect(std.err).toMatchInlineSnapshot(`""`);
514+
});
452515
});

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

+30-10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { existsSync, readFileSync } from "node:fs";
1+
import { existsSync, lstatSync, readFileSync } from "node:fs";
22
import { tmpdir } from "node:os";
3-
import { join, resolve as resolvePath } from "node:path";
3+
import { join, resolve, resolve as resolvePath } from "node:path";
44
import { cwd } from "node:process";
55
import { File, FormData } from "undici";
66
import { fetchResult } from "../../cfetch";
@@ -17,6 +17,7 @@ import {
1717
} from "../../pages/functions/buildWorker";
1818
import { validateRoutes } from "../../pages/functions/routes-validation";
1919
import { upload } from "../../pages/upload";
20+
import traverseModuleGraph from "../../traverse-module-graph";
2021
import { createUploadWorkerBundleContents } from "./create-worker-bundle-contents";
2122
import type { BundleResult } from "../../bundle";
2223
import type { Project, Deployment } from "@cloudflare/types";
@@ -95,9 +96,10 @@ export async function publish({
9596
_redirects: string | undefined,
9697
_routesGenerated: string | undefined,
9798
_routesCustom: string | undefined,
99+
_workerJSDirectory = false,
98100
_workerJS: string | undefined;
99101

100-
const workerScriptPath = resolvePath(directory, "_worker.js");
102+
const _workerPath = resolvePath(directory, "_worker.js");
101103

102104
try {
103105
_headers = readFileSync(join(directory, "_headers"), "utf-8");
@@ -116,7 +118,10 @@ export async function publish({
116118
} catch {}
117119

118120
try {
119-
_workerJS = readFileSync(workerScriptPath, "utf-8");
121+
_workerJSDirectory = lstatSync(_workerPath).isDirectory();
122+
if (!_workerJSDirectory) {
123+
_workerJS = readFileSync(_workerPath, "utf-8");
124+
}
120125
} catch {}
121126

122127
// Grab the bindings from the API, we need these for shims and other such hacky inserts
@@ -243,13 +248,28 @@ export async function publish({
243248
* When using a _worker.js file, the entire /functions directory is ignored
244249
* – this includes its routing and middleware characteristics.
245250
*/
246-
if (_workerJS) {
251+
if (_workerJSDirectory) {
252+
workerBundle = await traverseModuleGraph(
253+
{
254+
file: resolve(join(_workerPath, "index.js")),
255+
directory: resolve(_workerPath),
256+
format: "modules",
257+
moduleRoot: resolve(_workerPath),
258+
},
259+
[
260+
{
261+
type: "ESModule",
262+
globs: ["**/*.js"],
263+
},
264+
]
265+
);
266+
} else if (_workerJS) {
247267
if (bundle) {
248268
const outfile = join(tmpdir(), `./bundledWorker-${Math.random()}.mjs`);
249269
workerBundle = await buildRawWorker({
250-
workerScriptPath,
270+
workerScriptPath: _workerPath,
251271
outfile,
252-
directory: directory ?? ".",
272+
directory,
253273
local: false,
254274
sourcemap: true,
255275
watch: false,
@@ -258,13 +278,13 @@ export async function publish({
258278
nodejsCompat,
259279
});
260280
} else {
261-
await checkRawWorker(workerScriptPath, () => {});
262-
// TODO: Replace this with the cool new no-bundle stuff when that lands: https://github.com/cloudflare/workers-sdk/pull/2769
281+
await checkRawWorker(_workerPath, () => {});
282+
// TODO: Let users configure this in the future.
263283
workerBundle = {
264284
modules: [],
265285
dependencies: {},
266286
stop: undefined,
267-
resolvedEntryPointPath: workerScriptPath,
287+
resolvedEntryPointPath: _workerPath,
268288
bundleType: "esm",
269289
};
270290
}

packages/wrangler/src/pages/build.ts

+42-17
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
1-
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
2-
import { basename, dirname, relative, resolve as resolvePath } from "node:path";
1+
import { existsSync, lstatSync, mkdirSync, writeFileSync } from "node:fs";
2+
import {
3+
basename,
4+
dirname,
5+
join,
6+
relative,
7+
resolve,
8+
resolve as resolvePath,
9+
} from "node:path";
310
import { createUploadWorkerBundleContents } from "../api/pages/create-worker-bundle-contents";
411
import { FatalError } from "../errors";
512
import { logger } from "../logger";
613
import * as metrics from "../metrics";
14+
import traverseModuleGraph from "../traverse-module-graph";
715
import { buildFunctions } from "./buildFunctions";
816
import { isInPagesCI } from "./constants";
917
import {
@@ -208,21 +216,38 @@ export const Handler = async (args: PagesBuildArgs) => {
208216
* and if we were able to resolve _worker.js
209217
*/
210218
if (workerScriptPath) {
211-
/**
212-
* `buildRawWorker` builds `_worker.js`, but doesn't give us the bundle
213-
* we want to return, which includes the external dependencies (like wasm,
214-
* binary, text). Let's output that build result to memory and only write
215-
* to disk once we have the final bundle
216-
*/
217-
bundle = await buildRawWorker({
218-
workerScriptPath,
219-
outdir,
220-
directory: buildOutputDirectory,
221-
local: false,
222-
sourcemap,
223-
watch,
224-
betaD1Shims: d1Databases,
225-
});
219+
if (lstatSync(workerScriptPath).isDirectory()) {
220+
bundle = await traverseModuleGraph(
221+
{
222+
file: resolve(join(workerScriptPath, "index.js")),
223+
directory: resolve(workerScriptPath),
224+
format: "modules",
225+
moduleRoot: resolve(workerScriptPath),
226+
},
227+
[
228+
{
229+
type: "ESModule",
230+
globs: ["**/*.js"],
231+
},
232+
]
233+
);
234+
} else {
235+
/**
236+
* `buildRawWorker` builds `_worker.js`, but doesn't give us the bundle
237+
* we want to return, which includes the external dependencies (like wasm,
238+
* binary, text). Let's output that build result to memory and only write
239+
* to disk once we have the final bundle
240+
*/
241+
bundle = await buildRawWorker({
242+
workerScriptPath,
243+
outdir,
244+
directory: buildOutputDirectory,
245+
local: false,
246+
sourcemap,
247+
watch,
248+
betaD1Shims: d1Databases,
249+
});
250+
}
226251
} else {
227252
try {
228253
/**

0 commit comments

Comments
 (0)