fix(javascript): preserve semantic evidence through satisfies
This commit is contained in:
parent
4896adcd0f
commit
e995b19f83
|
|
@ -0,0 +1,57 @@
|
|||
import { writeFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
|
||||
import { expect, it } from "vitest";
|
||||
|
||||
import { createTestTempDirectory } from "../../../tests/fixtures/temporaryDirectory.js";
|
||||
import { javascriptApplicationAnalysisResultSchema } from "../../domain/javascript/javascriptApplicationAnalysis.js";
|
||||
import { analyzeJavaScriptSemantics } from "../../domain/javascript/javascriptSemanticAnalysis.js";
|
||||
import { topLevelBinding } from "../../domain/javascript/javascriptSemanticAnalysis.fixture.js";
|
||||
import { analyzeJavaScriptApplication } from "./JavaScriptApplicationService.js";
|
||||
|
||||
it.each([
|
||||
'"ready" satisfies string',
|
||||
'("ready" satisfies string) as string',
|
||||
'("ready" satisfies string)!',
|
||||
])("retains the runtime primitive through %s", (expression) => {
|
||||
const ir = analyzeJavaScriptSemantics(`const value = ${expression};`);
|
||||
expect(topLevelBinding(ir, "value").value).toEqual({
|
||||
status: "literal",
|
||||
value: "ready",
|
||||
});
|
||||
});
|
||||
|
||||
it("retains exported constant evidence through a type-only satisfies expression", async () => {
|
||||
const root = await createTestTempDirectory("rea-ts-satisfies-");
|
||||
await writeFile(
|
||||
join(root, "app.ts"),
|
||||
'export const value = "ready" satisfies string;\n',
|
||||
);
|
||||
const result = await analyzeJavaScriptApplication({
|
||||
input_path: root,
|
||||
format: "directory",
|
||||
});
|
||||
if (!result.ok) throw result.error;
|
||||
const analysis = javascriptApplicationAnalysisResultSchema.parse(
|
||||
result.value.normalized_result,
|
||||
);
|
||||
expect(
|
||||
analysis.semantic_graph.nodes.filter(
|
||||
({ kind, properties }) =>
|
||||
kind === "literal" && properties.value === "ready",
|
||||
),
|
||||
).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("retains local call targets and exported callable links through satisfies", () => {
|
||||
const ir = analyzeJavaScriptSemantics(
|
||||
'export const render = (() => "ready") satisfies (() => string); render();',
|
||||
);
|
||||
const callable = ir.callables[0];
|
||||
if (callable === undefined) throw new Error("Missing callable");
|
||||
expect(
|
||||
ir.moduleLinks.find(({ localName }) => localName === "render")?.callableId,
|
||||
).toBe(callable.callableId);
|
||||
expect(ir.callSites[0]?.calleeCallableIds).toEqual([callable.callableId]);
|
||||
expect(ir.callSites[0]?.resolution).toBe("exact");
|
||||
});
|
||||
|
|
@ -62,7 +62,8 @@ const resolveCallables = (
|
|||
if (
|
||||
t.isTSAsExpression(node) ||
|
||||
t.isTSTypeAssertion(node) ||
|
||||
t.isTSNonNullExpression(node)
|
||||
t.isTSNonNullExpression(node) ||
|
||||
t.isTSSatisfiesExpression(node)
|
||||
)
|
||||
return resolveCallables(node.expression, context);
|
||||
if (t.isConditionalExpression(node) || t.isLogicalExpression(node))
|
||||
|
|
|
|||
|
|
@ -188,7 +188,8 @@ const callableIdsForNode = (
|
|||
if (
|
||||
t.isTSAsExpression(node) ||
|
||||
t.isTSTypeAssertion(node) ||
|
||||
t.isTSNonNullExpression(node)
|
||||
t.isTSNonNullExpression(node) ||
|
||||
t.isTSSatisfiesExpression(node)
|
||||
)
|
||||
return callableIdsForNode(node.expression, state, admitted, seen);
|
||||
return [];
|
||||
|
|
|
|||
|
|
@ -199,7 +199,8 @@ const evaluateExpression = (
|
|||
if (
|
||||
(t.isTSAsExpression(node) ||
|
||||
t.isTSTypeAssertion(node) ||
|
||||
t.isTSNonNullExpression(node)) &&
|
||||
t.isTSNonNullExpression(node) ||
|
||||
t.isTSSatisfiesExpression(node)) &&
|
||||
t.isExpression(node.expression)
|
||||
)
|
||||
return evaluateExpression(node.expression, nestedContext(context));
|
||||
|
|
|
|||
Loading…
Reference in New Issue