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 <76467478+morluto@users.noreply.github.com>
This commit is contained in:
Matt Van Hornandmorluto authored and GitHub committed 2026-10-12 01:17:58 +08:00
1 parent e8491e023a
commit d28b41ea4e
10 files changed
+1838 -83

No files matched your search

@@ -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",
});
});
});