mirror of
https://github.com/morluto/rea
synced 2026-10-26 22:28:27 -04:00
fix(javascript): omit self-referential import edges with disclosure (#1130)
* fix(javascript): omit self-referential import edges with disclosure A specifier that resolves back to the importing module itself produced an imports edge from a module to itself, which the application graph schema forbids; one such specifier failed result validation and lost the whole analysis. Skip those edges at composition time and disclose the omission count in the graph limitations. Fixes #1129 * test(javascript): verify self-import behavior through the compiled CLI --------- Co-authored-by: BaaRai <baarai@users.noreply.github.com> Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
This commit is contained in:
@@ -129,27 +129,40 @@ export const addJavaScriptSourceModules = (
|
||||
}
|
||||
};
|
||||
|
||||
/** Relationship compositions the application graph cannot represent, for disclosure. */
|
||||
export interface JavaScriptModuleRelationshipOmissions {
|
||||
/** Import specifiers that resolved back to the importing module itself. */
|
||||
readonly selfImports: number;
|
||||
}
|
||||
|
||||
type RelationshipOmissionCounter = { selfImports: number };
|
||||
|
||||
/** Compose bounded CommonJS and ESM binding relationships across artifact files. */
|
||||
export const addJavaScriptModuleRelationships = (
|
||||
context: JavaScriptArtifactGraphContext,
|
||||
): void => {
|
||||
): JavaScriptModuleRelationshipOmissions => {
|
||||
const omissions = { selfImports: 0 };
|
||||
for (const analyzed of context.analysis.files) {
|
||||
const { file, semantic } = analyzed;
|
||||
const source = context.sourceModuleNodes.get(file.path);
|
||||
if (semantic === null || source === undefined) continue;
|
||||
for (const link of semantic.ir.moduleLinks) {
|
||||
const input = { context, file, semantic, source, link };
|
||||
if (isExportLink(link)) addExportRelationship(input);
|
||||
if (isExportLink(link)) addExportRelationship(input, omissions);
|
||||
else if (link.specifier !== null)
|
||||
addImportRelationship(input, source, {
|
||||
addImportRelationship(input, source, omissions, {
|
||||
specifier: link.specifier,
|
||||
importedPath: link.importedName === null ? [] : [link.importedName],
|
||||
});
|
||||
}
|
||||
}
|
||||
return omissions;
|
||||
};
|
||||
|
||||
const addExportRelationship = (input: RelationshipInput): void => {
|
||||
const addExportRelationship = (
|
||||
input: RelationshipInput,
|
||||
omissions: RelationshipOmissionCounter,
|
||||
): void => {
|
||||
const { context, file, semantic, source, link } = input;
|
||||
if (link.exportedName === null) return;
|
||||
const baseCoverage = semanticCoverage(semantic);
|
||||
@@ -213,15 +226,25 @@ const addExportRelationship = (input: RelationshipInput): void => {
|
||||
evidence: relationshipEvidence(input, "expose-module-export", []),
|
||||
});
|
||||
const origin = moduleOriginForExport(semantic.ir, link);
|
||||
if (origin !== null) addImportRelationship(input, exported, origin);
|
||||
if (origin !== null)
|
||||
addImportRelationship(input, exported, omissions, origin);
|
||||
};
|
||||
|
||||
const addImportRelationship = (
|
||||
input: RelationshipInput,
|
||||
source: ApplicationNode,
|
||||
omissions: RelationshipOmissionCounter,
|
||||
origin: JavaScriptModuleOrigin,
|
||||
): void => {
|
||||
const target = resolveModuleTarget(input, origin.specifier);
|
||||
// A specifier that resolves back to the importing module itself (a literal
|
||||
// self-import, self-require, or artifact-confined path alias)
|
||||
// cannot become an edge: the application graph forbids self-referential
|
||||
// edges, and emitting one fails result validation for the whole analysis.
|
||||
if (target.node.node_id === source.node_id) {
|
||||
omissions.selfImports += 1;
|
||||
return;
|
||||
}
|
||||
input.context.accumulator.addEdge({
|
||||
source_node_id: source.node_id,
|
||||
target_node_id: target.node.node_id,
|
||||
|
||||
Reference in New Issue
Block a user