next-bun-compile 0.10.1 → 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);
@@ -335,36 +345,6 @@ function rewriteTurbopackAliases(standaloneNextDir, aliases, resolutions) {
335
345
  }
336
346
  return rewrittenPaths;
337
347
  }
338
- function findServerDir(standaloneDir) {
339
- if (existsSync(join(standaloneDir, "server.js"))) {
340
- return standaloneDir;
341
- }
342
- function search(dir) {
343
- if (!existsSync(dir))
344
- return null;
345
- for (const entry of readdirSync(dir)) {
346
- if (entry === "node_modules")
347
- continue;
348
- const full = join(dir, entry);
349
- const stat = tryStat(full);
350
- if (!stat || !stat.isDirectory())
351
- continue;
352
- if (existsSync(join(full, "server.js")))
353
- return full;
354
- const found2 = search(full);
355
- if (found2)
356
- return found2;
357
- }
358
- return null;
359
- }
360
- const found = search(standaloneDir);
361
- if (!found) {
362
- throw new Error("next-bun-compile: Could not find server.js in standalone output");
363
- }
364
- const rel = relative(standaloneDir, found);
365
- console.log(`next-bun-compile: Monorepo layout detected — server.js found at ${rel}/`);
366
- return found;
367
- }
368
348
  function patchRequireHook(standaloneDir) {
369
349
  const nextDirs = findPackageDirs(standaloneDir, "next");
370
350
  const target = "let resolve = process.env.NEXT_MINIMAL ? __non_webpack_require__.resolve : require.resolve;";
@@ -389,8 +369,12 @@ let resolve = (id) => { try { return _resolve(id); } catch { return ''; } };`;
389
369
  function collectExternalModules(standaloneDir) {
390
370
  const pkgRoots = new Map;
391
371
  function addPkg(name, path) {
392
- if (!pkgRoots.has(name))
393
- 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);
394
378
  }
395
379
  function scanDir(dir) {
396
380
  if (!existsSync(dir))
@@ -446,12 +430,16 @@ function collectExternalModules(standaloneDir) {
446
430
  if (pkgRoots.size === 0)
447
431
  return [];
448
432
  const results = [];
449
- for (const [name, pkgPath] of pkgRoots) {
450
- for (const f of walkDir(pkgPath)) {
451
- results.push({
452
- mod: `${name}/${f.relativePath.replace(/\\/g, "/")}`,
453
- src: f.absolutePath
454
- });
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
+ }
455
443
  }
456
444
  }
457
445
  return results;
@@ -498,9 +486,108 @@ function fixModuleResolution(standaloneDir) {
498
486
  }
499
487
  }
500
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
+ }
501
589
  function generateEntryPoint(options) {
502
- const { standaloneDir, distDir, projectDir } = options;
503
- const serverDir = findServerDir(standaloneDir);
590
+ const { standaloneDir, serverDir, distDir, projectDir } = options;
504
591
  generateStubs(standaloneDir);
505
592
  patchRequireHook(standaloneDir);
506
593
  fixModuleResolution(standaloneDir);
@@ -514,7 +601,30 @@ function generateEntryPoint(options) {
514
601
  ...f,
515
602
  urlPath: `/${f.relativePath.replace(/\\/g, "/")}`
516
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
+ }
517
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
+ }
518
628
  const turbopackAliases = findTurbopackAliases(standaloneNextDir);
519
629
  const aliasNames = new Set(turbopackAliases.map((a) => a.alias));
520
630
  const runtimeFiles = walkDir(standaloneNextDir).filter((f) => {
@@ -544,7 +654,6 @@ function generateEntryPoint(options) {
544
654
  const canonicalResolutions = buildCanonicalResolutions(externalDir, turbopackAliases);
545
655
  validateAliasResolutions(turbopackAliases, canonicalResolutions);
546
656
  const rewrittenChunks = rewriteTurbopackAliases(standaloneNextDir, turbopackAliases, canonicalResolutions);
547
- const assetPrefix = readAssetPrefix(distDir);
548
657
  const assetsToEmbed = assetPrefix ? [...publicFiles, ...runtimeFiles] : [...staticFiles, ...publicFiles, ...runtimeFiles];
549
658
  if (assetPrefix) {
550
659
  console.log(`next-bun-compile: assetPrefix detected — skipping ${staticFiles.length} static assets (served from CDN)`);
@@ -572,11 +681,8 @@ ${mapEntries.join(`
572
681
  `)}
573
682
  ]);
574
683
  `);
575
- const standaloneServerSrc = readFileSync(join(serverDir, "server.js"), "utf-8");
576
- const configMatch = standaloneServerSrc.match(/const nextConfig = ({[\s\S]*?})\n/);
577
- if (!configMatch) {
578
- throw new Error("next-bun-compile: Could not extract nextConfig from standalone server.js");
579
- }
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"));
580
686
  const assetExtractions = assetsToEmbed.map((a) => {
581
687
  let diskPath;
582
688
  if (a.urlPath.startsWith("__runtime/")) {
@@ -593,7 +699,13 @@ const path = require("path");
593
699
  const fs = require("fs");
594
700
  const Module = require("module");
595
701
 
596
- 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 });
597
709
  process.chdir(baseDir);
598
710
  process.env.NODE_ENV = "production";
599
711
 
@@ -757,7 +869,7 @@ Module._resolveFilename = function(request, parent, isMain, options) {
757
869
  }
758
870
  };
759
871
 
760
- const nextConfig = ${configMatch[1]};
872
+ const nextConfig = ${JSON.stringify(rsfConfig)};
761
873
  process.env.__NEXT_PRIVATE_STANDALONE_CONFIG = JSON.stringify(nextConfig);
762
874
 
763
875
  const currentPort = parseInt(process.env.PORT, 10) || 3000;
@@ -818,12 +930,24 @@ async function extractAssets() {
818
930
  console.log(\`Extracted \${extractions.length} assets\`);
819
931
  }
820
932
 
933
+ const __NBC_TIER1 = ${JSON.stringify(tier1)};
934
+ const __NBC_STATIC_PAGES = ${JSON.stringify(staticPages)};
935
+
821
936
  extractAssets().then(() => {
822
- require("next");
823
- const { startServer } = require("next/dist/server/lib/start-server");
824
- return startServer({
825
- dir: baseDir, isDev: false, config: nextConfig,
826
- 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)},
827
951
  });
828
952
  }).catch((err) => { console.error(err); process.exit(1); });
829
953
  `;
@@ -844,6 +968,16 @@ function compile(options) {
844
968
  "--compile",
845
969
  "--minify",
846
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",
847
981
  "--define",
848
982
  "process.env.TURBOPACK=1",
849
983
  "--define",
@@ -852,6 +986,7 @@ function compile(options) {
852
986
  'process.env.NEXT_RUNTIME="nodejs"',
853
987
  "--outfile",
854
988
  outfile,
989
+ ...process.env.NBC_TARGET ? [`--target=${process.env.NBC_TARGET}`] : [],
855
990
  ...extraArgs
856
991
  ];
857
992
  console.log(`next-bun-compile: Compiling to ${outfile}...`);
@@ -867,4 +1002,193 @@ function compile(options) {
867
1002
  console.log(`next-bun-compile: Done → ${outfile}`);
868
1003
  }
869
1004
 
870
- 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 };
package/dist/index.js CHANGED
@@ -1,8 +1,12 @@
1
1
  import {
2
+ adapter_default,
2
3
  compile,
3
- generateEntryPoint
4
- } from "./index-hb6dh1nm.js";
4
+ generateEntryPoint,
5
+ runBuild
6
+ } from "./index-ajd4bx9z.js";
5
7
  export {
8
+ runBuild,
6
9
  generateEntryPoint,
7
- compile
10
+ compile,
11
+ adapter_default as adapter
8
12
  };