diff --git a/src/plugins/bundled-runtime-deps-jiti-aliases.test.ts b/src/plugins/bundled-runtime-deps-jiti-aliases.test.ts index 3c7d5d3018c..b75d275924b 100644 --- a/src/plugins/bundled-runtime-deps-jiti-aliases.test.ts +++ b/src/plugins/bundled-runtime-deps-jiti-aliases.test.ts @@ -111,6 +111,72 @@ describe("bundled runtime dependency Jiti aliases", () => { }); }); + it("honors package condition order before top-level require fallbacks", () => { + const rootDir = makeTempRoot(); + writeJson(path.join(rootDir, "package.json"), { + dependencies: { + conditional: "1.0.0", + }, + }); + const conditionalRoot = packageRoot(rootDir, "conditional"); + writeJson(path.join(conditionalRoot, "package.json"), { + exports: { + ".": { + browser: { + default: "./dist/web/index.js", + }, + node: { + import: "./dist/node/index.mjs", + require: "./dist/node/index.cjs", + default: "./dist/node/index.mjs", + }, + import: "./dist/index.mjs", + require: "./dist/index.cjs", + default: "./dist/index.mjs", + }, + }, + }); + writeFile(path.join(conditionalRoot, "dist/index.cjs")); + writeFile(path.join(conditionalRoot, "dist/node/index.cjs")); + + registerBundledRuntimeDependencyJitiAliases(rootDir); + + expect(resolveBundledRuntimeDependencyJitiAliasMap()).toEqual({ + conditional: path.join(conditionalRoot, "dist/node/index.cjs"), + }); + }); + + it("falls back to import-only conditional exports for staged runtime deps", () => { + const rootDir = makeTempRoot(); + writeJson(path.join(rootDir, "package.json"), { + dependencies: { + "import-only": "1.0.0", + }, + }); + const importOnlyRoot = packageRoot(rootDir, "import-only"); + writeJson(path.join(importOnlyRoot, "package.json"), { + exports: { + ".": { + types: "./dist/index.d.ts", + import: "./dist/index.js", + }, + "./provider": { + types: "./dist/provider.d.ts", + import: "./dist/provider.js", + }, + }, + }); + writeFile(path.join(importOnlyRoot, "dist/index.js")); + writeFile(path.join(importOnlyRoot, "dist/provider.js")); + + registerBundledRuntimeDependencyJitiAliases(rootDir); + + expect(resolveBundledRuntimeDependencyJitiAliasMap()).toEqual({ + "import-only/provider": path.join(importOnlyRoot, "dist/provider.js"), + "import-only": path.join(importOnlyRoot, "dist/index.js"), + }); + }); + it("ignores missing, private, and escaping export targets", () => { const rootDir = makeTempRoot(); writeJson(path.join(rootDir, "package.json"), { diff --git a/src/plugins/bundled-runtime-deps-jiti-aliases.ts b/src/plugins/bundled-runtime-deps-jiti-aliases.ts index b39880527c0..54880dcad0d 100644 --- a/src/plugins/bundled-runtime-deps-jiti-aliases.ts +++ b/src/plugins/bundled-runtime-deps-jiti-aliases.ts @@ -13,6 +13,10 @@ type RuntimeDependencyPackageJson = { }; const bundledRuntimeDependencyJitiAliases = new Map(); +const RUNTIME_DEPENDENCY_JITI_CONDITION_PASSES = [ + new Set(["node", "require", "default"]), + new Set(["node", "import", "default"]), +] as const; function readRuntimeDependencyPackageJson( packageJsonPath: string, @@ -32,13 +36,16 @@ function collectRuntimeDependencyNames(pkg: RuntimeDependencyPackageJson): strin ].toSorted((left, right) => left.localeCompare(right)); } -function resolveRuntimePackageImportTarget(exportsField: unknown): string | null { +function resolveRuntimePackageImportTargetForConditions( + exportsField: unknown, + activeConditions: ReadonlySet, +): string | null { if (typeof exportsField === "string") { return exportsField; } if (Array.isArray(exportsField)) { for (const entry of exportsField) { - const resolved = resolveRuntimePackageImportTarget(entry); + const resolved = resolveRuntimePackageImportTargetForConditions(entry, activeConditions); if (resolved) { return resolved; } @@ -50,10 +57,23 @@ function resolveRuntimePackageImportTarget(exportsField: unknown): string | null } const record = exportsField as Record; if (Object.prototype.hasOwnProperty.call(record, ".")) { - return resolveRuntimePackageImportTarget(record["."]); + return resolveRuntimePackageImportTargetForConditions(record["."], activeConditions); } - for (const condition of ["require", "node", "default", "import"] as const) { - const resolved = resolveRuntimePackageImportTarget(record[condition]); + for (const [condition, target] of Object.entries(record)) { + if (!activeConditions.has(condition)) { + continue; + } + const resolved = resolveRuntimePackageImportTargetForConditions(target, activeConditions); + if (resolved) { + return resolved; + } + } + return null; +} + +function resolveRuntimePackageImportTarget(exportsField: unknown): string | null { + for (const activeConditions of RUNTIME_DEPENDENCY_JITI_CONDITION_PASSES) { + const resolved = resolveRuntimePackageImportTargetForConditions(exportsField, activeConditions); if (resolved) { return resolved; }