Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/four-planes-attend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"wrangler": patch
---

Fix source phase imports in bundled and non-bundled Workers

Wrangler now preserves `import source` syntax when it runs esbuild, including module format detection and bundled deploy output. This fixes both `--no-bundle` and bundled deployments for Workers that import WebAssembly using source phase imports.
12 changes: 12 additions & 0 deletions packages/wrangler/src/__tests__/deploy/build.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1153,6 +1153,18 @@ describe("deploy", () => {
await runWrangler("deploy index.js --dry-run --outdir dist");
expect(fs.readFileSync("dist/index.js", "utf-8")).toMatch(scriptContent);
});

it("should preserve source phase imports without error", async ({
expect,
}) => {
writeWranglerConfig();
const scriptContent = `import source mod from './mod.wasm';
export default { fetch() { return new Response(mod); } };`;
fs.writeFileSync("index.js", scriptContent);
fs.writeFileSync("mod.wasm", "");
await runWrangler("deploy index.js --no-bundle --dry-run --outdir dist");
expect(fs.readFileSync("dist/index.js", "utf-8")).toMatch(scriptContent);
});
});
describe("--no-bundle --minify", () => {
it("should warn that no-bundle and minify can't be used together", async ({
Expand Down
96 changes: 96 additions & 0 deletions packages/wrangler/src/__tests__/deploy/formats.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -931,6 +931,52 @@ export default{
}
`);
});

it("should copy source phase wasm imports to --outdir if specified", async ({
expect,
}) => {
writeWranglerConfig();
fs.writeFileSync(
"./index.js",
`
import txt from './textfile.txt';
import source hello from './hello.wasm';
export default {
async fetch() {
return new Response(txt + hello);
}
}
`
);
fs.writeFileSync("./textfile.txt", "Hello, World!");
fs.writeFileSync("./hello.wasm", "Hello wasm World!");
mockSubDomainRequest();
mockUploadWorkerRequest({
expectedModules: {
"./0a0a9f2a6772942557ab5355d76af442f8f65e01-textfile.txt":
"Hello, World!",
"./d025a03cd31e98e96fb5bd5bce87f9bca4e8ce2c-hello.wasm":
"Hello wasm World!",
},
});
await runWrangler("deploy index.js --outdir some-dir");

expect(fs.existsSync("some-dir/index.js")).toBe(true);
expect(fs.existsSync("some-dir/index.js.map")).toBe(true);
expect(fs.readFileSync("some-dir/index.js", "utf8")).toContain(
'import source hello from "./d025a03cd31e98e96fb5bd5bce87f9bca4e8ce2c-hello.wasm";'
);
expect(
fs.existsSync(
"some-dir/0a0a9f2a6772942557ab5355d76af442f8f65e01-textfile.txt"
)
).toBe(true);
expect(
fs.existsSync(
"some-dir/d025a03cd31e98e96fb5bd5bce87f9bca4e8ce2c-hello.wasm"
)
).toBe(true);
});
});
describe("--outfile", () => {
it("should generate worker bundle at --outfile if specified", async ({
Expand Down Expand Up @@ -1056,6 +1102,56 @@ export default{
`);
});

it("should include source phase wasm imports in the worker bundle", async ({
expect,
}) => {
writeWranglerConfig();
fs.writeFileSync(
"./index.js",
`
import txt from './textfile.txt';
import source hello from './hello.wasm';
export default {
async fetch() {
return new Response(txt + hello);
}
}
`
);
fs.writeFileSync("./textfile.txt", "Hello, World!");
fs.writeFileSync("./hello.wasm", "Hello wasm World!");
mockSubDomainRequest();
mockUploadWorkerRequest({
expectedModules: {
"./0a0a9f2a6772942557ab5355d76af442f8f65e01-textfile.txt":
"Hello, World!",
"./d025a03cd31e98e96fb5bd5bce87f9bca4e8ce2c-hello.wasm":
"Hello wasm World!",
},
});
await runWrangler("deploy index.js --outfile some-dir/worker.bundle");

expect(fs.existsSync("some-dir/worker.bundle")).toBe(true);
expect(
fs
.readFileSync("some-dir/worker.bundle", "utf8")
.replace(
/------formdata-undici-0.[0-9]*/g,
"------formdata-undici-0.test"
)
.replace(/wrangler_(.+?)_default/g, "wrangler_default")
).toContain(
dedent`
// index.js
import txt from "./0a0a9f2a6772942557ab5355d76af442f8f65e01-textfile.txt";
import source hello from "./d025a03cd31e98e96fb5bd5bce87f9bca4e8ce2c-hello.wasm";
`
);
expect(fs.readFileSync("some-dir/worker.bundle", "utf8")).toContain(
'Content-Disposition: form-data; name="./d025a03cd31e98e96fb5bd5bce87f9bca4e8ce2c-hello.wasm"; filename="./d025a03cd31e98e96fb5bd5bce87f9bca4e8ce2c-hello.wasm"'
);
});

it("should include bindings in the worker bundle", async ({ expect }) => {
writeWranglerConfig({
kv_namespaces: [{ binding: "KV", id: "kv-namespace-id" }],
Expand Down
17 changes: 17 additions & 0 deletions packages/wrangler/src/__tests__/guess-worker-format.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,21 @@ describe("guess worker format", () => {
);
expect(guess.exports).toStrictEqual(["Hello", "default"]);
});

it("should detect a modules worker that uses source phase imports", async ({
expect,
}) => {
await writeFile("./mod.wasm", "");
await writeFile(
"./index.js",
`import source mod from './mod.wasm';
export default { fetch() { return new Response(mod); } };`
);
const guess = await guessWorkerFormat(
path.join(process.cwd(), "./index.js"),
process.cwd(),
undefined
);
expect(guess.format).toBe("modules");
});
});
2 changes: 2 additions & 0 deletions packages/wrangler/src/deployment-bundle/bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export const COMMON_ESBUILD_OPTIONS = {
// v8 supports es2024 features as of 11.9
// workerd uses [v8 version 14.2 as of 2025-10-17](https://developers.cloudflare.com/workers/platform/changelog/#2025-10-17)
target: "es2024",
supported: { "import-source": true },
loader: { ".js": "jsx", ".mjs": "jsx", ".cjs": "jsx" },
} as const;

Expand Down Expand Up @@ -384,6 +385,7 @@ export async function bundleWorker(
: undefined,
format: entry.format === "modules" ? "esm" : "iife",
target: COMMON_ESBUILD_OPTIONS.target,
supported: COMMON_ESBUILD_OPTIONS.supported,
sourcemap: sourcemap ?? true,
// Include a reference to the output folder in the sourcemap.
// This is omitted by default, but we need it to properly resolve source paths in error output.
Expand Down
Loading