import ts from "typescript"; import type { Emitter } from "../types.ts"; import { createIfElseCall } from "../../builtins/ifelse.ts"; import { registerSyntheticCallType, selectDataFlowsReferencedIn, } from "../../../ast/mod.ts"; import type { NormalizedDataFlowSet } from "../../../ast/mod.ts"; import { isSimpleOpaqueRefAccess } from "../opaque-ref.ts"; import { createBindingPlan } from "../bindings.ts"; import { createComputedCallForExpression, filterRelevantDataFlows, } from "../helpers.ts"; // Helper to process a conditional branch (whenTrue/whenFalse) function processBranch( expr: ts.Expression, dataFlows: NormalizedDataFlowSet, analysis: ReturnType[0]["analyze"]>, context: Parameters[0]["context"], analyze: Parameters[0]["analyze"], rewriteChildren: Parameters[0]["rewriteChildren"], ): ts.Expression { const branchDataFlows = filterRelevantDataFlows( selectDataFlowsReferencedIn(dataFlows, expr), analysis, context, ); const branchAnalysis = analyze(expr); if ( branchDataFlows.length > 0 && branchAnalysis.requiresRewrite && !isSimpleOpaqueRefAccess(expr, context.checker) ) { const plan = createBindingPlan(branchDataFlows); const derived = createComputedCallForExpression(expr, plan, context); if (derived) { return derived; } } // Fallback: rewrite children const rewritten = rewriteChildren(expr); return rewritten || expr; } export const emitConditionalExpression: Emitter = ({ expression, dataFlows, analysis, context, analyze, rewriteChildren, }) => { if (!ts.isConditionalExpression(expression)) return undefined; if (dataFlows.all.length === 0) return undefined; const predicateDataFlows = selectDataFlowsReferencedIn( dataFlows, expression.condition, ); const shouldDerivePredicate = predicateDataFlows.length > 0 && !isSimpleOpaqueRefAccess(expression.condition, context.checker); let predicate: ts.Expression = expression.condition; if (shouldDerivePredicate) { const plan = createBindingPlan(predicateDataFlows); const derivedPredicate = createComputedCallForExpression( expression.condition, plan, context, ); if (derivedPredicate) { predicate = derivedPredicate; } } const whenTrue = processBranch( expression.whenTrue, dataFlows, analysis, context, analyze, rewriteChildren, ); const whenFalse = processBranch( expression.whenFalse, dataFlows, analysis, context, analyze, rewriteChildren, ); const ifElseCall = createIfElseCall({ expression, factory: context.factory, ctHelpers: context.ctHelpers, sourceFile: context.sourceFile, overrides: { predicate, whenTrue, whenFalse, }, }); // Register the result type for schema injection // The result type is the union of whenTrue and whenFalse types (from the original ternary) if (context.options.typeRegistry) { const resultType = context.checker.getTypeAtLocation(expression); registerSyntheticCallType( ifElseCall, resultType, context.options.typeRegistry, ); } return ifElseCall; };