fix(javascript): preserve loader-specific package ancestry

Require a package directory for bare ESM imports. Continue CommonJS root and subpath lookup through ancestor packages after ordinary misses, while keeping exports authority, unavailable metadata, and failed nonempty main entrypoints terminal.

Carry failed-main ownership in the internal resolution result and retain the nearest local miss after exhausting ancestors. Consolidate package selection at its owning boundary and qualify loader differences with real Node 24, CLI, and MCP regressions.
This commit is contained in:
morluto committed 2026-10-12 11:26:48 +08:00
1 parent ee50ed121e
commit 9f67e44e5d
3 files changed
+296 -30

No files matched your search

@@ -121,7 +121,66 @@ describe("loader-owned path lookup", () => {
}),
).toMatchObject({ resolved_path: "app/feature.cjs" });
});
});
describe("bare package root file lookup", () => {
it("uses loader-specific bare package roots when only a sibling .js file exists", () => {
const absentDirectory = fileMap([
file("app/entry.mjs", "root"),
file("app/node_modules/fixture.js", "root"),
]);
expect(
resolve({
declaredPath: "fixture",
sourcePath: "app/entry.mjs",
context: "module-specifier",
moduleKind: "import",
files: absentDirectory,
}),
).toMatchObject({ resolution_status: "external", resolved_path: null });
expect(
resolve({
declaredPath: "fixture",
sourcePath: "app/entry.mjs",
context: "module-specifier",
moduleKind: "require",
files: absentDirectory,
}),
).toMatchObject({
resolution_status: "resolved",
resolved_path: "app/node_modules/fixture.js",
});
const packageDirectory = fileMap([
file("app/entry.mjs", "root"),
file("app/node_modules/fixture.js", "root"),
file("app/node_modules/fixture/package.json", "root", "{}"),
]);
expect(
resolve({
declaredPath: "fixture",
sourcePath: "app/entry.mjs",
context: "module-specifier",
moduleKind: "import",
files: packageDirectory,
}),
).toMatchObject({ resolution_status: "not-found", resolved_path: null });
expect(
resolve({
declaredPath: "fixture",
sourcePath: "app/entry.mjs",
context: "module-specifier",
moduleKind: "require",
files: packageDirectory,
}),
).toMatchObject({
resolution_status: "resolved",
resolved_path: "app/node_modules/fixture.js",
});
});
});
describe("legacy package main lookup", () => {
it("treats an empty package main as absent and resolves the root index", () => {
const files = fileMap([
file("app/src/entry.cjs", "root"),
@@ -208,6 +267,133 @@ describe("loader-owned path lookup", () => {
});
});
describe("CommonJS bare package ancestor lookup", () => {
it.each([
["absent metadata", undefined, true],
["empty metadata", "{}", true],
["empty main", '{"main":""}', true],
["truthy missing main", '{"main":"missing"}', false],
] as const)(
"handles %s before an ancestor package for both loaders",
(_name, packageJson, commonJsUsesAncestor) => {
const files = fileMap([
file("app/src/entry.js", "root"),
file("app/src/node_modules/fixture/placeholder.js", "root"),
...(packageJson === undefined
? []
: [
file(
"app/src/node_modules/fixture/package.json",
"root",
packageJson,
),
]),
file("app/node_modules/fixture/index.js", "root"),
]);
for (const moduleKind of ["import", "require"] as const) {
const resolution = resolve({
declaredPath: "fixture",
sourcePath: "app/src/entry.js",
context: "module-specifier",
moduleKind,
files,
});
if (moduleKind === "require" && commonJsUsesAncestor) {
expect(resolution).toMatchObject({
resolution_status: "resolved",
resolved_path: "app/node_modules/fixture/index.js",
});
} else {
expect(resolution).toMatchObject({
resolution_status: "not-found",
resolved_path: null,
});
}
}
},
);
});
describe("CommonJS bare package subpath ancestor lookup", () => {
it.each([
["absent subpath", undefined, true],
["empty subpath metadata", "{}", true],
["truthy missing subpath main", '{"main":"missing"}', false],
] as const)(
"handles %s before an ancestor package for both loaders",
(_name, packageJson, commonJsUsesAncestor) => {
const files = fileMap([
file("app/src/entry.js", "root"),
file("app/src/node_modules/fixture/placeholder.js", "root"),
...(packageJson === undefined
? []
: [
file(
"app/src/node_modules/fixture/parser/package.json",
"root",
packageJson,
),
file(
"app/src/node_modules/fixture/parser/placeholder.js",
"root",
),
]),
file("app/node_modules/fixture/parser.js", "root"),
]);
const commonJsResolution = resolve({
declaredPath: "fixture/parser",
sourcePath: "app/src/entry.js",
context: "module-specifier",
moduleKind: "require",
files,
});
expect(commonJsResolution).toMatchObject({
resolved_path: commonJsUsesAncestor
? "app/node_modules/fixture/parser.js"
: null,
});
const esmResolution = resolve({
declaredPath: "fixture/parser",
sourcePath: "app/src/entry.js",
context: "module-specifier",
moduleKind: "import",
files,
});
expect(esmResolution.resolved_path).toBeNull();
},
);
});
describe("CommonJS bare package exhausted lookup", () => {
it.each([
["empty main", "fixture", '{"main":""}', "app/src/node_modules/fixture"],
[
"missing subpath",
"fixture/parser",
"{}",
"app/src/node_modules/fixture/parser",
],
] as const)(
"preserves not-found for an exhausted %s lookup",
(_name, declaredPath, packageJson, packageDirectory) => {
const files = fileMap([
file("app/src/entry.js", "root"),
file("app/src/node_modules/fixture/package.json", "root", packageJson),
file(`${packageDirectory}/placeholder.js`, "root"),
]);
expect(
resolve({
declaredPath,
sourcePath: "app/src/entry.js",
context: "module-specifier",
moduleKind: "require",
files,
}),
).toMatchObject({ resolution_status: "not-found", resolved_path: null });
},
);
});
describe("logical path identity", () => {
it("resolves an NFD relative import to the NFC inventory path and rejects a root escape", () => {
const nfc = "caf\u00e9".normalize("NFC");