rea/tests/composition/javascript-applications/javascriptSemanticAnalysis....

62 lines
1.9 KiB
TypeScript

import { expect } from "vitest";
import type {
JavaScriptSemanticBinding,
JavaScriptSemanticIr,
} from "../../../src/domain/javascriptSemanticIr.js";
export const programScope = (ir: JavaScriptSemanticIr) => {
const scope = ir.scopes.find(({ kind }) => kind === "program");
if (scope === undefined) throw new Error("Missing program scope");
return scope;
};
export const bindingsNamed = (
ir: JavaScriptSemanticIr,
name: string,
): JavaScriptSemanticBinding[] =>
ir.bindings.filter(({ name: candidate }) => candidate === name);
export const onlyBinding = (
ir: JavaScriptSemanticIr,
name: string,
): JavaScriptSemanticBinding => {
const bindings = bindingsNamed(ir, name);
expect(bindings).toHaveLength(1);
const binding = bindings[0];
if (binding === undefined) throw new Error(`Missing binding ${name}`);
return binding;
};
export const onlyCallable = (ir: JavaScriptSemanticIr, name: string) => {
const callables = ir.callables.filter(
({ name: candidate }) => candidate === name,
);
if (callables.length !== 1 || callables[0] === undefined)
throw new Error(`Expected one callable named ${name}`);
return callables[0];
};
export const topLevelBinding = (
ir: JavaScriptSemanticIr,
name: string,
): JavaScriptSemanticBinding => {
const scopeId = programScope(ir).scopeId;
const binding = ir.bindings.find(
({ name: candidate, scopeId: candidateScope }) =>
candidate === name && candidateScope === scopeId,
);
if (binding === undefined)
throw new Error(`Missing top-level binding ${name}`);
return binding;
};
export const origin = (binding: JavaScriptSemanticBinding) => {
expect(binding.provenance.status).toBe("module");
expect(binding.provenance.origins).toHaveLength(1);
const value = binding.provenance.origins[0];
if (value === undefined)
throw new Error(`Missing origin for ${binding.name}`);
return value;
};