@solcreek/adapter-creek 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +190 -0
- package/README.md +184 -0
- package/dist/build.d.ts +57 -0
- package/dist/build.js +1382 -0
- package/dist/bundler.d.ts +20 -0
- package/dist/bundler.js +991 -0
- package/dist/cache-handler.d.ts +32 -0
- package/dist/cache-handler.js +100 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +247 -0
- package/dist/manifest.d.ts +40 -0
- package/dist/manifest.js +27 -0
- package/dist/worker-entry.d.ts +133 -0
- package/dist/worker-entry.js +7734 -0
- package/package.json +64 -0
- package/src/shims/als-polyfill.js +7 -0
- package/src/shims/critters.js +7 -0
- package/src/shims/empty.js +2 -0
- package/src/shims/env.js +3 -0
- package/src/shims/fast-set-immediate.js +285 -0
- package/src/shims/fs.js +225 -0
- package/src/shims/http.js +240 -0
- package/src/shims/image-optimizer.js +18 -0
- package/src/shims/load-manifest.js +123 -0
- package/src/shims/opentelemetry.js +229 -0
- package/src/shims/sharp.js +12 -0
- package/src/shims/sqlite3-binding.js +517 -0
- package/src/shims/track-module-loading.js +68 -0
- package/src/shims/vm.js +49 -0
package/dist/bundler.js
ADDED
|
@@ -0,0 +1,991 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bundler for Cloudflare Workers.
|
|
3
|
+
*
|
|
4
|
+
* Uses wrangler (which internally uses esbuild + Turbopack-aware resolution)
|
|
5
|
+
* to bundle the generated worker entry into CF Workers-compatible output.
|
|
6
|
+
*
|
|
7
|
+
* This works with both webpack and Turbopack output — wrangler handles
|
|
8
|
+
* the custom chunk format that plain esbuild cannot follow.
|
|
9
|
+
*/
|
|
10
|
+
import * as fs from "node:fs/promises";
|
|
11
|
+
import * as path from "node:path";
|
|
12
|
+
import { execSync } from "node:child_process";
|
|
13
|
+
import { builtinModules, createRequire } from "node:module";
|
|
14
|
+
/**
|
|
15
|
+
* Patch Turbopack runtime to inline chunk loading.
|
|
16
|
+
*
|
|
17
|
+
* Turbopack generates a runtime that loads chunks via R.c("path").
|
|
18
|
+
* These dynamic loads fail in CF Workers (no filesystem).
|
|
19
|
+
*
|
|
20
|
+
* Solution (same as @opennextjs/cloudflare):
|
|
21
|
+
* 1. Find [turbopack]_runtime.js
|
|
22
|
+
* 2. Collect all chunk file paths
|
|
23
|
+
* 3. Replace the loadRuntimeChunkPath function's require(resolved) with requireChunk(chunkPath)
|
|
24
|
+
* 4. Append a requireChunk() switch that maps paths to static require()
|
|
25
|
+
*/
|
|
26
|
+
async function patchTurbopackRuntime(distDir) {
|
|
27
|
+
// Find ALL Turbopack runtime files — there can be multiple:
|
|
28
|
+
// .next/server/chunks/ssr/[turbopack]_runtime.js
|
|
29
|
+
// .next/server/chunks/[turbopack]_runtime.js
|
|
30
|
+
const runtimePaths = [];
|
|
31
|
+
const searchDirs = [
|
|
32
|
+
path.join(distDir, "server", "chunks", "ssr"),
|
|
33
|
+
path.join(distDir, "server", "chunks"),
|
|
34
|
+
path.join(distDir, "server", "edge", "chunks"),
|
|
35
|
+
];
|
|
36
|
+
async function walkRuntimes(dir) {
|
|
37
|
+
let entries;
|
|
38
|
+
try {
|
|
39
|
+
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
for (const entry of entries) {
|
|
45
|
+
const full = path.join(dir, entry.name);
|
|
46
|
+
if (entry.isDirectory()) {
|
|
47
|
+
await walkRuntimes(full);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (!entry.name.endsWith(".js"))
|
|
51
|
+
continue;
|
|
52
|
+
if (entry.name.includes("[turbopack]_runtime") ||
|
|
53
|
+
(entry.name.startsWith("turbopack-") && entry.name.includes("edge-wrapper")) ||
|
|
54
|
+
entry.name.includes("edge-wrapper")) {
|
|
55
|
+
runtimePaths.push(full);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
for (const dir of searchDirs) {
|
|
60
|
+
await walkRuntimes(dir);
|
|
61
|
+
}
|
|
62
|
+
if (runtimePaths.length === 0)
|
|
63
|
+
return; // Not Turbopack
|
|
64
|
+
// Collect all chunk files from .next/server/chunks/ AND .next/server/edge/chunks/
|
|
65
|
+
const allChunks = [];
|
|
66
|
+
async function walkChunks(dir) {
|
|
67
|
+
let entries;
|
|
68
|
+
try {
|
|
69
|
+
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
for (const entry of entries) {
|
|
75
|
+
const full = path.join(dir, entry.name);
|
|
76
|
+
if (entry.isDirectory()) {
|
|
77
|
+
await walkChunks(full);
|
|
78
|
+
}
|
|
79
|
+
else if (entry.name.endsWith(".js") && !entry.name.includes("[turbopack]_runtime")) {
|
|
80
|
+
allChunks.push(full);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
await walkChunks(path.join(distDir, "server", "chunks"));
|
|
85
|
+
// Include edge chunks — required for middleware and edge runtime pages
|
|
86
|
+
await walkChunks(path.join(distDir, "server", "edge", "chunks"));
|
|
87
|
+
if (allChunks.length === 0)
|
|
88
|
+
return;
|
|
89
|
+
// Generate the requireChunk switch statement
|
|
90
|
+
const cases = [];
|
|
91
|
+
for (const chunk of allChunks) {
|
|
92
|
+
// Extract the relative path after .next/ for the case label
|
|
93
|
+
const relFromDotNext = chunk.replace(/.*\/\.next\//, "");
|
|
94
|
+
cases.push(` case "${relFromDotNext}": return require("${chunk}");`);
|
|
95
|
+
// For edge chunks, also add a short form (relative to server/edge/)
|
|
96
|
+
// because the edge Turbopack runtime resolves chunks relative to itself.
|
|
97
|
+
if (relFromDotNext.startsWith("server/edge/")) {
|
|
98
|
+
const shortRel = relFromDotNext.replace("server/edge/", "");
|
|
99
|
+
cases.push(` case "${shortRel}": return require("${chunk}");`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const requireChunkFn = `
|
|
103
|
+
function requireChunk(chunkPath) {
|
|
104
|
+
// Decode URL-encoded paths (edge runtime encodes [, ] as %5B, %5D)
|
|
105
|
+
var decoded = decodeURIComponent(chunkPath);
|
|
106
|
+
switch(decoded) {
|
|
107
|
+
${cases.join("\n")}
|
|
108
|
+
default:
|
|
109
|
+
// Try with original (encoded) path
|
|
110
|
+
switch(chunkPath) {
|
|
111
|
+
${cases.join("\n")}
|
|
112
|
+
default:
|
|
113
|
+
throw new Error("Chunk not found: " + chunkPath);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
`;
|
|
118
|
+
// Patch each Turbopack runtime file
|
|
119
|
+
for (const runtimePath of runtimePaths) {
|
|
120
|
+
const runtimeCode = await fs.readFile(runtimePath, "utf-8");
|
|
121
|
+
let patched = runtimeCode;
|
|
122
|
+
let modified = false;
|
|
123
|
+
// Standard SSR runtime: replace require(resolved) with requireChunk(chunkPath)
|
|
124
|
+
if (runtimeCode.includes("loadRuntimeChunkPath") && runtimeCode.includes("require(resolved)")) {
|
|
125
|
+
patched = patched.replace(/require\(resolved\)/g, "requireChunk(chunkPath)");
|
|
126
|
+
patched = patched + "\n" + requireChunkFn;
|
|
127
|
+
modified = true;
|
|
128
|
+
}
|
|
129
|
+
// Edge runtime: replace "chunk loading is not supported" with actual chunk loading.
|
|
130
|
+
// The edge Turbopack runtime has loadChunkCached that throws — we replace it
|
|
131
|
+
// to return a resolved promise after loading the chunk via requireChunk.
|
|
132
|
+
if (runtimeCode.includes("chunk loading is not supported")) {
|
|
133
|
+
patched = patched.replace(/loadChunkCached\([^)]*\)\s*\{[^}]*throw\s+Error\s*\(\s*"chunk loading is not supported"\s*\)[^}]*\}/, `loadChunkCached(e2, t2) {
|
|
134
|
+
try {
|
|
135
|
+
var decoded = decodeURIComponent(t2);
|
|
136
|
+
requireChunk(decoded);
|
|
137
|
+
} catch (err) {
|
|
138
|
+
console.error("[creek-chunk] Failed to load chunk:", t2, "decoded:", decodeURIComponent(t2), "error:", err.message);
|
|
139
|
+
}
|
|
140
|
+
return Promise.resolve();
|
|
141
|
+
}`);
|
|
142
|
+
// Also add requireChunk if not already appended
|
|
143
|
+
if (!modified) {
|
|
144
|
+
patched = patched + "\n" + requireChunkFn;
|
|
145
|
+
}
|
|
146
|
+
modified = true;
|
|
147
|
+
}
|
|
148
|
+
if (modified) {
|
|
149
|
+
await fs.writeFile(runtimePath, patched);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
async function patchNodeExternalRequireConditions(distDir) {
|
|
154
|
+
const serverChunksDir = path.join(distDir, "server", "chunks");
|
|
155
|
+
const projectRoot = path.dirname(distDir);
|
|
156
|
+
const projectRequire = createRequire(path.join(projectRoot, "package.json"));
|
|
157
|
+
const builtins = new Set([
|
|
158
|
+
...builtinModules,
|
|
159
|
+
...builtinModules.map((name) => `node:${name}`),
|
|
160
|
+
]);
|
|
161
|
+
// Narrow scope: only `[externals]*.js` files in `server/chunks/`.
|
|
162
|
+
// Those are Turbopack's dedicated external-require-registration bundles,
|
|
163
|
+
// which contain nothing but `.x("pkg", () => require("pkg"))` lines —
|
|
164
|
+
// rewriting require() to an absolute path there has no module-identity
|
|
165
|
+
// risk. We tried extending this to `server/chunks/ssr/*.js` to fix the
|
|
166
|
+
// styled-jsx-subpath case, but those chunks contain real SSR render
|
|
167
|
+
// code whose require() calls participate in Turbopack's module graph
|
|
168
|
+
// (same package resolved two ways = two React instances). The
|
|
169
|
+
// styled-jsx case is now handled via wrangler `alias` entries instead —
|
|
170
|
+
// see collectSsrLazyRequireAliases() + the wranglerConfig assembly.
|
|
171
|
+
let entries;
|
|
172
|
+
try {
|
|
173
|
+
entries = await fs.readdir(serverChunksDir);
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const chunkFiles = [];
|
|
179
|
+
for (const entry of entries) {
|
|
180
|
+
if (!entry.startsWith("[externals]") || !entry.endsWith(".js"))
|
|
181
|
+
continue;
|
|
182
|
+
chunkFiles.push(path.join(serverChunksDir, entry));
|
|
183
|
+
}
|
|
184
|
+
if (chunkFiles.length === 0)
|
|
185
|
+
return;
|
|
186
|
+
const externalRequirePattern = /(\w+\.x\(\s*(["'])([^"']+)\2\s*,\s*\(\)\s*=>\s*)require\(\s*(["'])\3\4\s*\)(\s*\))/g;
|
|
187
|
+
const stripTurbopackPackageAlias = (specifier) => {
|
|
188
|
+
const parts = specifier.split("/");
|
|
189
|
+
const packageIndex = specifier.startsWith("@") ? 1 : 0;
|
|
190
|
+
const packageName = parts[packageIndex];
|
|
191
|
+
if (!packageName)
|
|
192
|
+
return null;
|
|
193
|
+
const match = /^(.*)-[0-9a-f]{16}$/i.exec(packageName);
|
|
194
|
+
if (!match || !match[1])
|
|
195
|
+
return null;
|
|
196
|
+
parts[packageIndex] = match[1];
|
|
197
|
+
return parts.join("/");
|
|
198
|
+
};
|
|
199
|
+
for (const filePath of chunkFiles) {
|
|
200
|
+
let code;
|
|
201
|
+
try {
|
|
202
|
+
code = await fs.readFile(filePath, "utf-8");
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
if (!code.includes(".x(") || !code.includes("require("))
|
|
208
|
+
continue;
|
|
209
|
+
const patched = code.replace(externalRequirePattern, (match, prefix, _quote, specifier, _requireQuote, suffix) => {
|
|
210
|
+
if (specifier.startsWith(".") ||
|
|
211
|
+
specifier.startsWith("/") ||
|
|
212
|
+
builtins.has(specifier) ||
|
|
213
|
+
specifier === "next" ||
|
|
214
|
+
specifier.startsWith("next/") ||
|
|
215
|
+
specifier.startsWith("@next/")) {
|
|
216
|
+
return match;
|
|
217
|
+
}
|
|
218
|
+
let resolved;
|
|
219
|
+
try {
|
|
220
|
+
resolved = projectRequire.resolve(specifier);
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
const unaliased = stripTurbopackPackageAlias(specifier);
|
|
224
|
+
if (!unaliased)
|
|
225
|
+
return match;
|
|
226
|
+
try {
|
|
227
|
+
resolved = projectRequire.resolve(unaliased);
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
return match;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (resolved.endsWith(".node"))
|
|
234
|
+
return match;
|
|
235
|
+
return `${prefix}require(${JSON.stringify(resolved)})${suffix}`;
|
|
236
|
+
});
|
|
237
|
+
if (patched !== code) {
|
|
238
|
+
await fs.writeFile(filePath, patched);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Collect `.x("pkg", () => require("pkg"))` lazy-require specifiers out of
|
|
244
|
+
* `server/chunks/ssr/*.js` and return a map of specifiers → absolute paths
|
|
245
|
+
* that wrangler's esbuild should alias.
|
|
246
|
+
*
|
|
247
|
+
* Why this exists: Turbopack emits those registrations for SSR chunks when
|
|
248
|
+
* a transitive (indirect) dep like `styled-jsx/style.js` is needed. In
|
|
249
|
+
* pnpm projects the package lives at `.pnpm/styled-jsx@X/node_modules/...`
|
|
250
|
+
* and isn't hoisted to a top-level `node_modules/`. esbuild's walker
|
|
251
|
+
* therefore fails with `Could not resolve "styled-jsx/style.js"`.
|
|
252
|
+
*
|
|
253
|
+
* The previous attempt was to rewrite `require("pkg")` inline to an
|
|
254
|
+
* absolute path, mirroring what we do for `[externals]*.js` chunks — but
|
|
255
|
+
* `ssr/*.js` chunks also contain real SSR render code whose require()
|
|
256
|
+
* calls participate in Turbopack's module graph. Absolute-path rewrites
|
|
257
|
+
* there risk creating two module instances of the same package (once via
|
|
258
|
+
* Turbopack, once via our rewrite), which silently breaks React etc.
|
|
259
|
+
* and surfaced as "uncached or runtime data" prerender failures in
|
|
260
|
+
* `resume-data-cache` on Linux CI only.
|
|
261
|
+
*
|
|
262
|
+
* Returning an alias map instead keeps all source untouched. esbuild's
|
|
263
|
+
* alias is a global resolver override, so every reference to the same
|
|
264
|
+
* specifier goes through the same absolute path — module identity stays
|
|
265
|
+
* intact.
|
|
266
|
+
*/
|
|
267
|
+
async function collectSsrLazyRequireAliases(distDir) {
|
|
268
|
+
const ssrDir = path.join(distDir, "server", "chunks", "ssr");
|
|
269
|
+
const projectRoot = path.dirname(distDir);
|
|
270
|
+
const projectRequire = createRequire(path.join(projectRoot, "package.json"));
|
|
271
|
+
const builtins = new Set([
|
|
272
|
+
...builtinModules,
|
|
273
|
+
...builtinModules.map((name) => `node:${name}`),
|
|
274
|
+
]);
|
|
275
|
+
let entries;
|
|
276
|
+
try {
|
|
277
|
+
entries = await fs.readdir(ssrDir);
|
|
278
|
+
}
|
|
279
|
+
catch {
|
|
280
|
+
return {};
|
|
281
|
+
}
|
|
282
|
+
// Same regex as the [externals] patcher: `.x("X", () => require("X"))`
|
|
283
|
+
// where the two strings are identical.
|
|
284
|
+
const lazyRequirePattern = /\w+\.x\(\s*(["'])([^"']+)\1\s*,\s*\(\)\s*=>\s*require\(\s*(["'])\2\3\s*\)\s*\)/g;
|
|
285
|
+
const seen = new Set();
|
|
286
|
+
const aliasMap = {};
|
|
287
|
+
for (const entry of entries) {
|
|
288
|
+
if (!entry.endsWith(".js"))
|
|
289
|
+
continue;
|
|
290
|
+
const filePath = path.join(ssrDir, entry);
|
|
291
|
+
let code;
|
|
292
|
+
try {
|
|
293
|
+
code = await fs.readFile(filePath, "utf-8");
|
|
294
|
+
}
|
|
295
|
+
catch {
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
if (!code.includes(".x(") || !code.includes("require("))
|
|
299
|
+
continue;
|
|
300
|
+
let m;
|
|
301
|
+
while ((m = lazyRequirePattern.exec(code)) !== null) {
|
|
302
|
+
const specifier = m[2];
|
|
303
|
+
if (!specifier || seen.has(specifier))
|
|
304
|
+
continue;
|
|
305
|
+
seen.add(specifier);
|
|
306
|
+
if (specifier.startsWith(".") ||
|
|
307
|
+
specifier.startsWith("/") ||
|
|
308
|
+
builtins.has(specifier) ||
|
|
309
|
+
specifier === "next" ||
|
|
310
|
+
specifier.startsWith("next/") ||
|
|
311
|
+
specifier.startsWith("@next/")) {
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
let resolved = null;
|
|
315
|
+
try {
|
|
316
|
+
resolved = projectRequire.resolve(specifier);
|
|
317
|
+
}
|
|
318
|
+
catch {
|
|
319
|
+
// no-op — specifier will fail esbuild too, nothing we can fix
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
if (!resolved || resolved.endsWith(".node"))
|
|
323
|
+
continue;
|
|
324
|
+
aliasMap[specifier] = resolved;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return aliasMap;
|
|
328
|
+
}
|
|
329
|
+
async function patchAppPageManifestSingletons(distDir) {
|
|
330
|
+
const ssrDir = path.join(distDir, "server", "chunks", "ssr");
|
|
331
|
+
let entries = [];
|
|
332
|
+
try {
|
|
333
|
+
entries = await fs.readdir(ssrDir);
|
|
334
|
+
}
|
|
335
|
+
catch {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
const manifestProxyPattern = /case"moduleLoading":case"entryCSSFiles":case"entryJSFiles":\{if\(!(\w+)\)throw[\s\S]*?let (\w+)=(\w+)\.get\(\1\.route\);if\(!\2\)throw[\s\S]*?return \2\[(\w+)\]\}/g;
|
|
339
|
+
for (const entry of entries) {
|
|
340
|
+
if (!entry.endsWith(".js"))
|
|
341
|
+
continue;
|
|
342
|
+
const filePath = path.join(ssrDir, entry);
|
|
343
|
+
let code;
|
|
344
|
+
try {
|
|
345
|
+
code = await fs.readFile(filePath, "utf-8");
|
|
346
|
+
}
|
|
347
|
+
catch {
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
if (!code.includes('entryCSSFiles') || !code.includes('without a work store')) {
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
const patched = code.replace(manifestProxyPattern, (_match, workStoreVar, manifestVar, manifestsVar, propVar) => `case"moduleLoading":case"entryCSSFiles":case"entryJSFiles":{if(!${workStoreVar}){for(let a of ${manifestsVar}.values()){let b=a[${propVar}];if(void 0!==b)return b}return}let ${manifestVar}=${manifestsVar}.get(${workStoreVar}.route);if(!${manifestVar}){for(let a of ${manifestsVar}.values()){let b=a[${propVar}];if(void 0!==b)return b}return}return ${manifestVar}[${propVar}]}`);
|
|
354
|
+
if (patched !== code) {
|
|
355
|
+
await fs.writeFile(filePath, patched);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
function patchBundledManifestSingleton(workerCode) {
|
|
360
|
+
const bundledManifestProxyPattern = /case "moduleLoading":\s*case "entryCSSFiles":\s*case "entryJSFiles": \{\s*if \(!(\w+)\) throw[\s\S]*?let (\w+) = (\w+)\.get\(\1\.route\);\s*if \(!\2\) throw[\s\S]*?return \2\[(\w+)\];\s*\}/g;
|
|
361
|
+
const bundledManifestLookupPattern = /if \((\w+)\) \{\s*let (\w+) = (\w+)\.get\(\1\.route\);\s*if \(null == \2 \? void 0 : \2\[(\w+)\]\[(\w+)\]\) return \2\[\4\]\[\5\];\s*\} else for \(let (\w+) of \3\.values\(\)\) \{\s*let (\w+) = \6\[\4\]\[\5\];\s*if \(void 0 !== \7\) return \7;\s*\}/g;
|
|
362
|
+
workerCode = workerCode.replace(bundledManifestProxyPattern, (_match, workStoreVar, manifestVar, manifestsVar, propVar) => `case "moduleLoading":
|
|
363
|
+
case "entryCSSFiles":
|
|
364
|
+
case "entryJSFiles": {
|
|
365
|
+
if (!${workStoreVar}) {
|
|
366
|
+
for (const manifest of ${manifestsVar}.values()) {
|
|
367
|
+
const entry = manifest[${propVar}];
|
|
368
|
+
if (entry !== undefined) {
|
|
369
|
+
return entry;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return undefined;
|
|
373
|
+
}
|
|
374
|
+
let ${manifestVar} = ${manifestsVar}.get(${workStoreVar}.route);
|
|
375
|
+
if (!${manifestVar}) {
|
|
376
|
+
for (const manifest of ${manifestsVar}.values()) {
|
|
377
|
+
const entry = manifest[${propVar}];
|
|
378
|
+
if (entry !== undefined) {
|
|
379
|
+
return entry;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
return undefined;
|
|
383
|
+
}
|
|
384
|
+
return ${manifestVar}[${propVar}];
|
|
385
|
+
}`);
|
|
386
|
+
workerCode = workerCode.replace(bundledManifestLookupPattern, (_match, workStoreVar, manifestVar, manifestsVar, propVar, idVar, iterManifestVar, iterEntryVar) => `if (${workStoreVar}) {
|
|
387
|
+
let ${manifestVar} = ${manifestsVar}.get(${workStoreVar}.route);
|
|
388
|
+
let ${iterEntryVar} = null == ${manifestVar} ? void 0 : ${manifestVar}[${propVar}][${idVar}];
|
|
389
|
+
if (void 0 === ${iterEntryVar} && ${propVar} === "edgeSSRModuleMapping") ${iterEntryVar} = null == ${manifestVar} ? void 0 : ${manifestVar}.ssrModuleMapping[${idVar}];
|
|
390
|
+
if (void 0 === ${iterEntryVar} && ${propVar} === "edgeRscModuleMapping") ${iterEntryVar} = null == ${manifestVar} ? void 0 : ${manifestVar}.rscModuleMapping[${idVar}];
|
|
391
|
+
if (typeof process !== "undefined" && process.env.CREEK_DEBUG_MANIFESTS === "1" && (${idVar} === "99807" || ${idVar} === 99807 || String(${workStoreVar}.route || "").includes("basic-edge"))) {
|
|
392
|
+
console.error("[creek:bundled-manifest:route]", JSON.stringify({
|
|
393
|
+
route: ${workStoreVar}.route,
|
|
394
|
+
prop: ${propVar},
|
|
395
|
+
id: ${idVar},
|
|
396
|
+
routeHit: !!${manifestVar},
|
|
397
|
+
entryId: ${iterEntryVar} && typeof ${iterEntryVar} === "object" ? ${iterEntryVar}.id ?? (typeof ${iterEntryVar}["*"] === "object" ? ${iterEntryVar}["*"].id : undefined) : undefined,
|
|
398
|
+
}));
|
|
399
|
+
}
|
|
400
|
+
if (void 0 !== ${iterEntryVar}) return ${iterEntryVar};
|
|
401
|
+
}
|
|
402
|
+
let __creekNodeFallback;
|
|
403
|
+
for (let ${iterManifestVar} of ${manifestsVar}.values()) {
|
|
404
|
+
let ${iterEntryVar} = ${iterManifestVar}[${propVar}][${idVar}];
|
|
405
|
+
if (typeof process !== "undefined" && process.env.CREEK_DEBUG_MANIFESTS === "1" && (${idVar} === "99807" || ${idVar} === 99807) && void 0 !== ${iterEntryVar}) {
|
|
406
|
+
console.error("[creek:bundled-manifest:scan-hit]", JSON.stringify({
|
|
407
|
+
prop: ${propVar},
|
|
408
|
+
id: ${idVar},
|
|
409
|
+
entryId: ${iterEntryVar} && typeof ${iterEntryVar} === "object" ? ${iterEntryVar}.id ?? (typeof ${iterEntryVar}["*"] === "object" ? ${iterEntryVar}["*"].id : undefined) : undefined,
|
|
410
|
+
}));
|
|
411
|
+
}
|
|
412
|
+
if (void 0 !== ${iterEntryVar}) return ${iterEntryVar};
|
|
413
|
+
if (void 0 === __creekNodeFallback && ${propVar} === "edgeSSRModuleMapping") __creekNodeFallback = ${iterManifestVar}.ssrModuleMapping[${idVar}];
|
|
414
|
+
if (void 0 === __creekNodeFallback && ${propVar} === "edgeRscModuleMapping") __creekNodeFallback = ${iterManifestVar}.rscModuleMapping[${idVar}];
|
|
415
|
+
}
|
|
416
|
+
if (typeof process !== "undefined" && process.env.CREEK_DEBUG_MANIFESTS === "1" && (${idVar} === "99807" || ${idVar} === 99807) && void 0 !== __creekNodeFallback) {
|
|
417
|
+
console.error("[creek:bundled-manifest:node-fallback]", JSON.stringify({
|
|
418
|
+
prop: ${propVar},
|
|
419
|
+
id: ${idVar},
|
|
420
|
+
entryId: __creekNodeFallback && typeof __creekNodeFallback === "object" ? __creekNodeFallback.id ?? (typeof __creekNodeFallback["*"] === "object" ? __creekNodeFallback["*"].id : undefined) : undefined,
|
|
421
|
+
}));
|
|
422
|
+
}
|
|
423
|
+
if (void 0 !== __creekNodeFallback) return __creekNodeFallback;`);
|
|
424
|
+
// Cloudflare Workers executes the bundled app through the edge runtime path,
|
|
425
|
+
// but many app pages only populate the node/RSC module maps. Keep true edge
|
|
426
|
+
// routes on the edge maps when they exist, otherwise fall back to the node
|
|
427
|
+
// maps so React Server Consumer Manifest lookups can still resolve.
|
|
428
|
+
workerCode = workerCode.replace(/moduleMap: j2, serverModuleMap:/g, "moduleMap: Object.keys(i2 || {}).length ? i2 : j2, serverModuleMap:");
|
|
429
|
+
return workerCode;
|
|
430
|
+
}
|
|
431
|
+
function stripAsyncLocalStorageSnapshotTernaries(workerCode) {
|
|
432
|
+
const snapshotCall = ".snapshot()";
|
|
433
|
+
let out = "";
|
|
434
|
+
let last = 0;
|
|
435
|
+
let searchFrom = 0;
|
|
436
|
+
const isIdent = (ch) => /[A-Za-z0-9_$]/.test(ch);
|
|
437
|
+
const skipWsBack = (i) => {
|
|
438
|
+
while (i > 0 && /\s/.test(workerCode[i - 1]))
|
|
439
|
+
i--;
|
|
440
|
+
return i;
|
|
441
|
+
};
|
|
442
|
+
const skipWsForward = (i) => {
|
|
443
|
+
while (i < workerCode.length && /\s/.test(workerCode[i]))
|
|
444
|
+
i++;
|
|
445
|
+
return i;
|
|
446
|
+
};
|
|
447
|
+
while (true) {
|
|
448
|
+
const snapshotIndex = workerCode.indexOf(snapshotCall, searchFrom);
|
|
449
|
+
if (snapshotIndex === -1)
|
|
450
|
+
break;
|
|
451
|
+
let rightNameStart = snapshotIndex;
|
|
452
|
+
while (rightNameStart > 0 && isIdent(workerCode[rightNameStart - 1])) {
|
|
453
|
+
rightNameStart--;
|
|
454
|
+
}
|
|
455
|
+
const name = workerCode.slice(rightNameStart, snapshotIndex);
|
|
456
|
+
if (!name) {
|
|
457
|
+
searchFrom = snapshotIndex + snapshotCall.length;
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
let qIndex = skipWsBack(rightNameStart);
|
|
461
|
+
if (workerCode[qIndex - 1] !== "?") {
|
|
462
|
+
searchFrom = snapshotIndex + snapshotCall.length;
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
qIndex--;
|
|
466
|
+
let leftNameEnd = skipWsBack(qIndex);
|
|
467
|
+
let leftNameStart = leftNameEnd;
|
|
468
|
+
while (leftNameStart > 0 && isIdent(workerCode[leftNameStart - 1])) {
|
|
469
|
+
leftNameStart--;
|
|
470
|
+
}
|
|
471
|
+
if (workerCode.slice(leftNameStart, leftNameEnd) !== name) {
|
|
472
|
+
searchFrom = snapshotIndex + snapshotCall.length;
|
|
473
|
+
continue;
|
|
474
|
+
}
|
|
475
|
+
let colonIndex = skipWsForward(snapshotIndex + snapshotCall.length);
|
|
476
|
+
if (workerCode[colonIndex] !== ":") {
|
|
477
|
+
searchFrom = snapshotIndex + snapshotCall.length;
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
480
|
+
colonIndex = skipWsForward(colonIndex + 1);
|
|
481
|
+
out += workerCode.slice(last, leftNameStart);
|
|
482
|
+
last = colonIndex;
|
|
483
|
+
searchFrom = colonIndex;
|
|
484
|
+
}
|
|
485
|
+
return out ? out + workerCode.slice(last) : workerCode;
|
|
486
|
+
}
|
|
487
|
+
function patchUseCachePrerenderDanglingPromiseBailout(workerCode) {
|
|
488
|
+
const serializedKeyPattern = /let\s+(\w+)\s*=\s*"string"\s*==\s*typeof\s+(\w+)\s*\?\s*\2\s*:\s*await\s+(\w+)\(\2\),\s*(\w+)\s*=\s*(\w+)\.rootParams/g;
|
|
489
|
+
return workerCode.replace(serializedKeyPattern, (match, serializedKeyVar, encodedKeyVar, encodeFnVar, rootParamsVar, workUnitStoreVar, offset, fullCode) => {
|
|
490
|
+
const followingCode = fullCode.slice(offset, offset + 3000);
|
|
491
|
+
const hangingCallPattern = new RegExp(String.raw `\(0,\s*(\w+)\.makeHangingPromise\)\(\s*${workUnitStoreVar}\.renderSignal\s*,\s*(\w+)\.route`);
|
|
492
|
+
const hangingCallMatch = hangingCallPattern.exec(followingCode);
|
|
493
|
+
if (!hangingCallMatch)
|
|
494
|
+
return match;
|
|
495
|
+
const makeHangingPromiseVar = hangingCallMatch[1];
|
|
496
|
+
const workStoreVar = hangingCallMatch[2];
|
|
497
|
+
return `let ${serializedKeyVar} = "string" == typeof ${encodedKeyVar} ? ${encodedKeyVar} : await ${encodeFnVar}(${encodedKeyVar}); if ("prerender" === ${workUnitStoreVar}.type && "string" == typeof ${serializedKeyVar}) { let __creekDanglingThenableStart = ${serializedKeyVar}.lastIndexOf("$@"); if (__creekDanglingThenableStart !== -1 && ${serializedKeyVar}.indexOf(":", __creekDanglingThenableStart) === -1) return (0, ${makeHangingPromiseVar}.makeHangingPromise)(${workUnitStoreVar}.renderSignal, ${workStoreVar}.route, 'dynamic "use cache"'); } let ${rootParamsVar} = ${workUnitStoreVar}.rootParams`;
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
function patchNullFallbackPartialShellBlocking(workerCode) {
|
|
501
|
+
const minifiedPartialFallbackUpgradePattern = /true\s*!==\s*(\w+)\.experimental\.partialFallbacks\s*\|\|\s*\(null\s*==\s*(\w+)\s*\?\s*void\s*0\s*:\s*\2\.fallback\)\s*!==\s*null\s*\|\|\s*(\w+)\s*\|\|\s*(\w+)\s*\|\|\s*!\((\w+)\.length\s*>\s*0\)\s*\|\|\s*\((\w+)\s*=\s*(\w+)\.FallbackMode\.PRERENDER\)/g;
|
|
502
|
+
return workerCode.replace(minifiedPartialFallbackUpgradePattern, (_match, nextConfigVar, prerenderInfoVar, omittedFallbackParamVar, unresolvedRootParamsVar, remainingParamsVar, fallbackModeVar, fallbackEnumVar) => `true !== ${nextConfigVar}.experimental.partialFallbacks || (null == ${prerenderInfoVar} ? void 0 : ${prerenderInfoVar}.fallback) !== null || ${omittedFallbackParamVar} || ${unresolvedRootParamsVar} || !(${remainingParamsVar}.length > 0) || (${fallbackModeVar} = ${fallbackEnumVar}.FallbackMode.BLOCKING_STATIC_RENDER)`);
|
|
503
|
+
}
|
|
504
|
+
export async function bundleForWorkers(opts) {
|
|
505
|
+
// Patch Turbopack runtime BEFORE wrangler bundles.
|
|
506
|
+
// Turbopack's R.c() dynamically loads chunks from the filesystem.
|
|
507
|
+
// CF Workers has no filesystem, so we replace R.c() with a switch
|
|
508
|
+
// statement that maps chunk paths to static require() calls.
|
|
509
|
+
await patchTurbopackRuntime(opts.distDir);
|
|
510
|
+
await patchNodeExternalRequireConditions(opts.distDir);
|
|
511
|
+
await patchAppPageManifestSingletons(opts.distDir);
|
|
512
|
+
const ssrLazyRequireAliases = await collectSsrLazyRequireAliases(opts.distDir);
|
|
513
|
+
// Write the generated worker entry
|
|
514
|
+
const entryPath = path.join(opts.outputDir, "__entry.mjs");
|
|
515
|
+
await fs.writeFile(entryPath, opts.workerSource);
|
|
516
|
+
if (process.env.CREEK_DEBUG) {
|
|
517
|
+
await fs.writeFile(path.join(opts.outputDir, "__entry_debug.mjs"), opts.workerSource);
|
|
518
|
+
}
|
|
519
|
+
// Copy WASM files alongside the bundle BEFORE wrangler runs. wrangler's
|
|
520
|
+
// bundler needs the files resolvable from the entry `import __wasm_0 from
|
|
521
|
+
// "./wasm_<hex>.wasm"` to apply the CompiledWasm rule — copying later
|
|
522
|
+
// (after the bundle step) is too late.
|
|
523
|
+
for (const [name, absPath] of opts.wasmFiles) {
|
|
524
|
+
const destName = name.endsWith(".wasm") ? name : name + ".wasm";
|
|
525
|
+
const destPath = path.join(opts.outputDir, destName);
|
|
526
|
+
await fs.copyFile(absPath, destPath);
|
|
527
|
+
}
|
|
528
|
+
// Resolve adapter paths
|
|
529
|
+
const adapterDir = path.dirname(path.dirname(new URL(import.meta.url).pathname));
|
|
530
|
+
// Generate wrangler config for the bundle step
|
|
531
|
+
const wranglerConfig = {
|
|
532
|
+
name: "creek-adapter-build",
|
|
533
|
+
main: entryPath,
|
|
534
|
+
compatibility_date: "2026-03-28",
|
|
535
|
+
compatibility_flags: ["nodejs_compat"],
|
|
536
|
+
define: {
|
|
537
|
+
__dirname: '""',
|
|
538
|
+
__filename: '""',
|
|
539
|
+
"process.env.NODE_ENV": '"production"',
|
|
540
|
+
"process.env.NEXT_RUNTIME": '"nodejs"',
|
|
541
|
+
},
|
|
542
|
+
// Mark optional/unavailable deps as external to prevent build errors.
|
|
543
|
+
// These are caught at runtime and handled gracefully.
|
|
544
|
+
//
|
|
545
|
+
// `ssrLazyRequireAliases` is spread FIRST so static entries below take
|
|
546
|
+
// precedence if they collide — our shims (fs, vm, critters, etc.)
|
|
547
|
+
// always win over a happenstance SSR lazy-require of the same name.
|
|
548
|
+
alias: {
|
|
549
|
+
...ssrLazyRequireAliases,
|
|
550
|
+
"@opentelemetry/api": path.join(adapterDir, "src", "shims", "opentelemetry.js"),
|
|
551
|
+
// fs shim — intercept both bare and node: prefixed imports.
|
|
552
|
+
// Turbopack runtime uses require("fs") which wrangler must redirect
|
|
553
|
+
// to our shim that reads from embedded __MANIFESTS.
|
|
554
|
+
"fs": path.join(adapterDir, "src", "shims", "fs.js"),
|
|
555
|
+
"node:fs": path.join(adapterDir, "src", "shims", "fs.js"),
|
|
556
|
+
"vm": path.join(adapterDir, "src", "shims", "vm.js"),
|
|
557
|
+
"node:vm": path.join(adapterDir, "src", "shims", "vm.js"),
|
|
558
|
+
// critters is bundled by Next.js for CSS inlining — not needed on Workers.
|
|
559
|
+
"critters": path.join(adapterDir, "src", "shims", "critters.js"),
|
|
560
|
+
// sharp has native .node bindings that workerd can't load. Without this
|
|
561
|
+
// alias, wrangler pulls in ~1MB of sharp's JS wrapper and the module
|
|
562
|
+
// ends up non-callable at runtime — \`@vercel/og\`'s node path then
|
|
563
|
+
// throws \`sharp is not a function\`. Aliasing to a shim whose default
|
|
564
|
+
// is undefined makes \`@vercel/og\` fall back to its resvg.wasm path.
|
|
565
|
+
"sharp": path.join(adapterDir, "src", "shims", "sharp.js"),
|
|
566
|
+
// Some packages intentionally leave unreachable dynamic imports such as
|
|
567
|
+
// `if (Math.random() < 0) import("fail")` in their ESM entry. Turbopack
|
|
568
|
+
// externalizes the package and never resolves that branch, but wrangler
|
|
569
|
+
// follows it once we preload the concrete ESM file. Stub it so the dead
|
|
570
|
+
// branch remains harmless.
|
|
571
|
+
"fail": path.join(adapterDir, "src", "shims", "empty.js"),
|
|
572
|
+
// Replace Next's track-module-loading.{instance,external} with a
|
|
573
|
+
// per-request AsyncLocalStorage version. The original keeps a
|
|
574
|
+
// module-level CacheSignal whose internal setImmediate closure
|
|
575
|
+
// leaks IoContext across requests on workerd — second-and-later
|
|
576
|
+
// requests throw "Cannot perform I/O on behalf of a different
|
|
577
|
+
// request" when CacheSignal.pendingTimeoutCleanup fires
|
|
578
|
+
// clearImmediate on an Immediate from the first request. Repros
|
|
579
|
+
// on any route that does dynamic imports during render (notably
|
|
580
|
+
// \`new ImageResponse(...)\` — every \`@vercel/og\` call triggers
|
|
581
|
+
// trackPendingImport). We alias \`.external\` as well because
|
|
582
|
+
// call sites import from that module (the internal relative
|
|
583
|
+
// \`./track-module-loading.instance\` import never passes through
|
|
584
|
+
// esbuild's bare-specifier alias map). See
|
|
585
|
+
// src/shims/track-module-loading.js.
|
|
586
|
+
"next/dist/server/app-render/module-loading/track-module-loading.external": path.join(adapterDir, "src", "shims", "track-module-loading.js"),
|
|
587
|
+
"next/dist/server/app-render/module-loading/track-module-loading.external.js": path.join(adapterDir, "src", "shims", "track-module-loading.js"),
|
|
588
|
+
"next/dist/server/app-render/module-loading/track-module-loading.instance": path.join(adapterDir, "src", "shims", "track-module-loading.js"),
|
|
589
|
+
"next/dist/server/app-render/module-loading/track-module-loading.instance.js": path.join(adapterDir, "src", "shims", "track-module-loading.js"),
|
|
590
|
+
// Next's fast-set-immediate module patches node:timers/promises, whose
|
|
591
|
+
// ESM namespace is frozen in Workers. More importantly, workerd can run
|
|
592
|
+
// the next setTimeout(0) stage before a setImmediate scheduled by the
|
|
593
|
+
// previous cache-components stage. The shim keeps native setImmediate
|
|
594
|
+
// except during Next's explicit fast-immediate capture window.
|
|
595
|
+
"next/dist/server/node-environment-extensions/fast-set-immediate.external": path.join(adapterDir, "src", "shims", "fast-set-immediate.js"),
|
|
596
|
+
"next/dist/server/node-environment-extensions/fast-set-immediate.external.js": path.join(adapterDir, "src", "shims", "fast-set-immediate.js"),
|
|
597
|
+
// NOTE: load-manifest shim exists in src/shims/ but is handled by the
|
|
598
|
+
// fs shim (manifest loading), so no alias needed.
|
|
599
|
+
// NOTE: http/node:http is NOT aliased — CF Workers nodejs_compat provides it.
|
|
600
|
+
// The worker entry uses our custom IncomingMessage/ServerResponse inline via
|
|
601
|
+
// the NODE_BRIDGE_CODE template, which imports from "http" (the built-in).
|
|
602
|
+
},
|
|
603
|
+
};
|
|
604
|
+
const configPath = path.join(opts.outputDir, "__wrangler.json");
|
|
605
|
+
await fs.writeFile(configPath, JSON.stringify(wranglerConfig));
|
|
606
|
+
// Bundle with wrangler --dry-run
|
|
607
|
+
// Wrangler internally uses esbuild but with Turbopack-aware resolution
|
|
608
|
+
// and proper CJS/ESM interop for CF Workers.
|
|
609
|
+
// Ensure @next/routing is resolvable from the project directory. It's a
|
|
610
|
+
// dependency of the adapter, not the user's project, so wrangler can't
|
|
611
|
+
// find it when run from the project's cwd. Resolve via \`createRequire\`
|
|
612
|
+
// rather than guessing \`adapterDir/node_modules/@next/routing\` — pnpm's
|
|
613
|
+
// virtual-store layout means that path often doesn't exist (the real
|
|
614
|
+
// install lives under \`node_modules/.pnpm/@next+routing@X/\`), and the
|
|
615
|
+
// guess only works for the link-protocol install of the adapter.
|
|
616
|
+
const projectNodeModules = path.join(path.dirname(opts.distDir), "node_modules");
|
|
617
|
+
const routingDest = path.join(projectNodeModules, "@next", "routing");
|
|
618
|
+
const adapterRequire = createRequire(path.join(adapterDir, "package.json"));
|
|
619
|
+
let routingSrc;
|
|
620
|
+
try {
|
|
621
|
+
routingSrc = path.dirname(adapterRequire.resolve("@next/routing/package.json"));
|
|
622
|
+
}
|
|
623
|
+
catch {
|
|
624
|
+
// Fallback to the legacy guess so a classic link-install still works.
|
|
625
|
+
routingSrc = path.join(adapterDir, "node_modules", "@next", "routing");
|
|
626
|
+
}
|
|
627
|
+
try {
|
|
628
|
+
await fs.access(routingDest);
|
|
629
|
+
}
|
|
630
|
+
catch {
|
|
631
|
+
await fs.mkdir(path.join(projectNodeModules, "@next"), { recursive: true });
|
|
632
|
+
try {
|
|
633
|
+
await fs.symlink(routingSrc, routingDest, "junction");
|
|
634
|
+
}
|
|
635
|
+
catch (err) {
|
|
636
|
+
// Racy repeat runs (same project dir rebuilt back-to-back) can leave a
|
|
637
|
+
// dangling symlink that \`access\` reports as missing while \`symlink\`
|
|
638
|
+
// still refuses to overwrite.
|
|
639
|
+
if (err?.code !== "EEXIST")
|
|
640
|
+
throw err;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
const bundleDir = path.join(opts.outputDir, "__bundle");
|
|
644
|
+
// Resolve wrangler binary from the adapter's own node_modules
|
|
645
|
+
const wranglerBin = path.join(adapterDir, "node_modules", ".bin", "wrangler");
|
|
646
|
+
try {
|
|
647
|
+
execSync(`"${wranglerBin}" deploy --dry-run --outdir "${bundleDir}" --config "${configPath}"`, {
|
|
648
|
+
cwd: path.dirname(opts.distDir),
|
|
649
|
+
stdio: "pipe",
|
|
650
|
+
env: process.env,
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
catch (err) {
|
|
654
|
+
const stderr = err instanceof Error && "stderr" in err
|
|
655
|
+
? err.stderr?.toString() ?? ""
|
|
656
|
+
: "";
|
|
657
|
+
const stdout = err instanceof Error && "stdout" in err
|
|
658
|
+
? err.stdout?.toString() ?? ""
|
|
659
|
+
: "";
|
|
660
|
+
// Try to read wrangler log for details
|
|
661
|
+
let logContent = "";
|
|
662
|
+
try {
|
|
663
|
+
const { readdirSync, readFileSync } = await import("node:fs");
|
|
664
|
+
const logDir = path.join(process.env.HOME || "/tmp", ".wrangler/logs");
|
|
665
|
+
const logs = readdirSync(logDir).sort().reverse();
|
|
666
|
+
if (logs[0])
|
|
667
|
+
logContent = readFileSync(path.join(logDir, logs[0]), "utf-8").slice(-1000);
|
|
668
|
+
}
|
|
669
|
+
catch { }
|
|
670
|
+
throw new Error(`Wrangler bundle failed:\nSTDERR: ${stderr.slice(-2000)}\nSTDOUT: ${stdout.slice(-500)}\nLOG: ${logContent}`);
|
|
671
|
+
}
|
|
672
|
+
// Move bundled files to output directory
|
|
673
|
+
const bundledFiles = await fs.readdir(bundleDir);
|
|
674
|
+
for (const f of bundledFiles) {
|
|
675
|
+
if (f.endsWith(".map") || f === "README.md")
|
|
676
|
+
continue;
|
|
677
|
+
await fs.rename(path.join(bundleDir, f), path.join(opts.outputDir, f));
|
|
678
|
+
}
|
|
679
|
+
// Rename the main entry to worker.js
|
|
680
|
+
const mainFile = bundledFiles.find(f => f.endsWith(".mjs") || f.endsWith(".js"));
|
|
681
|
+
if (mainFile && mainFile !== "worker.js") {
|
|
682
|
+
const src = path.join(opts.outputDir, mainFile);
|
|
683
|
+
const dest = path.join(opts.outputDir, "worker.js");
|
|
684
|
+
if (await fs.access(src).then(() => true).catch(() => false)) {
|
|
685
|
+
await fs.rename(src, dest);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
// Post-process bundled worker to fix CF Workers compatibility issues.
|
|
689
|
+
const workerPath = path.join(opts.outputDir, "worker.js");
|
|
690
|
+
try {
|
|
691
|
+
let workerCode = await fs.readFile(workerPath, "utf-8");
|
|
692
|
+
// Fix instrumentation module loading — Next.js's catch only handles
|
|
693
|
+
// ENOENT/MODULE_NOT_FOUND error codes, but CF Workers __require throws
|
|
694
|
+
// "Dynamic require of ... is not supported" without those codes.
|
|
695
|
+
// Patch the catch to also handle "is not supported" errors.
|
|
696
|
+
workerCode = workerCode.replace(/err\.code !== "ENOENT" && err\.code !== "MODULE_NOT_FOUND" && err\.code !== "ERR_MODULE_NOT_FOUND"/g, 'err.code !== "ENOENT" && err.code !== "MODULE_NOT_FOUND" && err.code !== "ERR_MODULE_NOT_FOUND" && !err.message?.includes("is not supported")');
|
|
697
|
+
// Some libraries hide builtin requires from the bundler, e.g.
|
|
698
|
+
// @typescript/vfs constructs `require("path")` via String.fromCharCode.
|
|
699
|
+
// Wrangler can't statically rewrite that, so esbuild's __require helper
|
|
700
|
+
// throws at runtime even though the node:path wrapper is already present
|
|
701
|
+
// elsewhere in the bundle. Route those builtin dynamic requires back to
|
|
702
|
+
// wrangler's generated require_path() module when available.
|
|
703
|
+
workerCode = workerCode.replace(/throw Error\('Dynamic require of "' \+ x \+ '" is not supported'\);/, 'if ((x === "path" || x === "node:path") && typeof require_path === "function") return require_path();\n throw Error(\'Dynamic require of "\' + x + \'" is not supported\');');
|
|
704
|
+
workerCode = workerCode.replace(/(throw Error\('Dynamic require of "' \+ x \+ '" is not supported'\);\n\}\);)/, '$1\n__require.resolve = (id) => id === "typescript" ? "node_modules/typescript/lib/typescript.js" : id;');
|
|
705
|
+
// `next/dist/server/node-environment.js` imports
|
|
706
|
+
// `./node-environment-extensions/fast-set-immediate.external` by relative
|
|
707
|
+
// path, so Wrangler's bare-specifier alias cannot intercept it. If the
|
|
708
|
+
// original module installs first, our scoped shim stores Next's patched
|
|
709
|
+
// timer functions as its "native" originals and the workerd ordering fix
|
|
710
|
+
// becomes re-entrant. Route that side-effect import to the aliased shim
|
|
711
|
+
// module that Wrangler emitted for Turbopack's external import.
|
|
712
|
+
workerCode = workerCode.replace(/\brequire_fast_set_immediate_external\(\);/g, 'typeof init_fast_set_immediate === "function" ? init_fast_set_immediate() : require_fast_set_immediate_external();');
|
|
713
|
+
// Next.js's \`getInstrumentationModule\` does
|
|
714
|
+
// \`await __require(path.join(projectDir, distDir, "server",
|
|
715
|
+
// \`\${INSTRUMENTATION_HOOK_FILENAME}.js\`))\`. workerd can't resolve
|
|
716
|
+
// dynamic-require paths — the call rejects, the catch above swallows it,
|
|
717
|
+
// and \`instrumentation.register()\` is never invoked. Worker-entry
|
|
718
|
+
// static-imports the user file onto \`globalThis.__CREEK_INSTRUMENTATION\`
|
|
719
|
+
// when present, so prefer that over \`__require\` here. Falls through
|
|
720
|
+
// to the original call when no user instrumentation is registered so
|
|
721
|
+
// the \`module.exports = {}\` placeholder path still works.
|
|
722
|
+
workerCode = workerCode.replace(/(cachedInstrumentationModule\s*=\s*\(0,\s*_interopdefault\.interopDefault\)\s*\()\s*await __require\(/g, "$1globalThis.__CREEK_INSTRUMENTATION || await __require(");
|
|
723
|
+
workerCode = patchBundledManifestSingleton(workerCode);
|
|
724
|
+
// depd (via raw-body) uses `eval("(function ("+args+") {...})")` to build a
|
|
725
|
+
// deprecation-wrapping thunk. workerd + CF Workers block code generation
|
|
726
|
+
// from strings ("Code generation from strings disallowed for this context"),
|
|
727
|
+
// so any module that pulls in raw-body — notably the Pages Router API
|
|
728
|
+
// body parser — throws at module load time, and the outer try/catch in
|
|
729
|
+
// parse-body.ts surfaces it to the browser as `400 Invalid body`.
|
|
730
|
+
//
|
|
731
|
+
// Replace the minified eval expression with a direct function literal
|
|
732
|
+
// that preserves the runtime semantics (log + call). We lose the
|
|
733
|
+
// `function.length` preservation the eval form achieved, but nothing
|
|
734
|
+
// in the raw-body → parse-body call chain reads it.
|
|
735
|
+
//
|
|
736
|
+
// Fixes middleware-redirects "should redirect to api route with locale"
|
|
737
|
+
// (and the /fr variant) — both fail because their navigation ends at
|
|
738
|
+
// an API route whose parse-body path can't load raw-body.
|
|
739
|
+
workerCode = workerCode.replace(/var\s+(\w+)\s*=\s*eval\("\(function \(".*?return\s+\1\s*;?\s*\}/s, "return function(){log.call(deprecate,message,site);return fn.apply(this,arguments)}}");
|
|
740
|
+
// Route \`externalImport(id)\` through \`globalThis.__CREEK_EXT_MODS\` so
|
|
741
|
+
// we can serve bundled-but-externalized-by-Turbopack modules from
|
|
742
|
+
// our worker-entry static imports. Turbopack emits chunks like
|
|
743
|
+
// \`[externals]_next_dist_compiled_@vercel_og_index_node_...\` that do
|
|
744
|
+
// \`await e.y("next/dist/compiled/@vercel/og/index.node.js")\`; on
|
|
745
|
+
// workerd that \`await import(id)\` path throws "No such module" and
|
|
746
|
+
// the handler 500s. When our entry registers the module in
|
|
747
|
+
// \`__CREEK_EXT_MODS\`, the patched \`externalImport\` returns it
|
|
748
|
+
// directly without going through workerd's external loader.
|
|
749
|
+
// Fixes og-api \`/og-node\` (node runtime) +
|
|
750
|
+
// use-cache-metadata-route-handler opengraph/icon tests.
|
|
751
|
+
workerCode = workerCode.replace(/async function externalImport\((\w+)\)\s*\{\s*let\s+raw;\s*try\s*\{\s*raw\s*=\s*await import\(\1\);/g, (match, idVar) => `async function externalImport(${idVar}) {\n` +
|
|
752
|
+
` let raw;\n` +
|
|
753
|
+
` { const __loaders = globalThis.__CREEK_EXT_LOADERS; if (__loaders && __loaders[${idVar}]) {\n` +
|
|
754
|
+
` const __cached = globalThis.__CREEK_EXT_MODS = globalThis.__CREEK_EXT_MODS || {};\n` +
|
|
755
|
+
` if (${idVar} in __cached) { raw = __cached[${idVar}]; }\n` +
|
|
756
|
+
` else { try { raw = await __loaders[${idVar}](); __cached[${idVar}] = raw; } catch (err) { throw new Error(\`Failed to load external module \${${idVar}}: \${err}\`); } }\n` +
|
|
757
|
+
` if (raw && raw.__esModule && raw.default && "default" in raw.default) { return interopEsm(raw.default, createNS(raw), true); }\n` +
|
|
758
|
+
` return raw;\n` +
|
|
759
|
+
` } }\n` +
|
|
760
|
+
` try {\n` +
|
|
761
|
+
` raw = await import(${idVar});`);
|
|
762
|
+
// \`@vercel/og/index.node.js\` evaluates at module load:
|
|
763
|
+
//
|
|
764
|
+
// var fontData = fs.readFileSync(fileURLToPath(new URL("./Geist-Regular.ttf", import.meta.url)));
|
|
765
|
+
// var resvg_wasm = fs.readFileSync(fileURLToPath(new URL("./resvg.wasm", import.meta.url)));
|
|
766
|
+
//
|
|
767
|
+
// workerd rejects \`new URL("./X", import.meta.url)\` with
|
|
768
|
+
// "Invalid URL string" in the bundled-worker context, so evaluation
|
|
769
|
+
// aborts before any request hits the route. Rewrite these two calls
|
|
770
|
+
// to pass literal paths into fs.readFileSync directly — our fs shim
|
|
771
|
+
// has a basename fallback for .wasm/.ttf, so the embedded bundled
|
|
772
|
+
// bytes resolve regardless of path.
|
|
773
|
+
workerCode = workerCode.replace(/fileURLToPath\(new URL\(("\.\/[^"]+\.(?:wasm|ttf|otf|woff2?|png|jpg|jpeg|gif|webp|svg|ico)")\s*,\s*import\.meta\.url\)\)/g, (_match, filename) => filename.replace(/^"\.\//, '"'));
|
|
774
|
+
// Strip `AsyncLocalStorage.snapshot()` bindings in Next's
|
|
775
|
+
// `server/app-render/async-local-storage.js` and its Turbopack-inlined
|
|
776
|
+
// variants. On workerd, `ALS.snapshot()` captures the CURRENT IoContext
|
|
777
|
+
// and invoking the returned function from a later request throws
|
|
778
|
+
// "Cannot perform I/O on behalf of a different request". Next uses
|
|
779
|
+
// `createSnapshot()` / `runInCleanSnapshot: ALS.snapshot()` for cache-
|
|
780
|
+
// invalidation + edge-action callback propagation — both paths cross
|
|
781
|
+
// request boundaries on workerd. Replace the snapshot binding with a
|
|
782
|
+
// direct passthrough, matching @opennextjs/cloudflare's `patchUseCacheIO`
|
|
783
|
+
// (their comment: "TODO: Find a better fix for this issue.").
|
|
784
|
+
//
|
|
785
|
+
// Four forms exist post-bundling:
|
|
786
|
+
// 1. `function createSnapshot() { if (X) return X.snapshot(); return function(fn,...args){...}; }`
|
|
787
|
+
// — clean esm form from next/dist/esm (one copy embedded by wrangler).
|
|
788
|
+
// 2. `function <name>() { return X ? X.snapshot() : function(a,...b){ return a(...b); }; }`
|
|
789
|
+
// — Turbopack's minified module-level shim (edge-side copies).
|
|
790
|
+
// 3. `a.s(["bindSnapshot", 0, q, "createSnapshot", 0, function(){ return p ? p.snapshot() : ...; }])`
|
|
791
|
+
// — Turbopack's export-binding form in the same shim.
|
|
792
|
+
// 4. `runInCleanSnapshot: X ? X.snapshot() : function(a,...b){ return a(...b); }`
|
|
793
|
+
// — inlined call-site in the bundled work-store constructor.
|
|
794
|
+
// A single regex `(\w+) ? \1.snapshot() : ` stripped from the minified
|
|
795
|
+
// forms covers 2/3/4; form 1 uses an if-guard so handle it separately.
|
|
796
|
+
workerCode = workerCode.replace(/if\s*\(\s*(\w+)\s*\)\s*\{\s*return\s+\1\.snapshot\(\);\s*\}/g, "// Ignored snapshot");
|
|
797
|
+
workerCode = stripAsyncLocalStorageSnapshotTernaries(workerCode);
|
|
798
|
+
// `use cache` arguments that contain an unresolved React thenable marker
|
|
799
|
+
// (`$@`) during a prerender are request-bound values. Next's dynamic
|
|
800
|
+
// access instrumentation usually catches this before cache fill, but an
|
|
801
|
+
// async params transform can hide the access until after serialization.
|
|
802
|
+
// Node eventually trips the 50s cache-fill timeout; under workerd that
|
|
803
|
+
// blocks the whole response. Treat the cache function as a dynamic hole
|
|
804
|
+
// immediately so the PPR fallback shell can resume.
|
|
805
|
+
workerCode = patchUseCachePrerenderDanglingPromiseBailout(workerCode);
|
|
806
|
+
// Null-fallback partial routes don't have a concrete fallback artifact
|
|
807
|
+
// on disk; under workerd the follow-up path that tries to specialize a
|
|
808
|
+
// generic shell on the first request can surface as a static-generation
|
|
809
|
+
// bailout instead of completing the request. Keep these routes on the
|
|
810
|
+
// blocking path and let the concrete request render/cache directly.
|
|
811
|
+
workerCode = patchNullFallbackPartialShellBlocking(workerCode);
|
|
812
|
+
// Unify Next's `*AsyncStorageInstance` singletons across Turbopack
|
|
813
|
+
// chunks via a `globalThis` key. Turbopack emits the `work-unit-async-
|
|
814
|
+
// storage-instance.js` / `work-async-storage-instance.js` / etc.
|
|
815
|
+
// factories INLINE into every chunk that references them (see the
|
|
816
|
+
// `'turbopack-transition': 'next-shared'` import attribute in the
|
|
817
|
+
// next-shared layer); each chunk evaluates its own
|
|
818
|
+
// `createAsyncLocalStorage()` call and gets a fresh ALS instance.
|
|
819
|
+
// On the edge-runtime server-action path this fragments the store:
|
|
820
|
+
// the action handler runs `workUnitAsyncStorage.run(d, fn)` inside
|
|
821
|
+
// chunk A's ALS, the inner `headers()` call reads from chunk B's
|
|
822
|
+
// ALS, finds no store, and throws "headers was called outside a
|
|
823
|
+
// request scope". Node action path survives because its caller +
|
|
824
|
+
// callee happen to resolve to the same chunk's copy.
|
|
825
|
+
//
|
|
826
|
+
// Fix: rewrite the module factory bodies to key the ALS on the
|
|
827
|
+
// exported instance name (`workUnitAsyncStorageInstance`, etc.)
|
|
828
|
+
// under a single `globalThis.__CREEK_ALS` bag, so all chunks share
|
|
829
|
+
// one instance per logical store. Name-keyed is narrower than
|
|
830
|
+
// "dedup every `new AsyncLocalStorage()`" — we don't accidentally
|
|
831
|
+
// merge unrelated per-store ALSes (tracing requestStorage,
|
|
832
|
+
// react-server-dom temporaryReferences, etc.).
|
|
833
|
+
//
|
|
834
|
+
// Target pattern (both `a.i(N)` and `a.r(N)` create-variants
|
|
835
|
+
// exist):
|
|
836
|
+
// let <v> = (0, a.i(43291).createAsyncLocalStorage)();
|
|
837
|
+
// …short gap…
|
|
838
|
+
// a.s(["<Name>AsyncStorageInstance", 0, <v>], …)
|
|
839
|
+
// Replacement keeps the original create call so the per-isolate
|
|
840
|
+
// fallback works if globalThis isn't carried through an unusual
|
|
841
|
+
// loader; the `??=` idempotently promotes the first copy into the
|
|
842
|
+
// shared bag.
|
|
843
|
+
workerCode = workerCode.replace(/let\s+(\w+)\s*=\s*(\(0,\s*a\.[ir]\(\d+\)\.createAsyncLocalStorage\)\(\));([\s\S]{0,400}?a\.s\(\["(\w+AsyncStorageInstance)",\s*0,\s*\1\])/g, (_match, varName, createCall, tail, storeName) => `let ${varName} = ((globalThis.__CREEK_ALS ??= {})["${storeName}"] ??= ${createCall});${tail}`);
|
|
844
|
+
// Same dedup for the CJS-wrapped variants esbuild produces when it
|
|
845
|
+
// bundles Next's non-Turbopack ESM copy. Two observed shapes:
|
|
846
|
+
// a. `var <Name>AsyncStorageInstance = (0, _als.createAsyncLocalStorage)();`
|
|
847
|
+
// — top-level from `work-unit-async-storage-instance.js` etc.
|
|
848
|
+
// b. `Object.defineProperty(c, "<Name>AsyncStorageInstance", {…get…return <v>…}); let <v> = (0, a.r(N).createAsyncLocalStorage)();`
|
|
849
|
+
// — Turbopack-emitted CJS compiled form (edge chunks).
|
|
850
|
+
workerCode = workerCode.replace(/var\s+(\w+AsyncStorageInstance)\s*=\s*(\(0,\s*\w+\.createAsyncLocalStorage\)\(\));/g, (_match, storeName, createCall) => `var ${storeName} = ((globalThis.__CREEK_ALS ??= {})["${storeName}"] ??= ${createCall});`);
|
|
851
|
+
workerCode = workerCode.replace(/(Object\.defineProperty\(c,\s*"(\w+AsyncStorageInstance)",\s*\{[^}]*get:\s*(?:\/\*[^*]*\*\/\s*)?(?:__name\()?function\(\)\s*\{\s*return (\w+);[^}]*\}[\s\S]*?\}\);)\s*let\s+\3\s*=\s*(\(0,\s*a\.[ir]\(\d+\)\.createAsyncLocalStorage\)\(\))/g, (_match, prefix, storeName, varName, createCall) => `${prefix}\nlet ${varName} = ((globalThis.__CREEK_ALS ??= {})["${storeName}"] ??= ${createCall})`);
|
|
852
|
+
// Turbopack's node.js runtime implements top-level-await by assigning
|
|
853
|
+
// \`module.exports = <Promise>\`. esbuild's \`__toESM\` wraps imports with
|
|
854
|
+
// \`__create(__getProtoOf(mod))\`, and for a Promise that yields a plain
|
|
855
|
+
// object whose \`__proto__\` is \`Promise.prototype\`. Awaiting that object
|
|
856
|
+
// invokes \`Promise.prototype.then\` with a non-Promise receiver — workerd
|
|
857
|
+
// rejects it as "incompatible receiver" and the route 500s. Detect a
|
|
858
|
+
// Promise input and return it unchanged so await resolves the real
|
|
859
|
+
// async-module promise instead. Fixes \`metadata-edge\` and
|
|
860
|
+
// \`metadata-dynamic-routes-async-deps\` \`opengraph-image\` routes.
|
|
861
|
+
workerCode = workerCode.replace(/var __toESM = \(mod, isNodeMode, target\) => \(target = mod != null \? __create\(__getProtoOf\(mod\)\) : \{\}, __copyProps\(\s*(?:\/\/[^\n]*\n\s*)*isNodeMode \|\| !mod \|\| !mod\.__esModule \? __defProp\(target, "default", \{ value: mod, enumerable: true \}\) : target,\s*mod\s*\)\);/, `var __toESM = (mod, isNodeMode, target) => {
|
|
862
|
+
if (mod != null && typeof mod === "object" && typeof mod.then === "function" && __getProtoOf(mod) === Promise.prototype) {
|
|
863
|
+
return mod;
|
|
864
|
+
}
|
|
865
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
866
|
+
return __copyProps(
|
|
867
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
868
|
+
mod
|
|
869
|
+
);
|
|
870
|
+
};`);
|
|
871
|
+
await fs.writeFile(workerPath, workerCode);
|
|
872
|
+
}
|
|
873
|
+
catch { }
|
|
874
|
+
// Emit wrangler.toml that covers both local dev (workerd via wrangler dev)
|
|
875
|
+
// and \`wrangler deploy\`. Dev and prod MUST share the same config so
|
|
876
|
+
// deployment behavior stays predictable — that's what the adapter is
|
|
877
|
+
// expected to guarantee.
|
|
878
|
+
//
|
|
879
|
+
// The file lives alongside worker.js in \`.creek/adapter-output/server/\`.
|
|
880
|
+
// Users can override specific values by creating a \`wrangler.toml\`
|
|
881
|
+
// at their project root — the adapter reads that at build time and
|
|
882
|
+
// merges over our defaults (support added in a follow-up TODO).
|
|
883
|
+
await emitWranglerConfig({
|
|
884
|
+
outputDir: opts.outputDir,
|
|
885
|
+
assetsRelPath: "../assets", // server/ → assets/
|
|
886
|
+
// Same normalization as the copy loop above — ensure every wasm
|
|
887
|
+
// filename declared in the rules ends with \`.wasm\` so wrangler's
|
|
888
|
+
// CompiledWasm rule discovers the file we actually wrote to disk.
|
|
889
|
+
wasmFilenames: [...opts.wasmFiles.keys()].map((n) => n.endsWith(".wasm") ? n : n + ".wasm"),
|
|
890
|
+
});
|
|
891
|
+
// Clean up temp files
|
|
892
|
+
await fs.rm(entryPath, { force: true });
|
|
893
|
+
await fs.rm(configPath, { force: true });
|
|
894
|
+
await fs.rm(bundleDir, { recursive: true, force: true });
|
|
895
|
+
// List output files
|
|
896
|
+
const files = await fs.readdir(opts.outputDir);
|
|
897
|
+
return files.filter(f => !f.startsWith("__"));
|
|
898
|
+
}
|
|
899
|
+
/**
|
|
900
|
+
* Write a wrangler.toml next to worker.js so the same config drives both
|
|
901
|
+
* \`wrangler dev\` (local dev / test harness) and \`wrangler deploy\` (prod).
|
|
902
|
+
*
|
|
903
|
+
* Contents:
|
|
904
|
+
* - \`name\`, \`main\`, \`compatibility_date\`, \`compatibility_flags\` — runtime
|
|
905
|
+
* - \`[assets]\` — binds the assets directory with \`run_worker_first\` so our
|
|
906
|
+
* worker handles routing before workerd's static asset shortcut
|
|
907
|
+
* - \`[[durable_objects.bindings]]\` + \`[[migrations]]\` — the three DO
|
|
908
|
+
* classes the worker entry declares (DOQueueHandler, DOShardedTagCache,
|
|
909
|
+
* BucketCachePurge)
|
|
910
|
+
* - \`[[rules]] type = "CompiledWasm"\` — declares wasm siblings (next/og
|
|
911
|
+
* yoga/resvg, sharp, etc.) as module imports for workerd's loader
|
|
912
|
+
*/
|
|
913
|
+
async function emitWranglerConfig(opts) {
|
|
914
|
+
const { outputDir, assetsRelPath, wasmFilenames } = opts;
|
|
915
|
+
// Turbopack + wrangler both emit wasm siblings (hashed \`wasm_<xxh3>\`
|
|
916
|
+
// from us, \`<sha1>-yoga.wasm\` / \`<sha1>-resvg.wasm\` from wrangler's
|
|
917
|
+
// own esbuild pass when the app imports next/og). A wildcard glob
|
|
918
|
+
// covers every sibling regardless of who emitted it. Listing specific
|
|
919
|
+
// filenames fails because wrangler normalises import paths with a
|
|
920
|
+
// leading \`./\` that literal-name globs don't match — the built-in
|
|
921
|
+
// \`**/*.wasm\` rule then fires and aborts the build with
|
|
922
|
+
// "ignored because a previous rule ... was not marked as fallthrough".
|
|
923
|
+
let hasWasm = wasmFilenames.length > 0;
|
|
924
|
+
if (!hasWasm) {
|
|
925
|
+
try {
|
|
926
|
+
const entries = await fs.readdir(outputDir);
|
|
927
|
+
hasWasm = entries.some((e) => e.endsWith(".wasm"));
|
|
928
|
+
}
|
|
929
|
+
catch { }
|
|
930
|
+
}
|
|
931
|
+
const wasmRule = hasWasm
|
|
932
|
+
? [
|
|
933
|
+
"",
|
|
934
|
+
"# Declare every wasm sibling as a CompiledWasm module so \`import",
|
|
935
|
+
"# foo from \"./<hash>.wasm\"\` in the bundle resolves at runtime.",
|
|
936
|
+
"# Without this, Turbopack's runtime registry returns undefined and",
|
|
937
|
+
"# throws \"dynamically loading WebAssembly is not supported\".",
|
|
938
|
+
"[[rules]]",
|
|
939
|
+
`globs = ["**/*.wasm"]`,
|
|
940
|
+
`type = "CompiledWasm"`,
|
|
941
|
+
`fallthrough = false`,
|
|
942
|
+
].join("\n")
|
|
943
|
+
: "";
|
|
944
|
+
const toml = `# Generated by @solcreek/adapter-creek. Hand edits will be overwritten on
|
|
945
|
+
# the next \`next build\`. To extend (add KV, queues, env vars, etc.), create
|
|
946
|
+
# a \`wrangler.toml\` at your project root — the adapter merges it on top.
|
|
947
|
+
|
|
948
|
+
name = "creek"
|
|
949
|
+
main = "worker.js"
|
|
950
|
+
compatibility_date = "2026-03-23"
|
|
951
|
+
compatibility_flags = ["nodejs_compat"]
|
|
952
|
+
|
|
953
|
+
# Static assets from Next.js's \`.next/static\` + \`public/\` live alongside
|
|
954
|
+
# this server dir under \`../assets/\`. \`run_worker_first = true\` tells
|
|
955
|
+
# workerd to invoke our worker BEFORE serving static files — the adapter
|
|
956
|
+
# handles middleware, routing, and cache headers before any asset shortcut.
|
|
957
|
+
[assets]
|
|
958
|
+
directory = "${assetsRelPath}"
|
|
959
|
+
binding = "ASSETS"
|
|
960
|
+
run_worker_first = true
|
|
961
|
+
|
|
962
|
+
# Durable Object classes declared by the adapter's worker-entry. The
|
|
963
|
+
# binding \`name\` (what runtime code reads via \`env.<NAME>\`) must match
|
|
964
|
+
# what Creek's control plane injects in production — see
|
|
965
|
+
# \`packages/control-plane/src/modules/deployments/deploy.ts:129-140\` in
|
|
966
|
+
# the Creek repo. Do NOT rename these without coordinating with Creek.
|
|
967
|
+
#
|
|
968
|
+
# \`class_name\` is our internal export name from worker-entry.ts.
|
|
969
|
+
# \`new_sqlite_classes\` (not \`new_classes\`) selects SQLite-backed DO
|
|
970
|
+
# storage — DOShardedTagCache and BucketCachePurge both use
|
|
971
|
+
# \`ctx.storage.sql\`. DOQueueHandler is currently a placeholder but
|
|
972
|
+
# declared under SQLite too so future queue persistence has no migration
|
|
973
|
+
# cost.
|
|
974
|
+
[[durable_objects.bindings]]
|
|
975
|
+
name = "NEXT_CACHE_DO_QUEUE"
|
|
976
|
+
class_name = "DOQueueHandler"
|
|
977
|
+
[[durable_objects.bindings]]
|
|
978
|
+
name = "NEXT_TAG_CACHE_DO_SHARDED"
|
|
979
|
+
class_name = "DOShardedTagCache"
|
|
980
|
+
[[durable_objects.bindings]]
|
|
981
|
+
name = "NEXT_CACHE_DO_BUCKET_PURGE"
|
|
982
|
+
class_name = "BucketCachePurge"
|
|
983
|
+
|
|
984
|
+
[[migrations]]
|
|
985
|
+
tag = "v1"
|
|
986
|
+
new_sqlite_classes = ["DOQueueHandler", "DOShardedTagCache", "BucketCachePurge"]
|
|
987
|
+
${wasmRule}
|
|
988
|
+
`;
|
|
989
|
+
await fs.writeFile(path.join(outputDir, "wrangler.toml"), toml);
|
|
990
|
+
}
|
|
991
|
+
//# sourceMappingURL=bundler.js.map
|