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 +31 -28
- package/dist/adapter.js +8 -0
- package/dist/{index-hb6dh1nm.js → index-ajd4bx9z.js} +385 -61
- package/dist/index.js +7 -3
- package/dist/runtime/serve.js +700 -0
- package/package.json +16 -8
- package/dist/cli.js +0 -19
package/README.md
CHANGED
|
@@ -23,45 +23,56 @@ bun add -D next-bun-compile
|
|
|
23
23
|
|
|
24
24
|
## Setup
|
|
25
25
|
|
|
26
|
-
|
|
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
|
-
|
|
32
|
+
adapterPath: "next-bun-compile/adapter",
|
|
33
33
|
};
|
|
34
34
|
|
|
35
35
|
export default nextConfig;
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
Or enable it without touching the config at all:
|
|
39
39
|
|
|
40
|
-
```
|
|
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
|
-
|
|
52
|
-
./server
|
|
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
|
-
|
|
70
|
+
Set `NBC_TARGET` to cross-compile for a different platform:
|
|
60
71
|
|
|
61
72
|
```bash
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
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
|
-
|
|
145
|
+
adapterPath: "next-bun-compile/adapter",
|
|
143
146
|
transpilePackages: ["pino", "pino-pretty"],
|
|
144
147
|
};
|
|
145
148
|
```
|
package/dist/adapter.js
ADDED
|
@@ -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
|
-
|
|
393
|
-
|
|
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
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
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
|
|
576
|
-
|
|
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
|
-
|
|
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 = ${
|
|
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("
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
};
|