1
- import { NextjsAppPaths } from "../nextjs-paths " ;
1
+ import { Config } from "../config " ;
2
2
import { build , Plugin } from "esbuild" ;
3
- import { existsSync , readFileSync } from "node:fs" ;
3
+ import { existsSync , readFileSync , cpSync } from "node:fs" ;
4
4
import { cp , readFile , writeFile } from "node:fs/promises" ;
5
5
import path from "node:path" ;
6
6
import { fileURLToPath } from "node:url" ;
7
7
8
8
import { patchRequire } from "./patches/investigated/patch-require" ;
9
- import { copyTemplates } from "./patches/investigated/copy-templates " ;
9
+ import { copyPackage } from "./patches/investigated/copy-package " ;
10
10
11
11
import { patchReadFile } from "./patches/to-investigate/patch-read-file" ;
12
12
import { patchFindDir } from "./patches/to-investigate/patch-find-dir" ;
13
13
import { inlineNextRequire } from "./patches/to-investigate/inline-next-require" ;
14
14
import { inlineEvalManifest } from "./patches/to-investigate/inline-eval-manifest" ;
15
15
import { patchWranglerDeps } from "./patches/to-investigate/wrangler-deps" ;
16
16
import { updateWebpackChunksFile } from "./patches/investigated/update-webpack-chunks-file" ;
17
+ import { patchCache } from "./patches/investigated/patch-cache" ;
17
18
18
19
/** The directory containing the Cloudflare template files. */
19
- const templateSrcDir = path . join ( path . dirname ( fileURLToPath ( import . meta. url ) ) , "templates" ) ;
20
+ const packageDir = path . dirname ( fileURLToPath ( import . meta. url ) ) ;
20
21
21
22
/**
22
23
* Using the Next.js build output in the `.next` directory builds a workerd compatible output
23
24
*
24
25
* @param outputDir the directory where to save the output
25
- * @param nextjsAppPaths
26
+ * @param config
26
27
*/
27
- export async function buildWorker (
28
- appDir : string ,
29
- outputDir : string ,
30
- nextjsAppPaths : NextjsAppPaths
31
- ) : Promise < void > {
32
- const templateDir = copyTemplates ( templateSrcDir , nextjsAppPaths ) ;
33
-
34
- const workerEntrypoint = `${ templateDir } /worker.ts` ;
35
- const workerOutputFile = `${ outputDir } /index.mjs` ;
28
+ export async function buildWorker ( config : Config ) : Promise < void > {
29
+ console . log ( `\x1b[35m⚙️ Copying files...\n\x1b[0m` ) ;
30
+
31
+ // Copy over client-side generated files
32
+ await cp (
33
+ path . join ( config . paths . dotNext , "static" ) ,
34
+ path . join ( config . paths . builderOutput , "assets" , "_next" , "static" ) ,
35
+ {
36
+ recursive : true ,
37
+ }
38
+ ) ;
39
+
40
+ // Copy over any static files (e.g. images) from the source project
41
+ const publicDir = path . join ( config . paths . nextApp , "public" ) ;
42
+ if ( existsSync ( publicDir ) ) {
43
+ await cp ( publicDir , path . join ( config . paths . builderOutput , "assets" ) , {
44
+ recursive : true ,
45
+ } ) ;
46
+ }
47
+
48
+ copyPackage ( packageDir , config ) ;
49
+
50
+ const templateDir = path . join ( config . paths . internalPackage , "templates" ) ;
51
+
52
+ const workerEntrypoint = path . join ( templateDir , "worker.ts" ) ;
53
+ const workerOutputFile = path . join ( config . paths . builderOutput , "index.mjs" ) ;
54
+
36
55
const nextConfigStr =
37
- readFileSync ( nextjsAppPaths . standaloneAppDir + "/server.js" , "utf8" ) ?. match (
56
+ readFileSync ( path . join ( config . paths . standaloneApp , "/server.js" ) , "utf8" ) ?. match (
38
57
/ c o n s t n e x t C o n f i g = ( { .+ ?} ) \n /
39
58
) ?. [ 1 ] ?? { } ;
40
59
41
60
console . log ( `\x1b[35m⚙️ Bundling the worker file...\n\x1b[0m` ) ;
42
61
43
- patchWranglerDeps ( nextjsAppPaths ) ;
44
- updateWebpackChunksFile ( nextjsAppPaths ) ;
62
+ patchWranglerDeps ( config ) ;
63
+ updateWebpackChunksFile ( config ) ;
45
64
46
65
await build ( {
47
66
entryPoints : [ workerEntrypoint ] ,
@@ -55,15 +74,15 @@ export async function buildWorker(
55
74
// Note: we apply an empty shim to next/dist/compiled/ws because it generates two `eval`s:
56
75
// eval("require")("bufferutil");
57
76
// eval("require")("utf-8-validate");
58
- "next/dist/compiled/ws" : ` ${ templateDir } / shims/ empty.ts` ,
77
+ "next/dist/compiled/ws" : path . join ( templateDir , " shims" , " empty.ts" ) ,
59
78
// Note: we apply an empty shim to next/dist/compiled/edge-runtime since (amongst others) it generated the following `eval`:
60
79
// eval(getModuleCode)(module, module.exports, throwingRequire, params.context, ...Object.values(params.scopedContext));
61
80
// which comes from https://github.com/vercel/edge-runtime/blob/6e96b55f/packages/primitives/src/primitives/load.js#L57-L63
62
81
// QUESTION: Why did I encountered this but mhart didn't?
63
- "next/dist/compiled/edge-runtime" : ` ${ templateDir } / shims/ empty.ts` ,
82
+ "next/dist/compiled/edge-runtime" : path . join ( templateDir , " shims" , " empty.ts" ) ,
64
83
// `@next/env` is a library Next.js uses for loading dotenv files, for obvious reasons we need to stub it here
65
84
// source: https://github.com/vercel/next.js/tree/0ac10d79720/packages/next-env
66
- "@next/env" : ` ${ templateDir } / shims/ env.ts` ,
85
+ "@next/env" : path . join ( templateDir , " shims" , " env.ts" ) ,
67
86
} ,
68
87
define : {
69
88
// config file used by Next.js, see: https://github.com/vercel/next.js/blob/68a7128/packages/next/src/build/utils.ts#L2137-L2139
@@ -98,22 +117,21 @@ export async function buildWorker(
98
117
// Do not crash on cache not supported
99
118
// https://github.com/cloudflare/workerd/pull/2434
100
119
// compatibility flag "cache_option_enabled" -> does not support "force-cache"
101
- let isPatchedAlready = globalThis.fetch.__nextPatched;
102
120
const curFetch = globalThis.fetch;
103
121
globalThis.fetch = (input, init) => {
104
- console.log("globalThis.fetch", input);
105
- if (init) delete init.cache;
122
+ if (init) {
123
+ delete init.cache;
124
+ }
106
125
return curFetch(input, init);
107
126
};
108
127
import { Readable } from 'node:stream';
109
- globalThis.fetch.__nextPatched = isPatchedAlready;
110
128
fetch = globalThis.fetch;
111
129
const CustomRequest = class extends globalThis.Request {
112
130
constructor(input, init) {
113
- console.log("CustomRequest", input);
114
131
if (init) {
115
132
delete init.cache;
116
133
if (init.body?.__node_stream__ === true) {
134
+ // https://github.com/cloudflare/workerd/issues/2746
117
135
init.body = Readable.toWeb(init.body);
118
136
}
119
137
}
@@ -122,25 +140,11 @@ const CustomRequest = class extends globalThis.Request {
122
140
};
123
141
globalThis.Request = CustomRequest;
124
142
Request = globalThis.Request;
125
- `,
143
+ ` ,
126
144
} ,
127
145
} ) ;
128
146
129
- await updateWorkerBundledCode ( workerOutputFile , nextjsAppPaths ) ;
130
-
131
- console . log ( `\x1b[35m⚙️ Copying asset files...\n\x1b[0m` ) ;
132
-
133
- // Copy over client-side generated files
134
- await cp ( `${ nextjsAppPaths . dotNextDir } /static` , `${ outputDir } /assets/_next/static` , {
135
- recursive : true ,
136
- } ) ;
137
-
138
- // Copy over any static files (e.g. images) from the source project
139
- if ( existsSync ( `${ appDir } /public` ) ) {
140
- await cp ( `${ appDir } /public` , `${ outputDir } /assets` , {
141
- recursive : true ,
142
- } ) ;
143
- }
147
+ await updateWorkerBundledCode ( workerOutputFile , config ) ;
144
148
145
149
console . log ( `\x1b[35mWorker saved in \`${ workerOutputFile } \` 🚀\n\x1b[0m` ) ;
146
150
}
@@ -151,21 +155,19 @@ Request = globalThis.Request;
151
155
* Needless to say all the logic in this function is something we should avoid as much as possible!
152
156
*
153
157
* @param workerOutputFile
154
- * @param nextjsAppPaths
158
+ * @param config
155
159
*/
156
- async function updateWorkerBundledCode (
157
- workerOutputFile : string ,
158
- nextjsAppPaths : NextjsAppPaths
159
- ) : Promise < void > {
160
+ async function updateWorkerBundledCode ( workerOutputFile : string , config : Config ) : Promise < void > {
160
161
const originalCode = await readFile ( workerOutputFile , "utf8" ) ;
161
162
162
163
let patchedCode = originalCode ;
163
164
164
165
patchedCode = patchRequire ( patchedCode ) ;
165
- patchedCode = patchReadFile ( patchedCode , nextjsAppPaths ) ;
166
- patchedCode = inlineNextRequire ( patchedCode , nextjsAppPaths ) ;
167
- patchedCode = patchFindDir ( patchedCode , nextjsAppPaths ) ;
168
- patchedCode = inlineEvalManifest ( patchedCode , nextjsAppPaths ) ;
166
+ patchedCode = patchReadFile ( patchedCode , config ) ;
167
+ patchedCode = inlineNextRequire ( patchedCode , config ) ;
168
+ patchedCode = patchFindDir ( patchedCode , config ) ;
169
+ patchedCode = inlineEvalManifest ( patchedCode , config ) ;
170
+ patchedCode = patchCache ( patchedCode , config ) ;
169
171
170
172
await writeFile ( workerOutputFile , patchedCode ) ;
171
173
}
@@ -176,10 +178,10 @@ function createFixRequiresESBuildPlugin(templateDir: string): Plugin {
176
178
setup ( build ) {
177
179
// Note: we (empty) shim require-hook modules as they generate problematic code that uses requires
178
180
build . onResolve ( { filter : / ^ \. \/ r e q u i r e - h o o k $ / } , ( args ) => ( {
179
- path : ` ${ templateDir } / shims/ empty.ts` ,
181
+ path : path . join ( templateDir , " shims" , " empty.ts" ) ,
180
182
} ) ) ;
181
183
build . onResolve ( { filter : / \. \/ l i b \/ n o d e - f s - m e t h o d s $ / } , ( args ) => ( {
182
- path : ` ${ templateDir } / shims/node-fs .ts` ,
184
+ path : path . join ( templateDir , " shims" , "empty .ts" ) ,
183
185
} ) ) ;
184
186
} ,
185
187
} ;
0 commit comments