mirror of
https://github.com/morluto/rea
synced 2026-10-25 22:08:11 -04:00
fix(reference): retain unknown package scope for JavaScript loaders
This commit is contained in:
+3
-1
@@ -468,7 +468,9 @@ base are proven: this includes the exact
|
||||
as `require.resolve?.("./dep.cjs")` use the same loader and mutation facts. The
|
||||
nearest inventoried package manifest supplies `.js` module scope; an explicit
|
||||
`type: "module"` supplies no CommonJS wrapper, while an unreadable, excluded or
|
||||
malformed manifest leaves candidate loader resolution unknown. Other custom bases and search
|
||||
malformed manifest, or package scope outside the selected inventory, leaves
|
||||
candidate loader resolution unknown. A nearest manifest without a `type` field
|
||||
uses the shared parser's JavaScript module-syntax facts. Other custom bases and search
|
||||
options remain partial with unknown resolution and the literal requested
|
||||
specifier. Overwritten resolver methods retain that uncertainty; they are not
|
||||
attributed to Node. `require.main` is a module object, so calling it does not
|
||||
|
||||
@@ -2,6 +2,7 @@ import { err, ok, type Result } from "../domain/result.js";
|
||||
import { createHash } from "node:crypto";
|
||||
import { posix } from "node:path";
|
||||
|
||||
import type { ReferenceSourcePackageType } from "../domain/referenceSourceRequireBindings.js";
|
||||
import { decodeJavaScriptModuleUrlPath } from "../domain/javascript/javascriptModuleSpecifier.js";
|
||||
import { logicalPathEscapesRoot } from "../domain/artifactIdentity.js";
|
||||
import {
|
||||
@@ -102,10 +103,9 @@ type PackageMain =
|
||||
| { readonly state: "none" }
|
||||
| { readonly state: "unknown" };
|
||||
|
||||
type PackageType = "module" | "commonjs" | "unknown" | undefined;
|
||||
interface PackageMetadata {
|
||||
readonly main: PackageMain;
|
||||
readonly type: PackageType;
|
||||
readonly type: ReferenceSourcePackageType;
|
||||
}
|
||||
const UNKNOWN_PACKAGE: PackageMetadata = {
|
||||
main: { state: "unknown" },
|
||||
@@ -158,7 +158,7 @@ const readPackageMetadata = (
|
||||
? Reflect.get(manifest, "main")
|
||||
: undefined;
|
||||
packages.set(directory, {
|
||||
type: type === "module" || type === "commonjs" ? type : undefined,
|
||||
type: type === "module" || type === "commonjs" ? type : "unspecified",
|
||||
main:
|
||||
typeof main === "string" && main !== ""
|
||||
? { state: "declared", main }
|
||||
@@ -401,21 +401,18 @@ type ReadFile = Extract<ReferenceSourceEntry, { status: "read"; kind: "file" }>;
|
||||
const packageTypeForFile = (
|
||||
path: string,
|
||||
context: SpecifierContext,
|
||||
): "module" | "commonjs" | "unknown" | undefined => {
|
||||
): ReferenceSourcePackageType => {
|
||||
let directory = posix.dirname(path);
|
||||
while (true) {
|
||||
const manifest = packageMainForDirectory(directory, context);
|
||||
if (manifest !== undefined)
|
||||
return (
|
||||
context.packages.get(manifest.directory)?.type ??
|
||||
(manifest.main.state === "unknown" ? "unknown" : undefined)
|
||||
);
|
||||
return context.packages.get(manifest.directory)?.type ?? "unknown";
|
||||
if (
|
||||
directory === "." ||
|
||||
directory === "/" ||
|
||||
posix.basename(directory) === "node_modules"
|
||||
)
|
||||
return undefined;
|
||||
return "unknown";
|
||||
directory = posix.dirname(directory);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -400,3 +400,28 @@ it.each([
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
it("retains unknown historical .js loader scope until a package manifest establishes it", () => {
|
||||
const bytes = new TextEncoder().encode('require("./dep.cjs");');
|
||||
const relationships = (
|
||||
mode?: "module" | "commonjs" | "unspecified" | "unknown",
|
||||
) =>
|
||||
parseReferenceSourceImports("main.js", bytes, "JavaScript", mode)
|
||||
.relationships;
|
||||
expect(relationships()).toEqual([
|
||||
expect.objectContaining({
|
||||
to: "./dep.cjs",
|
||||
resolution: "unknown",
|
||||
parse_state: "partial",
|
||||
}),
|
||||
]);
|
||||
expect(relationships("module")).toEqual([]);
|
||||
for (const mode of ["commonjs", "unspecified"] as const)
|
||||
expect(relationships(mode)).toEqual([
|
||||
expect.objectContaining({
|
||||
to: "./dep.cjs",
|
||||
resolution: "internal",
|
||||
parse_state: "parsed",
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
import {
|
||||
referenceSourceRequireCalls,
|
||||
type ReferenceSourceRequireCall,
|
||||
type ReferenceSourcePackageType,
|
||||
} from "./referenceSourceRequireBindings.js";
|
||||
import { isRelativeJavaScriptModuleSpecifier } from "./javascript/javascriptModuleSpecifier.js";
|
||||
import {
|
||||
@@ -261,7 +262,7 @@ export const parseReferenceSourceImports = (
|
||||
path: string,
|
||||
bytes: Uint8Array,
|
||||
language: string | null,
|
||||
packageType?: "module" | "commonjs" | "unknown",
|
||||
packageType?: ReferenceSourcePackageType,
|
||||
): ReferenceSourceImportParseResult => {
|
||||
if (language !== null && !PARSEABLE_REFERENCE_SOURCE_LANGUAGES.has(language))
|
||||
return { relationships: [], parse_failures: [], limitations: [] };
|
||||
|
||||
@@ -23,6 +23,13 @@ import {
|
||||
type ParsedJavaScriptSource,
|
||||
} from "./javascript/javascriptSourceParser.js";
|
||||
|
||||
/** Package scope observed inside the selected inventory; missing scope stays unknown. */
|
||||
export type ReferenceSourcePackageType =
|
||||
| "module"
|
||||
| "commonjs"
|
||||
| "unspecified"
|
||||
| "unknown";
|
||||
|
||||
export type ReferenceSourceRequireResolutionBase =
|
||||
| "source-file"
|
||||
| { readonly kind: "source-relative-file"; readonly path: string }
|
||||
@@ -37,7 +44,7 @@ export interface ReferenceSourceRequireCall {
|
||||
export const referenceSourceRequireCalls = (
|
||||
file: Pick<ParsedJavaScriptSource, "program">,
|
||||
path: string,
|
||||
packageType?: "module" | "commonjs" | "unknown",
|
||||
packageType: ReferenceSourcePackageType = "unknown",
|
||||
): ReadonlyMap<
|
||||
t.CallExpression | t.OptionalCallExpression,
|
||||
ReferenceSourceRequireCall
|
||||
@@ -103,7 +110,7 @@ export const referenceSourceRequireCalls = (
|
||||
path: normalizedSourceRelativePath,
|
||||
}
|
||||
: fact === "commonjs-loader" && callFact.loader === "commonjs-loader"
|
||||
? path.endsWith(".js") && packageType === "unknown"
|
||||
? path.endsWith(".js") && wrapperMode === "unknown"
|
||||
? "unknown"
|
||||
: "source-file"
|
||||
: "unknown";
|
||||
@@ -177,7 +184,7 @@ type CommonJsWrapperMode = "present" | "absent" | "unknown";
|
||||
const commonJsWrapperMode = (
|
||||
path: string,
|
||||
program: t.Program,
|
||||
packageType: "module" | "commonjs" | "unknown" | undefined,
|
||||
packageType: ReferenceSourcePackageType,
|
||||
): CommonJsWrapperMode => {
|
||||
if (path.endsWith(".cjs")) return "present";
|
||||
if (isExplicitModuleSourcePath(path)) return "absent";
|
||||
@@ -186,6 +193,7 @@ const commonJsWrapperMode = (
|
||||
if (packageType === "commonjs") return "present";
|
||||
if (packageType === "unknown") return "unknown";
|
||||
if (javaScriptRuntimeContext(program) === "module") return "absent";
|
||||
return "present";
|
||||
}
|
||||
return "unknown";
|
||||
};
|
||||
|
||||
@@ -33,6 +33,8 @@ cliTest(
|
||||
"dep.ts": "export interface Marker { id: string }\n",
|
||||
"nested/package.json": '{"type":"commonjs","main":"main.js"}\n',
|
||||
"nested/main.js": 'require("../dep.cjs");\n',
|
||||
"untyped/package.json": "{}\n",
|
||||
"untyped/main.js": 'require("../dep.cjs");\n',
|
||||
"unknown/package.json": "{invalid",
|
||||
"unknown/main.js": 'require("../dep.cjs");\n',
|
||||
"url.mjs":
|
||||
@@ -45,7 +47,7 @@ cliTest(
|
||||
"oracle.mjs":
|
||||
'import { fileURLToPath } from "node:url"; import { createRequire } from "node:module"; const require = createRequire(import.meta.url); console.log(JSON.stringify([fileURLToPath(import.meta.resolve("./dep%20name.mjs?v=1#first")), fileURLToPath(import.meta.resolve("./dep%2520name.mjs")), require.resolve("./dep%20name.mjs"), require.resolve("./dep?literal.cjs")]));\n',
|
||||
};
|
||||
for (const directory of ["nested", "unknown"])
|
||||
for (const directory of ["nested", "unknown", "untyped"])
|
||||
await mkdir(join(root, directory));
|
||||
await Promise.all(
|
||||
Object.entries(files).map(([path, source]) =>
|
||||
@@ -85,7 +87,7 @@ cliTest(
|
||||
},
|
||||
]);
|
||||
expect(edges("shadowed.cjs")).toEqual([]);
|
||||
for (const from of ["optional.cjs", "nested/main.js"])
|
||||
for (const from of ["optional.cjs", "nested/main.js", "untyped/main.js"])
|
||||
expect(edges(from)).toEqual([
|
||||
{
|
||||
from_path: from,
|
||||
|
||||
@@ -798,8 +798,9 @@ describe("reference source rooted module specifiers", () => {
|
||||
await Promise.all([
|
||||
writeFile(
|
||||
join(root, "main.js"),
|
||||
'import ".hidden.js"; import "..pkg.js"; import "./.hidden.js"; require(".hidden.js");\n',
|
||||
'import ".hidden.js"; import "..pkg.js"; import "./.hidden.js";\n',
|
||||
),
|
||||
writeFile(join(root, "common.cjs"), 'require(".hidden.js");\n'),
|
||||
writeFile(join(root, ".hidden.js"), "export {};\n"),
|
||||
writeFile(join(root, "..pkg.js"), "export {};\n"),
|
||||
]);
|
||||
|
||||
Reference in New Issue
Block a user