feat(javascript): resolve exact package import aliases (#1862)

* feat(javascript): resolve exact package import aliases

Resolve exact #alias specifiers against the importer's enclosing
package.json imports map using the shared condition, array, and null
target selection. Nested packages keep their own maps; missing keys,
explicit nulls, and out-of-scope aliases report a precise reason.
Wildcard, recursive, and external-package import targets remain
unresolved.

* fix(javascript): keep nested import aliases in CLI graphs

Bound nested #alias requires produce application-graph require edges
from the enclosing package. Node reports MODULE_NOT_FOUND when a nested
package.json has no imports field, without inheriting the ancestor map.

* fix(javascript): resolve literal package import keys before path admission

* fix(javascript): preserve import keys and invalid-target fallback

* fix(javascript): preserve unmatched package target arrays

* fix(javascript): retain package target refusal evidence

* test(browser): match real verifier startup budget

---------

Co-authored-by: morluto <[email protected]>
This commit is contained in:
Matt Van Horn
2026-10-12 01:17:58 +08:00
committed by GitHub
co-authored by morluto
parent e8491e023a
commit d28b41ea4e
10 changed files with 1838 additions and 83 deletions
@@ -9,7 +9,10 @@ provenance; inputs and recovered code are never executed. Recovery requires a
caller-supplied Wakaru on Linux x64; see [the recovery guide](https://github.com/morluto/rea/blob/main/docs/javascript-recovery.md).
Use `analyze_javascript_application` directly on the operator-supplied ASAR or
extracted tree. When size is unknown or large and the connected schema advertises
extracted tree. Exact `#alias` specifiers resolve through the importer's
enclosing package `imports` map to package-local files; nested `node_modules`
packages use their own map. Wildcard, recursive, and external-package import
targets stay unresolved with that limitation. When size is unknown or large and the connected schema advertises
`detail`, request `detail: "summary"`. This returns an `inspect_analysis_view`
Evidence record with a distinct ID and a `normalized_result.parent_evidence_id`
for the complete retained application analysis. Source-map contents are part of
@@ -452,9 +452,29 @@ such as `./web3:app.js` still names a local artifact.
Package `exports` fallback arrays are supported both at the top level and under
the root `"."` entry. The resolver selects targets in declared order using the
same conditional and invalid-entry handling as nested exports arrays.
Nonempty arrays containing only inactive conditions remain unmatched, allowing
the enclosing condition object to continue to a later active condition.
Empty arrays and explicit null targets keep their blocking outcome.
Refusal evidence distinguishes those source values and retains condition names
from unmatched nested targets rather than reporting array indices.
For imports and requires, the active Node conditions include `module-sync`;
earlier active conditions retain precedence over later ones.
Exact package-private `#alias` specifiers resolve against the importer's
enclosing `package.json` `imports` map — the nearest same-container package
scope, stopping at a `node_modules` directory so a nested package cannot inherit
an ancestor's aliases. Selected targets are package-local `./` files with no
extension or index fallback, using the same condition, array, and null selection
as exports subpaths. A missing key, explicit null, or out-of-scope alias keeps
an unresolved edge with that reason. Wildcard keys (`#internal/*`), recursive
`#` targets, and external-package import targets remain unresolved.
Exact keys such as `#/x` are literal lookup identities. `#` and names ending
in `/` are invalid. Imports arrays skip invalid parent-relative, absolute, and
URL targets before selecting a later package-local file; an unsupported bare
package or recursive alias keeps its unresolved outcome rather than selecting
a later fallback.
ESM relative module paths and selected package exports targets use URL suffix handling
and one percent-decoding pass. CommonJS relative paths and legacy package main
fields retain literal filename punctuation. Exports targets must start with
@@ -868,6 +868,365 @@ describe("package exports exact targets", () => {
});
});
describe("package import alias resolution", () => {
it.each(["import", "require"] as const)(
"resolves an exact #%s alias from the enclosing package imports map",
(moduleKind) => {
const files = aliasFiles({
"package.json": JSON.stringify({
imports: { "#config": "./config.cjs" },
}),
"main.js": "",
"config.cjs": "",
});
expect(
resolve({
declaredPath: "#config",
sourcePath: "main.js",
context: "module-specifier",
moduleKind,
files,
}),
).toMatchObject({
resolution_status: "resolved",
resolved_path: "config.cjs",
declared_path: "#config",
});
},
);
it("resolves an alias from a nested file through the enclosing package root", () => {
const files = aliasFiles({
"package.json": JSON.stringify({
imports: { "#config": "./config.cjs" },
}),
"src/main.js": "",
"config.cjs": "",
});
expect(
resolve({
declaredPath: "#config",
sourcePath: "src/main.js",
context: "module-specifier",
moduleKind: "require",
files,
}),
).toMatchObject({
resolution_status: "resolved",
resolved_path: "config.cjs",
});
});
it("uses a nested package imports map rather than the application root", () => {
const files = aliasFiles({
"package.json": JSON.stringify({
imports: { "#config": "./root.cjs" },
}),
"root.cjs": "",
"node_modules/fixture/package.json": JSON.stringify({
imports: { "#config": "./local.cjs" },
}),
"node_modules/fixture/index.js": "",
"node_modules/fixture/local.cjs": "",
});
expect(
resolve({
declaredPath: "#config",
sourcePath: "node_modules/fixture/index.js",
context: "module-specifier",
moduleKind: "require",
files,
}),
).toMatchObject({
resolution_status: "resolved",
resolved_path: "node_modules/fixture/local.cjs",
});
});
it("does not inherit an ancestor imports map across a nested package.json", () => {
const files = aliasFiles({
"package.json": JSON.stringify({
imports: { "#config": "./root.cjs" },
}),
"root.cjs": "",
"node_modules/fixture/package.json": "{}",
"node_modules/fixture/index.js": "",
});
const outcome = resolve({
declaredPath: "#config",
sourcePath: "node_modules/fixture/index.js",
context: "module-specifier",
moduleKind: "require",
files,
});
expect(outcome).toMatchObject({
resolution_status: "external",
resolved_path: null,
});
expect(outcome.limitations.join(" ")).toContain(
"node_modules/fixture/package.json",
);
expect(outcome.limitations.join(" ")).not.toContain("root.cjs");
});
it("does not apply a nested package map to the application root importer", () => {
const files = aliasFiles({
"package.json": JSON.stringify({
imports: { "#config": "./root.cjs" },
}),
"main.js": "",
"root.cjs": "",
"node_modules/fixture/package.json": JSON.stringify({
imports: { "#config": "./local.cjs" },
}),
"node_modules/fixture/local.cjs": "",
});
expect(
resolve({
declaredPath: "#config",
sourcePath: "main.js",
context: "module-specifier",
moduleKind: "require",
files,
}),
).toMatchObject({
resolution_status: "resolved",
resolved_path: "root.cjs",
});
});
});
describe("package import alias target selection", () => {
it("selects import and require conditions from an imports target", () => {
const files = aliasFiles({
"package.json": JSON.stringify({
imports: {
"#config": {
import: "./config.mjs",
require: "./config.cjs",
default: "./fallback.js",
},
},
}),
"main.js": "",
"config.mjs": "",
"config.cjs": "",
"fallback.js": "",
});
expect(
resolve({
declaredPath: "#config",
sourcePath: "main.js",
context: "module-specifier",
moduleKind: "import",
files,
}).resolved_path,
).toBe("config.mjs");
expect(
resolve({
declaredPath: "#config",
sourcePath: "main.js",
context: "module-specifier",
moduleKind: "require",
files,
}).resolved_path,
).toBe("config.cjs");
});
it("resolves array import targets at their first string entry", () => {
const files = aliasFiles({
"package.json": JSON.stringify({
imports: { "#config": [{ import: "./missing.mjs" }, "./config.cjs"] },
}),
"main.js": "",
"config.cjs": "",
});
expect(
resolve({
declaredPath: "#config",
sourcePath: "main.js",
context: "module-specifier",
moduleKind: "require",
files,
}),
).toMatchObject({
resolution_status: "resolved",
resolved_path: "config.cjs",
});
});
it("does not add an extension or index to a selected import target", () => {
const files = aliasFiles({
"package.json": JSON.stringify({
imports: { "#config": "./config" },
}),
"main.js": "",
"config.cjs": "",
"config/index.js": "",
});
const outcome = resolve({
declaredPath: "#config",
sourcePath: "main.js",
context: "module-specifier",
moduleKind: "require",
files,
});
expect(outcome).toMatchObject({
resolution_status: "not-found",
resolved_path: null,
});
expect(outcome.limitations.join(" ")).toContain(
"import targets are exact files",
);
});
});
describe("package import alias refusals", () => {
it("reports a missing exact alias without looking in another package", () => {
const files = aliasFiles({
"package.json": JSON.stringify({
imports: { "#config": "./config.cjs" },
}),
"main.js": "",
"config.cjs": "",
});
const outcome = resolve({
declaredPath: "#other",
sourcePath: "main.js",
context: "module-specifier",
moduleKind: "require",
files,
});
expect(outcome).toMatchObject({
resolution_status: "external",
resolved_path: null,
});
expect(outcome.limitations.join(" ")).toContain("#other");
expect(outcome.limitations.join(" ")).toContain("#config");
});
it("reports an explicit null import target as blocked", () => {
const files = aliasFiles({
"package.json": JSON.stringify({
imports: { "#config": null },
}),
"main.js": "",
"config.cjs": "",
});
const outcome = resolve({
declaredPath: "#config",
sourcePath: "main.js",
context: "module-specifier",
moduleKind: "require",
files,
});
expect(outcome).toMatchObject({
resolution_status: "external",
resolved_path: null,
});
expect(outcome.limitations.join(" ")).toContain("explicit null");
});
it("leaves wildcard import aliases unresolved", () => {
const files = aliasFiles({
"package.json": JSON.stringify({
imports: { "#internal/*": "./src/*.js" },
}),
"main.js": "",
"src/util.js": "",
});
const outcome = resolve({
declaredPath: "#internal/util",
sourcePath: "main.js",
context: "module-specifier",
moduleKind: "import",
files,
});
expect(outcome).toMatchObject({
resolution_status: "external",
resolved_path: null,
});
expect(outcome.limitations.join(" ")).toMatch(/wildcard/i);
});
it("leaves recursive and external-package import targets unresolved", () => {
const files = aliasFiles({
"package.json": JSON.stringify({
imports: { "#loop": "#config", "#dep": "fixture" },
}),
"main.js": "",
"node_modules/fixture/index.js": "",
});
const recursive = resolve({
declaredPath: "#loop",
sourcePath: "main.js",
context: "module-specifier",
moduleKind: "require",
files,
});
const external = resolve({
declaredPath: "#dep",
sourcePath: "main.js",
context: "module-specifier",
moduleKind: "require",
files,
});
expect(recursive).toMatchObject({
resolution_status: "external",
resolved_path: null,
});
expect(recursive.limitations.join(" ")).toMatch(/recursive/i);
expect(external).toMatchObject({
resolution_status: "external",
resolved_path: null,
});
expect(external.limitations.join(" ")).toMatch(/external-package/i);
});
it("rejects reserved hash specifiers", () => {
const files = aliasFiles({
"package.json": JSON.stringify({
imports: { "#config": "./config.cjs" },
}),
"main.js": "",
"config.cjs": "",
});
expect(
resolve({
declaredPath: "#",
sourcePath: "main.js",
context: "module-specifier",
moduleKind: "import",
files,
}),
).toMatchObject({ resolution_status: "rejected", resolved_path: null });
});
it("does not use a foreign-container enclosing package.json", () => {
const files = fileMap([
file(
"package.json",
"other",
JSON.stringify({ imports: { "#config": "./config.cjs" } }),
),
file("main.js", "root"),
file("config.cjs", "root"),
]);
const outcome = resolve({
declaredPath: "#config",
sourcePath: "main.js",
context: "module-specifier",
moduleKind: "require",
files,
});
expect(outcome).toMatchObject({
resolution_status: "external",
resolved_path: null,
});
expect(outcome.limitations.join(" ")).toContain("package scopes");
});
});
describe("directory package entrypoint precedence", () => {
it.each([
["require", '{"main":"actual.cjs"}', "actual.cjs"],
@@ -1211,6 +1570,15 @@ const file = (
: { included: true, value: text },
});
const aliasFiles = (
entries: Readonly<Record<string, string | null>>,
): ReturnType<typeof fileMap> =>
fileMap(
Object.entries(entries).map(([path, text]) =>
file(path, "root", text ?? ""),
),
);
describe("exact HTML resource resolution", () => {
it.each([
["extension", ["app.js"]],
@@ -14,6 +14,11 @@ import {
percentDecodeUrlPath,
stripQueryAndFragment,
} from "../../domain/artifactPathSyntax.js";
import {
classifyPackageImportSpecifier,
classifyPackageImportTargetString,
lookupExactPackageImport,
} from "../../domain/javascript/javascriptPackageImportAliases.js";
type ArtifactPathResolutionContext =
| "package-entrypoint"
@@ -103,6 +108,13 @@ export const resolveArtifactPathByContext = (
};
const resolvePath = (input: ResolveArtifactPathInput): CandidateResolution => {
// Package import keys are literal lookup identities, not artifact paths.
// The selected target is admitted separately by resolveExportTarget.
if (
input.context === "module-specifier" &&
input.declaredPath.startsWith("#")
)
return resolvePackageImportAlias(input, input.declaredPath);
const rejected = rejectDeclaration(input.declaredPath);
if (rejected !== null) return rejected;
const candidate = contextualCandidate(input);
@@ -202,9 +214,9 @@ const bareModuleCandidate = (
declared: string,
): CandidateResolution => {
const packageName = barePackageName(declared);
if (packageName === null || isBuiltin(declared) || declared.startsWith("#"))
if (packageName === null || isBuiltin(declared))
return unresolvedCandidate("external", [
"The bare specifier is a Node builtin, package import map, or invalid package name.",
"The bare specifier is a Node builtin or invalid package name.",
]);
if (input.moduleKind === "import" && packageName.includes("%"))
return unresolvedCandidate("rejected", [
@@ -235,6 +247,115 @@ const bareModuleCandidate = (
]);
};
/**
* Nearest same-container package.json, stopping at a `node_modules` directory
* so a nested package cannot inherit an ancestor's map.
*/
const enclosingPackageMetadata = (
input: ResolveArtifactPathInput,
):
| Extract<PackageMetadata, { readonly kind: "available" | "unavailable" }>
| { readonly kind: "none" } => {
let directory = posix.dirname(input.sourcePath);
while (posix.basename(directory) !== "node_modules") {
const metadata = readPackageMetadata(input, directory);
if (metadata.kind !== "absent") return metadata;
if (directory === "." || directory === "") break;
directory = posix.dirname(directory);
}
return { kind: "none" };
};
/**
* Resolve an exact `#alias` against the importer's enclosing `imports` map.
* Selected relative targets are exact files; patterns, recursive aliases, and
* external-package targets stay unresolved without falling through.
*/
const resolvePackageImportAlias = (
input: ResolveArtifactPathInput,
specifier: string,
): CandidateResolution => {
const classified = classifyPackageImportSpecifier(specifier);
if (classified.kind === "invalid")
return unresolvedCandidate("rejected", [
`The package import specifier ${JSON.stringify(specifier)} is invalid; "#" and specifiers that end with "/" are not valid import aliases.`,
]);
if (classified.kind === "pattern")
return unresolvedCandidate("external", [
`Wildcard and directory package import aliases remain unresolved; ${JSON.stringify(specifier)} is not an exact imports key.`,
]);
const metadata = enclosingPackageMetadata(input);
if (metadata.kind === "none")
return unresolvedCandidate("external", [
`No enclosing package.json defines package import alias ${specifier}; package import maps do not cross package scopes.`,
]);
if (metadata.kind === "unavailable")
return unresolvedCandidate("unavailable", [metadata.limitation]);
const lookup = lookupExactPackageImport(metadata.imports, specifier);
if (lookup.kind === "undefined-map")
return unresolvedCandidate("external", [
`Package metadata ${metadata.path} does not define package import alias ${specifier}.`,
]);
if (lookup.kind === "missing-key")
return unresolvedCandidate("external", [
`Package metadata ${metadata.path} does not define package import alias ${specifier}; it declares ${lookup.declared.join(", ") || "no imports keys"}.`,
]);
if (lookup.kind === "pattern-unresolved")
return unresolvedCandidate("external", [
`Wildcard package import aliases remain unresolved; package metadata ${metadata.path} has no exact imports key ${specifier}.`,
]);
const selected = selectPackageImportTarget(lookup.target, input.moduleKind);
if (selected.status === "rejected")
return unresolvedCandidate("rejected", [
`Package metadata ${metadata.path}: ${selected.limitation}`,
]);
if (selected.status === "unmatched")
return unresolvedCandidate("external", [
`Package metadata ${metadata.path} declares no imports target for alias ${specifier} under the active conditions; it declares ${selected.declared.join(", ") || "no conditions"}.`,
]);
if (selected.status === "blocked")
return unresolvedCandidate("external", [
`Package metadata ${metadata.path} blocks import alias ${specifier} with ${selected.cause === "null" ? "an explicit null imports target" : "an empty imports target array"}.`,
]);
if (selected.status === "non-relative") {
const target = classifyPackageImportTargetString(selected.value);
return unresolvedCandidate("external", [
target.kind === "recursive"
? `Recursive package import aliases remain unresolved; package metadata ${metadata.path} maps ${specifier} to ${JSON.stringify(target.target)}.`
: `External-package import targets remain unresolved; package metadata ${metadata.path} maps ${specifier} to ${JSON.stringify(target.target)}.`,
]);
}
return resolveExportTarget(input, metadata.path, selected.value, "imports");
};
const selectPackageImportTarget = (
value: unknown,
moduleKind: ResolveArtifactPathInput["moduleKind"],
):
| { readonly status: "value"; readonly value: string }
| { readonly status: "non-relative"; readonly value: string }
| { readonly status: "blocked"; readonly cause: PackageTargetBlockCause }
| { readonly status: "unmatched"; readonly declared: readonly string[] }
| { readonly status: "rejected"; readonly limitation: string } => {
const selected = selectExportTarget(
value,
packageExportConditions(moduleKind),
"imports",
);
if (selected.kind === "invalid" || selected.kind === "invalid-config")
return { status: "rejected", limitation: selected.limitation };
if (selected.kind === "unmatched")
return {
status: "unmatched",
declared: selected.declared,
};
if (selected.kind === "blocked")
return { status: "blocked", cause: selected.cause };
if (selected.kind === "non-relative")
return { status: "non-relative", value: selected.value };
return { status: "value", value: selected.value };
};
/** Self references use the nearest package scope before dependency lookup. */
const commonJsSelfCandidate = (
input: ResolveArtifactPathInput,
@@ -242,35 +363,22 @@ const commonJsSelfCandidate = (
): CandidateResolution | null => {
if (input.moduleKind !== "require" || !input.files.has(input.sourcePath))
return null;
let directory = posix.dirname(input.sourcePath);
while (posix.basename(directory) !== "node_modules") {
const metadata = readPackageMetadata(input, directory);
if (metadata.kind === "unavailable")
return unresolvedCandidate("unavailable", [metadata.limitation]);
if (metadata.kind === "available") {
if (
metadata.name === undefined ||
metadata.exports === undefined ||
(declared !== metadata.name &&
!declared.startsWith(`${metadata.name}/`))
)
return null;
const subpath =
declared === metadata.name
? "."
: `.${declared.slice(metadata.name.length)}`;
// Self resolution does not apply the CommonJS dependency-name matcher.
return resolvePackageExports(
input,
metadata.path,
subpath,
metadata.exports,
);
}
if (directory === "." || directory === "") break;
directory = posix.dirname(directory);
}
return null;
const metadata = enclosingPackageMetadata(input);
if (metadata.kind === "none") return null;
if (metadata.kind === "unavailable")
return unresolvedCandidate("unavailable", [metadata.limitation]);
if (
metadata.name === undefined ||
metadata.exports === undefined ||
(declared !== metadata.name && !declared.startsWith(`${metadata.name}/`))
)
return null;
const subpath =
declared === metadata.name
? "."
: `.${declared.slice(metadata.name.length)}`;
// Self resolution does not apply the CommonJS dependency-name matcher.
return resolvePackageExports(input, metadata.path, subpath, metadata.exports);
};
/** Package roots and exact subpaths share metadata and exports selection. */
@@ -324,16 +432,17 @@ const resolvePackageExports = (
]);
if (exported.status === "blocked")
return unresolvedCandidate("external", [
`Directory package metadata ${packagePath} blocks subpath ${subpath} with an explicit null exports target.`,
`Directory package metadata ${packagePath} blocks subpath ${subpath} with ${exported.cause === "null" ? "an explicit null exports target" : "an empty exports target array"}.`,
]);
return resolveExportTarget(input, packagePath, exported.value);
};
/** An exports target is a URL naming one exact file for both Node loaders. */
/** An exports or import target is a URL naming one exact file for both Node loaders. */
const resolveExportTarget = (
input: ResolveArtifactPathInput,
packagePath: string,
target: string,
mapKind: "exports" | "imports" = "exports",
): CandidateResolution => {
const rejected = rejectDeclaration(target);
if (rejected !== null) return rejected;
@@ -343,7 +452,7 @@ const resolveExportTarget = (
if (typeof candidate !== "string") return candidate;
return (
resolveFileCandidates(input, [candidate]) ??
exactExportsTargetNotFound(candidate)
exactPackageTargetNotFound(candidate, mapKind)
);
};
@@ -492,6 +601,7 @@ type PackageMetadata =
readonly path: string;
readonly name: string | undefined;
readonly exports: string | object | undefined;
readonly imports: object | undefined;
readonly main: unknown;
readonly module: unknown;
}
@@ -532,6 +642,7 @@ const readPackageMetadata = (
};
const name = Reflect.get(value, "name");
const exports = Reflect.get(value, "exports");
const imports = Reflect.get(value, "imports");
return {
kind: "available",
path,
@@ -543,6 +654,11 @@ const readPackageMetadata = (
(typeof exports === "object" && exports !== null)
? exports
: undefined,
// Import maps must be non-array objects; other shapes are not a map.
imports:
typeof imports === "object" && imports !== null && !Array.isArray(imports)
? imports
: undefined,
main: Reflect.get(value, "main"),
module: Reflect.get(value, "module"),
};
@@ -644,10 +760,12 @@ const fileCandidates = (candidate: string): readonly string[] => [
const indexCandidates = (candidate: string): readonly string[] =>
EXTENSIONS.map((extension) => posix.join(candidate, `index${extension}`));
type PackageTargetBlockCause = "null" | "empty-array";
type PackageExportOutcome =
| { readonly status: "value"; readonly value: string }
| { readonly status: "rejected"; readonly limitation: string }
| { readonly status: "blocked" }
| { readonly status: "blocked"; readonly cause: PackageTargetBlockCause }
| { readonly status: "unmatched"; readonly declared: readonly string[] };
/** Only exact non-directory keys participate; wildcard expansion is unsupported. */
@@ -687,29 +805,33 @@ const selectPackageExport = (
if (selected.kind === "unmatched")
return {
status: "unmatched",
declared:
typeof target === "object" && target !== null
? Object.keys(target)
: declared,
declared: selected.declared,
};
return selected.kind === "blocked"
? { status: "blocked" }
: { status: "value", value: selected.value };
if (selected.kind === "blocked")
return { status: "blocked", cause: selected.cause };
if (selected.kind === "non-relative")
return {
status: "rejected",
limitation: `The package exports target ${JSON.stringify(selected.value)} must start with "./".`,
};
return { status: "value", value: selected.value };
};
type ExportTargetOutcome =
| { readonly kind: "value"; readonly value: string }
| { readonly kind: "blocked" }
| { readonly kind: "unmatched" }
| { readonly kind: "non-relative"; readonly value: string }
| { readonly kind: "blocked"; readonly cause: PackageTargetBlockCause }
| { readonly kind: "unmatched"; readonly declared: readonly string[] }
| { readonly kind: "invalid"; readonly limitation: string }
| { readonly kind: "invalid-config"; readonly limitation: string };
const invalidExportTarget = (
value: unknown,
reason: string,
mapKind: "exports" | "imports" = "exports",
): ExportTargetOutcome => ({
kind: "invalid",
limitation: `The package exports target ${JSON.stringify(value) ?? String(value)} ${reason}.`,
limitation: `The package ${mapKind === "imports" ? "import" : "exports"} target ${JSON.stringify(value) ?? String(value)} ${reason}.`,
});
// Node checks raw target segments before URL normalization. Compare encoded
@@ -729,50 +851,80 @@ const forbiddenExportSegment = (value: string): string | undefined =>
);
});
const selectStringPackageTarget = (
value: string,
mapKind: "exports" | "imports",
): ExportTargetOutcome => {
if (mapKind === "imports") {
const target = classifyPackageImportTargetString(value);
if (target.kind === "invalid")
return invalidExportTarget(
value,
"cannot be parent-relative, absolute, or a URL",
mapKind,
);
if (target.kind !== "relative") return { kind: "non-relative", value };
} else if (!value.startsWith("./"))
return invalidExportTarget(value, 'must start with "./"', mapKind);
const forbidden = forbiddenExportSegment(value);
return forbidden === undefined
? { kind: "value", value }
: invalidExportTarget(
value,
`contains forbidden path segment ${JSON.stringify(forbidden)} before URL normalization`,
mapKind,
);
};
const selectArrayPackageTarget = (
value: readonly unknown[],
conditions: ReadonlySet<string>,
mapKind: "exports" | "imports",
): ExportTargetOutcome => {
if (value.length === 0) return { kind: "blocked", cause: "empty-array" };
let lastRefusal:
| Extract<ExportTargetOutcome, { readonly kind: "invalid" | "blocked" }>
| undefined;
const declared = new Set<string>();
for (const entry of value) {
const nested = selectExportTarget(entry, conditions, mapKind);
// Node's array fallback catches invalid targets, not invalid configuration.
if (nested.kind === "invalid-config") return nested;
if (nested.kind === "unmatched") {
for (const condition of nested.declared) declared.add(condition);
continue;
}
if (nested.kind === "invalid" || nested.kind === "blocked") {
lastRefusal = nested;
continue;
}
return nested;
}
return lastRefusal ?? { kind: "unmatched", declared: [...declared] };
};
/**
* Resolve a target in Node's declared order. Explicit null blocks an active
* condition, whereas an unmatched nested object permits the next condition.
* An array skips unmatched and invalid entries, stops at its first string, and
* preserves the final invalid-versus-null refusal if no string is selected.
* preserves the last invalid/null outcome if no string is selected. A nonempty
* array of unmatched conditions stays unmatched; an empty array blocks.
*/
const selectExportTarget = (
value: unknown,
conditions: ReadonlySet<string>,
mapKind: "exports" | "imports" = "exports",
): ExportTargetOutcome => {
if (typeof value === "string") {
if (!value.startsWith("./"))
return invalidExportTarget(value, 'must start with "./"');
const forbidden = forbiddenExportSegment(value);
return forbidden === undefined
? { kind: "value", value }
: invalidExportTarget(
value,
`contains forbidden path segment ${JSON.stringify(forbidden)} before URL normalization`,
);
}
if (value === null) return { kind: "blocked" };
if (Array.isArray(value)) {
let invalid: string | null = null;
for (const entry of value) {
const nested = selectExportTarget(entry, conditions);
// Node's array fallback catches invalid targets, not invalid configuration.
if (nested.kind === "invalid-config") return nested;
if (nested.kind === "invalid") {
invalid = nested.limitation;
continue;
}
if (nested.kind === "unmatched") continue;
invalid = null;
if (nested.kind === "value") return nested;
}
return invalid === null
? { kind: "blocked" }
: { kind: "invalid", limitation: invalid };
}
if (typeof value === "string")
return selectStringPackageTarget(value, mapKind);
if (value === null) return { kind: "blocked", cause: "null" };
if (Array.isArray(value))
return selectArrayPackageTarget(value, conditions, mapKind);
if (typeof value !== "object")
return invalidExportTarget(
value,
"must be a relative string, object, array, or null",
mapKind,
);
for (const key of Object.keys(value)) {
const numeric = Number(key);
@@ -782,12 +934,15 @@ const selectExportTarget = (
limitation: `The package exports configuration contains numeric condition key ${JSON.stringify(key)}.`,
};
}
const declared = new Set<string>();
for (const [condition, target] of Object.entries(value)) {
declared.add(condition);
if (!conditions.has(condition)) continue;
const nested = selectExportTarget(target, conditions);
const nested = selectExportTarget(target, conditions, mapKind);
if (nested.kind !== "unmatched") return nested;
for (const name of nested.declared) declared.add(name);
}
return { kind: "unmatched" };
return { kind: "unmatched", declared: [...declared] };
};
/**
@@ -816,11 +971,14 @@ const notFoundCandidate = (): CandidateResolution => ({
],
});
const exactExportsTargetNotFound = (path: string): CandidateResolution => ({
const exactPackageTargetNotFound = (
path: string,
mapKind: "exports" | "imports",
): CandidateResolution => ({
resolvedPath: null,
status: "not-found",
limitations: [
`The selected package exports target ${path} does not exist in the inventoried artifact container; exports targets are exact files, so no extension or directory index was tried.`,
`The selected package ${mapKind === "imports" ? "import" : "exports"} target ${path} does not exist in the inventoried artifact container; ${mapKind === "imports" ? "import" : "exports"} targets are exact files, so no extension or directory index was tried.`,
],
});
@@ -0,0 +1,147 @@
import { describe, expect, it } from "vitest";
import {
classifyPackageImportSpecifier,
classifyPackageImportTargetString,
lookupExactPackageImport,
} from "./javascriptPackageImportAliases.js";
describe("package import specifier classification", () => {
it("treats an exact hash alias as an exact key", () => {
expect(classifyPackageImportSpecifier("#config")).toEqual({
kind: "exact",
key: "#config",
});
});
it.each(["#internal/env", "#/hidden"])(
"treats %j as an exact key",
(specifier) => {
expect(classifyPackageImportSpecifier(specifier)).toEqual({
kind: "exact",
key: specifier,
});
},
);
it.each(["#", "#/", "#utils/"])(
"rejects reserved specifier %j",
(specifier) => {
expect(classifyPackageImportSpecifier(specifier)).toEqual({
kind: "invalid",
});
},
);
it.each(["#internal/*", "#*"])(
"leaves pattern specifier %j unresolved",
(specifier) => {
expect(classifyPackageImportSpecifier(specifier)).toEqual({
kind: "pattern",
});
},
);
});
describe("exact package import lookup", () => {
it("returns the exact target without falling through to another key", () => {
expect(
lookupExactPackageImport(
{ "#config": "./config.cjs", "#other": "./other.cjs" },
"#config",
),
).toEqual({
kind: "found",
target: "./config.cjs",
declared: ["#config", "#other"],
});
});
it("names declared exact keys when the requested alias is absent", () => {
expect(
lookupExactPackageImport({ "#config": "./config.cjs" }, "#missing"),
).toEqual({
kind: "missing-key",
declared: ["#config"],
});
});
it("does not claim a miss when the map only has pattern keys", () => {
expect(
lookupExactPackageImport({ "#internal/*": "./src/*.js" }, "#internal/a"),
).toEqual({
kind: "pattern-unresolved",
declared: ["#internal/*"],
});
});
it("still selects an exact key when the same map also has patterns", () => {
expect(
lookupExactPackageImport(
{ "#config": "./config.cjs", "#internal/*": "./src/*.js" },
"#config",
),
).toEqual({
kind: "found",
target: "./config.cjs",
declared: ["#config", "#internal/*"],
});
});
it.each([undefined, null, "./config.cjs", ["#config"], 7] as const)(
"treats non-object imports value %j as an undefined map",
(imports) => {
expect(lookupExactPackageImport(imports, "#config")).toEqual({
kind: "undefined-map",
});
},
);
it("does not treat a pattern key as an exact match for itself", () => {
expect(
lookupExactPackageImport({ "#internal/*": "./src/*.js" }, "#internal/*"),
).toEqual({
kind: "pattern-unresolved",
declared: ["#internal/*"],
});
});
});
describe("package import target string classification", () => {
it("keeps package-local relative targets", () => {
expect(classifyPackageImportTargetString("./config.cjs")).toEqual({
kind: "relative",
target: "./config.cjs",
});
});
it("marks recursive hash aliases as recursive", () => {
expect(classifyPackageImportTargetString("#other")).toEqual({
kind: "recursive",
target: "#other",
});
});
it.each(["lodash", "pkg/subpath"])(
"marks %j as an external-package target",
(target) => {
expect(classifyPackageImportTargetString(target)).toEqual({
kind: "external-package",
target,
});
},
);
it.each([
"../outside.js",
"/outside.js",
"https://example.invalid/mod.js",
"file:///outside.js",
"node:fs",
])("marks %j as an invalid import target", (target) => {
expect(classifyPackageImportTargetString(target)).toEqual({
kind: "invalid",
target,
});
});
});
@@ -0,0 +1,98 @@
/**
* Exact package-private import-alias lookup. Node resolves `#` specifiers
* against the importer's enclosing package `imports` map; this module owns the
* specifier and exact-key rules. Condition, array, and null target selection
* stay with the shared package-target machinery. Wildcard keys, recursive
* `#` targets, and external-package targets remain explicit unknowns.
*/
/** How one `#` specifier participates in exact import-alias lookup. */
export type PackageImportSpecifierClassification =
| { readonly kind: "invalid" }
| { readonly kind: "pattern" }
| { readonly kind: "exact"; readonly key: string };
/** Outcome of looking up one exact `#` key in a package `imports` object. */
export type PackageImportExactLookup =
| { readonly kind: "undefined-map" }
| {
readonly kind: "missing-key";
readonly declared: readonly string[];
}
| {
readonly kind: "pattern-unresolved";
readonly declared: readonly string[];
}
| {
readonly kind: "found";
readonly target: unknown;
readonly declared: readonly string[];
};
/** How a selected string target is interpreted for package-private imports. */
export type PackageImportTargetClassification =
| { readonly kind: "relative"; readonly target: string }
| { readonly kind: "invalid"; readonly target: string }
| { readonly kind: "recursive"; readonly target: string }
| { readonly kind: "external-package"; readonly target: string };
/**
* Classify a `#` module specifier before any package-scope walk.
* `#` and names ending in `/` are invalid. Other literal keys, including
* `#/…`, participate in exact lookup; wildcard expansion remains unsupported.
*/
export const classifyPackageImportSpecifier = (
specifier: string,
): PackageImportSpecifierClassification => {
if (specifier === "#" || specifier.endsWith("/")) return { kind: "invalid" };
if (specifier.includes("*")) return { kind: "pattern" };
return { kind: "exact", key: specifier };
};
/** True when an `imports` key is a pattern rather than an exact alias. */
export const isPackageImportPatternKey = (key: string): boolean =>
key.includes("*") || key.endsWith("/");
/**
* Look up one exact `#` key in a package `imports` value.
* A missing exact key is reported as missing when every declared key is also
* exact; if the map contains pattern keys, the miss stays pattern-unresolved
* rather than claiming the specifier is undefined.
*/
export const lookupExactPackageImport = (
imports: unknown,
key: string,
): PackageImportExactLookup => {
if (typeof imports !== "object" || imports === null || Array.isArray(imports))
return { kind: "undefined-map" };
const declared = Object.keys(imports);
if (Object.hasOwn(imports, key) && !isPackageImportPatternKey(key))
return {
kind: "found",
target: Reflect.get(imports, key),
declared,
};
return declared.some(isPackageImportPatternKey)
? { kind: "pattern-unresolved", declared }
: { kind: "missing-key", declared };
};
/**
* Classify a selected string import target. Only package-local `./` paths are
* resolved as files; recursive `#` aliases and bare packages stay unknown.
* Parent-relative, absolute, and URL targets are invalid, allowing array
* selection to skip them before choosing a package-local fallback.
*/
export const classifyPackageImportTargetString = (
target: string,
): PackageImportTargetClassification => {
if (target.startsWith("./")) return { kind: "relative", target };
if (
target.startsWith("../") ||
target.startsWith("/") ||
URL.canParse(target)
)
return { kind: "invalid", target };
if (target.startsWith("#")) return { kind: "recursive", target };
return { kind: "external-package", target };
};
@@ -0,0 +1,251 @@
import { createHash } from "node:crypto";
import { mkdir, writeFile } from "node:fs/promises";
import { createRequire } from "node:module";
import { dirname, join, relative } from "node:path";
import { expect } from "vitest";
import { z } from "zod";
import { javascriptApplicationAnalysisResultSchema } from "../../../src/domain/javascript/javascriptApplicationAnalysis.js";
import { connectLocalToolsMcp } from "../../fixtures/localToolsMcp.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { cliTest } from "../../support/cli/cliFixture.js";
const literalAliases = ["#/x", "#x%2Fy", "#x%2Ey", "#x\\y", "#x\0y"];
const fallbackAliases = {
"#inactive-array": {
node: [{ browser: "./browser.cjs" }],
default: "./config.cjs",
},
"#nested-inactive-array": {
node: [[{ browser: "./browser.cjs" }]],
default: "./config.cjs",
},
"#parent": ["../outside.cjs", "./config.cjs"],
"#absolute": { node: [["/outside.cjs"], "./config.cjs"] },
"#url": {
import: ["https://example.invalid/outside.cjs", "./config.cjs"],
require: ["file:///outside.cjs", "./config.cjs"],
},
};
const resolvedAliases = [...literalAliases, ...Object.keys(fallbackAliases)];
const refusedAliases = {
"#empty": { target: [], limitation: "an empty imports target array" },
"#empty-active": {
target: { node: [] },
limitation: "an empty imports target array",
},
"#null-last": {
target: [[], null],
limitation: "an explicit null imports target",
},
"#empty-last": {
target: [null, []],
limitation: "an empty imports target array",
},
"#inactive": {
target: [{ browser: "./browser.cjs" }],
limitation: "it declares browser.",
},
"#nested-inactive": {
target: [
[{ browser: "./browser.cjs" }, { "rea-inactive": "./inactive.cjs" }],
],
limitation: "it declares browser, rea-inactive.",
},
};
const allAliases = [...resolvedAliases, ...Object.keys(refusedAliases)];
const createPackageImportAliasFixture = async () => {
const root = await createTestTempDirectory("rea-package-import-aliases-");
const mainSource = [
'const config = require("#config");',
'const nested = require("./node_modules/fixture/index.cjs");',
'const missing = require("#missing");',
...allAliases.map((specifier) => `require(${JSON.stringify(specifier)});`),
].join("\n");
const moduleSource = allAliases
.map((specifier) => `import ${JSON.stringify(specifier)};`)
.join("\n");
const nestedSource = [
'const config = require("#config");',
"module.exports = config;",
].join("\n");
const files = {
"package.json": JSON.stringify({
name: "app",
imports: {
"#config": "./config.cjs",
...Object.fromEntries(
literalAliases.map((specifier) => [specifier, "./config.cjs"]),
),
...fallbackAliases,
...Object.fromEntries(
Object.entries(refusedAliases).map(([specifier, { target }]) => [
specifier,
target,
]),
),
},
}),
"main.cjs": mainSource,
"main.mjs": moduleSource,
"config.cjs": "module.exports = 'app-config';",
"node_modules/fixture/package.json": JSON.stringify({
name: "fixture",
imports: { "#config": "./local.cjs" },
}),
"node_modules/fixture/index.cjs": nestedSource,
"node_modules/fixture/local.cjs": "module.exports = 'fixture-config';",
};
await Promise.all(
Object.entries(files).map(async ([path, content]) => {
const absolute = join(root, path);
await mkdir(dirname(absolute), { recursive: true });
await writeFile(absolute, content);
}),
);
return { root, mainSource, moduleSource, nestedSource };
};
cliTest.for(["CLI", "stdio MCP"] as const)(
"resolves exact package import aliases with package scope through %s",
async (surface, { cli }) => {
const { root, mainSource, moduleSource, nestedSource } =
await createPackageImportAliasFixture();
const nativeRequire = createRequire(join(root, "main.cjs"));
expect(
relative(root, nativeRequire.resolve("#config")).replaceAll("\\", "/"),
).toBe("config.cjs");
expect(
relative(
root,
createRequire(join(root, "node_modules/fixture/index.cjs")).resolve(
"#config",
),
).replaceAll("\\", "/"),
).toBe("node_modules/fixture/local.cjs");
expect(() => nativeRequire.resolve("#missing")).toThrow(
expect.objectContaining({ code: "ERR_PACKAGE_IMPORT_NOT_DEFINED" }),
);
let document: unknown;
if (surface === "CLI") {
const response = await cli.run({
arguments: ["analyze-javascript-application", root, "--format", "json"],
environment: { REA_LOG_LEVEL: "silent" },
});
expect(response.exitCode, response.stderr).toBe(0);
document = response.json;
} else {
const { call } = await connectLocalToolsMcp();
const response = await call("analyze_javascript_application", {
input_path: root,
format: "directory",
});
expect(response.isError, JSON.stringify(response)).not.toBe(true);
document = response.structuredContent;
}
const { graph } = z
.object({ normalized_result: javascriptApplicationAnalysisResultSchema })
.parse(document).normalized_result;
for (const specifier of resolvedAliases)
for (const [moduleKind, sourcePath, source] of [
["require", "main.cjs", mainSource],
["import", "main.mjs", moduleSource],
] as const)
expect(graph.edges).toContainEqual(
expect.objectContaining({
properties: expect.objectContaining({
module_link_kind: moduleKind,
specifier,
resolved_path: "config.cjs",
resolution_status: "resolved",
}),
evidence: sourceEvidence(sourcePath, source),
}),
);
const imports = graph.edges.filter(
({ properties }) => properties.module_link_kind === "require",
);
for (const [specifier, { limitation }] of Object.entries(refusedAliases))
for (const [moduleKind, sourcePath, source] of [
["require", "main.cjs", mainSource],
["import", "main.mjs", moduleSource],
] as const)
expect(graph.edges).toContainEqual(
expect.objectContaining({
properties: expect.objectContaining({
module_link_kind: moduleKind,
specifier,
resolved_path: null,
resolution_status: "external",
}),
evidence: sourceEvidence(sourcePath, source, limitation),
}),
);
expect(imports).toContainEqual(
expect.objectContaining({
properties: expect.objectContaining({
specifier: "#config",
resolved_path: "config.cjs",
resolution_status: "resolved",
}),
evidence: sourceEvidence("main.cjs", mainSource),
}),
);
expect(imports).toContainEqual(
expect.objectContaining({
properties: expect.objectContaining({
specifier: "#config",
resolved_path: "node_modules/fixture/local.cjs",
resolution_status: "resolved",
}),
evidence: sourceEvidence(
"node_modules/fixture/index.cjs",
nestedSource,
),
}),
);
expect(imports).toContainEqual(
expect.objectContaining({
properties: expect.objectContaining({
specifier: "#missing",
resolved_path: null,
resolution_status: "external",
}),
evidence: expect.objectContaining({
limitations: expect.arrayContaining([
expect.stringContaining("does not define package import alias"),
]),
}),
}),
);
},
);
const sourceEvidence = (
sourcePath: string,
source: string,
limitation?: string,
): unknown =>
expect.objectContaining({
artifact: expect.objectContaining({
available: true,
sha256: createHash("sha256").update(source).digest("hex"),
}),
location: expect.objectContaining({
available: true,
value: expect.objectContaining({
kind: "source-range",
source: sourcePath,
}),
}),
...(limitation === undefined
? {}
: {
limitations: expect.arrayContaining([
expect.stringContaining(limitation),
]),
}),
});
@@ -29,6 +29,11 @@ const chromeExecutable = [
existsSync(candidate),
);
// Match the real-browser verifier: a cold Chrome launch on a loaded coverage
// runner can exceed 20 seconds before publishing DevToolsActivePort.
const CHROME_STARTUP_TIMEOUT_MS = 60_000;
const CHROME_TEST_TIMEOUT_MS = CHROME_STARTUP_TIMEOUT_MS + 30_000;
// Issue 1586's page, plus the HTML, image, use, and foreign-origin cases the
// real snapshot must distinguish. The XHTML page is the prefixed-svg gap
// reported on PR 1602. The tests are skipped when no Chrome-family executable
@@ -90,7 +95,7 @@ it.skipIf(chromeExecutable === undefined)(
executable,
activePortPath: join(profile, "DevToolsActivePort"),
stderr: () => stderr,
timeoutMs: 20_000,
timeoutMs: CHROME_STARTUP_TIMEOUT_MS,
});
const endpoint = `http://127.0.0.1:${String(port)}`;
const provider = new CdpBrowserProvider();
@@ -158,6 +163,7 @@ it.skipIf(chromeExecutable === undefined)(
});
}
},
CHROME_TEST_TIMEOUT_MS,
);
const xhtmlPage = `<?xml version="1.0" encoding="UTF-8"?>
@@ -261,7 +267,7 @@ for (const { format, page, contentType, nodeNames, link, destinations } of [
executable,
activePortPath: join(profile, "DevToolsActivePort"),
stderr: () => stderr,
timeoutMs: 20_000,
timeoutMs: CHROME_STARTUP_TIMEOUT_MS,
});
const endpoint = `http://127.0.0.1:${String(port)}`;
const provider = new CdpBrowserProvider();
@@ -312,6 +318,7 @@ for (const { format, page, contentType, nodeNames, link, destinations } of [
});
}
},
CHROME_TEST_TIMEOUT_MS,
);
}
@@ -742,6 +742,30 @@ describe("nested and array package export targets", () => {
);
});
describe("unmatched package export conditions", () => {
it.each([
[[{ browser: "./default.cjs" }], "browser"],
[
[[{ browser: "./default.cjs" }, { "rea-inactive": "./node.cjs" }]],
"browser, rea-inactive",
],
[{ node: [{ browser: "./default.cjs" }] }, "node, browser"],
] as const)(
"preserves condition names in unmatched target %j",
async (target, declared) => {
for (const moduleKind of ["import", "require"] as const) {
const { node_error, outcome } = await compareWithNode(
target,
moduleKind,
);
expect(node_error).toBe("ERR_PACKAGE_PATH_NOT_EXPORTED");
expect(outcome).toMatchObject({
resolution_status: "external",
resolved_path: null,
});
expect(outcome.limitations[0]).toContain(`It declares ${declared}.`);
}
},
);
it.each(["import", "require"] as const)(
"names the declared conditions for %s",
async (moduleKind) => {
@@ -769,6 +793,31 @@ describe("unmatched package export conditions", () => {
});
});
describe("blocked package export provenance", () => {
it.each([
[[], "an empty exports target array"],
[{ node: [] }, "an empty exports target array"],
[[null, []], "an empty exports target array"],
[[[], null], "an explicit null exports target"],
] as const)(
"preserves the blocking source value in %j",
async (target, limitation) => {
for (const moduleKind of ["import", "require"] as const) {
const { node_error, outcome } = await compareWithNode(
target,
moduleKind,
);
expect(node_error).toBe("ERR_PACKAGE_PATH_NOT_EXPORTED");
expect(outcome).toMatchObject({
resolution_status: "external",
resolved_path: null,
});
expect(outcome.limitations[0]).toContain(limitation);
}
},
);
});
describe("package exports fallback refusal reasons", () => {
it.each([
[{ import: [42, 7] }, "ERR_INVALID_PACKAGE_TARGET", "rejected"],
@@ -0,0 +1,654 @@
import { execFile } from "node:child_process";
import { mkdir, writeFile } from "node:fs/promises";
import { createRequire } from "node:module";
import { dirname, join, relative } from "node:path";
import { promisify } from "node:util";
import { fileURLToPath } from "node:url";
import { describe, expect, it } from "vitest";
import { z } from "zod";
import {
resolveArtifactPathByContext,
type ArtifactPathResolution,
} from "../../../src/application/javascript/JavaScriptArtifactPathResolution.js";
import type { JavaScriptArtifactFile } from "../../../src/domain/javascript/javascriptArtifactFiles.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
const execute = promisify(execFile);
const nodeOutcomeSchema = z.strictObject({
target: z.string().nullable(),
error_code: z.string().nullable(),
});
const posixRelative = (root: string, absolute: string): string =>
relative(root, absolute).replaceAll("\\", "/");
const inventory = (
contents: ReadonlyMap<string, string>,
): Map<string, JavaScriptArtifactFile> =>
new Map(
[...contents].map(([path, value]) => [
path,
{
path,
container_sha256: "a".repeat(64),
sha256: "b".repeat(64),
bytes: Buffer.byteLength(value),
inventory_artifact_id: path,
kind: path.endsWith("package.json") ? "package-json" : "javascript",
unpacked: false,
text: { included: true, value },
},
]),
);
const writeTree = async (
root: string,
files: Readonly<Record<string, string>>,
): Promise<Map<string, string>> => {
const contents = new Map<string, string>();
await Promise.all(
Object.entries(files).map(async ([path, value]) => {
const absolute = join(root, path);
await mkdir(dirname(absolute), { recursive: true });
await writeFile(absolute, value);
contents.set(path, value);
}),
);
return contents;
};
const nodeRequireTarget = async (
importer: string,
specifier: string,
): Promise<z.infer<typeof nodeOutcomeSchema>> => {
try {
return {
target: createRequire(importer).resolve(specifier),
error_code: null,
};
} catch (error) {
const code =
error instanceof Error && "code" in error
? z.string().parse(Reflect.get(error, "code"))
: null;
return { target: null, error_code: code };
}
};
const nodeImportTarget = async (
cwd: string,
specifier: string,
): Promise<z.infer<typeof nodeOutcomeSchema>> => {
const { stdout } = await execute(
process.execPath,
[
"--input-type=module",
"-e",
`
try {
const url = await import.meta.resolve(${JSON.stringify(specifier)});
console.log(JSON.stringify({ target: url, error_code: null }));
} catch (error) {
console.log(JSON.stringify({
target: null,
error_code: error.code ?? null,
}));
}
`,
],
{ cwd },
);
return nodeOutcomeSchema.parse(JSON.parse(stdout));
};
const resolveAlias = (
files: Map<string, JavaScriptArtifactFile>,
declaredPath: string,
sourcePath: string,
moduleKind: "import" | "require",
): ArtifactPathResolution =>
resolveArtifactPathByContext({
declaredPath,
sourcePath,
context: "module-specifier",
moduleKind,
files,
});
describe("literal package import keys compared with Node", () => {
it("matches literal imports keys containing path-like bytes in both Node loaders", async () => {
const root = await createTestTempDirectory("rea-import-alias-literal-");
const specifiers = ["#/x", "#x%2Fy", "#x%2Ey", "#x\\y", "#x\0y"];
const contents = await writeTree(root, {
"package.json": JSON.stringify({
imports: Object.fromEntries(
specifiers.map((specifier) => [specifier, "./target.cjs"]),
),
}),
"main.cjs": "module.exports = 1;",
"target.cjs": "module.exports = 1;",
});
const inventoried = inventory(contents);
for (const specifier of specifiers) {
const required = await nodeRequireTarget(
join(root, "main.cjs"),
specifier,
);
expect(required.error_code).toBeNull();
expect(posixRelative(root, required.target ?? "")).toBe("target.cjs");
const imported = await nodeImportTarget(root, specifier);
expect(imported.error_code).toBeNull();
expect(posixRelative(root, fileURLToPath(imported.target ?? ""))).toBe(
"target.cjs",
);
for (const moduleKind of ["require", "import"] as const)
expect(
resolveAlias(inventoried, specifier, "main.cjs", moduleKind),
JSON.stringify({ specifier, moduleKind }),
).toMatchObject({
declared_path: specifier,
resolution_status: "resolved",
resolved_path: "target.cjs",
});
}
});
it("still rejects unsafe selected targets for literal imports keys", async () => {
const root = await createTestTempDirectory("rea-import-alias-target-");
const contents = await writeTree(root, {
"package.json": JSON.stringify({
imports: { "#x%2Fy": "./nested%2Ftarget.cjs" },
}),
"main.cjs": "module.exports = 1;",
"nested/target.cjs": "module.exports = 1;",
});
const inventoried = inventory(contents);
const required = await nodeRequireTarget(join(root, "main.cjs"), "#x%2Fy");
expect(required.error_code).toBe("ERR_INVALID_MODULE_SPECIFIER");
const imported = await nodeImportTarget(root, "#x%2Fy");
expect(imported.error_code).toBe("ERR_INVALID_MODULE_SPECIFIER");
for (const moduleKind of ["require", "import"] as const)
expect(
resolveAlias(inventoried, "#x%2Fy", "main.cjs", moduleKind),
).toMatchObject({ resolution_status: "rejected", resolved_path: null });
});
});
describe("inactive package target arrays compared with Node", () => {
it("continues past inactive arrays in enclosing imports and exports conditions", async () => {
const root = await createTestTempDirectory("rea-inactive-package-array-");
const conditional = {
node: [{ browser: "./browser.cjs" }],
default: "./fallback.cjs",
};
const nested = {
node: [
[{ browser: "./browser.cjs" }],
{ "rea-inactive": "./inactive.cjs" },
],
default: "./fallback.cjs",
};
const contents = await writeTree(root, {
"package.json": JSON.stringify({
imports: { "#conditional": conditional, "#nested": nested },
}),
"main.cjs": "module.exports = 1;",
"fallback.cjs": "module.exports = 1;",
"node_modules/fixture/package.json": JSON.stringify({
name: "fixture",
exports: { ".": conditional, "./nested": nested },
}),
"node_modules/fixture/fallback.cjs": "module.exports = 2;",
});
const inventoried = inventory(contents);
for (const [specifier, expected] of [
["#conditional", "fallback.cjs"],
["#nested", "fallback.cjs"],
["fixture", "node_modules/fixture/fallback.cjs"],
["fixture/nested", "node_modules/fixture/fallback.cjs"],
] as const) {
const required = await nodeRequireTarget(
join(root, "main.cjs"),
specifier,
);
expect(required.error_code, specifier).toBeNull();
expect(posixRelative(root, required.target ?? "")).toBe(expected);
const imported = await nodeImportTarget(root, specifier);
expect(imported.error_code).toBeNull();
expect(posixRelative(root, fileURLToPath(imported.target ?? ""))).toBe(
expected,
);
for (const moduleKind of ["require", "import"] as const)
expect(
resolveAlias(inventoried, specifier, "main.cjs", moduleKind),
).toMatchObject({
declared_path: specifier,
resolution_status: "resolved",
resolved_path: expected,
});
}
});
});
describe("package target refusal evidence compared with Node", () => {
it("keeps empty, null, invalid, and unmatched array outcomes distinct", async () => {
const root = await createTestTempDirectory("rea-inactive-package-refusal-");
const inactive = { browser: "./browser.cjs" };
const contents = await writeTree(root, {
"package.json": JSON.stringify({
imports: {
"#empty-direct": [],
"#empty": { node: [], default: "./fallback.cjs" },
"#null": { node: [null, inactive], default: "./fallback.cjs" },
"#null-last": [[], null],
"#empty-last": [null, []],
"#invalid": {
node: ["../outside.cjs", inactive],
default: "./fallback.cjs",
},
"#unmatched": [inactive],
"#unmatched-nested": [
[inactive, { "rea-inactive": "./inactive.cjs" }],
],
"#unmatched-active": { node: [inactive] },
},
}),
"main.cjs": "module.exports = 1;",
"fallback.cjs": "module.exports = 1;",
});
const inventoried = inventory(contents);
for (const [specifier, errorCode, status, limitation] of [
[
"#empty-direct",
"ERR_PACKAGE_IMPORT_NOT_DEFINED",
"external",
"an empty imports target array",
],
[
"#empty",
"ERR_PACKAGE_IMPORT_NOT_DEFINED",
"external",
"an empty imports target array",
],
[
"#null",
"ERR_PACKAGE_IMPORT_NOT_DEFINED",
"external",
"an explicit null imports target",
],
[
"#null-last",
"ERR_PACKAGE_IMPORT_NOT_DEFINED",
"external",
"an explicit null imports target",
],
[
"#empty-last",
"ERR_PACKAGE_IMPORT_NOT_DEFINED",
"external",
"an empty imports target array",
],
["#invalid", "ERR_INVALID_PACKAGE_TARGET", "rejected", "../outside.cjs"],
[
"#unmatched",
"ERR_PACKAGE_IMPORT_NOT_DEFINED",
"external",
"it declares browser.",
],
[
"#unmatched-nested",
"ERR_PACKAGE_IMPORT_NOT_DEFINED",
"external",
"it declares browser, rea-inactive.",
],
[
"#unmatched-active",
"ERR_PACKAGE_IMPORT_NOT_DEFINED",
"external",
"it declares node, browser.",
],
] as const) {
expect(
(await nodeRequireTarget(join(root, "main.cjs"), specifier)).error_code,
).toBe(errorCode);
expect((await nodeImportTarget(root, specifier)).error_code).toBe(
errorCode,
);
for (const moduleKind of ["require", "import"] as const) {
const result = resolveAlias(
inventoried,
specifier,
"main.cjs",
moduleKind,
);
expect(result).toMatchObject({
resolution_status: status,
resolved_path: null,
});
expect(result.limitations.join(" "), specifier).toContain(limitation);
}
}
});
});
describe("package import target arrays compared with Node", () => {
it("skips invalid parent, absolute, and URL targets before an exact fallback", async () => {
const root = await createTestTempDirectory("rea-import-alias-fallback-");
const imports = {
"#parent": ["../outside.cjs", "./fallback.cjs"],
"#absolute": { node: [["/outside.cjs"], "./fallback.cjs"] },
"#url": {
import: ["https://example.invalid/outside.cjs", "./fallback.cjs"],
require: ["file:///outside.cjs", "./fallback.cjs"],
},
};
const contents = await writeTree(root, {
"package.json": JSON.stringify({ imports }),
"main.cjs": "module.exports = 1;",
"fallback.cjs": "module.exports = 1;",
});
const inventoried = inventory(contents);
for (const specifier of Object.keys(imports)) {
const required = await nodeRequireTarget(
join(root, "main.cjs"),
specifier,
);
expect(required.error_code).toBeNull();
expect(posixRelative(root, required.target ?? "")).toBe("fallback.cjs");
const imported = await nodeImportTarget(root, specifier);
expect(imported.error_code).toBeNull();
expect(posixRelative(root, fileURLToPath(imported.target ?? ""))).toBe(
"fallback.cjs",
);
for (const moduleKind of ["require", "import"] as const)
expect(
resolveAlias(inventoried, specifier, "main.cjs", moduleKind),
).toMatchObject({
declared_path: specifier,
resolution_status: "resolved",
resolved_path: "fallback.cjs",
});
}
});
it("preserves invalid, null, and unsupported-package outcomes in arrays", async () => {
const root = await createTestTempDirectory(
"rea-import-alias-fallback-refuse-",
);
const contents = await writeTree(root, {
"package.json": JSON.stringify({
imports: {
"#invalid": [null, "../outside.cjs"],
"#blocked": ["../outside.cjs", null],
"#package": ["fixture", "./fallback.cjs"],
},
}),
"main.cjs": "module.exports = 1;",
"fallback.cjs": "module.exports = 1;",
"node_modules/fixture/package.json": JSON.stringify({
name: "fixture",
main: "index.cjs",
}),
"node_modules/fixture/index.cjs": "module.exports = 2;",
});
const inventoried = inventory(contents);
for (const [specifier, errorCode, resolutionStatus, reason] of [
["#invalid", "ERR_INVALID_PACKAGE_TARGET", "rejected", "../outside.cjs"],
[
"#blocked",
"ERR_PACKAGE_IMPORT_NOT_DEFINED",
"external",
"explicit null",
],
] as const) {
const required = await nodeRequireTarget(
join(root, "main.cjs"),
specifier,
);
expect(required.error_code).toBe(errorCode);
expect((await nodeImportTarget(root, specifier)).error_code).toBe(
errorCode,
);
for (const moduleKind of ["require", "import"] as const) {
const result = resolveAlias(
inventoried,
specifier,
"main.cjs",
moduleKind,
);
expect(result).toMatchObject({
resolution_status: resolutionStatus,
resolved_path: null,
});
expect(result.limitations.join(" ")).toContain(reason);
}
}
const required = await nodeRequireTarget(
join(root, "main.cjs"),
"#package",
);
expect(posixRelative(root, required.target ?? "")).toBe(
"node_modules/fixture/index.cjs",
);
const imported = await nodeImportTarget(root, "#package");
expect(imported.error_code).toBeNull();
expect(posixRelative(root, fileURLToPath(imported.target ?? ""))).toBe(
"node_modules/fixture/index.cjs",
);
for (const moduleKind of ["require", "import"] as const) {
const result = resolveAlias(
inventoried,
"#package",
"main.cjs",
moduleKind,
);
expect(result).toMatchObject({
resolution_status: "external",
resolved_path: null,
});
expect(result.limitations.join(" ")).toContain(
"External-package import targets remain unresolved",
);
}
});
});
describe("package import aliases compared with Node", () => {
it("matches Node require and import for an exact root alias", async () => {
const root = await createTestTempDirectory("rea-import-alias-root-");
const files = {
"package.json": JSON.stringify({
name: "app",
imports: { "#config": "./config.cjs" },
}),
"main.cjs": 'module.exports = require("#config");',
"config.cjs": "module.exports = 'config.cjs';",
};
const contents = await writeTree(root, files);
const inventoried = inventory(contents);
const required = await nodeRequireTarget(join(root, "main.cjs"), "#config");
expect(posixRelative(root, required.target ?? "")).toBe("config.cjs");
expect(
resolveAlias(inventoried, "#config", "main.cjs", "require"),
).toMatchObject({
resolution_status: "resolved",
resolved_path: "config.cjs",
});
const imported = await nodeImportTarget(root, "#config");
expect(imported.error_code).toBeNull();
expect(
resolveAlias(inventoried, "#config", "main.cjs", "import"),
).toMatchObject({
resolution_status: "resolved",
resolved_path: "config.cjs",
});
});
it("matches Node when a nested package has its own imports map", async () => {
const root = await createTestTempDirectory("rea-import-alias-nested-");
const files = {
"package.json": JSON.stringify({
name: "app",
imports: { "#config": "./root.cjs" },
}),
"root.cjs": "module.exports = 'root';",
"node_modules/fixture/package.json": JSON.stringify({
name: "fixture",
imports: { "#config": "./local.cjs" },
}),
"node_modules/fixture/index.cjs": 'module.exports = require("#config");',
"node_modules/fixture/local.cjs": "module.exports = 'local';",
};
const contents = await writeTree(root, files);
const inventoried = inventory(contents);
const nested = await nodeRequireTarget(
join(root, "node_modules/fixture/index.cjs"),
"#config",
);
expect(posixRelative(root, nested.target ?? "")).toBe(
"node_modules/fixture/local.cjs",
);
expect(
resolveAlias(
inventoried,
"#config",
"node_modules/fixture/index.cjs",
"require",
),
).toMatchObject({
resolution_status: "resolved",
resolved_path: "node_modules/fixture/local.cjs",
});
const fromRoot = await nodeRequireTarget(join(root, "root.cjs"), "#config");
expect(posixRelative(root, fromRoot.target ?? "")).toBe("root.cjs");
expect(
resolveAlias(inventoried, "#config", "root.cjs", "require"),
).toMatchObject({
resolution_status: "resolved",
resolved_path: "root.cjs",
});
});
it("matches Node when a nested package does not define the alias", async () => {
const root = await createTestTempDirectory("rea-import-alias-scope-");
const files = {
"package.json": JSON.stringify({
name: "app",
imports: { "#config": "./root.cjs" },
}),
"root.cjs": "module.exports = 'root';",
"node_modules/fixture/package.json": JSON.stringify({
name: "fixture",
}),
"node_modules/fixture/index.cjs": 'module.exports = require("#config");',
};
await writeTree(root, files);
const nested = await nodeRequireTarget(
join(root, "node_modules/fixture/index.cjs"),
"#config",
);
expect(nested.target).toBeNull();
// CJS reports MODULE_NOT_FOUND when the nested package.json has no
// `imports` field at all; it still does not inherit the ancestor map.
expect(nested.error_code).toBe("MODULE_NOT_FOUND");
const outcome = resolveAlias(
inventory(new Map(Object.entries(files))),
"#config",
"node_modules/fixture/index.cjs",
"require",
);
expect(outcome).toMatchObject({
resolution_status: "external",
resolved_path: null,
});
expect(outcome.limitations.join(" ")).toContain(
"node_modules/fixture/package.json",
);
});
});
describe("package import alias refusals compared with Node", () => {
it("matches Node refusal for a missing key, explicit null, and reserved specifier", async () => {
const root = await createTestTempDirectory("rea-import-alias-refuse-");
const files = {
"package.json": JSON.stringify({
name: "app",
imports: {
"#config": "./config.cjs",
"#blocked": null,
"#": "./config.cjs",
"#/": "./config.cjs",
"#config/": "./config.cjs",
},
}),
"main.cjs": "module.exports = 1;",
"config.cjs": "module.exports = 1;",
};
await writeTree(root, files);
const inventoried = inventory(new Map(Object.entries(files)));
const missing = await nodeRequireTarget(join(root, "main.cjs"), "#missing");
expect(missing.error_code).toBe("ERR_PACKAGE_IMPORT_NOT_DEFINED");
expect(
resolveAlias(inventoried, "#missing", "main.cjs", "require"),
).toMatchObject({ resolution_status: "external", resolved_path: null });
const blocked = await nodeRequireTarget(join(root, "main.cjs"), "#blocked");
expect(blocked.error_code).toBe("ERR_PACKAGE_IMPORT_NOT_DEFINED");
expect(
resolveAlias(inventoried, "#blocked", "main.cjs", "require"),
).toMatchObject({ resolution_status: "external", resolved_path: null });
for (const specifier of ["#", "#/", "#config/"]) {
const reserved = await nodeRequireTarget(
join(root, "main.cjs"),
specifier,
);
expect(reserved.error_code).toBe("ERR_INVALID_MODULE_SPECIFIER");
expect((await nodeImportTarget(root, specifier)).error_code).toBe(
"ERR_INVALID_MODULE_SPECIFIER",
);
for (const moduleKind of ["require", "import"] as const)
expect(
resolveAlias(inventoried, specifier, "main.cjs", moduleKind),
).toMatchObject({ resolution_status: "rejected", resolved_path: null });
}
});
it("matches Node when an import condition selects a different exact file", async () => {
const root = await createTestTempDirectory("rea-import-alias-cond-");
const files = {
"package.json": JSON.stringify({
name: "app",
type: "module",
imports: {
"#config": {
import: "./config.mjs",
require: "./config.cjs",
},
},
}),
"main.cjs": 'module.exports = require("#config");',
"config.cjs": "export default 'cjs'; module.exports = 'cjs';",
"config.mjs": "export default 'mjs';",
};
await writeTree(root, files);
const inventoried = inventory(new Map(Object.entries(files)));
const required = await nodeRequireTarget(join(root, "main.cjs"), "#config");
expect(posixRelative(root, required.target ?? "")).toBe("config.cjs");
expect(
resolveAlias(inventoried, "#config", "main.cjs", "require"),
).toMatchObject({
resolution_status: "resolved",
resolved_path: "config.cjs",
});
const imported = await nodeImportTarget(root, "#config");
expect(imported.error_code).toBeNull();
expect(imported.target ?? "").toContain("config.mjs");
expect(
resolveAlias(inventoried, "#config", "main.cjs", "import"),
).toMatchObject({
resolution_status: "resolved",
resolved_path: "config.mjs",
});
});
});