next-bun-compile 0.6.12 → 0.7.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
@@ -8,6 +8,8 @@ One command. One binary. No runtime dependencies.
8
8
  next build # → ./server (single executable with embedded assets)
9
9
  ```
10
10
 
11
+ **📖 Docs: [ramonmalcolm10.github.io/next-bun-compile](https://ramonmalcolm10.github.io/next-bun-compile/)**
12
+
11
13
  ## Requirements
12
14
 
13
15
  - [Bun](https://bun.sh) >= 1.3
package/dist/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  compile,
4
4
  generateEntryPoint
5
- } from "./index-7dvf3g60.js";
5
+ } from "./index-np1qb2b3.js";
6
6
 
7
7
  // src/cli.ts
8
8
  import { existsSync } from "node:fs";
@@ -46,24 +46,56 @@ function toVarName(filePath) {
46
46
  const safe = filePath.replace(/[^a-zA-Z0-9]/g, "_").slice(0, 40);
47
47
  return `asset_${safe}_${hash}`;
48
48
  }
49
- function findPackageDirs(nodeModulesDir, pkg) {
49
+ function findPackageDirs(standaloneDir, pkg) {
50
50
  const dirs = [];
51
- const direct = join(nodeModulesDir, pkg);
52
- if (existsSync(direct))
53
- dirs.push(direct);
54
51
  const prefix = pkg.startsWith("@") ? pkg.split("/")[0] + "+" + pkg.split("/")[1] : pkg;
55
- for (const store of [".bun", ".pnpm"]) {
56
- const storeDir = join(nodeModulesDir, store);
57
- if (!existsSync(storeDir))
58
- continue;
59
- for (const entry of readdirSync(storeDir)) {
60
- if (!entry.startsWith(prefix + "@"))
52
+ const seen = new Set;
53
+ const checkNodeModules = (nodeModulesDir) => {
54
+ const direct = join(nodeModulesDir, pkg);
55
+ if (existsSync(direct) && !seen.has(direct)) {
56
+ seen.add(direct);
57
+ dirs.push(direct);
58
+ }
59
+ for (const store of [".bun", ".pnpm"]) {
60
+ const storeDir = join(nodeModulesDir, store);
61
+ if (!existsSync(storeDir))
62
+ continue;
63
+ let entries;
64
+ try {
65
+ entries = readdirSync(storeDir);
66
+ } catch {
61
67
  continue;
62
- const hoisted = join(storeDir, entry, "node_modules", pkg);
63
- if (existsSync(hoisted))
64
- dirs.push(hoisted);
68
+ }
69
+ for (const entry of entries) {
70
+ if (!entry.startsWith(prefix + "@"))
71
+ continue;
72
+ const hoisted = join(storeDir, entry, "node_modules", pkg);
73
+ if (existsSync(hoisted) && !seen.has(hoisted)) {
74
+ seen.add(hoisted);
75
+ dirs.push(hoisted);
76
+ }
77
+ }
65
78
  }
66
- }
79
+ };
80
+ const walk = (dir) => {
81
+ let entries;
82
+ try {
83
+ entries = readdirSync(dir);
84
+ } catch {
85
+ return;
86
+ }
87
+ for (const entry of entries) {
88
+ const full = join(dir, entry);
89
+ if (entry === "node_modules") {
90
+ checkNodeModules(full);
91
+ continue;
92
+ }
93
+ const stat = tryStat(full);
94
+ if (stat && stat.isDirectory())
95
+ walk(full);
96
+ }
97
+ };
98
+ walk(standaloneDir);
67
99
  return dirs;
68
100
  }
69
101
  function generateStubs(standaloneDir) {
@@ -92,7 +124,7 @@ function generateStubs(standaloneDir) {
92
124
  const nodeModulesDir = join(standaloneDir, "node_modules");
93
125
  let count = 0;
94
126
  for (const stub of stubs) {
95
- const pkgDirs = findPackageDirs(nodeModulesDir, stub.pkg);
127
+ const pkgDirs = findPackageDirs(standaloneDir, stub.pkg);
96
128
  if (pkgDirs.length === 0)
97
129
  pkgDirs.push(join(nodeModulesDir, stub.pkg));
98
130
  for (const pkgDir of pkgDirs) {
@@ -167,15 +199,112 @@ function findTurbopackAliases(standaloneNextDir) {
167
199
  subpaths: Array.from(subpaths)
168
200
  }));
169
201
  }
170
- function rewriteTurbopackAliases(standaloneNextDir, aliases) {
171
- if (aliases.length === 0)
202
+ function buildCanonicalResolutions(externalRoot, aliases) {
203
+ const out = new Map;
204
+ const findFile = (dir, candidates) => {
205
+ for (const c of candidates) {
206
+ if (!c)
207
+ continue;
208
+ const p = join(dir, c);
209
+ if (existsSync(p) && statSync(p).isFile())
210
+ return c.replace(/\\/g, "/");
211
+ }
212
+ return null;
213
+ };
214
+ const resolveMain = (canonicalDir) => {
215
+ const pkgPath = join(canonicalDir, "package.json");
216
+ let main = "index.js";
217
+ if (existsSync(pkgPath)) {
218
+ try {
219
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
220
+ if (typeof pkg.main === "string")
221
+ main = pkg.main;
222
+ } catch {}
223
+ }
224
+ return findFile(canonicalDir, [
225
+ main,
226
+ main + ".js",
227
+ main + ".cjs",
228
+ main + ".mjs",
229
+ join(main, "index.js"),
230
+ join(main, "index.cjs"),
231
+ join(main, "index.mjs"),
232
+ "index.js",
233
+ "index.cjs",
234
+ "index.mjs"
235
+ ]);
236
+ };
237
+ const resolveSub = (canonicalDir, sub) => {
238
+ const stripped = sub.replace(/\.(?:js|cjs|mjs|json)$/, "");
239
+ return findFile(canonicalDir, [
240
+ sub,
241
+ stripped + ".mjs",
242
+ stripped + ".js",
243
+ stripped + ".cjs",
244
+ stripped + ".json",
245
+ join(stripped, "index.mjs"),
246
+ join(stripped, "index.js"),
247
+ join(stripped, "index.cjs")
248
+ ]);
249
+ };
250
+ for (const { alias, target, subpaths } of aliases) {
251
+ const canonicalDir = join(externalRoot, target);
252
+ if (!existsSync(canonicalDir))
253
+ continue;
254
+ const main = resolveMain(canonicalDir);
255
+ if (main)
256
+ out.set(alias, `.next/node_modules/${target}/${main}`);
257
+ for (const sub of subpaths) {
258
+ const file = resolveSub(canonicalDir, sub);
259
+ if (file)
260
+ out.set(`${alias}/${sub}`, `.next/node_modules/${target}/${file}`);
261
+ }
262
+ }
263
+ return out;
264
+ }
265
+ function validateAliasResolutions(aliases, resolutions) {
266
+ const verbose = process.env.NEXT_BUN_COMPILE_VERBOSE === "1";
267
+ const all = [];
268
+ for (const { alias, target, subpaths } of aliases) {
269
+ all.push({ ref: alias, canonical: target, file: resolutions.get(alias) ?? null });
270
+ for (const sub of subpaths) {
271
+ const ref = `${alias}/${sub}`;
272
+ all.push({
273
+ ref,
274
+ canonical: `${target}/${sub}`,
275
+ file: resolutions.get(ref) ?? null
276
+ });
277
+ }
278
+ }
279
+ const unresolved = all.filter((e) => !e.file).map(({ ref, canonical }) => ({ ref, canonical }));
280
+ if (verbose && all.length > 0) {
281
+ console.log(`next-bun-compile: Validating ${all.length} turbopack alias reference(s):`);
282
+ for (const { ref, canonical, file } of all) {
283
+ if (file) {
284
+ const display = file.replace(/^\.next\/node_modules\//, "");
285
+ console.log(` ✓ ${ref} → ${canonical} (${display})`);
286
+ } else {
287
+ console.log(` ✗ ${ref} → ${canonical} (NOT FOUND)`);
288
+ }
289
+ }
290
+ }
291
+ if (unresolved.length > 0) {
292
+ console.warn(`next-bun-compile: ⚠ ${unresolved.length} of ${all.length} turbopack alias reference(s) won't resolve at runtime:`);
293
+ for (const { ref, canonical } of unresolved) {
294
+ console.warn(` ✗ ${ref} → ${canonical}`);
295
+ }
296
+ 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.`);
297
+ }
298
+ return unresolved;
299
+ }
300
+ function rewriteTurbopackAliases(standaloneNextDir, aliases, resolutions) {
301
+ if (aliases.length === 0 || resolutions.size === 0)
172
302
  return;
173
303
  const serverDir = join(standaloneNextDir, "server");
174
304
  if (!existsSync(serverDir))
175
305
  return;
176
306
  const escape = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
177
- const pattern = new RegExp(`(["'])(` + aliases.map((a) => escape(a.alias)).join("|") + `)(?=[/"'])`, "g");
178
- const targetByAlias = new Map(aliases.map((a) => [a.alias, a.target]));
307
+ const pattern = new RegExp(`(["'])((?:` + aliases.map((a) => escape(a.alias)).join("|") + `)(?:/[^"']+)?)\\1`, "g");
179
308
  let rewritten = 0;
180
309
  for (const f of walkDir(serverDir)) {
181
310
  if (!f.absolutePath.endsWith(".js"))
@@ -186,7 +315,12 @@ function rewriteTurbopackAliases(standaloneNextDir, aliases) {
186
315
  } catch {
187
316
  continue;
188
317
  }
189
- const next = content.replace(pattern, (_m, q, alias) => q + targetByAlias.get(alias));
318
+ const next = content.replace(pattern, (match, quote, spec) => {
319
+ const rel = resolutions.get(spec);
320
+ if (!rel)
321
+ return match;
322
+ return `${quote}__NBC_BASE__/${rel}${quote}`;
323
+ });
190
324
  if (next !== content) {
191
325
  writeFileSync(f.absolutePath, next);
192
326
  rewritten++;
@@ -227,8 +361,7 @@ function findServerDir(standaloneDir) {
227
361
  return found;
228
362
  }
229
363
  function patchRequireHook(standaloneDir) {
230
- const nodeModulesDir = join(standaloneDir, "node_modules");
231
- const nextDirs = findPackageDirs(nodeModulesDir, "next");
364
+ const nextDirs = findPackageDirs(standaloneDir, "next");
232
365
  const target = "let resolve = process.env.NEXT_MINIMAL ? __non_webpack_require__.resolve : require.resolve;";
233
366
  const replacement = `let _resolve = process.env.NEXT_MINIMAL ? __non_webpack_require__.resolve : require.resolve;
234
367
  let resolve = (id) => { try { return _resolve(id); } catch { return ''; } };`;
@@ -249,9 +382,6 @@ let resolve = (id) => { try { return _resolve(id); } catch { return ''; } };`;
249
382
  }
250
383
  }
251
384
  function collectExternalModules(standaloneDir) {
252
- const nodeModulesDir = join(standaloneDir, "node_modules");
253
- if (!existsSync(nodeModulesDir))
254
- return [];
255
385
  const pkgRoots = new Map;
256
386
  function addPkg(name, path) {
257
387
  if (!pkgRoots.has(name))
@@ -279,17 +409,37 @@ function collectExternalModules(standaloneDir) {
279
409
  }
280
410
  }
281
411
  }
282
- scanDir(nodeModulesDir);
283
- for (const store of [".bun", ".pnpm"]) {
284
- const storeDir = join(nodeModulesDir, store);
285
- if (!existsSync(storeDir))
286
- continue;
287
- for (const storeEntry of readdirSync(storeDir)) {
288
- const nested = join(storeDir, storeEntry, "node_modules");
289
- if (existsSync(nested))
290
- scanDir(nested);
412
+ function walkForNodeModules(dir) {
413
+ let entries;
414
+ try {
415
+ entries = readdirSync(dir);
416
+ } catch {
417
+ return;
418
+ }
419
+ for (const entry of entries) {
420
+ const full = join(dir, entry);
421
+ if (entry === "node_modules") {
422
+ scanDir(full);
423
+ for (const store of [".bun", ".pnpm"]) {
424
+ const storeDir = join(full, store);
425
+ if (!existsSync(storeDir))
426
+ continue;
427
+ for (const storeEntry of readdirSync(storeDir)) {
428
+ const nested = join(storeDir, storeEntry, "node_modules");
429
+ if (existsSync(nested))
430
+ scanDir(nested);
431
+ }
432
+ }
433
+ continue;
434
+ }
435
+ const stat = tryStat(full);
436
+ if (stat && stat.isDirectory())
437
+ walkForNodeModules(full);
291
438
  }
292
439
  }
440
+ walkForNodeModules(standaloneDir);
441
+ if (pkgRoots.size === 0)
442
+ return [];
293
443
  const results = [];
294
444
  for (const [name, pkgPath] of pkgRoots) {
295
445
  for (const f of walkDir(pkgPath)) {
@@ -302,8 +452,7 @@ function collectExternalModules(standaloneDir) {
302
452
  return results;
303
453
  }
304
454
  function fixModuleResolution(standaloneDir) {
305
- const nodeModulesDir = join(standaloneDir, "node_modules");
306
- for (const pkgDir of findPackageDirs(nodeModulesDir, "next")) {
455
+ for (const pkgDir of findPackageDirs(standaloneDir, "next")) {
307
456
  const compiledDir = join(pkgDir, "dist/compiled");
308
457
  if (!existsSync(compiledDir))
309
458
  continue;
@@ -322,7 +471,7 @@ function fixModuleResolution(standaloneDir) {
322
471
  }
323
472
  }
324
473
  }
325
- for (const helpersDir of findPackageDirs(nodeModulesDir, "@swc/helpers")) {
474
+ for (const helpersDir of findPackageDirs(standaloneDir, "@swc/helpers")) {
326
475
  const cjsDir = join(helpersDir, "cjs");
327
476
  if (!existsSync(cjsDir))
328
477
  continue;
@@ -357,7 +506,6 @@ function generateEntryPoint(options) {
357
506
  }));
358
507
  const standaloneNextDir = join(serverDir, ".next");
359
508
  const turbopackAliases = findTurbopackAliases(standaloneNextDir);
360
- rewriteTurbopackAliases(standaloneNextDir, turbopackAliases);
361
509
  const aliasNames = new Set(turbopackAliases.map((a) => a.alias));
362
510
  const runtimeFiles = walkDir(standaloneNextDir).filter((f) => {
363
511
  const m = f.relativePath.replace(/\\/g, "/").match(/^node_modules\/([^/]+)/);
@@ -383,6 +531,9 @@ function generateEntryPoint(options) {
383
531
  if (externalModules.length > 0) {
384
532
  console.log(`next-bun-compile: Embedding ${externalModules.length} external modules for SSR`);
385
533
  }
534
+ const canonicalResolutions = buildCanonicalResolutions(externalDir, turbopackAliases);
535
+ validateAliasResolutions(turbopackAliases, canonicalResolutions);
536
+ rewriteTurbopackAliases(standaloneNextDir, turbopackAliases, canonicalResolutions);
386
537
  const ctx = JSON.parse(readFileSync(join(distDir, "bun-compile-ctx.json"), "utf-8"));
387
538
  const { assetPrefix } = ctx;
388
539
  const assetsToEmbed = assetPrefix ? [...publicFiles, ...runtimeFiles] : [...staticFiles, ...publicFiles, ...runtimeFiles];
@@ -443,6 +594,20 @@ process.env.NODE_ENV = "production";
443
594
  // honor exports maps. Generic: every externalized package's internal deps
444
595
  // get resolved without per-package patching.
445
596
  const __nbcAliases = ${JSON.stringify(Object.fromEntries(turbopackAliases.map((a) => [a.alias, a.target])))};
597
+ // Debug mode: set NEXT_BUN_COMPILE_DEBUG=1 to log every resolver-hook
598
+ // decision (alias redirects, fallback walks, fallback failures). Off by
599
+ // default so production logs stay clean; turn it on when reproducing a
600
+ // resolution bug — that one log line is usually enough to know which
601
+ // package was missing and where the walk gave up.
602
+ const __nbcDebug = process.env.NEXT_BUN_COMPILE_DEBUG === "1";
603
+ function __nbcLog(msg) { console.log("next-bun-compile [debug]:", msg); }
604
+ if (__nbcDebug) {
605
+ const n = Object.keys(__nbcAliases).length;
606
+ __nbcLog(\`resolver hook installed; \${n} alias mapping(s):\`);
607
+ for (const [k, v] of Object.entries(__nbcAliases)) {
608
+ __nbcLog(\` \${k} → \${v}\`);
609
+ }
610
+ }
446
611
  const __nbcOrigResolveFilename = Module._resolveFilename;
447
612
  function __nbcStatFile(p) {
448
613
  try { return fs.statSync(p).isFile() ? p : null; } catch { return null; }
@@ -472,12 +637,34 @@ function __nbcResolveSubpath(pkgDir, pkgJson, sub) {
472
637
  }
473
638
  }
474
639
  }
475
- return __nbcStatFile(path.join(pkgDir, sub))
640
+ // Direct file forms
641
+ const direct = __nbcStatFile(path.join(pkgDir, sub))
476
642
  || __nbcStatFile(path.join(pkgDir, sub + ".js"))
477
643
  || __nbcStatFile(path.join(pkgDir, sub + ".cjs"))
478
644
  || __nbcStatFile(path.join(pkgDir, sub + ".mjs"))
479
- || __nbcStatFile(path.join(pkgDir, sub + ".json"))
480
- || __nbcStatFile(path.join(pkgDir, sub, "index.js"))
645
+ || __nbcStatFile(path.join(pkgDir, sub + ".json"));
646
+ if (direct) return direct;
647
+ // Subpath is a directory: read its own package.json + main (e.g.
648
+ // next/dist/compiled/source-map has main: "source-map.js"). Falls
649
+ // back to index.js / index.cjs lookup if no package.json.
650
+ const subDir = path.join(pkgDir, sub);
651
+ try {
652
+ if (fs.statSync(subDir).isDirectory()) {
653
+ const subPkgPath = path.join(subDir, "package.json");
654
+ if (fs.existsSync(subPkgPath)) {
655
+ try {
656
+ const subPkg = JSON.parse(fs.readFileSync(subPkgPath, "utf-8"));
657
+ const main = typeof subPkg.main === "string" ? subPkg.main : null;
658
+ if (main) {
659
+ const f = __nbcStatFile(path.join(subDir, main))
660
+ || __nbcStatFile(path.join(subDir, main + ".js"));
661
+ if (f) return f;
662
+ }
663
+ } catch {}
664
+ }
665
+ }
666
+ } catch {}
667
+ return __nbcStatFile(path.join(pkgDir, sub, "index.js"))
481
668
  || __nbcStatFile(path.join(pkgDir, sub, "index.cjs"));
482
669
  }
483
670
  function __nbcResolvePackage(request, fromDir) {
@@ -528,6 +715,10 @@ function __nbcRedirectAlias(request) {
528
715
  }
529
716
  Module._resolveFilename = function(request, parent, isMain, options) {
530
717
  const redirected = __nbcRedirectAlias(request);
718
+ if (__nbcDebug && redirected !== request) {
719
+ const from = parent && parent.filename ? parent.filename : "<unknown>";
720
+ __nbcLog(\`redirected "\${request}" → "\${redirected}" (from \${from})\`);
721
+ }
531
722
  try {
532
723
  return __nbcOrigResolveFilename.call(this, redirected, parent, isMain, options);
533
724
  } catch (err) {
@@ -537,7 +728,15 @@ Module._resolveFilename = function(request, parent, isMain, options) {
537
728
  }
538
729
  const fromDir = parent && parent.filename ? path.dirname(parent.filename) : process.cwd();
539
730
  const resolved = __nbcResolvePackage(redirected, fromDir);
540
- if (resolved) return resolved;
731
+ if (resolved) {
732
+ if (__nbcDebug) {
733
+ __nbcLog(\`fallback resolved "\${redirected}" → \${resolved} (from \${fromDir})\`);
734
+ }
735
+ return resolved;
736
+ }
737
+ if (__nbcDebug) {
738
+ __nbcLog(\`fallback FAILED for "\${redirected}" (from \${fromDir}); throwing original ResolveMessage\`);
739
+ }
541
740
  throw err;
542
741
  }
543
742
  };
@@ -560,7 +759,21 @@ async function extractAssets() {
560
759
  if (fs.existsSync(fullPath)) continue;
561
760
  fs.mkdirSync(path.dirname(fullPath), { recursive: true });
562
761
  const embedded = assetMap.get(urlPath);
563
- if (embedded) { await Bun.write(fullPath, Bun.file(embedded)); n++; }
762
+ if (!embedded) continue;
763
+ // Server chunks may contain __NBC_BASE__ placeholders injected at
764
+ // build time by rewriteTurbopackAliases. Substitute the real baseDir
765
+ // before writing so bun resolves the absolute paths at chunk load
766
+ // (works for both CJS require and ESM import).
767
+ if (diskPath.startsWith(".next/server/")) {
768
+ const text = await Bun.file(embedded).text();
769
+ if (text.indexOf("__NBC_BASE__") !== -1) {
770
+ await Bun.write(fullPath, text.split("__NBC_BASE__").join(baseDir));
771
+ n++;
772
+ continue;
773
+ }
774
+ }
775
+ await Bun.write(fullPath, Bun.file(embedded));
776
+ n++;
564
777
  }
565
778
  if (n > 0) console.log(\`Extracted \${n} assets\`);
566
779
  }
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  __require,
3
3
  compile,
4
4
  generateEntryPoint
5
- } from "./index-7dvf3g60.js";
5
+ } from "./index-np1qb2b3.js";
6
6
 
7
7
  // src/index.ts
8
8
  import { join } from "node:path";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-bun-compile",
3
- "version": "0.6.12",
3
+ "version": "0.7.0",
4
4
  "description": "Next.js Build Adapter that compiles your app into a Bun single-file executable",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",