mirror of
https://github.com/morluto/rea
synced 2026-10-26 14:25:49 -04:00
fix(javascript): retain compiler-dependent module resolution facts
Compare import and require outcomes for static TypeScript references when compiler options are unknown. Preserve branch facts through worker IR and both graph projections; retain declaration fallbacks as candidates without changing explicitly selected paths. Validation: 210 focused resolver and semantic tests; 21 production-boundary and source-extension tests. A bounded 2,000-reference probe adds about 16–17 ms wall time for dual resolution; full task token costs and real compiled application behavior remain unmeasured.
This commit is contained in:
@@ -12,7 +12,6 @@ import {
|
||||
javascriptAnalysisCoverage,
|
||||
linkElectronRoleToAsset,
|
||||
moduleLookupKey,
|
||||
resolveArtifactPath,
|
||||
selfReferenceOmissions,
|
||||
sourceNodeFor,
|
||||
type JavaScriptArtifactGraphContext,
|
||||
@@ -23,9 +22,11 @@ import {
|
||||
staticInferenceEvidence,
|
||||
} from "./JavaScriptArtifactGraphEvidence.js";
|
||||
import {
|
||||
moduleKindForJavaScriptReference,
|
||||
artifactPathResolutionProperties,
|
||||
resolveArtifactPathByContext,
|
||||
type ArtifactPathResolution,
|
||||
} from "./JavaScriptArtifactPathResolution.js";
|
||||
import { moduleResolutionModeForJavaScriptReference } from "../../domain/javascript/javascriptModuleResolutionMode.js";
|
||||
|
||||
type StaticReference = JavaScriptStaticAnalysis["references"][number];
|
||||
type StaticEndpoint = JavaScriptStaticAnalysis["endpoints"][number];
|
||||
@@ -41,9 +42,12 @@ interface FindingInput<Value> {
|
||||
}
|
||||
|
||||
interface ResolvedReference {
|
||||
readonly path: string;
|
||||
readonly file: JavaScriptArtifactFile;
|
||||
readonly path: string | null;
|
||||
readonly file: JavaScriptArtifactFile | null;
|
||||
readonly node: ApplicationNode;
|
||||
readonly limitations: readonly string[];
|
||||
readonly resolutionStatus?: string;
|
||||
readonly resolution?: ArtifactPathResolution;
|
||||
}
|
||||
|
||||
/** Project imports, workers, roles, endpoints, storage, and source-map links. */
|
||||
@@ -90,7 +94,7 @@ const addReference = (
|
||||
const resolved =
|
||||
input.value.specifier === null
|
||||
? null
|
||||
: resolveReference(context, input.file.path, input.value);
|
||||
: resolveReference(context, input.file.path, input.value, input.coverage);
|
||||
if (input.value.kind === "worker" || input.value.kind === "service-worker") {
|
||||
const worker = context.accumulator.addNode({
|
||||
kind: input.value.kind === "worker" ? "worker" : "service-worker",
|
||||
@@ -126,7 +130,7 @@ const addReference = (
|
||||
relation: "loads",
|
||||
properties: { kind: input.value.kind },
|
||||
});
|
||||
if (resolved !== null)
|
||||
if (resolved !== null && resolved.path !== null)
|
||||
addStaticInferenceEdge(context, {
|
||||
source: worker,
|
||||
target: resolved.node,
|
||||
@@ -135,6 +139,7 @@ const addReference = (
|
||||
coverage: input.coverage,
|
||||
relation: "maps_to",
|
||||
properties: { resolved_path: resolved.path },
|
||||
limitations: resolved.limitations,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -154,13 +159,25 @@ const addReference = (
|
||||
file: input.file,
|
||||
range: input.value.location,
|
||||
coverage: input.coverage,
|
||||
relation: resolved?.file.kind === "native-addon" ? "loads" : "imports",
|
||||
relation: resolved?.file?.kind === "native-addon" ? "loads" : "imports",
|
||||
properties: {
|
||||
kind: input.value.kind,
|
||||
specifier: input.value.specifier,
|
||||
expression: input.value.expression,
|
||||
resolution_mode:
|
||||
input.value.kind === "static-import" ||
|
||||
input.value.kind === "dynamic-import" ||
|
||||
input.value.kind === "require"
|
||||
? moduleResolutionModeForJavaScriptReference(
|
||||
input.file.path,
|
||||
input.value.kind,
|
||||
)
|
||||
: null,
|
||||
resolved_path: resolved?.path ?? null,
|
||||
resolution_status: resolved?.resolutionStatus ?? null,
|
||||
...artifactPathResolutionProperties(resolved?.resolution),
|
||||
},
|
||||
...(resolved === null ? {} : { limitations: resolved.limitations }),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -373,6 +390,7 @@ const resolveReference = (
|
||||
context: JavaScriptArtifactGraphContext,
|
||||
sourcePath: string,
|
||||
reference: StaticReference,
|
||||
coverage: JavaScriptArtifactGraphCoverage,
|
||||
): ResolvedReference | null => {
|
||||
const specifier = reference.specifier;
|
||||
if (specifier === null) return null;
|
||||
@@ -392,30 +410,70 @@ const resolveReference = (
|
||||
const file = context.filesByPath.get(sourcePath);
|
||||
return file === undefined
|
||||
? null
|
||||
: { path: `${sourcePath}#module:${specifier}`, file, node: module };
|
||||
: {
|
||||
path: `${sourcePath}#module:${specifier}`,
|
||||
file,
|
||||
node: module,
|
||||
limitations: [],
|
||||
};
|
||||
}
|
||||
// Worker scripts are URLs relative to the script, not module specifiers.
|
||||
const path = isWorker
|
||||
const resolution = isWorker
|
||||
? resolveArtifactPathByContext({
|
||||
declaredPath: specifier,
|
||||
sourcePath,
|
||||
context: "url-reference",
|
||||
files: context.filesByPath,
|
||||
}).resolved_path
|
||||
: resolveArtifactPath(
|
||||
specifier,
|
||||
})
|
||||
: resolveArtifactPathByContext({
|
||||
declaredPath: specifier,
|
||||
sourcePath,
|
||||
context.filesByPath,
|
||||
reference.kind === "require" ||
|
||||
reference.kind === "static-import" ||
|
||||
reference.kind === "dynamic-import"
|
||||
? moduleKindForJavaScriptReference(sourcePath, reference.kind)
|
||||
: undefined,
|
||||
);
|
||||
if (path === null) return null;
|
||||
context: "module-specifier",
|
||||
files: context.filesByPath,
|
||||
...("require" === reference.kind ||
|
||||
"static-import" === reference.kind ||
|
||||
"dynamic-import" === reference.kind
|
||||
? {
|
||||
moduleKind: moduleResolutionModeForJavaScriptReference(
|
||||
sourcePath,
|
||||
reference.kind,
|
||||
),
|
||||
}
|
||||
: {}),
|
||||
});
|
||||
const path = resolution.resolved_path;
|
||||
if (path === null) {
|
||||
if (isWorker) return null;
|
||||
const source = context.filesByPath.get(sourcePath);
|
||||
return source === undefined
|
||||
? null
|
||||
: {
|
||||
path: null,
|
||||
file: null,
|
||||
node: unresolvedReferenceNode(
|
||||
context,
|
||||
source,
|
||||
reference,
|
||||
coverage,
|
||||
resolution,
|
||||
),
|
||||
limitations: resolution.limitations,
|
||||
resolutionStatus: resolution.resolution_status,
|
||||
resolution,
|
||||
};
|
||||
}
|
||||
const file = context.filesByPath.get(path);
|
||||
const node = context.fileNodes.get(path);
|
||||
return file === undefined || node === undefined ? null : { path, file, node };
|
||||
return file === undefined || node === undefined
|
||||
? null
|
||||
: {
|
||||
path,
|
||||
file,
|
||||
node,
|
||||
limitations: resolution.limitations,
|
||||
resolutionStatus: resolution.resolution_status,
|
||||
resolution,
|
||||
};
|
||||
};
|
||||
|
||||
const resolveBundlerChunkReference = (
|
||||
@@ -453,7 +511,7 @@ const resolveBundlerChunkReference = (
|
||||
const node = unique[0];
|
||||
return node === undefined
|
||||
? null
|
||||
: { path: `${sourcePath}#chunk:${key}`, file, node };
|
||||
: { path: `${sourcePath}#chunk:${key}`, file, node, limitations: [] };
|
||||
};
|
||||
|
||||
const unresolvedReferenceNode = (
|
||||
@@ -461,6 +519,7 @@ const unresolvedReferenceNode = (
|
||||
file: JavaScriptArtifactFile,
|
||||
reference: StaticReference,
|
||||
coverage: JavaScriptArtifactGraphCoverage,
|
||||
resolution?: ArtifactPathResolution,
|
||||
): ApplicationNode =>
|
||||
context.accumulator.addNode({
|
||||
kind: "javascript-module",
|
||||
@@ -477,6 +536,10 @@ const unresolvedReferenceNode = (
|
||||
specifier: reference.specifier,
|
||||
expression: reference.expression,
|
||||
resolution: "unresolved",
|
||||
...(resolution === undefined
|
||||
? {}
|
||||
: { resolution_status: resolution.resolution_status }),
|
||||
...artifactPathResolutionProperties(resolution),
|
||||
},
|
||||
evidence: staticInferenceEvidence({
|
||||
sha256: file.sha256,
|
||||
@@ -487,6 +550,7 @@ const unresolvedReferenceNode = (
|
||||
confidence: "low",
|
||||
limitations: [
|
||||
"The static reference could not be resolved within the inventoried artifact.",
|
||||
...(resolution?.limitations ?? []),
|
||||
],
|
||||
}),
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user