* If node is a lazy builtin like Each(arr, varName, template), temporarily * scope the iterator variable during materialization so template refs resolve. * Returns the materialized Comp node, or null if not a lazy builtin.
(
node: ASTNode & { k: "Comp" },
ctx: MaterializeCtx,
scopedRefs: ReadonlySet<string>,
)
| 78 | * Returns the materialized Comp node, or null if not a lazy builtin. |
| 79 | */ |
| 80 | function materializeLazyBuiltin( |
| 81 | node: ASTNode & { k: "Comp" }, |
| 82 | ctx: MaterializeCtx, |
| 83 | scopedRefs: ReadonlySet<string>, |
| 84 | ): ASTNode | null { |
| 85 | if (!LAZY_BUILTINS.has(node.name) || node.args.length < 3) return null; |
| 86 | const varArg = node.args[1]; |
| 87 | const varName = varArg.k === "Ref" ? varArg.n : varArg.k === "Str" ? varArg.v : null; |
| 88 | if (!varName) return null; |
| 89 | |
| 90 | const nextScopedRefs = new Set(scopedRefs); |
| 91 | nextScopedRefs.add(varName); |
| 92 | // Skip args[1] (the iterator declaration) but preserve scoped refs elsewhere. |
| 93 | const recursedArgs = node.args.map((a, i) => |
| 94 | i === 1 ? a : materializeExprInternal(a, ctx, nextScopedRefs), |
| 95 | ); |
| 96 | return { ...node, args: recursedArgs }; |
| 97 | } |
| 98 | |
| 99 | function materializeExprInternal( |
| 100 | node: ASTNode, |
no test coverage detected