Skip to content

Commit adb8142

Browse files
committed
fix: patch the webpack runtime when there is a single chunk
1 parent 2a6b571 commit adb8142

File tree

3 files changed

+142
-53
lines changed

3 files changed

+142
-53
lines changed

.changeset/smart-boxes-destroy.md

Lines changed: 5 additions & 0 deletions
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
Lines changed: 94 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,111 @@
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+
installChunk(require("./chunks/710.js"));
86+
installedChunks[chunkId] = 1;
87+
}
88+
}
89+
;
90+
"
91+
`);
92+
});
93+
94+
test("patch minified runtime", () => {
95+
const code = `
96+
o.f.require=(t,a)=>{e[t]||(710==t?r(require("./chunks/"+o.u(t))):e[t]=1)}
97+
`;
98+
99+
expect(patchCode(code, singleChunkRule)).toMatchInlineSnapshot(`
100+
"o.f.require=(t, _) => {
101+
if (!e[t]) {
102+
r(require("./chunks/710.js"));
103+
e[t] = 1;
56104
}
57105
}
58-
}
59106
60-
"
61-
`
62-
);
107+
"
108+
`);
109+
});
63110
});
64111
});

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

Lines changed: 43 additions & 6 deletions
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,28 @@ ${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+
export const singleChunkRule = `
55+
rule:
56+
pattern: ($CHUNK_ID, $_PROMISES) => { $$$ }
57+
inside: {pattern: $_.$_.require = $$$_, stopBy: end}
58+
all:
59+
- has: {pattern: $INSTALL(require("./chunks/" + $$$)), stopBy: end}
60+
- has: {pattern: $SELF_ID == $CHUNK_ID, stopBy: end}
61+
- has: {pattern: "$INSTALLED_CHUNK[$CHUNK_ID] = 1", stopBy: end}
62+
fix: |
63+
($CHUNK_ID, _) => {
64+
if (!$INSTALLED_CHUNK[$CHUNK_ID]) {
65+
$INSTALL(require("./chunks/$SELF_ID.js"));
66+
$INSTALLED_CHUNK[$CHUNK_ID] = 1;
67+
}
68+
}
69+
`;
70+
4771
/**
48-
* Fixes the webpack-runtime.js file by removing its webpack dynamic requires.
72+
* Fixes the webpack-runtime.js and webpack-api-runtime.js files by inlining
73+
* the webpack dynamic requires.
4974
*/
5075
export async function patchWebpackRuntime(buildOpts: BuildOptions) {
5176
const { outputDir } = buildOpts;
@@ -57,14 +82,26 @@ export async function patchWebpackRuntime(buildOpts: BuildOptions) {
5782
".next/server"
5883
);
5984

60-
const runtimeFile = join(dotNextServerDir, "webpack-runtime.js");
61-
const runtimeCode = readFileSync(runtimeFile, "utf-8");
6285
// Look for all the chunks.
6386
const chunks = readdirSync(join(dotNextServerDir, "chunks"))
6487
.filter((chunk) => /^\d+\.js$/.test(chunk))
6588
.map((chunk) => {
6689
return Number(chunk.replace(/\.js$/, ""));
6790
});
6891

69-
writeFileSync(runtimeFile, patchCode(runtimeCode, buildInlineChunksRule(chunks)));
92+
const runtimeFile = join(dotNextServerDir, "webpack-runtime.js");
93+
if (existsSync(runtimeFile)) {
94+
let code = readFileSync(runtimeFile, "utf-8");
95+
code = patchCode(code, buildMultipleChunksRule(chunks));
96+
code = patchCode(code, singleChunkRule);
97+
writeFileSync(runtimeFile, code);
98+
}
99+
100+
const apiRuntimeFile = join(dotNextServerDir, "webpack-api-runtime.js");
101+
if (existsSync(apiRuntimeFile)) {
102+
let code = readFileSync(runtimeFile, "utf-8");
103+
code = patchCode(code, buildMultipleChunksRule(chunks));
104+
code = patchCode(code, singleChunkRule);
105+
writeFileSync(apiRuntimeFile, code);
106+
}
70107
}

0 commit comments

Comments
 (0)