next-bun-compile 0.6.8 → 0.6.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  compile,
4
4
  generateEntryPoint
5
- } from "./index-mx555e90.js";
5
+ } from "./index-rx8tn52q.js";
6
6
 
7
7
  // src/cli.ts
8
8
  import { existsSync } from "node:fs";
@@ -113,9 +113,17 @@ function generateStubs(standaloneDir) {
113
113
  }
114
114
  function findTurbopackAliases(standaloneNextDir) {
115
115
  const seen = new Map;
116
+ const ensure = (alias) => {
117
+ let e = seen.get(alias);
118
+ if (!e) {
119
+ e = { target: alias.replace(/-[0-9a-f]{16}$/, ""), subpaths: new Set };
120
+ seen.set(alias, e);
121
+ }
122
+ return e;
123
+ };
116
124
  const serverDir = join(standaloneNextDir, "server");
117
125
  if (existsSync(serverDir)) {
118
- const re = /["']([^"'\s/]+-[0-9a-f]{16})(?:\/[^"'\s]+)?["']/g;
126
+ const re = /["']([^"'\s/]+-[0-9a-f]{16})(?:\/([^"'\s]+))?["']/g;
119
127
  for (const f of walkDir(serverDir)) {
120
128
  if (!f.absolutePath.endsWith(".js"))
121
129
  continue;
@@ -127,10 +135,9 @@ function findTurbopackAliases(standaloneNextDir) {
127
135
  }
128
136
  let m;
129
137
  while (m = re.exec(content)) {
130
- const alias = m[1];
131
- if (seen.has(alias))
132
- continue;
133
- seen.set(alias, alias.replace(/-[0-9a-f]{16}$/, ""));
138
+ const entry = ensure(m[1]);
139
+ if (m[2])
140
+ entry.subpaths.add(m[2]);
134
141
  }
135
142
  }
136
143
  }
@@ -145,23 +152,94 @@ function findTurbopackAliases(standaloneNextDir) {
145
152
  try {
146
153
  if (!lstatSync(aliasPath).isSymbolicLink())
147
154
  continue;
148
- seen.set(name, basename(realpathSync(aliasPath)));
155
+ seen.set(name, {
156
+ target: basename(realpathSync(aliasPath)),
157
+ subpaths: new Set
158
+ });
149
159
  } catch {
150
160
  continue;
151
161
  }
152
162
  }
153
163
  }
154
- return Array.from(seen, ([alias, target]) => ({ alias, target }));
164
+ return Array.from(seen, ([alias, { target, subpaths }]) => ({
165
+ alias,
166
+ target,
167
+ subpaths: Array.from(subpaths)
168
+ }));
169
+ }
170
+ function buildCanonicalResolutions(externalRoot, aliases) {
171
+ const out = new Map;
172
+ const findFile = (dir, candidates) => {
173
+ for (const c of candidates) {
174
+ if (!c)
175
+ continue;
176
+ const p = join(dir, c);
177
+ if (existsSync(p) && statSync(p).isFile())
178
+ return c.replace(/\\/g, "/");
179
+ }
180
+ return null;
181
+ };
182
+ const resolveMain = (canonicalDir) => {
183
+ const pkgPath = join(canonicalDir, "package.json");
184
+ let main = "index.js";
185
+ if (existsSync(pkgPath)) {
186
+ try {
187
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
188
+ if (typeof pkg.main === "string")
189
+ main = pkg.main;
190
+ } catch {}
191
+ }
192
+ return findFile(canonicalDir, [
193
+ main,
194
+ main + ".js",
195
+ main + ".cjs",
196
+ main + ".mjs",
197
+ join(main, "index.js"),
198
+ join(main, "index.cjs"),
199
+ join(main, "index.mjs"),
200
+ "index.js",
201
+ "index.cjs",
202
+ "index.mjs"
203
+ ]);
204
+ };
205
+ const resolveSub = (canonicalDir, sub) => {
206
+ const stripped = sub.replace(/\.(?:js|cjs|mjs|json)$/, "");
207
+ return findFile(canonicalDir, [
208
+ sub,
209
+ stripped + ".js",
210
+ stripped + ".mjs",
211
+ stripped + ".cjs",
212
+ stripped + ".json",
213
+ join(stripped, "index.js"),
214
+ join(stripped, "index.mjs"),
215
+ join(stripped, "index.cjs")
216
+ ]);
217
+ };
218
+ for (const { alias, target, subpaths } of aliases) {
219
+ const canonicalDir = join(externalRoot, target);
220
+ if (!existsSync(canonicalDir))
221
+ continue;
222
+ const main = resolveMain(canonicalDir);
223
+ if (main) {
224
+ out.set(alias, `.next/node_modules/${target}/${main}`);
225
+ }
226
+ for (const sub of subpaths) {
227
+ const file = resolveSub(canonicalDir, sub);
228
+ if (file) {
229
+ out.set(`${alias}/${sub}`, `.next/node_modules/${target}/${file}`);
230
+ }
231
+ }
232
+ }
233
+ return out;
155
234
  }
156
- function rewriteTurbopackAliases(standaloneNextDir, aliases) {
157
- if (aliases.length === 0)
235
+ function rewriteTurbopackAliases(standaloneNextDir, aliases, resolutions) {
236
+ if (aliases.length === 0 || resolutions.size === 0)
158
237
  return;
159
238
  const serverDir = join(standaloneNextDir, "server");
160
239
  if (!existsSync(serverDir))
161
240
  return;
162
241
  const escape = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
163
- const pattern = new RegExp(`(["'])(` + aliases.map((a) => escape(a.alias)).join("|") + `)(?=[/"'])`, "g");
164
- const targetByAlias = new Map(aliases.map((a) => [a.alias, a.target]));
242
+ const pattern = new RegExp(`(["'])((?:` + aliases.map((a) => escape(a.alias)).join("|") + `)(?:/[^"']+)?)\\1`, "g");
165
243
  let rewritten = 0;
166
244
  for (const f of walkDir(serverDir)) {
167
245
  if (!f.absolutePath.endsWith(".js"))
@@ -172,7 +250,12 @@ function rewriteTurbopackAliases(standaloneNextDir, aliases) {
172
250
  } catch {
173
251
  continue;
174
252
  }
175
- const next = content.replace(pattern, (_m, q, alias) => q + targetByAlias.get(alias));
253
+ const next = content.replace(pattern, (match, quote, spec) => {
254
+ const rel = resolutions.get(spec);
255
+ if (!rel)
256
+ return match;
257
+ return `${quote}__NBC_BASE__/${rel}${quote}`;
258
+ });
176
259
  if (next !== content) {
177
260
  writeFileSync(f.absolutePath, next);
178
261
  rewritten++;
@@ -343,7 +426,6 @@ function generateEntryPoint(options) {
343
426
  }));
344
427
  const standaloneNextDir = join(serverDir, ".next");
345
428
  const turbopackAliases = findTurbopackAliases(standaloneNextDir);
346
- rewriteTurbopackAliases(standaloneNextDir, turbopackAliases);
347
429
  const aliasNames = new Set(turbopackAliases.map((a) => a.alias));
348
430
  const runtimeFiles = walkDir(standaloneNextDir).filter((f) => {
349
431
  const m = f.relativePath.replace(/\\/g, "/").match(/^node_modules\/([^/]+)/);
@@ -369,6 +451,8 @@ function generateEntryPoint(options) {
369
451
  if (externalModules.length > 0) {
370
452
  console.log(`next-bun-compile: Embedding ${externalModules.length} external modules for SSR`);
371
453
  }
454
+ const canonicalResolutions = buildCanonicalResolutions(externalDir, turbopackAliases);
455
+ rewriteTurbopackAliases(standaloneNextDir, turbopackAliases, canonicalResolutions);
372
456
  const ctx = JSON.parse(readFileSync(join(distDir, "bun-compile-ctx.json"), "utf-8"));
373
457
  const { assetPrefix } = ctx;
374
458
  const assetsToEmbed = assetPrefix ? [...publicFiles, ...runtimeFiles] : [...staticFiles, ...publicFiles, ...runtimeFiles];
@@ -410,11 +494,103 @@ ${mapEntries.join(`
410
494
  const serverEntry = `import { assetMap } from "./assets.generated.js";
411
495
  const path = require("path");
412
496
  const fs = require("fs");
497
+ const Module = require("module");
413
498
 
414
499
  const baseDir = path.dirname(process.execPath);
415
500
  process.chdir(baseDir);
416
501
  process.env.NODE_ENV = "production";
417
502
 
503
+ // Install a fallback Module._resolveFilename hook. bun's compiled-binary
504
+ // resolver doesn't walk node_modules from inside a node_modules entry, so
505
+ // once execution enters an extracted package (sharp/lib/index.js → require
506
+ // of detect-libc, @img/sharp-linux-x64/sharp.node, etc.) bun gives up. This
507
+ // hook runs ONLY when bun's resolver throws and reimplements Node-compatible
508
+ // resolution from scratch — walk node_modules, read package.json main,
509
+ // honor exports maps. Generic: every externalized package's internal deps
510
+ // get resolved without per-package patching.
511
+ const __nbcOrigResolveFilename = Module._resolveFilename;
512
+ function __nbcStatFile(p) {
513
+ try { return fs.statSync(p).isFile() ? p : null; } catch { return null; }
514
+ }
515
+ function __nbcResolveMain(pkgDir, pkgJson) {
516
+ const main = pkgJson && typeof pkgJson.main === "string" ? pkgJson.main : "index.js";
517
+ return __nbcStatFile(path.join(pkgDir, main))
518
+ || __nbcStatFile(path.join(pkgDir, main + ".js"))
519
+ || __nbcStatFile(path.join(pkgDir, main + ".cjs"))
520
+ || __nbcStatFile(path.join(pkgDir, main + ".mjs"))
521
+ || __nbcStatFile(path.join(pkgDir, main, "index.js"))
522
+ || __nbcStatFile(path.join(pkgDir, "index.js"))
523
+ || __nbcStatFile(path.join(pkgDir, "index.cjs"));
524
+ }
525
+ function __nbcResolveSubpath(pkgDir, pkgJson, sub) {
526
+ // Honor exports map (CJS-relevant conditions only)
527
+ if (pkgJson && pkgJson.exports && typeof pkgJson.exports === "object") {
528
+ const key = "./" + sub;
529
+ const entry = pkgJson.exports[key];
530
+ if (entry) {
531
+ let target = typeof entry === "string"
532
+ ? entry
533
+ : entry.require || entry.node || entry.default;
534
+ if (typeof target === "string" && target.startsWith("./")) {
535
+ const f = __nbcStatFile(path.join(pkgDir, target.slice(2)));
536
+ if (f) return f;
537
+ }
538
+ }
539
+ }
540
+ return __nbcStatFile(path.join(pkgDir, sub))
541
+ || __nbcStatFile(path.join(pkgDir, sub + ".js"))
542
+ || __nbcStatFile(path.join(pkgDir, sub + ".cjs"))
543
+ || __nbcStatFile(path.join(pkgDir, sub + ".mjs"))
544
+ || __nbcStatFile(path.join(pkgDir, sub + ".json"))
545
+ || __nbcStatFile(path.join(pkgDir, sub, "index.js"))
546
+ || __nbcStatFile(path.join(pkgDir, sub, "index.cjs"));
547
+ }
548
+ function __nbcResolvePackage(request, fromDir) {
549
+ let pkgName, sub = "";
550
+ if (request[0] === "@") {
551
+ const m = request.match(/^(@[^/]+\\/[^/]+)(?:\\/(.+))?$/);
552
+ if (!m) return null;
553
+ pkgName = m[1]; sub = m[2] || "";
554
+ } else {
555
+ const idx = request.indexOf("/");
556
+ if (idx === -1) { pkgName = request; }
557
+ else { pkgName = request.slice(0, idx); sub = request.slice(idx + 1); }
558
+ }
559
+ let dir = fromDir;
560
+ while (dir.length > 1) {
561
+ const pkgDir = path.join(dir, "node_modules", pkgName);
562
+ if (fs.existsSync(pkgDir)) {
563
+ let pkgJson = null;
564
+ const pkgJsonPath = path.join(pkgDir, "package.json");
565
+ if (fs.existsSync(pkgJsonPath)) {
566
+ try { pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8")); } catch {}
567
+ }
568
+ const resolved = sub
569
+ ? __nbcResolveSubpath(pkgDir, pkgJson, sub)
570
+ : __nbcResolveMain(pkgDir, pkgJson);
571
+ if (resolved) return resolved;
572
+ }
573
+ const parent = path.dirname(dir);
574
+ if (parent === dir) break;
575
+ dir = parent;
576
+ }
577
+ return null;
578
+ }
579
+ Module._resolveFilename = function(request, parent, isMain, options) {
580
+ try {
581
+ return __nbcOrigResolveFilename.call(this, request, parent, isMain, options);
582
+ } catch (err) {
583
+ // Only attempt fallback for bare package specifiers
584
+ if (typeof request !== "string" || request[0] === "." || request[0] === "/" || /^[a-z]+:/.test(request)) {
585
+ throw err;
586
+ }
587
+ const fromDir = parent && parent.filename ? path.dirname(parent.filename) : process.cwd();
588
+ const resolved = __nbcResolvePackage(request, fromDir);
589
+ if (resolved) return resolved;
590
+ throw err;
591
+ }
592
+ };
593
+
418
594
  const nextConfig = ${configMatch[1]};
419
595
  process.env.__NEXT_PRIVATE_STANDALONE_CONFIG = JSON.stringify(nextConfig);
420
596
 
@@ -433,7 +609,20 @@ async function extractAssets() {
433
609
  if (fs.existsSync(fullPath)) continue;
434
610
  fs.mkdirSync(path.dirname(fullPath), { recursive: true });
435
611
  const embedded = assetMap.get(urlPath);
436
- if (embedded) { await Bun.write(fullPath, Bun.file(embedded)); n++; }
612
+ if (!embedded) continue;
613
+ // Server chunks may contain __NBC_BASE__ placeholders injected at
614
+ // build time by rewriteTurbopackAliases. Substitute the real baseDir
615
+ // before writing so bun resolves the absolute paths at chunk load.
616
+ if (diskPath.startsWith(".next/server/")) {
617
+ const text = await Bun.file(embedded).text();
618
+ if (text.indexOf("__NBC_BASE__") !== -1) {
619
+ await Bun.write(fullPath, text.split("__NBC_BASE__").join(baseDir));
620
+ n++;
621
+ continue;
622
+ }
623
+ }
624
+ await Bun.write(fullPath, Bun.file(embedded));
625
+ n++;
437
626
  }
438
627
  if (n > 0) console.log(\`Extracted \${n} assets\`);
439
628
  }
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  __require,
3
3
  compile,
4
4
  generateEntryPoint
5
- } from "./index-mx555e90.js";
5
+ } from "./index-rx8tn52q.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.8",
3
+ "version": "0.6.10",
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",