Files
rea/src/domain/referenceSourceImportParsing.ts
T
morluto d5df7fd944 fix(reference): preserve source scope and loader authority
Share parser-owned package and runtime facts across dependency consumers.
Preserve uncertain loader requests and explicit wrapper bindings without
inventing unbound require authority in known modules. Cover selected source
grammar through the public MCP boundary and retain ambient/type dependencies.

Open each selected directory before loading its scoped policy, retaining
partial directory failures and cleanup ownership. Preserve explicit caller
negation precedence and source diagnostics across inventory projections.

Clarify how version comparison retains container provenance without claiming
semantic drift. Use independent unknown source inputs to keep candidate-limit
fixtures meaningful as conditional branch evaluation becomes more precise.

Fixes #6891
Fixes #6882
Fixes #6883
Fixes #6859
2026-10-24 06:24:55 +08:00

336 lines
9.8 KiB
TypeScript

import type {
CallExpression,
File,
OptionalCallExpression,
Node,
} from "@babel/types";
import { traverseJavaScriptAst } from "./javascript/javascriptSemanticTraversal.js";
import { readExactJavaScriptLiteral } from "./javascript/javascriptAstValues.js";
import {
parseRecoveredJavaScript,
parserPluginsForPath,
type JavaScriptPackageType,
} from "./javascript/javascriptSourceParser.js";
import {
referenceSourceRequireCalls,
type ReferenceSourceRequireCall,
} from "./referenceSourceRequireBindings.js";
import { isRelativeJavaScriptModuleSpecifier } from "./javascript/javascriptModuleSpecifier.js";
import {
isCallExpression,
isExportAllDeclaration,
isExportNamedDeclaration,
isImportDeclaration,
isImportExpression,
isOptionalCallExpression,
isTSImportType,
isStringLiteral,
isTSExternalModuleReference,
isTSImportEqualsDeclaration,
isTSModuleDeclaration,
} from "@babel/types";
type ReferenceSourceImportKind =
| "imports"
| "requires"
| "references"
| "declares-module";
type ReferenceSourceImportResolution =
| "internal"
| "external"
| "unresolved"
| "unknown";
type ReferenceSourceImportParseState = "parsed" | "partial" | "unknown";
interface ReferenceSourceImportRelationship {
readonly from_path: string;
readonly to: string;
readonly kind: ReferenceSourceImportKind;
readonly resolution: ReferenceSourceImportResolution;
readonly parse_state: ReferenceSourceImportParseState;
/** Distinguishes call-form CommonJS require from TS import-equals `requires`. */
readonly resolutionStrategy?: "runtime-require";
/** Internal lookup origin for a proven createRequire URL base. */
readonly resolutionBasePath?: string;
}
interface ReferenceSourceParseFailure {
readonly path: string;
readonly parser: string;
readonly reason: string;
}
export interface ReferenceSourceImportParseResult {
readonly relationships: readonly ReferenceSourceImportRelationship[];
readonly parse_failures: readonly ReferenceSourceParseFailure[];
readonly limitations: readonly string[];
}
export const PARSEABLE_REFERENCE_SOURCE_LANGUAGES: ReadonlySet<string> =
new Set(["JavaScript", "TypeScript", "JSX", "TSX"]);
const safeModuleName = (node: Node | null | undefined): string | undefined => {
if (isStringLiteral(node)) return node.value;
return undefined;
};
const exactStringModuleName = (
node: Node | null | undefined,
): string | undefined => {
if (node === null || node === undefined) return undefined;
const literal = readExactJavaScriptLiteral(node);
return literal.found && typeof literal.value === "string"
? literal.value
: undefined;
};
const resolveSpecifier = (
specifier: string,
): ReferenceSourceImportResolution => {
if (
isRelativeJavaScriptModuleSpecifier(specifier) ||
specifier.startsWith("/")
)
return "internal";
return "external";
};
const appendRelationship = (
relationships: ReferenceSourceImportRelationship[],
limitations: string[],
input: {
readonly fromPath: string;
readonly to: string | undefined;
readonly kind: ReferenceSourceImportKind;
readonly parseState: ReferenceSourceImportParseState;
readonly resolution?: ReferenceSourceImportResolution;
readonly resolutionStrategy?: "runtime-require";
readonly resolutionBasePath?: string;
},
): void => {
if (input.to === undefined) return;
if (input.to.length === 0) {
limitations.push(
`Empty module specifier in ${input.fromPath} was not recorded as a relationship.`,
);
return;
}
relationships.push({
from_path: input.fromPath,
to: input.to,
kind: input.kind,
resolution: input.resolution ?? resolveSpecifier(input.to),
parse_state: input.parseState,
...(input.resolutionStrategy === undefined
? {}
: { resolutionStrategy: input.resolutionStrategy }),
...(input.resolutionBasePath === undefined
? {}
: { resolutionBasePath: input.resolutionBasePath }),
});
};
const appendImportEquals = (
statement: Node,
from_path: string,
relationships: ReferenceSourceImportRelationship[],
limitations: string[],
): void => {
if (!isTSImportEqualsDeclaration(statement)) return;
const reference = statement.moduleReference;
if (!isTSExternalModuleReference(reference)) return;
appendRelationship(relationships, limitations, {
fromPath: from_path,
to: safeModuleName(reference.expression),
kind: "requires",
parseState: "parsed",
});
};
/** One AST owner retains historical declarations, types and runtime requests. */
const extractImports = (
ast: File,
from_path: string,
relationships: ReferenceSourceImportRelationship[],
limitations: string[],
requireCalls: ReadonlyMap<
CallExpression | OptionalCallExpression,
ReferenceSourceRequireCall
>,
): void => {
traverseJavaScriptAst(ast.program, {
enter: (node) => {
if (
isImportDeclaration(node) ||
isExportAllDeclaration(node) ||
(isExportNamedDeclaration(node) && node.source != null)
) {
appendRelationship(relationships, limitations, {
fromPath: from_path,
to: safeModuleName(node.source),
kind: "imports",
parseState: "parsed",
});
} else if (isTSImportEqualsDeclaration(node)) {
appendImportEquals(node, from_path, relationships, limitations);
} else if (isTSModuleDeclaration(node)) {
if (node.declare === true && isStringLiteral(node.id))
appendRelationship(relationships, limitations, {
fromPath: from_path,
to: node.id.value,
kind: "declares-module",
parseState: "parsed",
resolution: "unknown",
});
} else if (isImportExpression(node) || isTSImportType(node)) {
const specifier = exactStringModuleName(node.source);
appendRelationship(relationships, limitations, {
fromPath: from_path,
to: specifier ?? "<dynamic-import>",
kind: "imports",
parseState: specifier === undefined ? "partial" : "parsed",
...(specifier === undefined ? { resolution: "unknown" } : {}),
});
} else if (isCallExpression(node) || isOptionalCallExpression(node)) {
const requireCall = requireCalls.get(node);
if (requireCall === undefined) return;
const specifier = exactStringModuleName(requireCall.arguments?.[0]);
if (specifier === undefined) return;
if (
requireCall.limitation !== undefined &&
!limitations.includes(requireCall.limitation)
)
limitations.push(requireCall.limitation);
const uncertainResolution = requireCall.resolutionBase === "unknown";
appendRelationship(relationships, limitations, {
fromPath: from_path,
to: specifier,
kind: "requires",
parseState: uncertainResolution ? "partial" : "parsed",
...(uncertainResolution ? { resolution: "unknown" } : {}),
...(uncertainResolution
? {}
: { resolutionStrategy: "runtime-require" as const }),
...(typeof requireCall.resolutionBase === "object"
? { resolutionBasePath: requireCall.resolutionBase.path }
: {}),
});
}
},
});
};
const parseWithBabel = (
path: string,
source: string,
packageType: JavaScriptPackageType | undefined,
): {
ast: File | undefined;
reasons: readonly string[];
moduleOnlyErrors: readonly string[];
} => {
// The shared parser owns filename precedence and observed package context.
const plugins = parserPluginsForPath(path);
try {
const { file: ast, moduleOnlyErrors } = parseRecoveredJavaScript(
source,
path,
{
allowImportExportEverywhere: false,
allowReturnOutsideFunction: true,
allowUndeclaredExports: true,
plugins,
},
packageType,
);
return {
ast,
reasons: ast.errors.map((error) => error.message),
moduleOnlyErrors,
};
} catch (cause: unknown) {
return {
ast: undefined,
reasons: [cause instanceof Error ? cause.message : "Parse failed"],
moduleOnlyErrors: [],
};
}
};
/** Parse ESM/CommonJS imports and dynamic imports from source bytes. */
export const parseReferenceSourceImports = (
path: string,
bytes: Uint8Array,
language: string | null,
packageType?: JavaScriptPackageType,
): ReferenceSourceImportParseResult => {
if (language !== null && !PARSEABLE_REFERENCE_SOURCE_LANGUAGES.has(language))
return { relationships: [], parse_failures: [], limitations: [] };
let source: string;
try {
source = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
} catch {
return {
relationships: [],
parse_failures: [
{
path,
parser: "utf-8",
reason:
"Source bytes are not valid UTF-8; import targets were not parsed.",
},
],
limitations: [],
};
}
const { ast, reasons, moduleOnlyErrors } = parseWithBabel(
path,
source,
packageType,
);
const limitations =
moduleOnlyErrors.length === 0
? []
: [
`Strict-mode errors apply only if this file is an ES module: ${moduleOnlyErrors.join("; ")}`,
];
const parseFailures = reasons.map((reason) => ({
path,
parser: "babel",
reason,
}));
if (ast === undefined) {
return {
relationships: [],
parse_failures: parseFailures,
limitations,
};
}
const relationships: ReferenceSourceImportRelationship[] = [];
extractImports(
ast,
path,
relationships,
limitations,
referenceSourceRequireCalls(ast, path),
);
return {
relationships: relationships.map((relationship) => ({
...relationship,
parse_state:
parseFailures.length > 0 && relationship.parse_state === "parsed"
? "partial"
: relationship.parse_state,
})),
parse_failures: parseFailures,
limitations,
};
};