fix(extensions): bundle nested wildcard export subpaths for compiled binaries
The compiled registry enumerated each `exports` wildcard with a single-level glob and explicitly skipped any key containing a slash, so a nested subpath like `slash-commands/helpers/active-oauth-account` never entered the bundled registry. Node matches `*` across `/`, so that import is legitimate: it resolves from source, then falls through to `Bun.resolveSync` inside a compiled binary and dies under bunfs. Reproducible on the published 17.2.1 binary with a real extension (`quota-hud.ts`). Enumeration is now recursive, with every path segment held to the same private/hidden rules as the leaf, so a `.private/` or `_internal/` directory is no more exported than a private file. Directory index modules stay excluded: `./x/*` must not serve `x/y` from `y/index.ts`, which Node would not resolve either.
This commit is contained in:
@@ -142,7 +142,13 @@ export async function collectBundledPiEntries(): Promise<BundledPiEntry[]> {
|
||||
|
||||
const sourceDir = path.join(packageRoot, pattern.sourcePrefix);
|
||||
try {
|
||||
const glob = new Bun.Glob(`*${pattern.sourceSuffix}`);
|
||||
// Recursive on purpose: Node matches `*` in an `exports` pattern across
|
||||
// `/`, so `./slash-commands/*` genuinely serves
|
||||
// `slash-commands/helpers/active-oauth-account`. Enumerating only the
|
||||
// top level left every nested key out of the compiled registry, where
|
||||
// it fell through to `Bun.resolveSync` and died under bunfs — so such
|
||||
// an import worked from source and failed inside a binary.
|
||||
const glob = new Bun.Glob(`**/*${pattern.sourceSuffix}`);
|
||||
const matches: string[] = [];
|
||||
for await (const match of glob.scan({ cwd: sourceDir, onlyFiles: true })) {
|
||||
matches.push(match);
|
||||
@@ -151,7 +157,11 @@ export async function collectBundledPiEntries(): Promise<BundledPiEntry[]> {
|
||||
for (const match of matches) {
|
||||
if (!match.endsWith(pattern.sourceSuffix)) continue;
|
||||
const basename = match.slice(0, match.length - pattern.sourceSuffix.length);
|
||||
if (!isSafeWildcardBasename(basename) || basename.includes("/")) continue;
|
||||
const segments = basename.split("/");
|
||||
// Every directory on the way has to be importable too: a private or
|
||||
// hidden folder is no more exported than a private file.
|
||||
if (segments.some(segment => segment.startsWith(".") || segment.startsWith("_"))) continue;
|
||||
if (!isSafeWildcardBasename(segments.at(-1) ?? "")) continue;
|
||||
const subpath = `${pattern.exportPrefix}${basename}${pattern.exportSuffix}`;
|
||||
const key = `${manifest.name}/${subpath}`;
|
||||
addEntry(key, bindingForSubpath(pkg.identifier, subpath), key);
|
||||
|
||||
Reference in New Issue
Block a user