next-bun-compile 0.11.2 → 0.12.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/dist/index.js CHANGED
@@ -1,9 +1,1265 @@
1
+ // src/generate.ts
1
2
  import {
2
- adapter_default,
3
- compile,
4
- generateEntryPoint,
5
- runBuild
6
- } from "./index-k3fkzeev.js";
3
+ writeFileSync,
4
+ readFileSync,
5
+ existsSync,
6
+ readdirSync,
7
+ statSync,
8
+ lstatSync,
9
+ realpathSync,
10
+ mkdirSync,
11
+ copyFileSync
12
+ } from "node:fs";
13
+ import { join, relative, basename } from "node:path";
14
+ import { createHash } from "node:crypto";
15
+ function tryStat(p) {
16
+ try {
17
+ return statSync(p);
18
+ } catch (err) {
19
+ const code = err.code;
20
+ if (code === "EPERM" || code === "EACCES" || code === "ENOENT")
21
+ return null;
22
+ throw err;
23
+ }
24
+ }
25
+ function walkDir(dir, base = dir) {
26
+ const results = [];
27
+ if (!existsSync(dir))
28
+ return results;
29
+ for (const entry of readdirSync(dir)) {
30
+ const full = join(dir, entry);
31
+ const stat = tryStat(full);
32
+ if (!stat)
33
+ continue;
34
+ if (stat.isDirectory()) {
35
+ results.push(...walkDir(full, base));
36
+ } else {
37
+ results.push({ absolutePath: full, relativePath: relative(base, full) });
38
+ }
39
+ }
40
+ return results;
41
+ }
42
+ function toVarName(filePath, index) {
43
+ const safe = filePath.replace(/[^a-zA-Z0-9]/g, "_").slice(0, 40);
44
+ return `asset_${index}_${safe}`;
45
+ }
46
+ function findPackageDirs(standaloneDir, pkg) {
47
+ const dirs = [];
48
+ const prefix = pkg.startsWith("@") ? pkg.split("/")[0] + "+" + pkg.split("/")[1] : pkg;
49
+ const seen = new Set;
50
+ const checkNodeModules = (nodeModulesDir) => {
51
+ const direct = join(nodeModulesDir, pkg);
52
+ if (existsSync(direct) && !seen.has(direct)) {
53
+ seen.add(direct);
54
+ dirs.push(direct);
55
+ }
56
+ for (const store of [".bun", ".pnpm"]) {
57
+ const storeDir = join(nodeModulesDir, store);
58
+ if (!existsSync(storeDir))
59
+ continue;
60
+ let entries;
61
+ try {
62
+ entries = readdirSync(storeDir);
63
+ } catch {
64
+ continue;
65
+ }
66
+ for (const entry of entries) {
67
+ if (!entry.startsWith(prefix + "@"))
68
+ continue;
69
+ const hoisted = join(storeDir, entry, "node_modules", pkg);
70
+ if (existsSync(hoisted) && !seen.has(hoisted)) {
71
+ seen.add(hoisted);
72
+ dirs.push(hoisted);
73
+ }
74
+ }
75
+ }
76
+ };
77
+ const walk = (dir) => {
78
+ let entries;
79
+ try {
80
+ entries = readdirSync(dir);
81
+ } catch {
82
+ return;
83
+ }
84
+ for (const entry of entries) {
85
+ const full = join(dir, entry);
86
+ if (entry === "node_modules") {
87
+ checkNodeModules(full);
88
+ continue;
89
+ }
90
+ const stat = tryStat(full);
91
+ if (stat && stat.isDirectory())
92
+ walk(full);
93
+ }
94
+ };
95
+ walk(standaloneDir);
96
+ return dirs;
97
+ }
98
+ function generateStubs(standaloneDir) {
99
+ const stubs = [
100
+ {
101
+ pkg: "next",
102
+ subpath: "dist/server/dev/next-dev-server.js",
103
+ content: "module.exports = { default: null };"
104
+ },
105
+ {
106
+ pkg: "next",
107
+ subpath: "dist/server/lib/router-utils/setup-dev-bundler.js",
108
+ content: "module.exports = {};"
109
+ },
110
+ {
111
+ pkg: "@opentelemetry/api",
112
+ subpath: "index.js",
113
+ content: "throw new Error('not installed');"
114
+ },
115
+ {
116
+ pkg: "critters",
117
+ subpath: "index.js",
118
+ content: "module.exports = {};"
119
+ }
120
+ ];
121
+ const nodeModulesDir = join(standaloneDir, "node_modules");
122
+ let count = 0;
123
+ for (const stub of stubs) {
124
+ const pkgDirs = findPackageDirs(standaloneDir, stub.pkg);
125
+ if (pkgDirs.length === 0)
126
+ pkgDirs.push(join(nodeModulesDir, stub.pkg));
127
+ for (const pkgDir of pkgDirs) {
128
+ const fullPath = join(pkgDir, stub.subpath);
129
+ if (!existsSync(fullPath)) {
130
+ const dir = join(fullPath, "..");
131
+ if (!existsSync(dir)) {
132
+ mkdirSync(dir, { recursive: true });
133
+ }
134
+ writeFileSync(fullPath, stub.content);
135
+ count++;
136
+ }
137
+ }
138
+ }
139
+ if (count > 0) {
140
+ console.log(`next-bun-compile: Created ${count} module stubs`);
141
+ }
142
+ }
143
+ function findTurbopackAliases(standaloneNextDir) {
144
+ const seen = new Map;
145
+ const ensure = (alias) => {
146
+ let e = seen.get(alias);
147
+ if (!e) {
148
+ e = { target: alias.replace(/-[0-9a-f]{16}$/, ""), subpaths: new Set };
149
+ seen.set(alias, e);
150
+ }
151
+ return e;
152
+ };
153
+ const serverDir = join(standaloneNextDir, "server");
154
+ if (existsSync(serverDir)) {
155
+ const re = /["']([^"'\s/]+-[0-9a-f]{16})(?:\/([^"'\s]+))?["']/g;
156
+ for (const f of walkDir(serverDir)) {
157
+ if (!f.absolutePath.endsWith(".js"))
158
+ continue;
159
+ let content;
160
+ try {
161
+ content = readFileSync(f.absolutePath, "utf-8");
162
+ } catch {
163
+ continue;
164
+ }
165
+ let m;
166
+ while (m = re.exec(content)) {
167
+ const entry = ensure(m[1]);
168
+ if (m[2])
169
+ entry.subpaths.add(m[2]);
170
+ }
171
+ }
172
+ }
173
+ const nodeModulesDir = join(standaloneNextDir, "node_modules");
174
+ if (existsSync(nodeModulesDir)) {
175
+ for (const name of readdirSync(nodeModulesDir)) {
176
+ if (!/-[0-9a-f]{16}$/.test(name))
177
+ continue;
178
+ if (seen.has(name))
179
+ continue;
180
+ const aliasPath = join(nodeModulesDir, name);
181
+ try {
182
+ if (!lstatSync(aliasPath).isSymbolicLink())
183
+ continue;
184
+ seen.set(name, {
185
+ target: basename(realpathSync(aliasPath)),
186
+ subpaths: new Set
187
+ });
188
+ } catch {
189
+ continue;
190
+ }
191
+ }
192
+ }
193
+ return Array.from(seen, ([alias, { target, subpaths }]) => ({
194
+ alias,
195
+ target,
196
+ subpaths: Array.from(subpaths)
197
+ }));
198
+ }
199
+ function buildCanonicalResolutions(externalRoot, aliases) {
200
+ const out = new Map;
201
+ const findFile = (dir, candidates) => {
202
+ for (const c of candidates) {
203
+ if (!c)
204
+ continue;
205
+ const p = join(dir, c);
206
+ if (existsSync(p) && statSync(p).isFile())
207
+ return c.replace(/\\/g, "/");
208
+ }
209
+ return null;
210
+ };
211
+ const resolveMain = (canonicalDir) => {
212
+ const pkgPath = join(canonicalDir, "package.json");
213
+ let main = "index.js";
214
+ if (existsSync(pkgPath)) {
215
+ try {
216
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
217
+ if (typeof pkg.main === "string")
218
+ main = pkg.main;
219
+ } catch {}
220
+ }
221
+ return findFile(canonicalDir, [
222
+ main,
223
+ main + ".js",
224
+ main + ".cjs",
225
+ main + ".mjs",
226
+ join(main, "index.js"),
227
+ join(main, "index.cjs"),
228
+ join(main, "index.mjs"),
229
+ "index.js",
230
+ "index.cjs",
231
+ "index.mjs"
232
+ ]);
233
+ };
234
+ const resolveSub = (canonicalDir, sub) => {
235
+ const stripped = sub.replace(/\.(?:js|cjs|mjs|json)$/, "");
236
+ return findFile(canonicalDir, [
237
+ sub,
238
+ stripped + ".mjs",
239
+ stripped + ".js",
240
+ stripped + ".cjs",
241
+ stripped + ".json",
242
+ join(stripped, "index.mjs"),
243
+ join(stripped, "index.js"),
244
+ join(stripped, "index.cjs")
245
+ ]);
246
+ };
247
+ for (const { alias, target, subpaths } of aliases) {
248
+ const canonicalDir = join(externalRoot, target);
249
+ if (!existsSync(canonicalDir))
250
+ continue;
251
+ const main = resolveMain(canonicalDir);
252
+ if (main)
253
+ out.set(alias, `.next/node_modules/${target}/${main}`);
254
+ for (const sub of subpaths) {
255
+ const file = resolveSub(canonicalDir, sub);
256
+ if (file)
257
+ out.set(`${alias}/${sub}`, `.next/node_modules/${target}/${file}`);
258
+ }
259
+ }
260
+ return out;
261
+ }
262
+ function validateAliasResolutions(aliases, resolutions) {
263
+ const verbose = process.env.NEXT_BUN_COMPILE_VERBOSE === "1";
264
+ const all = [];
265
+ for (const { alias, target, subpaths } of aliases) {
266
+ all.push({ ref: alias, canonical: target, file: resolutions.get(alias) ?? null });
267
+ for (const sub of subpaths) {
268
+ const ref = `${alias}/${sub}`;
269
+ all.push({
270
+ ref,
271
+ canonical: `${target}/${sub}`,
272
+ file: resolutions.get(ref) ?? null
273
+ });
274
+ }
275
+ }
276
+ const unresolved = all.filter((e) => !e.file).map(({ ref, canonical }) => ({ ref, canonical }));
277
+ if (verbose && all.length > 0) {
278
+ console.log(`next-bun-compile: Validating ${all.length} turbopack alias reference(s):`);
279
+ for (const { ref, canonical, file } of all) {
280
+ if (file) {
281
+ const display = file.replace(/^\.next\/node_modules\//, "");
282
+ console.log(` ✓ ${ref} → ${canonical} (${display})`);
283
+ } else {
284
+ console.log(` ✗ ${ref} → ${canonical} (NOT FOUND)`);
285
+ }
286
+ }
287
+ }
288
+ if (unresolved.length > 0) {
289
+ console.warn(`next-bun-compile: ⚠ ${unresolved.length} of ${all.length} turbopack alias reference(s) won't resolve at runtime:`);
290
+ for (const { ref, canonical } of unresolved) {
291
+ console.warn(` ✗ ${ref} → ${canonical}`);
292
+ }
293
+ console.warn(`next-bun-compile: These chunks will throw at runtime when the reference is hit. ` + `Either the package is missing from dependencies, or it's hidden behind a path the ` + `standalone trace didn't include. Try adding to transpilePackages in next.config.`);
294
+ }
295
+ return unresolved;
296
+ }
297
+ function rewriteTurbopackAliases(standaloneNextDir, aliases, resolutions) {
298
+ const rewrittenPaths = [];
299
+ if (aliases.length === 0 || resolutions.size === 0)
300
+ return rewrittenPaths;
301
+ const serverDir = join(standaloneNextDir, "server");
302
+ if (!existsSync(serverDir))
303
+ return rewrittenPaths;
304
+ const escape = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
305
+ const pattern = new RegExp(`(["'])((?:` + aliases.map((a) => escape(a.alias)).join("|") + `)(?:/[^"']+)?)\\1`, "g");
306
+ for (const f of walkDir(serverDir)) {
307
+ if (!f.absolutePath.endsWith(".js"))
308
+ continue;
309
+ let content;
310
+ try {
311
+ content = readFileSync(f.absolutePath, "utf-8");
312
+ } catch {
313
+ continue;
314
+ }
315
+ const next = content.replace(pattern, (match, quote, spec) => {
316
+ const rel = resolutions.get(spec);
317
+ if (!rel)
318
+ return match;
319
+ return `${quote}__NBC_BASE__/${rel}${quote}`;
320
+ });
321
+ if (next !== content) {
322
+ writeFileSync(f.absolutePath, next);
323
+ rewrittenPaths.push(`.next/server/${f.relativePath.replace(/\\/g, "/")}`);
324
+ }
325
+ }
326
+ if (rewrittenPaths.length > 0) {
327
+ console.log(`next-bun-compile: Rewrote turbopack-mangled aliases in ${rewrittenPaths.length} server chunks`);
328
+ }
329
+ return rewrittenPaths;
330
+ }
331
+ function patchRequireHook(standaloneDir) {
332
+ const nextDirs = findPackageDirs(standaloneDir, "next");
333
+ const target = "let resolve = process.env.NEXT_MINIMAL ? __non_webpack_require__.resolve : require.resolve;";
334
+ const replacement = `let _resolve = process.env.NEXT_MINIMAL ? __non_webpack_require__.resolve : require.resolve;
335
+ let resolve = (id) => { try { return _resolve(id); } catch { return ''; } };`;
336
+ let patched = 0;
337
+ for (const nextDir of nextDirs) {
338
+ const hookPath = join(nextDir, "dist/server/require-hook.js");
339
+ if (!existsSync(hookPath))
340
+ continue;
341
+ let content = readFileSync(hookPath, "utf-8");
342
+ if (!content.includes(target))
343
+ continue;
344
+ content = content.replace(target, replacement);
345
+ writeFileSync(hookPath, content);
346
+ patched++;
347
+ }
348
+ if (patched > 0) {
349
+ console.log("next-bun-compile: Patched require-hook.js for compiled binary compatibility");
350
+ }
351
+ }
352
+ function collectExternalModules(standaloneDir) {
353
+ const pkgRoots = new Map;
354
+ function addPkg(name, path) {
355
+ let set = pkgRoots.get(name);
356
+ if (!set) {
357
+ set = new Set;
358
+ pkgRoots.set(name, set);
359
+ }
360
+ set.add(path);
361
+ }
362
+ function scanDir(dir) {
363
+ if (!existsSync(dir))
364
+ return;
365
+ for (const entry of readdirSync(dir)) {
366
+ if (entry.startsWith(".") || entry === "next-bun-compile")
367
+ continue;
368
+ const entryPath = join(dir, entry);
369
+ const stat = tryStat(entryPath);
370
+ if (!stat || !stat.isDirectory())
371
+ continue;
372
+ if (entry.startsWith("@")) {
373
+ for (const sub of readdirSync(entryPath)) {
374
+ const subPath = join(entryPath, sub);
375
+ const subStat = tryStat(subPath);
376
+ if (subStat && subStat.isDirectory())
377
+ addPkg(`${entry}/${sub}`, subPath);
378
+ }
379
+ } else {
380
+ addPkg(entry, entryPath);
381
+ }
382
+ }
383
+ }
384
+ function walkForNodeModules(dir) {
385
+ let entries;
386
+ try {
387
+ entries = readdirSync(dir);
388
+ } catch {
389
+ return;
390
+ }
391
+ for (const entry of entries) {
392
+ const full = join(dir, entry);
393
+ if (entry === "node_modules") {
394
+ scanDir(full);
395
+ for (const store of [".bun", ".pnpm"]) {
396
+ const storeDir = join(full, store);
397
+ if (!existsSync(storeDir))
398
+ continue;
399
+ for (const storeEntry of readdirSync(storeDir)) {
400
+ const nested = join(storeDir, storeEntry, "node_modules");
401
+ if (existsSync(nested))
402
+ scanDir(nested);
403
+ }
404
+ }
405
+ continue;
406
+ }
407
+ const stat = tryStat(full);
408
+ if (stat && stat.isDirectory())
409
+ walkForNodeModules(full);
410
+ }
411
+ }
412
+ walkForNodeModules(standaloneDir);
413
+ if (pkgRoots.size === 0)
414
+ return [];
415
+ const results = [];
416
+ const seenMods = new Set;
417
+ for (const [name, paths] of pkgRoots) {
418
+ for (const pkgPath of paths) {
419
+ for (const f of walkDir(pkgPath)) {
420
+ const mod = `${name}/${f.relativePath.replace(/\\/g, "/")}`;
421
+ if (seenMods.has(mod))
422
+ continue;
423
+ seenMods.add(mod);
424
+ results.push({ mod, src: f.absolutePath });
425
+ }
426
+ }
427
+ }
428
+ return results;
429
+ }
430
+ function fixModuleResolution(standaloneDir) {
431
+ for (const pkgDir of findPackageDirs(standaloneDir, "next")) {
432
+ const compiledDir = join(pkgDir, "dist/compiled");
433
+ if (!existsSync(compiledDir))
434
+ continue;
435
+ for (const entry of readdirSync(compiledDir)) {
436
+ const dir = join(compiledDir, entry);
437
+ const stat = tryStat(dir);
438
+ if (!stat || !stat.isDirectory())
439
+ continue;
440
+ const pkgJsonPath = join(dir, "package.json");
441
+ const indexPath = join(dir, "index.js");
442
+ if (!existsSync(pkgJsonPath) || existsSync(indexPath))
443
+ continue;
444
+ let pkg;
445
+ try {
446
+ pkg = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
447
+ } catch {
448
+ continue;
449
+ }
450
+ if (pkg.main && pkg.main !== "index.js") {
451
+ writeFileSync(indexPath, `module.exports = require("./${pkg.main}");`);
452
+ }
453
+ }
454
+ }
455
+ for (const helpersDir of findPackageDirs(standaloneDir, "@swc/helpers")) {
456
+ const cjsDir = join(helpersDir, "cjs");
457
+ if (!existsSync(cjsDir))
458
+ continue;
459
+ for (const file of readdirSync(cjsDir)) {
460
+ if (!file.endsWith(".cjs"))
461
+ continue;
462
+ const name = file.slice(0, -4);
463
+ const shimDir = join(helpersDir, "_", name);
464
+ const shimFile = join(shimDir, "index.js");
465
+ if (existsSync(shimFile))
466
+ continue;
467
+ mkdirSync(shimDir, { recursive: true });
468
+ writeFileSync(shimFile, `module.exports = require("../../cjs/${file}");`);
469
+ }
470
+ }
471
+ }
472
+ function readAdapterSnapshot(distDir) {
473
+ try {
474
+ const raw = JSON.parse(readFileSync(join(distDir, "nbc-adapter-outputs.json"), "utf-8"));
475
+ if (raw?.version !== 1 || !Array.isArray(raw.prerenders))
476
+ return null;
477
+ const buildId = readFileSync(join(distDir, "BUILD_ID"), "utf-8").trim();
478
+ if (raw.buildId !== buildId)
479
+ return null;
480
+ return raw;
481
+ } catch {
482
+ return null;
483
+ }
484
+ }
485
+ function computeStaticTiersFromSnapshot(snapshot, args) {
486
+ const none = (why) => ({
487
+ tier1: [],
488
+ staticPages: [],
489
+ disabled: why,
490
+ customCacheHandler: snapshot.hasCustomCacheHandler
491
+ });
492
+ if (snapshot.basePath)
493
+ return none("basePath is set");
494
+ if (snapshot.i18n)
495
+ return none("i18n is configured");
496
+ const matchers = [];
497
+ for (const source of [
498
+ ...snapshot.middlewareMatchers,
499
+ ...snapshot.routingRules
500
+ ]) {
501
+ try {
502
+ matchers.push(new RegExp(source));
503
+ } catch {
504
+ matchers.push(/.*/);
505
+ }
506
+ }
507
+ const covered = (p) => matchers.some((re) => re.test(p));
508
+ const tier1 = [];
509
+ if (!args.assetPrefix) {
510
+ for (const f of args.staticFiles) {
511
+ if (!covered(f.urlPath)) {
512
+ tier1.push({ urlPath: f.urlPath, key: f.urlPath, kind: "static" });
513
+ }
514
+ }
515
+ }
516
+ for (const f of args.publicFiles) {
517
+ if (!covered(f.urlPath)) {
518
+ tier1.push({ urlPath: f.urlPath, key: f.urlPath, kind: "public" });
519
+ }
520
+ }
521
+ const byPathname = new Map(snapshot.prerenders.map((p) => [p.pathname, p]));
522
+ const staticPages = [];
523
+ for (const p of snapshot.prerenders) {
524
+ if (!p.file || !p.file.endsWith(".html"))
525
+ continue;
526
+ if (p.pathname.startsWith("/_"))
527
+ continue;
528
+ if (p.revalidate !== false || p.postponed)
529
+ continue;
530
+ if (covered(p.pathname))
531
+ continue;
532
+ const rscPathname = p.pathname === "/" ? "/index.rsc" : `${p.pathname}.rsc`;
533
+ const rscFile = byPathname.get(rscPathname)?.file ?? null;
534
+ const headers = {};
535
+ let tags = [];
536
+ for (const [k, v] of Object.entries(p.headers)) {
537
+ const lc = k.toLowerCase();
538
+ if (lc === "x-next-cache-tags") {
539
+ tags = v.split(",").map((t) => t.trim()).filter(Boolean);
540
+ } else if (lc !== "vary" && lc !== "content-type") {
541
+ headers[k] = v;
542
+ }
543
+ }
544
+ staticPages.push({
545
+ path: p.pathname,
546
+ htmlKey: `__runtime/.next/${p.file.replace(/\\/g, "/")}`,
547
+ rscKey: rscFile ? `__runtime/.next/${rscFile.replace(/\\/g, "/")}` : null,
548
+ headers,
549
+ status: p.status,
550
+ tags
551
+ });
552
+ }
553
+ const claimed = new Set(staticPages.map((s) => s.path));
554
+ for (const f of snapshot.staticFiles ?? []) {
555
+ if (f.pathname.startsWith("/_"))
556
+ continue;
557
+ if (f.pathname === "/404" || f.pathname === "/500")
558
+ continue;
559
+ if (claimed.has(f.pathname) || covered(f.pathname))
560
+ continue;
561
+ const file = f.file.replace(/\\/g, "/");
562
+ let meta = {};
563
+ const suffix = [".html", ".body"].find((s) => file.endsWith(s));
564
+ if (suffix) {
565
+ try {
566
+ meta = JSON.parse(readFileSync(join(args.distDir, file.slice(0, -suffix.length) + ".meta"), "utf-8"));
567
+ } catch {}
568
+ }
569
+ const isHtml = file.endsWith(".html");
570
+ const contentType = meta.headers?.["content-type"];
571
+ if (!isHtml && !contentType)
572
+ continue;
573
+ const headers = {};
574
+ let tags = [];
575
+ for (const [k, v] of Object.entries(meta.headers ?? {})) {
576
+ const lc = k.toLowerCase();
577
+ if (lc === "x-next-cache-tags") {
578
+ tags = v.split(",").map((t) => t.trim()).filter(Boolean);
579
+ } else if (lc !== "vary" && lc !== "content-type") {
580
+ headers[k] = v;
581
+ }
582
+ }
583
+ claimed.add(f.pathname);
584
+ staticPages.push({
585
+ path: f.pathname,
586
+ htmlKey: `__runtime/.next/${file}`,
587
+ rscKey: null,
588
+ headers,
589
+ status: meta.status ?? 200,
590
+ tags,
591
+ ...isHtml ? {} : { contentType }
592
+ });
593
+ }
594
+ return {
595
+ tier1,
596
+ staticPages,
597
+ disabled: null,
598
+ customCacheHandler: snapshot.hasCustomCacheHandler
599
+ };
600
+ }
601
+ function computeStaticTiers(args) {
602
+ const { distDir, staticFiles, publicFiles, assetPrefix } = args;
603
+ const snapshot = readAdapterSnapshot(distDir);
604
+ if (!snapshot) {
605
+ throw new Error('next-bun-compile: adapter outputs not found for this build. Build through the adapter: set `adapterPath: "next-bun-compile/adapter"` in next.config (or NEXT_ADAPTER_PATH=next-bun-compile/adapter) and run `next build`.');
606
+ }
607
+ return computeStaticTiersFromSnapshot(snapshot, {
608
+ distDir,
609
+ staticFiles,
610
+ publicFiles,
611
+ assetPrefix
612
+ });
613
+ }
614
+ function generateEntryPoint(options) {
615
+ const { standaloneDir, serverDir, distDir, projectDir } = options;
616
+ generateStubs(standaloneDir);
617
+ patchRequireHook(standaloneDir);
618
+ fixModuleResolution(standaloneDir);
619
+ const staticDir = join(distDir, "static");
620
+ const staticFiles = walkDir(staticDir).map((f) => ({
621
+ ...f,
622
+ urlPath: `/_next/static/${f.relativePath.replace(/\\/g, "/")}`
623
+ }));
624
+ const publicDir = join(projectDir, "public");
625
+ const publicFiles = walkDir(publicDir).map((f) => ({
626
+ ...f,
627
+ urlPath: `/${f.relativePath.replace(/\\/g, "/")}`
628
+ }));
629
+ const rsfConfig = JSON.parse(readFileSync(join(distDir, "required-server-files.json"), "utf-8")).config ?? {};
630
+ const assetPrefix = rsfConfig.assetPrefix ?? "";
631
+ const {
632
+ tier1,
633
+ staticPages,
634
+ disabled: tiersDisabled,
635
+ customCacheHandler
636
+ } = computeStaticTiers({
637
+ distDir,
638
+ staticFiles,
639
+ publicFiles,
640
+ assetPrefix
641
+ });
642
+ if (tiersDisabled) {
643
+ console.log(`next-bun-compile: memory tiers disabled (${tiersDisabled}) — all requests go through Next`);
644
+ } else {
645
+ console.log(`next-bun-compile: Serving ${tier1.length} assets + ${staticPages.length} prerendered pages from memory`);
646
+ }
647
+ const standaloneNextDir = join(serverDir, ".next");
648
+ const hasCustomCacheHandler = customCacheHandler;
649
+ if (staticPages.length > 0 && hasCustomCacheHandler) {
650
+ console.log("next-bun-compile: custom cacheHandler detected — prerendered pages stay with Next (Tier 2 off)");
651
+ staticPages.length = 0;
652
+ }
653
+ const turbopackAliases = findTurbopackAliases(standaloneNextDir);
654
+ const aliasNames = new Set(turbopackAliases.map((a) => a.alias));
655
+ const runtimeFiles = walkDir(standaloneNextDir).filter((f) => {
656
+ const m = f.relativePath.replace(/\\/g, "/").match(/^node_modules\/([^/]+)/);
657
+ return !(m && aliasNames.has(m[1]));
658
+ }).map((f) => ({
659
+ ...f,
660
+ urlPath: `__runtime/.next/${f.relativePath.replace(/\\/g, "/")}`
661
+ }));
662
+ const externalModules = collectExternalModules(standaloneDir);
663
+ const externalDir = join(serverDir, ".next/__external");
664
+ for (const { mod, src } of externalModules) {
665
+ if (!existsSync(src))
666
+ continue;
667
+ const dest = join(externalDir, mod);
668
+ mkdirSync(join(dest, ".."), { recursive: true });
669
+ copyFileSync(src, dest);
670
+ runtimeFiles.push({
671
+ absolutePath: dest,
672
+ relativePath: `__external/${mod}`,
673
+ urlPath: `__runtime/.next/node_modules/${mod.replace(/\\/g, "/")}`
674
+ });
675
+ }
676
+ if (externalModules.length > 0) {
677
+ console.log(`next-bun-compile: Embedding ${externalModules.length} external modules for SSR`);
678
+ }
679
+ const canonicalResolutions = buildCanonicalResolutions(externalDir, turbopackAliases);
680
+ validateAliasResolutions(turbopackAliases, canonicalResolutions);
681
+ const rewrittenChunks = rewriteTurbopackAliases(standaloneNextDir, turbopackAliases, canonicalResolutions);
682
+ const assetsToEmbed = assetPrefix ? [...publicFiles, ...runtimeFiles] : [...staticFiles, ...publicFiles, ...runtimeFiles];
683
+ if (assetPrefix) {
684
+ console.log(`next-bun-compile: assetPrefix detected — skipping ${staticFiles.length} static assets (served from CDN)`);
685
+ }
686
+ console.log(`next-bun-compile: Embedding ${assetsToEmbed.length} assets (${staticFiles.length} static + ${publicFiles.length} public + ${runtimeFiles.length} runtime)`);
687
+ const hasher = createHash("sha256");
688
+ for (const asset of assetsToEmbed) {
689
+ hasher.update(asset.urlPath);
690
+ hasher.update("\x00");
691
+ hasher.update(readFileSync(asset.absolutePath));
692
+ }
693
+ const buildHash = hasher.digest("hex");
694
+ const imports = [];
695
+ const mapEntries = [];
696
+ for (const [i, asset] of assetsToEmbed.entries()) {
697
+ const varName = toVarName(asset.urlPath, i);
698
+ const importPath = relative(serverDir, asset.absolutePath).replace(/\\/g, "/");
699
+ imports.push(`import ${varName} from "./${importPath}" with { type: "file" };`);
700
+ mapEntries.push(` ["${asset.urlPath}", ${varName}],`);
701
+ }
702
+ writeFileSync(join(serverDir, "assets.generated.js"), `${imports.join(`
703
+ `)}
704
+ export const assetMap = new Map([
705
+ ${mapEntries.join(`
706
+ `)}
707
+ ]);
708
+ `);
709
+ const serveRuntimeSrc = join(import.meta.dirname, "runtime/serve.js");
710
+ copyFileSync(existsSync(serveRuntimeSrc) ? serveRuntimeSrc : join(import.meta.dirname, "../src/runtime/serve.js"), join(serverDir, "nbc-serve.js"));
711
+ const assetExtractions = assetsToEmbed.map((a) => {
712
+ let diskPath;
713
+ if (a.urlPath.startsWith("__runtime/")) {
714
+ diskPath = a.urlPath.slice("__runtime/".length);
715
+ } else if (a.urlPath.startsWith("/_next/static/")) {
716
+ diskPath = ".next/static/" + a.relativePath;
717
+ } else {
718
+ diskPath = "public/" + a.relativePath;
719
+ }
720
+ return [a.urlPath, diskPath];
721
+ });
722
+ const serverEntry = `import { assetMap } from "./assets.generated.js";
723
+ const path = require("path");
724
+ const fs = require("fs");
725
+ const Module = require("module");
726
+
727
+ // NBC_RUNTIME_DIR relocates extraction + Next's working dir — point it at
728
+ // tmpfs (e.g. /tmp/app) for RAM-backed runtime files and compatibility
729
+ // with read-only root filesystems. Default: next to the binary.
730
+ const baseDir = process.env.NBC_RUNTIME_DIR
731
+ ? path.resolve(process.env.NBC_RUNTIME_DIR)
732
+ : path.dirname(process.execPath);
733
+ fs.mkdirSync(baseDir, { recursive: true });
734
+ process.chdir(baseDir);
735
+ process.env.NODE_ENV = "production";
736
+
737
+ // Install a fallback Module._resolveFilename hook. bun's compiled-binary
738
+ // resolver doesn't walk node_modules from inside a node_modules entry, so
739
+ // once execution enters an extracted package (sharp/lib/index.js → require
740
+ // of detect-libc, @img/sharp-linux-x64/sharp.node, etc.) bun gives up. The
741
+ // hook also redirects turbopack-mangled aliases ("sharp-457ea9eae1af1a9c"
742
+ // → "sharp") so the chunks' externalized require/import calls land on the
743
+ // canonical packages that collectExternalModules extracted.
744
+ //
745
+ // Runs ONLY when bun's resolver throws and reimplements Node-compatible
746
+ // resolution from scratch — walk node_modules, read package.json main,
747
+ // honor exports maps. Generic: every externalized package's internal deps
748
+ // get resolved without per-package patching.
749
+ const __nbcAliases = ${JSON.stringify(Object.fromEntries(turbopackAliases.map((a) => [a.alias, a.target])))};
750
+ // Debug mode: set NEXT_BUN_COMPILE_DEBUG=1 to log every resolver-hook
751
+ // decision (alias redirects, fallback walks, fallback failures). Off by
752
+ // default so production logs stay clean; turn it on when reproducing a
753
+ // resolution bug — that one log line is usually enough to know which
754
+ // package was missing and where the walk gave up.
755
+ const __nbcDebug = process.env.NEXT_BUN_COMPILE_DEBUG === "1";
756
+ function __nbcLog(msg) { console.log("next-bun-compile [debug]:", msg); }
757
+ if (__nbcDebug) {
758
+ const n = Object.keys(__nbcAliases).length;
759
+ __nbcLog(\`resolver hook installed; \${n} alias mapping(s):\`);
760
+ for (const [k, v] of Object.entries(__nbcAliases)) {
761
+ __nbcLog(\` \${k} → \${v}\`);
762
+ }
763
+ }
764
+ const __nbcOrigResolveFilename = Module._resolveFilename;
765
+ function __nbcStatFile(p) {
766
+ try { return fs.statSync(p).isFile() ? p : null; } catch { return null; }
767
+ }
768
+ function __nbcResolveMain(pkgDir, pkgJson) {
769
+ const main = pkgJson && typeof pkgJson.main === "string" ? pkgJson.main : "index.js";
770
+ return __nbcStatFile(path.join(pkgDir, main))
771
+ || __nbcStatFile(path.join(pkgDir, main + ".js"))
772
+ || __nbcStatFile(path.join(pkgDir, main + ".cjs"))
773
+ || __nbcStatFile(path.join(pkgDir, main + ".mjs"))
774
+ || __nbcStatFile(path.join(pkgDir, main, "index.js"))
775
+ || __nbcStatFile(path.join(pkgDir, "index.js"))
776
+ || __nbcStatFile(path.join(pkgDir, "index.cjs"));
777
+ }
778
+ function __nbcResolveSubpath(pkgDir, pkgJson, sub) {
779
+ // Honor exports map (CJS-relevant conditions only)
780
+ if (pkgJson && pkgJson.exports && typeof pkgJson.exports === "object") {
781
+ const key = "./" + sub;
782
+ const entry = pkgJson.exports[key];
783
+ if (entry) {
784
+ let target = typeof entry === "string"
785
+ ? entry
786
+ : entry.require || entry.node || entry.default;
787
+ if (typeof target === "string" && target.startsWith("./")) {
788
+ const f = __nbcStatFile(path.join(pkgDir, target.slice(2)));
789
+ if (f) return f;
790
+ }
791
+ }
792
+ }
793
+ // Direct file forms
794
+ const direct = __nbcStatFile(path.join(pkgDir, sub))
795
+ || __nbcStatFile(path.join(pkgDir, sub + ".js"))
796
+ || __nbcStatFile(path.join(pkgDir, sub + ".cjs"))
797
+ || __nbcStatFile(path.join(pkgDir, sub + ".mjs"))
798
+ || __nbcStatFile(path.join(pkgDir, sub + ".json"));
799
+ if (direct) return direct;
800
+ // Subpath is a directory: read its own package.json + main (e.g.
801
+ // next/dist/compiled/source-map has main: "source-map.js"). Falls
802
+ // back to index.js / index.cjs lookup if no package.json.
803
+ const subDir = path.join(pkgDir, sub);
804
+ try {
805
+ if (fs.statSync(subDir).isDirectory()) {
806
+ const subPkgPath = path.join(subDir, "package.json");
807
+ if (fs.existsSync(subPkgPath)) {
808
+ try {
809
+ const subPkg = JSON.parse(fs.readFileSync(subPkgPath, "utf-8"));
810
+ const main = typeof subPkg.main === "string" ? subPkg.main : null;
811
+ if (main) {
812
+ const f = __nbcStatFile(path.join(subDir, main))
813
+ || __nbcStatFile(path.join(subDir, main + ".js"));
814
+ if (f) return f;
815
+ }
816
+ } catch {}
817
+ }
818
+ }
819
+ } catch {}
820
+ return __nbcStatFile(path.join(pkgDir, sub, "index.js"))
821
+ || __nbcStatFile(path.join(pkgDir, sub, "index.cjs"));
822
+ }
823
+ function __nbcResolvePackage(request, fromDir) {
824
+ let pkgName, sub = "";
825
+ if (request[0] === "@") {
826
+ const m = request.match(/^(@[^/]+\\/[^/]+)(?:\\/(.+))?$/);
827
+ if (!m) return null;
828
+ pkgName = m[1]; sub = m[2] || "";
829
+ } else {
830
+ const idx = request.indexOf("/");
831
+ if (idx === -1) { pkgName = request; }
832
+ else { pkgName = request.slice(0, idx); sub = request.slice(idx + 1); }
833
+ }
834
+ let dir = fromDir;
835
+ while (dir.length > 1) {
836
+ const pkgDir = path.join(dir, "node_modules", pkgName);
837
+ if (fs.existsSync(pkgDir)) {
838
+ let pkgJson = null;
839
+ const pkgJsonPath = path.join(pkgDir, "package.json");
840
+ if (fs.existsSync(pkgJsonPath)) {
841
+ try { pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8")); } catch {}
842
+ }
843
+ const resolved = sub
844
+ ? __nbcResolveSubpath(pkgDir, pkgJson, sub)
845
+ : __nbcResolveMain(pkgDir, pkgJson);
846
+ if (resolved) return resolved;
847
+ }
848
+ const parent = path.dirname(dir);
849
+ if (parent === dir) break;
850
+ dir = parent;
851
+ }
852
+ return null;
853
+ }
854
+ function __nbcRedirectAlias(request) {
855
+ if (typeof request !== "string" || request.length === 0) return request;
856
+ // Direct alias match
857
+ if (Object.prototype.hasOwnProperty.call(__nbcAliases, request)) {
858
+ return __nbcAliases[request];
859
+ }
860
+ // Alias-with-subpath match
861
+ const slash = request.indexOf("/", request[0] === "@" ? request.indexOf("/") + 1 : 0);
862
+ if (slash === -1) return request;
863
+ const head = request.slice(0, slash);
864
+ if (Object.prototype.hasOwnProperty.call(__nbcAliases, head)) {
865
+ return __nbcAliases[head] + request.slice(slash);
866
+ }
867
+ return request;
868
+ }
869
+ Module._resolveFilename = function(request, parent, isMain, options) {
870
+ const redirected = __nbcRedirectAlias(request);
871
+ if (__nbcDebug && redirected !== request) {
872
+ const from = parent && parent.filename ? parent.filename : "<unknown>";
873
+ __nbcLog(\`redirected "\${request}" → "\${redirected}" (from \${from})\`);
874
+ }
875
+ try {
876
+ return __nbcOrigResolveFilename.call(this, redirected, parent, isMain, options);
877
+ } catch (err) {
878
+ // Only attempt fallback for bare package specifiers
879
+ if (typeof redirected !== "string" || redirected[0] === "." || redirected[0] === "/" || /^[a-z]+:/.test(redirected)) {
880
+ throw err;
881
+ }
882
+ const fromDir = parent && parent.filename ? path.dirname(parent.filename) : process.cwd();
883
+ const resolved = __nbcResolvePackage(redirected, fromDir);
884
+ if (resolved) {
885
+ if (__nbcDebug) {
886
+ __nbcLog(\`fallback resolved "\${redirected}" → \${resolved} (from \${fromDir})\`);
887
+ }
888
+ return resolved;
889
+ }
890
+ if (__nbcDebug) {
891
+ __nbcLog(\`fallback FAILED for "\${redirected}" (from \${fromDir}); throwing original ResolveMessage\`);
892
+ }
893
+ throw err;
894
+ }
895
+ };
896
+
897
+ const nextConfig = ${JSON.stringify(rsfConfig)};
898
+ process.env.__NEXT_PRIVATE_STANDALONE_CONFIG = JSON.stringify(nextConfig);
899
+
900
+ const currentPort = parseInt(process.env.PORT, 10) || 3000;
901
+ const hostname = process.env.HOSTNAME || "0.0.0.0";
902
+ let keepAliveTimeout = parseInt(process.env.KEEP_ALIVE_TIMEOUT, 10);
903
+ if (!Number.isFinite(keepAliveTimeout) || keepAliveTimeout < 0) {
904
+ keepAliveTimeout = undefined;
905
+ }
906
+
907
+ const extractions = ${JSON.stringify(assetExtractions)};
908
+ // Chunks that got __NBC_BASE__ placeholders injected at build time by
909
+ // rewriteTurbopackAliases — only these need text substitution on extract;
910
+ // everything else streams straight from the binary.
911
+ const rewrittenChunks = new Set(${JSON.stringify(rewrittenChunks)});
912
+ // Written to the manifest after a complete extraction. Includes baseDir:
913
+ // if the deploy directory moves, the substituted absolute paths in the
914
+ // rewritten chunks are wrong and everything must be re-extracted.
915
+ const buildStamp = ${JSON.stringify(buildHash)} + "\\n" + baseDir;
916
+ const manifestPath = path.join(baseDir, ".next", ".nbc-extracted");
917
+ async function extractAssets() {
918
+ // Fast path: a previous boot of this exact build in this exact directory
919
+ // finished extracting — one file read, no per-asset stats.
920
+ try {
921
+ if (fs.readFileSync(manifestPath, "utf-8") === buildStamp) return;
922
+ } catch {}
923
+
924
+ // Full extraction, overwriting whatever is on disk. Skipping existing
925
+ // files would let stale ones (crashed half-extraction, previous build,
926
+ // pre-placed tampering) shadow the embedded assets forever.
927
+ const dirs = new Set();
928
+ for (const [, diskPath] of extractions) {
929
+ dirs.add(path.dirname(path.join(baseDir, diskPath)));
930
+ }
931
+ for (const d of dirs) fs.mkdirSync(d, { recursive: true });
932
+
933
+ // Concurrent writes, bounded so thousands of in-flight fds can't trip
934
+ // EMFILE under conservative ulimits.
935
+ let idx = 0;
936
+ async function worker() {
937
+ while (idx < extractions.length) {
938
+ const [urlPath, diskPath] = extractions[idx++];
939
+ const embedded = assetMap.get(urlPath);
940
+ if (!embedded) continue;
941
+ const fullPath = path.join(baseDir, diskPath);
942
+ if (rewrittenChunks.has(diskPath)) {
943
+ const text = await Bun.file(embedded).text();
944
+ await Bun.write(fullPath, text.split("__NBC_BASE__").join(baseDir));
945
+ } else {
946
+ await Bun.write(fullPath, Bun.file(embedded));
947
+ }
948
+ }
949
+ }
950
+ await Promise.all(
951
+ Array.from({ length: Math.min(64, extractions.length) }, worker)
952
+ );
953
+ fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
954
+ fs.writeFileSync(manifestPath, buildStamp);
955
+ console.log(\`Extracted \${extractions.length} assets\`);
956
+ }
957
+
958
+ const __NBC_TIER1 = ${JSON.stringify(tier1)};
959
+ const __NBC_STATIC_PAGES = ${JSON.stringify(staticPages)};
960
+
961
+ extractAssets().then(() => {
962
+ const { start } = require("./nbc-serve.js");
963
+ return start({
964
+ assetMap,
965
+ nextConfig,
966
+ port: currentPort,
967
+ hostname,
968
+ keepAliveTimeout,
969
+ tier1: __NBC_TIER1,
970
+ staticPages: __NBC_STATIC_PAGES,
971
+ baseDir,
972
+ // Revalidation events are observed on the default filesystem cache
973
+ // handler; with a custom handler they never fire, so response
974
+ // caching would serve stale pages.
975
+ enableL1: ${JSON.stringify(!hasCustomCacheHandler)},
976
+ });
977
+ }).catch((err) => { console.error(err); process.exit(1); });
978
+ `;
979
+ writeFileSync(join(serverDir, "server-entry.js"), serverEntry);
980
+ return serverDir;
981
+ }
982
+ // src/compile.ts
983
+ import { execFileSync } from "node:child_process";
984
+ import { join as join2 } from "node:path";
985
+ function compile(options) {
986
+ const { serverDir, outfile, extraArgs = [] } = options;
987
+ const entryPoint = join2(serverDir, "server-entry.js");
988
+ const args = [
989
+ "build",
990
+ entryPoint,
991
+ "--production",
992
+ "--compile",
993
+ "--minify",
994
+ "--sourcemap",
995
+ "--external",
996
+ "webpack",
997
+ "--external",
998
+ "webpack/*",
999
+ "--external",
1000
+ "next/dist/build/webpack/*",
1001
+ "--external",
1002
+ "sass",
1003
+ "--external",
1004
+ "critters",
1005
+ "--define",
1006
+ "process.env.TURBOPACK=1",
1007
+ "--define",
1008
+ "process.env.__NEXT_EXPERIMENTAL_REACT=",
1009
+ "--define",
1010
+ 'process.env.NEXT_RUNTIME="nodejs"',
1011
+ "--outfile",
1012
+ outfile,
1013
+ ...process.env.NBC_TARGET ? [`--target=${process.env.NBC_TARGET}`] : [],
1014
+ ...extraArgs
1015
+ ];
1016
+ console.log(`next-bun-compile: Compiling to ${outfile}...`);
1017
+ try {
1018
+ execFileSync("bun", args, { stdio: "inherit" });
1019
+ } catch (err) {
1020
+ if (err.code === "ENOENT") {
1021
+ console.error("next-bun-compile: `bun` was not found on PATH. Install it from https://bun.sh and re-run.");
1022
+ process.exit(1);
1023
+ }
1024
+ throw err;
1025
+ }
1026
+ console.log(`next-bun-compile: Done → ${outfile}`);
1027
+ }
1028
+ // src/build.ts
1029
+ import { copyFileSync as copyFileSync2, existsSync as existsSync2, mkdirSync as mkdirSync2, statSync as statSync2 } from "node:fs";
1030
+ import { createRequire } from "node:module";
1031
+ import { dirname, join as join3 } from "node:path";
1032
+ async function ensureServerRuntime(projectDir, standaloneDir) {
1033
+ const req = createRequire(join3(projectDir, "package.json"));
1034
+ const { nodeFileTrace } = req("next/dist/compiled/@vercel/nft");
1035
+ const entries = [
1036
+ req.resolve("next/dist/server/lib/router-server.js"),
1037
+ req.resolve("next/dist/server/lib/incremental-cache/file-system-cache.js")
1038
+ ];
1039
+ const { fileList } = await nodeFileTrace(entries, {
1040
+ base: "/",
1041
+ ignore: [
1042
+ "**/*.d.ts",
1043
+ "**/*.map",
1044
+ "**/next/dist/compiled/next-server/**/*.dev.js",
1045
+ "**/next/dist/compiled/webpack/*",
1046
+ "**/node_modules/webpack5/**/*",
1047
+ "**/next/dist/server/lib/route-resolver*",
1048
+ "**/next/dist/compiled/semver/semver/**/*.js",
1049
+ "**/next/dist/compiled/jest-worker/**/*",
1050
+ "**/node_modules/react/**/*.development.js",
1051
+ "**/node_modules/react-dom/**/*.development.js"
1052
+ ]
1053
+ });
1054
+ let copied = 0;
1055
+ for (const f of fileList) {
1056
+ const src = "/" + f;
1057
+ const idx = src.indexOf("/node_modules/");
1058
+ if (idx === -1)
1059
+ continue;
1060
+ const dest = join3(standaloneDir, src.slice(idx + 1));
1061
+ try {
1062
+ if (statSync2(src).isDirectory())
1063
+ continue;
1064
+ } catch {
1065
+ continue;
1066
+ }
1067
+ if (existsSync2(dest))
1068
+ continue;
1069
+ mkdirSync2(dirname(dest), { recursive: true });
1070
+ copyFileSync2(src, dest);
1071
+ copied++;
1072
+ }
1073
+ if (copied > 0) {
1074
+ console.log(`next-bun-compile: added ${copied} server-runtime files to the traced tree`);
1075
+ }
1076
+ }
1077
+ async function runBuild(options) {
1078
+ const { projectDir, standaloneDir, serverDir, extraArgs = [] } = options;
1079
+ const distDir = join3(projectDir, ".next");
1080
+ await ensureServerRuntime(projectDir, standaloneDir);
1081
+ generateEntryPoint({ standaloneDir, serverDir, distDir, projectDir });
1082
+ const outfile = join3(projectDir, "server");
1083
+ compile({ serverDir, outfile, extraArgs });
1084
+ return outfile;
1085
+ }
1086
+ // src/adapter.ts
1087
+ import {
1088
+ copyFileSync as copyFileSync3,
1089
+ existsSync as existsSync3,
1090
+ mkdirSync as mkdirSync3,
1091
+ readFileSync as readFileSync2,
1092
+ rmSync,
1093
+ statSync as statSync3,
1094
+ writeFileSync as writeFileSync2
1095
+ } from "node:fs";
1096
+ import { dirname as dirname2, join as join4, relative as relative2, resolve } from "node:path";
1097
+ var ADAPTER_OUTPUTS_FILE = "nbc-adapter-outputs.json";
1098
+ function flattenHeaders(headers) {
1099
+ const out = {};
1100
+ for (const [k, v] of Object.entries(headers ?? {})) {
1101
+ out[k] = Array.isArray(v) ? v.join(", ") : v;
1102
+ }
1103
+ return out;
1104
+ }
1105
+ async function assembleStandalone(ctx) {
1106
+ const staging = join4(ctx.distDir, "nbc-standalone");
1107
+ rmSync(staging, { recursive: true, force: true });
1108
+ const copied = new Set;
1109
+ let escaped = 0;
1110
+ const copyTo = (destRel, src) => {
1111
+ if (destRel.startsWith("..")) {
1112
+ escaped++;
1113
+ return;
1114
+ }
1115
+ if (copied.has(destRel))
1116
+ return;
1117
+ try {
1118
+ if (!statSync3(src).isFile())
1119
+ return;
1120
+ } catch {
1121
+ return;
1122
+ }
1123
+ copied.add(destRel);
1124
+ const dest = join4(staging, destRel);
1125
+ mkdirSync3(dirname2(dest), { recursive: true });
1126
+ copyFileSync3(src, dest);
1127
+ };
1128
+ const outputs = ctx.outputs ?? {};
1129
+ const routeOutputs = [
1130
+ ...outputs.pages ?? [],
1131
+ ...outputs.pagesApi ?? [],
1132
+ ...outputs.appPages ?? [],
1133
+ ...outputs.appRoutes ?? [],
1134
+ ...outputs.middleware ? [outputs.middleware] : []
1135
+ ];
1136
+ for (const o of routeOutputs) {
1137
+ if (o.filePath)
1138
+ copyTo(relative2(ctx.repoRoot, o.filePath), o.filePath);
1139
+ for (const [key, src] of Object.entries(o.assets ?? {}))
1140
+ copyTo(key, src);
1141
+ }
1142
+ for (const nft of ["next-server.js.nft.json", "next-minimal-server.js.nft.json"]) {
1143
+ const nftPath = join4(ctx.distDir, nft);
1144
+ if (!existsSync3(nftPath))
1145
+ continue;
1146
+ try {
1147
+ const { files } = JSON.parse(readFileSync2(nftPath, "utf-8"));
1148
+ for (const f of files) {
1149
+ const src = resolve(ctx.distDir, f);
1150
+ copyTo(relative2(ctx.repoRoot, src), src);
1151
+ }
1152
+ } catch {}
1153
+ }
1154
+ const copySeed = (file) => {
1155
+ copyTo(relative2(ctx.repoRoot, file), file);
1156
+ const suffix = [".html", ".body"].find((s) => file.endsWith(s));
1157
+ if (suffix) {
1158
+ const meta = file.slice(0, -suffix.length) + ".meta";
1159
+ copyTo(relative2(ctx.repoRoot, meta), meta);
1160
+ }
1161
+ };
1162
+ for (const p of outputs.prerenders ?? []) {
1163
+ if (p.fallback?.filePath)
1164
+ copySeed(p.fallback.filePath);
1165
+ }
1166
+ for (const f of outputs.staticFiles ?? []) {
1167
+ if (!f.filePath || f.pathname?.startsWith("/_next/"))
1168
+ continue;
1169
+ copySeed(f.filePath);
1170
+ if (f.filePath.endsWith(".body")) {
1171
+ const routeJs = join4(f.filePath.slice(0, -".body".length), "route.js");
1172
+ if (existsSync3(routeJs)) {
1173
+ copyTo(relative2(ctx.repoRoot, routeJs), routeJs);
1174
+ try {
1175
+ const { files } = JSON.parse(readFileSync2(routeJs + ".nft.json", "utf-8"));
1176
+ for (const dep of files) {
1177
+ const src = resolve(dirname2(routeJs), dep);
1178
+ copyTo(relative2(ctx.repoRoot, src), src);
1179
+ }
1180
+ } catch {}
1181
+ }
1182
+ }
1183
+ }
1184
+ for (const extra of ["BUILD_ID", "required-server-files.json"]) {
1185
+ const src = join4(ctx.distDir, extra);
1186
+ copyTo(relative2(ctx.repoRoot, src), src);
1187
+ }
1188
+ const appStaging = join4(staging, relative2(ctx.repoRoot, ctx.projectDir));
1189
+ mkdirSync3(appStaging, { recursive: true });
1190
+ if (escaped > 0) {
1191
+ console.warn(`next-bun-compile: ${escaped} traced file(s) outside the repo root were skipped`);
1192
+ }
1193
+ console.log(`next-bun-compile: assembled ${copied.size} traced files (no standalone output needed)`);
1194
+ return { standaloneDir: staging, serverDir: appStaging };
1195
+ }
1196
+ var adapter = {
1197
+ name: "next-bun-compile",
1198
+ async onBuildComplete(ctx) {
1199
+ const routingRules = [];
1200
+ for (const key of [
1201
+ "beforeMiddleware",
1202
+ "beforeFiles",
1203
+ "afterFiles",
1204
+ "onMatch",
1205
+ "fallback"
1206
+ ]) {
1207
+ const list = ctx.routing?.[key];
1208
+ if (!Array.isArray(list))
1209
+ continue;
1210
+ for (const route of list) {
1211
+ const headerKeys = Object.keys(route.headers ?? {});
1212
+ const isDeploymentIdRule = headerKeys.length === 1 && headerKeys[0] === "x-nextjs-deployment-id";
1213
+ if (route.sourceRegex && typeof route.source === "string" && !route.priority && !isDeploymentIdRule && (headerKeys.length > 0 || route.destination || route.status)) {
1214
+ routingRules.push(route.sourceRegex);
1215
+ }
1216
+ }
1217
+ }
1218
+ const prerenders = (ctx.outputs?.prerenders ?? []).flatMap((p) => {
1219
+ if (!p.pathname)
1220
+ return [];
1221
+ return [
1222
+ {
1223
+ pathname: p.pathname,
1224
+ file: p.fallback?.filePath ? relative2(ctx.distDir, p.fallback.filePath) : null,
1225
+ status: p.fallback?.initialStatus ?? 200,
1226
+ revalidate: p.fallback?.initialRevalidate ?? false,
1227
+ postponed: !!p.fallback?.postponedState,
1228
+ headers: flattenHeaders(p.fallback?.initialHeaders)
1229
+ }
1230
+ ];
1231
+ });
1232
+ const staticFiles = (ctx.outputs?.staticFiles ?? []).flatMap((f) => {
1233
+ if (!f.pathname || !f.filePath)
1234
+ return [];
1235
+ if (f.pathname.startsWith("/_next/"))
1236
+ return [];
1237
+ return [
1238
+ {
1239
+ pathname: f.pathname,
1240
+ file: relative2(ctx.distDir, f.filePath)
1241
+ }
1242
+ ];
1243
+ });
1244
+ const snapshot = {
1245
+ version: 1,
1246
+ buildId: ctx.buildId,
1247
+ nextVersion: ctx.nextVersion,
1248
+ basePath: ctx.config.basePath ?? "",
1249
+ i18n: !!ctx.config.i18n,
1250
+ hasCustomCacheHandler: !!ctx.config.cacheHandler,
1251
+ middlewareMatchers: (ctx.outputs?.middleware?.config?.matchers ?? []).map((m) => m.sourceRegex).filter((r) => !!r),
1252
+ routingRules,
1253
+ prerenders,
1254
+ staticFiles
1255
+ };
1256
+ writeFileSync2(join4(ctx.distDir, ADAPTER_OUTPUTS_FILE), JSON.stringify(snapshot, null, 2));
1257
+ console.log(`next-bun-compile: adapter outputs written (${prerenders.length} prerender entries)`);
1258
+ const { standaloneDir, serverDir } = await assembleStandalone(ctx);
1259
+ await runBuild({ projectDir: ctx.projectDir, standaloneDir, serverDir });
1260
+ }
1261
+ };
1262
+ var adapter_default = adapter;
7
1263
  export {
8
1264
  runBuild,
9
1265
  generateEntryPoint,