rea/src/application/javascript/JavaScriptSemanticGraphProj...

77 lines
2.7 KiB
TypeScript

import { compareCodePoints } from "../../domain/canonicalOrdering.js";
import type { JavaScriptSemanticGraphNode } from "../../domain/javascriptSemanticGraph.js";
import { JAVASCRIPT_SEMANTIC_RELATION_FAMILIES } from "../../domain/javascriptSemanticGraphSchemas.js";
import type { JavaScriptSemanticIr } from "../../domain/javascriptSemanticIr.js";
import type { JavaScriptSourceRange } from "../../domain/javascriptStaticAnalysisTypes.js";
import type { JavaScriptArtifactAnalysis } from "./JavaScriptArtifactAnalysisTypes.js";
/** Find the innermost callable that contains one exact source range. */
export const owningSemanticCallableNode = (
location: JavaScriptSourceRange | null,
ir: JavaScriptSemanticIr,
nodes: ReadonlyMap<string, JavaScriptSemanticGraphNode>,
): JavaScriptSemanticGraphNode | undefined => {
if (location === null) return undefined;
return ir.callables
.filter((callable) => contains(callable.location, location))
.sort((left, right) =>
compareCodePoints(
positionKey(right.location),
positionKey(left.location),
),
)
.map(({ callableId }) => nodes.get(callableId))
.find((node) => node !== undefined);
};
/** Find retained semantic nodes whose exact ranges are within one expression. */
export const semanticNodesWithinRange = (
nodes: readonly JavaScriptSemanticGraphNode[],
range: JavaScriptSourceRange,
): JavaScriptSemanticGraphNode[] =>
nodes.filter(
(node) =>
node.identity.source_range !== null &&
contains(range, node.identity.source_range),
);
/** Report extractor support without treating missing families as absence. */
export const semanticFamilyStatus = (
family: (typeof JAVASCRIPT_SEMANTIC_RELATION_FAMILIES)[number],
analysis: JavaScriptArtifactAnalysis,
): "complete" | "partial" | "unknown" | "unsupported" => {
if (
![
"call-flow",
"boundary",
"child-process",
"closure",
"configuration",
"data-flow",
"event",
"object-flow",
"promise-ownership",
"request",
"resource-lifecycle",
"timer",
].includes(family)
)
return "unsupported";
return analysis.truncated_scopes === 0 ? "partial" : "unknown";
};
const contains = (
outer: JavaScriptSourceRange,
inner: JavaScriptSourceRange,
): boolean =>
pointCompare(outer.start, inner.start) <= 0 &&
pointCompare(outer.end, inner.end) >= 0;
const pointCompare = (
left: JavaScriptSourceRange["start"],
right: JavaScriptSourceRange["start"],
): number => left.line - right.line || left.column - right.column;
const positionKey = (range: JavaScriptSourceRange): string =>
`${String(range.start.line).padStart(12, "0")}:${String(range.start.column).padStart(12, "0")}`;