Skip to content

Commit b3860c0

Browse files
committed
fix: auto-detect native binary distributor packages as externals
Packages like @secure-exec/v8 distribute platform-specific native binaries via optionalDependencies (e.g. @secure-exec/v8-darwin-arm64). These packages use createRequire(import.meta.url).resolve() at runtime to locate the correct platform binary, which breaks when esbuild bundles them because import.meta.url then points to the bundle output directory. Add a heuristic to the auto-external detection that checks if a package has optionalDependencies with platform-specific names (darwin, linux, win32, etc.). When 2+ such deps are found, the package is marked as external so it resolves binaries from its real node_modules location. https://claude.ai/code/session_01ScLShmDQ92z8bY9A2T1cPB
1 parent 2366b21 commit b3860c0

File tree

1 file changed

+33
-0
lines changed

1 file changed

+33
-0
lines changed

packages/cli-v3/src/build/externals.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,14 @@ function createExternalsCollector(
402402
return markExternal("binding.gyp exists");
403403
}
404404

405+
// Check if the package distributes platform-specific native binaries
406+
// via optionalDependencies (common pattern for Rust/napi-rs packages).
407+
// These packages use createRequire(import.meta.url).resolve() at runtime
408+
// to locate the correct platform binary, which breaks when bundled.
409+
if (hasPlatformSpecificOptionalDeps(packageJson)) {
410+
return markExternal("has platform-specific optionalDependencies");
411+
}
412+
405413
// Cache the negative result
406414
isExternalCache.set(packageRoot, false);
407415

@@ -655,3 +663,28 @@ async function findNearestPackageJson(
655663
cache.set(baseDir, packageJsonPath);
656664
return packageJsonPath;
657665
}
666+
667+
// Matches platform/arch identifiers commonly found in native binary package names
668+
// e.g. @secure-exec/v8-darwin-arm64, @rollup/rollup-linux-x64-gnu, esbuild-windows-64
669+
const platformPattern =
670+
/[-.](darwin|linux|win32|windows|freebsd|android|macos|sunos|openbsd|aix)/i;
671+
672+
function hasPlatformSpecificOptionalDeps(packageJson: Record<string, unknown>): boolean {
673+
const optionalDeps = packageJson.optionalDependencies;
674+
675+
if (!optionalDeps || typeof optionalDeps !== "object") {
676+
return false;
677+
}
678+
679+
const depNames = Object.keys(optionalDeps);
680+
681+
if (depNames.length === 0) {
682+
return false;
683+
}
684+
685+
// If a significant portion of optionalDependencies match platform patterns,
686+
// this is likely a native binary distributor package
687+
const platformDeps = depNames.filter((name) => platformPattern.test(name));
688+
689+
return platformDeps.length >= 2;
690+
}

0 commit comments

Comments
 (0)