Skip to content

Commit 161a5f7

Browse files
authoredMar 12, 2025··
fix: patch the webpack runtime when there is a single chunk (#452)
1 parent ceed6e4 commit 161a5f7

File tree

3 files changed

+148
-53
lines changed

3 files changed

+148
-53
lines changed
 

‎.changeset/smart-boxes-destroy.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@opennextjs/cloudflare": patch
3+
---
4+
5+
fix: patch the webpack runtime when there is a single chunk
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,113 @@
11
import { describe, expect, test } from "vitest";
22

33
import { patchCode } from "./util.js";
4-
import { buildInlineChunksRule } from "./webpack-runtime.js";
4+
import { buildMultipleChunksRule, singleChunkRule } from "./webpack-runtime.js";
55

66
describe("webpack runtime", () => {
7-
test("patch runtime", () => {
8-
const code = `
9-
/******/ // require() chunk loading for javascript
10-
/******/ __webpack_require__.f.require = (chunkId, promises) => {
11-
/******/ // "1" is the signal for "already loaded"
12-
/******/ if (!installedChunks[chunkId]) {
13-
/******/ if (658 != chunkId) {
14-
/******/ installChunk(require("./chunks/" + __webpack_require__.u(chunkId)));
15-
/******/
16-
} else installedChunks[chunkId] = 1;
17-
/******/
7+
describe("multiple chunks", () => {
8+
test("patch runtime", () => {
9+
const code = `
10+
/******/ // require() chunk loading for javascript
11+
/******/ __webpack_require__.f.require = (chunkId, promises) => {
12+
/******/ // "1" is the signal for "already loaded"
13+
/******/ if (!installedChunks[chunkId]) {
14+
/******/ if (658 != chunkId) {
15+
/******/ installChunk(require("./chunks/" + __webpack_require__.u(chunkId)));
16+
/******/
17+
} else installedChunks[chunkId] = 1;
18+
/******/
19+
}
20+
/******/
21+
};
22+
`;
23+
24+
expect(patchCode(code, buildMultipleChunksRule([1, 2, 3]))).toMatchInlineSnapshot(`
25+
"/******/ // require() chunk loading for javascript
26+
/******/ __webpack_require__.f.require = (chunkId, _) => {
27+
if (!installedChunks[chunkId]) {
28+
switch (chunkId) {
29+
case 1: installChunk(require("./chunks/1.js")); break;
30+
case 2: installChunk(require("./chunks/2.js")); break;
31+
case 3: installChunk(require("./chunks/3.js")); break;
32+
case 658: installedChunks[chunkId] = 1; break;
33+
default: throw new Error(\`Unknown chunk \${chunkId}\`);
1834
}
19-
/******/
20-
};
21-
`;
35+
}
36+
}
37+
;
38+
"
39+
`);
40+
});
2241

23-
expect(patchCode(code, buildInlineChunksRule([1, 2, 3]))).toMatchInlineSnapshot(`
24-
"/******/ // require() chunk loading for javascript
25-
/******/ __webpack_require__.f.require = (chunkId, _) => {
26-
if (!installedChunks[chunkId]) {
27-
switch (chunkId) {
28-
case 1: installChunk(require("./chunks/1.js")); break;
29-
case 2: installChunk(require("./chunks/2.js")); break;
30-
case 3: installChunk(require("./chunks/3.js")); break;
31-
case 658: installedChunks[chunkId] = 1; break;
32-
default: throw new Error(\`Unknown chunk \${chunkId}\`);
42+
test("patch minified runtime", () => {
43+
const code = `
44+
t.f.require=(o,n)=>{e[o]||(658!=o?r(require("./chunks/"+t.u(o))):e[o]=1)}
45+
`;
46+
47+
expect(patchCode(code, buildMultipleChunksRule([1, 2, 3]))).toMatchInlineSnapshot(
48+
`
49+
"t.f.require=(o, _) => {
50+
if (!e[o]) {
51+
switch (o) {
52+
case 1: r(require("./chunks/1.js")); break;
53+
case 2: r(require("./chunks/2.js")); break;
54+
case 3: r(require("./chunks/3.js")); break;
55+
case 658: e[o] = 1; break;
56+
default: throw new Error(\`Unknown chunk \${o}\`);
57+
}
3358
}
3459
}
35-
}
36-
;
37-
"
38-
`);
60+
61+
"
62+
`
63+
);
64+
});
3965
});
4066

41-
test("patch minified runtime", () => {
42-
const code = `
43-
t.f.require=(o,n)=>{e[o]||(658!=o?r(require("./chunks/"+t.u(o))):e[o]=1)}
44-
`;
67+
describe("single chunk", () => {
68+
test("patch runtime", () => {
69+
const code = `
70+
/******/ // require() chunk loading for javascript
71+
/******/ __webpack_require__.f.require = (chunkId, promises) => {
72+
/******/ // "1" is the signal for "already loaded"
73+
/******/ if(!installedChunks[chunkId]) {
74+
/******/ if(710 == chunkId) {
75+
/******/ installChunk(require("./chunks/" + __webpack_require__.u(chunkId)));
76+
/******/ } else installedChunks[chunkId] = 1;
77+
/******/ }
78+
/******/ };
79+
`;
4580

46-
expect(patchCode(code, buildInlineChunksRule([1, 2, 3]))).toMatchInlineSnapshot(
47-
`
48-
"t.f.require=(o, _) => {
49-
if (!e[o]) {
50-
switch (o) {
51-
case 1: r(require("./chunks/1.js")); break;
52-
case 2: r(require("./chunks/2.js")); break;
53-
case 3: r(require("./chunks/3.js")); break;
54-
case 658: e[o] = 1; break;
55-
default: throw new Error(\`Unknown chunk \${o}\`);
81+
expect(patchCode(code, singleChunkRule)).toMatchInlineSnapshot(`
82+
"/******/ // require() chunk loading for javascript
83+
/******/ __webpack_require__.f.require = (chunkId, _) => {
84+
if (!installedChunks[chunkId]) {
85+
try {
86+
installChunk(require("./chunks/710.js"));
87+
} catch {}
88+
}
89+
}
90+
;
91+
"
92+
`);
93+
});
94+
95+
test("patch minified runtime", () => {
96+
const code = `
97+
o.f.require=(t,a)=>{e[t]||(710==t?r(require("./chunks/"+o.u(t))):e[t]=1)}
98+
`;
99+
100+
expect(patchCode(code, singleChunkRule)).toMatchInlineSnapshot(`
101+
"o.f.require=(t, _) => {
102+
if (!e[t]) {
103+
try {
104+
r(require("./chunks/710.js"));
105+
} catch {}
56106
}
57107
}
58-
}
59108
60-
"
61-
`
62-
);
109+
"
110+
`);
111+
});
63112
});
64113
});

‎packages/cloudflare/src/cli/build/patches/ast/webpack-runtime.ts

+47-6
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,28 @@
77
*
88
* This patch unrolls the dynamic require for all the existing chunks:
99
*
10+
* For multiple chunks:
1011
* switch (chunkId) {
1112
* case ID1: installChunk(require("./chunks/ID1"); break;
1213
* case ID2: installChunk(require("./chunks/ID2"); break;
1314
* // ...
1415
* case SELF_ID: installedChunks[chunkId] = 1; break;
1516
* default: throw new Error(`Unknown chunk ${chunkId}`);
1617
* }
18+
*
19+
* For a single chunk:
20+
* require("./chunks/CHUNK_ID.js");
1721
*/
1822

19-
import { readdirSync, readFileSync, writeFileSync } from "node:fs";
23+
import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
2024
import { join } from "node:path";
2125

2226
import { type BuildOptions, getPackagePath } from "@opennextjs/aws/build/helper.js";
2327

2428
import { patchCode } from "./util.js";
2529

26-
export function buildInlineChunksRule(chunks: number[]) {
30+
// Inline the code when there are multiple chunks
31+
export function buildMultipleChunksRule(chunks: number[]) {
2732
return `
2833
rule:
2934
pattern: ($CHUNK_ID, $_PROMISES) => { $$$ }
@@ -44,8 +49,30 @@ ${chunks.map((chunk) => ` case ${chunk}: $INSTALL(require("./chunks/${ch
4449
}`;
4550
}
4651

52+
// Inline the code when there is a single chunk.
53+
// For example when there is a single Pages API route.
54+
// Note: The chunk does not always exist which explain the need for the try...catch.
55+
export const singleChunkRule = `
56+
rule:
57+
pattern: ($CHUNK_ID, $_PROMISES) => { $$$ }
58+
inside: {pattern: $_.$_.require = $$$_, stopBy: end}
59+
all:
60+
- has: {pattern: $INSTALL(require("./chunks/" + $$$)), stopBy: end}
61+
- has: {pattern: $SELF_ID == $CHUNK_ID, stopBy: end}
62+
- has: {pattern: "$INSTALLED_CHUNK[$CHUNK_ID] = 1", stopBy: end}
63+
fix: |
64+
($CHUNK_ID, _) => {
65+
if (!$INSTALLED_CHUNK[$CHUNK_ID]) {
66+
try {
67+
$INSTALL(require("./chunks/$SELF_ID.js"));
68+
} catch {}
69+
}
70+
}
71+
`;
72+
4773
/**
48-
* Fixes the webpack-runtime.js file by removing its webpack dynamic requires.
74+
* Fixes the webpack-runtime.js and webpack-api-runtime.js files by inlining
75+
* the webpack dynamic requires.
4976
*/
5077
export async function patchWebpackRuntime(buildOpts: BuildOptions) {
5178
const { outputDir } = buildOpts;
@@ -57,14 +84,28 @@ export async function patchWebpackRuntime(buildOpts: BuildOptions) {
5784
".next/server"
5885
);
5986

60-
const runtimeFile = join(dotNextServerDir, "webpack-runtime.js");
61-
const runtimeCode = readFileSync(runtimeFile, "utf-8");
6287
// Look for all the chunks.
6388
const chunks = readdirSync(join(dotNextServerDir, "chunks"))
6489
.filter((chunk) => /^\d+\.js$/.test(chunk))
6590
.map((chunk) => {
6691
return Number(chunk.replace(/\.js$/, ""));
6792
});
6893

69-
writeFileSync(runtimeFile, patchCode(runtimeCode, buildInlineChunksRule(chunks)));
94+
patchFile(join(dotNextServerDir, "webpack-runtime.js"), chunks);
95+
patchFile(join(dotNextServerDir, "webpack-api-runtime.js"), chunks);
96+
}
97+
98+
/**
99+
* Inline chunks when the file exists.
100+
*
101+
* @param filename Path to the webpack runtime.
102+
* @param chunks List of chunks in the chunks folder.
103+
*/
104+
function patchFile(filename: string, chunks: number[]) {
105+
if (existsSync(filename)) {
106+
let code = readFileSync(filename, "utf-8");
107+
code = patchCode(code, buildMultipleChunksRule(chunks));
108+
code = patchCode(code, singleChunkRule);
109+
writeFileSync(filename, code);
110+
}
70111
}

0 commit comments

Comments
 (0)
Please sign in to comment.