next-bun-compile 0.10.0 → 0.11.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/README.md CHANGED
@@ -23,45 +23,56 @@ bun add -D next-bun-compile
23
23
 
24
24
  ## Setup
25
25
 
26
- Enable Next.js standalone output in your `next.config.ts`:
26
+ next-bun-compile is a [Next.js Build Adapter](https://nextjs.org/docs/app/api-reference/config/next-config-js/adapterPath). Point `adapterPath` at it in `next.config.ts`:
27
27
 
28
28
  ```ts
29
29
  import type { NextConfig } from "next";
30
30
 
31
31
  const nextConfig: NextConfig = {
32
- output: "standalone",
32
+ adapterPath: "next-bun-compile/adapter",
33
33
  };
34
34
 
35
35
  export default nextConfig;
36
36
  ```
37
37
 
38
- Update your build script in `package.json`:
38
+ Or enable it without touching the config at all:
39
39
 
40
- ```json
41
- {
42
- "scripts": {
43
- "build": "next build && next-bun-compile"
44
- }
45
- }
40
+ ```bash
41
+ NEXT_ADAPTER_PATH=next-bun-compile/adapter next build
46
42
  ```
47
43
 
44
+ No `output: "standalone"` needed — the adapter assembles its own traced
45
+ output tree.
46
+
47
+ > **Monorepos:** Next resolves `adapterPath` from its own package location.
48
+ > If `next-bun-compile` is a nested workspace dependency, resolve it from
49
+ > the app dir instead:
50
+ >
51
+ > ```ts
52
+ > import { createRequire } from "node:module";
53
+ > const req = createRequire(process.cwd() + "/");
54
+ > const nextConfig: NextConfig = {
55
+ > adapterPath: req.resolve("next-bun-compile/adapter"),
56
+ > };
57
+ > ```
58
+
48
59
  ## Usage
49
60
 
50
61
  ```bash
51
- bun run build # Builds Next.js + compiles to ./server
52
- ./server # Starts on port 3000
62
+ next build # Builds Next.js + compiles to ./server, one command
63
+ ./server # Starts on port 3000
53
64
  ```
54
65
 
55
- The binary is fully self-contained — static assets, public files, and the Next.js server are all embedded. Just copy it anywhere and run.
66
+ The binary is fully self-contained — static assets, public files, prerendered pages, and the Next.js server are all embedded. Just copy it anywhere and run. Static assets and fully-static prerendered pages are served straight from memory; ISR and cache-component responses get an in-memory cache that Next's own revalidation invalidates.
56
67
 
57
68
  ### Cross-Compilation
58
69
 
59
- Any flags passed to `next-bun-compile` are forwarded to `bun build --compile`. Use `--target` to cross-compile for a different platform:
70
+ Set `NBC_TARGET` to cross-compile for a different platform:
60
71
 
61
72
  ```bash
62
- next-bun-compile --target=bun-linux-x64
63
- next-bun-compile --target=bun-linux-arm64
64
- next-bun-compile --target=bun-windows-x64
73
+ NBC_TARGET=bun-linux-x64 next build
74
+ NBC_TARGET=bun-linux-arm64 next build
75
+ NBC_TARGET=bun-windows-x64 next build
65
76
  ```
66
77
 
67
78
  See the [Bun cross-compilation docs](https://bun.sh/docs/bundler/executables#cross-compile) for all available targets.
@@ -73,6 +84,8 @@ See the [Bun cross-compilation docs](https://bun.sh/docs/bundler/executables#cro
73
84
  | `PORT` | `3000` | Server port |
74
85
  | `HOSTNAME` | `0.0.0.0` | Server hostname |
75
86
  | `KEEP_ALIVE_TIMEOUT` | — | HTTP keep-alive timeout (ms) |
87
+ | `NBC_RUNTIME_DIR` | binary's directory | Where runtime files extract and `.next/cache` lives. Point at tmpfs (e.g. `/tmp/app`) for RAM-backed runtime files and read-only root filesystems |
88
+ | `NBC_TARGET` | host platform | Cross-compile target (build time) |
76
89
 
77
90
  ### CDN / `assetPrefix`
78
91
 
@@ -80,7 +93,7 @@ If you configure `assetPrefix` in your `next.config.ts`, static assets (`/_next/
80
93
 
81
94
  ```ts
82
95
  const nextConfig: NextConfig = {
83
- output: "standalone",
96
+ adapterPath: "next-bun-compile/adapter",
84
97
  assetPrefix: "https://cdn.example.com",
85
98
  };
86
99
  ```
@@ -117,16 +130,6 @@ Some modules can't be resolved at compile time but are never reached in producti
117
130
 
118
131
  ## Troubleshooting
119
132
 
120
- ### Stale standalone output after upgrading Next.js
121
-
122
- Next.js doesn't clean the standalone output directory between builds. If you upgrade Next.js versions, stale files from the old version can cause runtime errors like `Cannot find module 'next/dist/compiled/source-map'`.
123
-
124
- **Fix:** Clean the standalone output before rebuilding:
125
-
126
- ```bash
127
- rm -rf .next/standalone && bun next build && next-bun-compile
128
- ```
129
-
130
133
  ### Packages with dynamic `require()` calls (e.g. pino)
131
134
 
132
135
  Some packages like `pino` use dynamic `require()` calls internally (for worker threads, transports, etc.). Turbopack can't resolve these at build time, so they fail at runtime inside the compiled binary with errors like:
@@ -139,7 +142,7 @@ Failed to load external module pino-142500b1eb3f4baf: Cannot find package ...
139
142
 
140
143
  ```ts
141
144
  const nextConfig: NextConfig = {
142
- output: "standalone",
145
+ adapterPath: "next-bun-compile/adapter",
143
146
  transpilePackages: ["pino", "pino-pretty"],
144
147
  };
145
148
  ```
@@ -0,0 +1,8 @@
1
+ import {
2
+ ADAPTER_OUTPUTS_FILE,
3
+ adapter_default
4
+ } from "./index-ajd4bx9z.js";
5
+ export {
6
+ adapter_default as default,
7
+ ADAPTER_OUTPUTS_FILE
8
+ };
@@ -1,3 +1,20 @@
1
+ // src/adapter.ts
2
+ import {
3
+ copyFileSync as copyFileSync3,
4
+ existsSync as existsSync3,
5
+ mkdirSync as mkdirSync3,
6
+ readFileSync as readFileSync2,
7
+ rmSync,
8
+ statSync as statSync3,
9
+ writeFileSync as writeFileSync2
10
+ } from "node:fs";
11
+ import { dirname as dirname2, join as join4, relative as relative2, resolve } from "node:path";
12
+
13
+ // src/build.ts
14
+ import { copyFileSync as copyFileSync2, existsSync as existsSync2, mkdirSync as mkdirSync2, statSync as statSync2 } from "node:fs";
15
+ import { createRequire } from "node:module";
16
+ import { dirname, join as join3 } from "node:path";
17
+
1
18
  // src/generate.ts
2
19
  import {
3
20
  writeFileSync,
@@ -12,13 +29,6 @@ import {
12
29
  } from "node:fs";
13
30
  import { join, relative, basename } from "node:path";
14
31
  import { createHash } from "node:crypto";
15
- function readAssetPrefix(distDir) {
16
- const rsfPath = join(distDir, "required-server-files.json");
17
- if (!existsSync(rsfPath))
18
- return "";
19
- const rsf = JSON.parse(readFileSync(rsfPath, "utf-8"));
20
- return rsf.config?.assetPrefix ?? "";
21
- }
22
32
  function tryStat(p) {
23
33
  try {
24
34
  return statSync(p);
@@ -46,10 +56,9 @@ function walkDir(dir, base = dir) {
46
56
  }
47
57
  return results;
48
58
  }
49
- function toVarName(filePath) {
50
- const hash = createHash("sha256").update(filePath).digest("hex").slice(0, 6);
59
+ function toVarName(filePath, index) {
51
60
  const safe = filePath.replace(/[^a-zA-Z0-9]/g, "_").slice(0, 40);
52
- return `asset_${safe}_${hash}`;
61
+ return `asset_${index}_${safe}`;
53
62
  }
54
63
  function findPackageDirs(standaloneDir, pkg) {
55
64
  const dirs = [];
@@ -336,36 +345,6 @@ function rewriteTurbopackAliases(standaloneNextDir, aliases, resolutions) {
336
345
  }
337
346
  return rewrittenPaths;
338
347
  }
339
- function findServerDir(standaloneDir) {
340
- if (existsSync(join(standaloneDir, "server.js"))) {
341
- return standaloneDir;
342
- }
343
- function search(dir) {
344
- if (!existsSync(dir))
345
- return null;
346
- for (const entry of readdirSync(dir)) {
347
- if (entry === "node_modules")
348
- continue;
349
- const full = join(dir, entry);
350
- const stat = tryStat(full);
351
- if (!stat || !stat.isDirectory())
352
- continue;
353
- if (existsSync(join(full, "server.js")))
354
- return full;
355
- const found2 = search(full);
356
- if (found2)
357
- return found2;
358
- }
359
- return null;
360
- }
361
- const found = search(standaloneDir);
362
- if (!found) {
363
- throw new Error("next-bun-compile: Could not find server.js in standalone output");
364
- }
365
- const rel = relative(standaloneDir, found);
366
- console.log(`next-bun-compile: Monorepo layout detected — server.js found at ${rel}/`);
367
- return found;
368
- }
369
348
  function patchRequireHook(standaloneDir) {
370
349
  const nextDirs = findPackageDirs(standaloneDir, "next");
371
350
  const target = "let resolve = process.env.NEXT_MINIMAL ? __non_webpack_require__.resolve : require.resolve;";
@@ -390,8 +369,12 @@ let resolve = (id) => { try { return _resolve(id); } catch { return ''; } };`;
390
369
  function collectExternalModules(standaloneDir) {
391
370
  const pkgRoots = new Map;
392
371
  function addPkg(name, path) {
393
- if (!pkgRoots.has(name))
394
- pkgRoots.set(name, path);
372
+ let set = pkgRoots.get(name);
373
+ if (!set) {
374
+ set = new Set;
375
+ pkgRoots.set(name, set);
376
+ }
377
+ set.add(path);
395
378
  }
396
379
  function scanDir(dir) {
397
380
  if (!existsSync(dir))
@@ -447,12 +430,16 @@ function collectExternalModules(standaloneDir) {
447
430
  if (pkgRoots.size === 0)
448
431
  return [];
449
432
  const results = [];
450
- for (const [name, pkgPath] of pkgRoots) {
451
- for (const f of walkDir(pkgPath)) {
452
- results.push({
453
- mod: `${name}/${f.relativePath.replace(/\\/g, "/")}`,
454
- src: f.absolutePath
455
- });
433
+ const seenMods = new Set;
434
+ for (const [name, paths] of pkgRoots) {
435
+ for (const pkgPath of paths) {
436
+ for (const f of walkDir(pkgPath)) {
437
+ const mod = `${name}/${f.relativePath.replace(/\\/g, "/")}`;
438
+ if (seenMods.has(mod))
439
+ continue;
440
+ seenMods.add(mod);
441
+ results.push({ mod, src: f.absolutePath });
442
+ }
456
443
  }
457
444
  }
458
445
  return results;
@@ -499,9 +486,108 @@ function fixModuleResolution(standaloneDir) {
499
486
  }
500
487
  }
501
488
  }
489
+ function readAdapterSnapshot(distDir) {
490
+ try {
491
+ const raw = JSON.parse(readFileSync(join(distDir, "nbc-adapter-outputs.json"), "utf-8"));
492
+ if (raw?.version !== 1 || !Array.isArray(raw.prerenders))
493
+ return null;
494
+ const buildId = readFileSync(join(distDir, "BUILD_ID"), "utf-8").trim();
495
+ if (raw.buildId !== buildId)
496
+ return null;
497
+ return raw;
498
+ } catch {
499
+ return null;
500
+ }
501
+ }
502
+ function computeStaticTiersFromSnapshot(snapshot, args) {
503
+ const none = (why) => ({
504
+ tier1: [],
505
+ staticPages: [],
506
+ disabled: why,
507
+ customCacheHandler: snapshot.hasCustomCacheHandler
508
+ });
509
+ if (snapshot.basePath)
510
+ return none("basePath is set");
511
+ if (snapshot.i18n)
512
+ return none("i18n is configured");
513
+ const matchers = [];
514
+ for (const source of [
515
+ ...snapshot.middlewareMatchers,
516
+ ...snapshot.routingRules
517
+ ]) {
518
+ try {
519
+ matchers.push(new RegExp(source));
520
+ } catch {
521
+ matchers.push(/.*/);
522
+ }
523
+ }
524
+ const covered = (p) => matchers.some((re) => re.test(p));
525
+ const tier1 = [];
526
+ if (!args.assetPrefix) {
527
+ for (const f of args.staticFiles) {
528
+ if (!covered(f.urlPath)) {
529
+ tier1.push({ urlPath: f.urlPath, key: f.urlPath, kind: "static" });
530
+ }
531
+ }
532
+ }
533
+ for (const f of args.publicFiles) {
534
+ if (!covered(f.urlPath)) {
535
+ tier1.push({ urlPath: f.urlPath, key: f.urlPath, kind: "public" });
536
+ }
537
+ }
538
+ const byPathname = new Map(snapshot.prerenders.map((p) => [p.pathname, p]));
539
+ const staticPages = [];
540
+ for (const p of snapshot.prerenders) {
541
+ if (!p.file || !p.file.endsWith(".html"))
542
+ continue;
543
+ if (p.pathname.startsWith("/_"))
544
+ continue;
545
+ if (p.revalidate !== false || p.postponed)
546
+ continue;
547
+ if (covered(p.pathname))
548
+ continue;
549
+ const rscPathname = p.pathname === "/" ? "/index.rsc" : `${p.pathname}.rsc`;
550
+ const rscFile = byPathname.get(rscPathname)?.file ?? null;
551
+ const headers = {};
552
+ let tags = [];
553
+ for (const [k, v] of Object.entries(p.headers)) {
554
+ const lc = k.toLowerCase();
555
+ if (lc === "x-next-cache-tags") {
556
+ tags = v.split(",").map((t) => t.trim()).filter(Boolean);
557
+ } else if (lc !== "vary" && lc !== "content-type") {
558
+ headers[k] = v;
559
+ }
560
+ }
561
+ staticPages.push({
562
+ path: p.pathname,
563
+ htmlKey: `__runtime/.next/${p.file.replace(/\\/g, "/")}`,
564
+ rscKey: rscFile ? `__runtime/.next/${rscFile.replace(/\\/g, "/")}` : null,
565
+ headers,
566
+ status: p.status,
567
+ tags
568
+ });
569
+ }
570
+ return {
571
+ tier1,
572
+ staticPages,
573
+ disabled: null,
574
+ customCacheHandler: snapshot.hasCustomCacheHandler
575
+ };
576
+ }
577
+ function computeStaticTiers(args) {
578
+ const { distDir, staticFiles, publicFiles, assetPrefix } = args;
579
+ const snapshot = readAdapterSnapshot(distDir);
580
+ if (!snapshot) {
581
+ 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`.');
582
+ }
583
+ return computeStaticTiersFromSnapshot(snapshot, {
584
+ staticFiles,
585
+ publicFiles,
586
+ assetPrefix
587
+ });
588
+ }
502
589
  function generateEntryPoint(options) {
503
- const { standaloneDir, distDir, projectDir } = options;
504
- const serverDir = findServerDir(standaloneDir);
590
+ const { standaloneDir, serverDir, distDir, projectDir } = options;
505
591
  generateStubs(standaloneDir);
506
592
  patchRequireHook(standaloneDir);
507
593
  fixModuleResolution(standaloneDir);
@@ -515,7 +601,30 @@ function generateEntryPoint(options) {
515
601
  ...f,
516
602
  urlPath: `/${f.relativePath.replace(/\\/g, "/")}`
517
603
  }));
604
+ const rsfConfig = JSON.parse(readFileSync(join(distDir, "required-server-files.json"), "utf-8")).config ?? {};
605
+ const assetPrefix = rsfConfig.assetPrefix ?? "";
606
+ const {
607
+ tier1,
608
+ staticPages,
609
+ disabled: tiersDisabled,
610
+ customCacheHandler
611
+ } = computeStaticTiers({
612
+ distDir,
613
+ staticFiles,
614
+ publicFiles,
615
+ assetPrefix
616
+ });
617
+ if (tiersDisabled) {
618
+ console.log(`next-bun-compile: memory tiers disabled (${tiersDisabled}) — all requests go through Next`);
619
+ } else {
620
+ console.log(`next-bun-compile: Serving ${tier1.length} assets + ${staticPages.length} prerendered pages from memory`);
621
+ }
518
622
  const standaloneNextDir = join(serverDir, ".next");
623
+ const hasCustomCacheHandler = customCacheHandler;
624
+ if (staticPages.length > 0 && hasCustomCacheHandler) {
625
+ console.log("next-bun-compile: custom cacheHandler detected — prerendered pages stay with Next (Tier 2 off)");
626
+ staticPages.length = 0;
627
+ }
519
628
  const turbopackAliases = findTurbopackAliases(standaloneNextDir);
520
629
  const aliasNames = new Set(turbopackAliases.map((a) => a.alias));
521
630
  const runtimeFiles = walkDir(standaloneNextDir).filter((f) => {
@@ -545,7 +654,6 @@ function generateEntryPoint(options) {
545
654
  const canonicalResolutions = buildCanonicalResolutions(externalDir, turbopackAliases);
546
655
  validateAliasResolutions(turbopackAliases, canonicalResolutions);
547
656
  const rewrittenChunks = rewriteTurbopackAliases(standaloneNextDir, turbopackAliases, canonicalResolutions);
548
- const assetPrefix = readAssetPrefix(distDir);
549
657
  const assetsToEmbed = assetPrefix ? [...publicFiles, ...runtimeFiles] : [...staticFiles, ...publicFiles, ...runtimeFiles];
550
658
  if (assetPrefix) {
551
659
  console.log(`next-bun-compile: assetPrefix detected — skipping ${staticFiles.length} static assets (served from CDN)`);
@@ -560,8 +668,8 @@ function generateEntryPoint(options) {
560
668
  const buildHash = hasher.digest("hex");
561
669
  const imports = [];
562
670
  const mapEntries = [];
563
- for (const asset of assetsToEmbed) {
564
- const varName = toVarName(asset.urlPath);
671
+ for (const [i, asset] of assetsToEmbed.entries()) {
672
+ const varName = toVarName(asset.urlPath, i);
565
673
  const importPath = relative(serverDir, asset.absolutePath).replace(/\\/g, "/");
566
674
  imports.push(`import ${varName} from "./${importPath}" with { type: "file" };`);
567
675
  mapEntries.push(` ["${asset.urlPath}", ${varName}],`);
@@ -573,11 +681,8 @@ ${mapEntries.join(`
573
681
  `)}
574
682
  ]);
575
683
  `);
576
- const standaloneServerSrc = readFileSync(join(serverDir, "server.js"), "utf-8");
577
- const configMatch = standaloneServerSrc.match(/const nextConfig = ({[\s\S]*?})\n/);
578
- if (!configMatch) {
579
- throw new Error("next-bun-compile: Could not extract nextConfig from standalone server.js");
580
- }
684
+ const serveRuntimeSrc = join(import.meta.dirname, "runtime/serve.js");
685
+ copyFileSync(existsSync(serveRuntimeSrc) ? serveRuntimeSrc : join(import.meta.dirname, "../src/runtime/serve.js"), join(serverDir, "nbc-serve.js"));
581
686
  const assetExtractions = assetsToEmbed.map((a) => {
582
687
  let diskPath;
583
688
  if (a.urlPath.startsWith("__runtime/")) {
@@ -594,7 +699,13 @@ const path = require("path");
594
699
  const fs = require("fs");
595
700
  const Module = require("module");
596
701
 
597
- const baseDir = path.dirname(process.execPath);
702
+ // NBC_RUNTIME_DIR relocates extraction + Next's working dir — point it at
703
+ // tmpfs (e.g. /tmp/app) for RAM-backed runtime files and compatibility
704
+ // with read-only root filesystems. Default: next to the binary.
705
+ const baseDir = process.env.NBC_RUNTIME_DIR
706
+ ? path.resolve(process.env.NBC_RUNTIME_DIR)
707
+ : path.dirname(process.execPath);
708
+ fs.mkdirSync(baseDir, { recursive: true });
598
709
  process.chdir(baseDir);
599
710
  process.env.NODE_ENV = "production";
600
711
 
@@ -758,7 +869,7 @@ Module._resolveFilename = function(request, parent, isMain, options) {
758
869
  }
759
870
  };
760
871
 
761
- const nextConfig = ${configMatch[1]};
872
+ const nextConfig = ${JSON.stringify(rsfConfig)};
762
873
  process.env.__NEXT_PRIVATE_STANDALONE_CONFIG = JSON.stringify(nextConfig);
763
874
 
764
875
  const currentPort = parseInt(process.env.PORT, 10) || 3000;
@@ -819,12 +930,24 @@ async function extractAssets() {
819
930
  console.log(\`Extracted \${extractions.length} assets\`);
820
931
  }
821
932
 
933
+ const __NBC_TIER1 = ${JSON.stringify(tier1)};
934
+ const __NBC_STATIC_PAGES = ${JSON.stringify(staticPages)};
935
+
822
936
  extractAssets().then(() => {
823
- require("next");
824
- const { startServer } = require("next/dist/server/lib/start-server");
825
- return startServer({
826
- dir: baseDir, isDev: false, config: nextConfig,
827
- hostname, port: currentPort, allowRetry: false, keepAliveTimeout,
937
+ const { start } = require("./nbc-serve.js");
938
+ return start({
939
+ assetMap,
940
+ nextConfig,
941
+ port: currentPort,
942
+ hostname,
943
+ keepAliveTimeout,
944
+ tier1: __NBC_TIER1,
945
+ staticPages: __NBC_STATIC_PAGES,
946
+ baseDir,
947
+ // Revalidation events are observed on the default filesystem cache
948
+ // handler; with a custom handler they never fire, so response
949
+ // caching would serve stale pages.
950
+ enableL1: ${JSON.stringify(!hasCustomCacheHandler)},
828
951
  });
829
952
  }).catch((err) => { console.error(err); process.exit(1); });
830
953
  `;
@@ -845,6 +968,16 @@ function compile(options) {
845
968
  "--compile",
846
969
  "--minify",
847
970
  "--sourcemap",
971
+ "--external",
972
+ "webpack",
973
+ "--external",
974
+ "webpack/*",
975
+ "--external",
976
+ "next/dist/build/webpack/*",
977
+ "--external",
978
+ "sass",
979
+ "--external",
980
+ "critters",
848
981
  "--define",
849
982
  "process.env.TURBOPACK=1",
850
983
  "--define",
@@ -853,6 +986,7 @@ function compile(options) {
853
986
  'process.env.NEXT_RUNTIME="nodejs"',
854
987
  "--outfile",
855
988
  outfile,
989
+ ...process.env.NBC_TARGET ? [`--target=${process.env.NBC_TARGET}`] : [],
856
990
  ...extraArgs
857
991
  ];
858
992
  console.log(`next-bun-compile: Compiling to ${outfile}...`);
@@ -868,4 +1002,193 @@ function compile(options) {
868
1002
  console.log(`next-bun-compile: Done → ${outfile}`);
869
1003
  }
870
1004
 
871
- export { generateEntryPoint, compile };
1005
+ // src/build.ts
1006
+ async function ensureServerRuntime(projectDir, standaloneDir) {
1007
+ const req = createRequire(join3(projectDir, "package.json"));
1008
+ const { nodeFileTrace } = req("next/dist/compiled/@vercel/nft");
1009
+ const entries = [
1010
+ req.resolve("next/dist/server/lib/router-server.js"),
1011
+ req.resolve("next/dist/server/lib/incremental-cache/file-system-cache.js")
1012
+ ];
1013
+ const { fileList } = await nodeFileTrace(entries, {
1014
+ base: "/",
1015
+ ignore: [
1016
+ "**/*.d.ts",
1017
+ "**/*.map",
1018
+ "**/next/dist/compiled/next-server/**/*.dev.js",
1019
+ "**/next/dist/compiled/webpack/*",
1020
+ "**/node_modules/webpack5/**/*",
1021
+ "**/next/dist/server/lib/route-resolver*",
1022
+ "**/next/dist/compiled/semver/semver/**/*.js",
1023
+ "**/next/dist/compiled/jest-worker/**/*",
1024
+ "**/node_modules/react/**/*.development.js",
1025
+ "**/node_modules/react-dom/**/*.development.js"
1026
+ ]
1027
+ });
1028
+ let copied = 0;
1029
+ for (const f of fileList) {
1030
+ const src = "/" + f;
1031
+ const idx = src.indexOf("/node_modules/");
1032
+ if (idx === -1)
1033
+ continue;
1034
+ const dest = join3(standaloneDir, src.slice(idx + 1));
1035
+ try {
1036
+ if (statSync2(src).isDirectory())
1037
+ continue;
1038
+ } catch {
1039
+ continue;
1040
+ }
1041
+ if (existsSync2(dest))
1042
+ continue;
1043
+ mkdirSync2(dirname(dest), { recursive: true });
1044
+ copyFileSync2(src, dest);
1045
+ copied++;
1046
+ }
1047
+ if (copied > 0) {
1048
+ console.log(`next-bun-compile: added ${copied} server-runtime files to the traced tree`);
1049
+ }
1050
+ }
1051
+ async function runBuild(options) {
1052
+ const { projectDir, standaloneDir, serverDir, extraArgs = [] } = options;
1053
+ const distDir = join3(projectDir, ".next");
1054
+ await ensureServerRuntime(projectDir, standaloneDir);
1055
+ generateEntryPoint({ standaloneDir, serverDir, distDir, projectDir });
1056
+ const outfile = join3(projectDir, "server");
1057
+ compile({ serverDir, outfile, extraArgs });
1058
+ return outfile;
1059
+ }
1060
+
1061
+ // src/adapter.ts
1062
+ var ADAPTER_OUTPUTS_FILE = "nbc-adapter-outputs.json";
1063
+ function flattenHeaders(headers) {
1064
+ const out = {};
1065
+ for (const [k, v] of Object.entries(headers ?? {})) {
1066
+ out[k] = Array.isArray(v) ? v.join(", ") : v;
1067
+ }
1068
+ return out;
1069
+ }
1070
+ async function assembleStandalone(ctx) {
1071
+ const staging = join4(ctx.distDir, "nbc-standalone");
1072
+ rmSync(staging, { recursive: true, force: true });
1073
+ const copied = new Set;
1074
+ let escaped = 0;
1075
+ const copyTo = (destRel, src) => {
1076
+ if (destRel.startsWith("..")) {
1077
+ escaped++;
1078
+ return;
1079
+ }
1080
+ if (copied.has(destRel))
1081
+ return;
1082
+ try {
1083
+ if (!statSync3(src).isFile())
1084
+ return;
1085
+ } catch {
1086
+ return;
1087
+ }
1088
+ copied.add(destRel);
1089
+ const dest = join4(staging, destRel);
1090
+ mkdirSync3(dirname2(dest), { recursive: true });
1091
+ copyFileSync3(src, dest);
1092
+ };
1093
+ const outputs = ctx.outputs ?? {};
1094
+ const routeOutputs = [
1095
+ ...outputs.pages ?? [],
1096
+ ...outputs.pagesApi ?? [],
1097
+ ...outputs.appPages ?? [],
1098
+ ...outputs.appRoutes ?? [],
1099
+ ...outputs.middleware ? [outputs.middleware] : []
1100
+ ];
1101
+ for (const o of routeOutputs) {
1102
+ if (o.filePath)
1103
+ copyTo(relative2(ctx.repoRoot, o.filePath), o.filePath);
1104
+ for (const [key, src] of Object.entries(o.assets ?? {}))
1105
+ copyTo(key, src);
1106
+ }
1107
+ for (const nft of ["next-server.js.nft.json", "next-minimal-server.js.nft.json"]) {
1108
+ const nftPath = join4(ctx.distDir, nft);
1109
+ if (!existsSync3(nftPath))
1110
+ continue;
1111
+ try {
1112
+ const { files } = JSON.parse(readFileSync2(nftPath, "utf-8"));
1113
+ for (const f of files) {
1114
+ const src = resolve(ctx.distDir, f);
1115
+ copyTo(relative2(ctx.repoRoot, src), src);
1116
+ }
1117
+ } catch {}
1118
+ }
1119
+ for (const p of outputs.prerenders ?? []) {
1120
+ const file = p.fallback?.filePath;
1121
+ if (!file)
1122
+ continue;
1123
+ copyTo(relative2(ctx.repoRoot, file), file);
1124
+ if (file.endsWith(".html")) {
1125
+ const meta = file.slice(0, -".html".length) + ".meta";
1126
+ copyTo(relative2(ctx.repoRoot, meta), meta);
1127
+ }
1128
+ }
1129
+ for (const extra of ["BUILD_ID", "required-server-files.json"]) {
1130
+ const src = join4(ctx.distDir, extra);
1131
+ copyTo(relative2(ctx.repoRoot, src), src);
1132
+ }
1133
+ const appStaging = join4(staging, relative2(ctx.repoRoot, ctx.projectDir));
1134
+ mkdirSync3(appStaging, { recursive: true });
1135
+ if (escaped > 0) {
1136
+ console.warn(`next-bun-compile: ${escaped} traced file(s) outside the repo root were skipped`);
1137
+ }
1138
+ console.log(`next-bun-compile: assembled ${copied.size} traced files (no standalone output needed)`);
1139
+ return { standaloneDir: staging, serverDir: appStaging };
1140
+ }
1141
+ var adapter = {
1142
+ name: "next-bun-compile",
1143
+ async onBuildComplete(ctx) {
1144
+ const routingRules = [];
1145
+ for (const key of [
1146
+ "beforeMiddleware",
1147
+ "beforeFiles",
1148
+ "afterFiles",
1149
+ "onMatch",
1150
+ "fallback"
1151
+ ]) {
1152
+ const list = ctx.routing?.[key];
1153
+ if (!Array.isArray(list))
1154
+ continue;
1155
+ for (const route of list) {
1156
+ if (route.sourceRegex && typeof route.source === "string" && !route.priority && (route.headers || route.destination || route.status)) {
1157
+ routingRules.push(route.sourceRegex);
1158
+ }
1159
+ }
1160
+ }
1161
+ const prerenders = (ctx.outputs?.prerenders ?? []).flatMap((p) => {
1162
+ if (!p.pathname)
1163
+ return [];
1164
+ return [
1165
+ {
1166
+ pathname: p.pathname,
1167
+ file: p.fallback?.filePath ? relative2(ctx.distDir, p.fallback.filePath) : null,
1168
+ status: p.fallback?.initialStatus ?? 200,
1169
+ revalidate: p.fallback?.initialRevalidate ?? false,
1170
+ postponed: !!p.fallback?.postponedState,
1171
+ headers: flattenHeaders(p.fallback?.initialHeaders)
1172
+ }
1173
+ ];
1174
+ });
1175
+ const snapshot = {
1176
+ version: 1,
1177
+ buildId: ctx.buildId,
1178
+ nextVersion: ctx.nextVersion,
1179
+ basePath: ctx.config.basePath ?? "",
1180
+ i18n: !!ctx.config.i18n,
1181
+ hasCustomCacheHandler: !!ctx.config.cacheHandler,
1182
+ middlewareMatchers: (ctx.outputs?.middleware?.config?.matchers ?? []).map((m) => m.sourceRegex).filter((r) => !!r),
1183
+ routingRules,
1184
+ prerenders
1185
+ };
1186
+ writeFileSync2(join4(ctx.distDir, ADAPTER_OUTPUTS_FILE), JSON.stringify(snapshot, null, 2));
1187
+ console.log(`next-bun-compile: adapter outputs written (${prerenders.length} prerender entries)`);
1188
+ const { standaloneDir, serverDir } = await assembleStandalone(ctx);
1189
+ await runBuild({ projectDir: ctx.projectDir, standaloneDir, serverDir });
1190
+ }
1191
+ };
1192
+ var adapter_default = adapter;
1193
+
1194
+ export { generateEntryPoint, compile, runBuild, ADAPTER_OUTPUTS_FILE, adapter_default };