* Ingest an `@for` block into the given `ViewCompilation`.
(unit: ViewCompilationUnit, forBlock: t.ForLoopBlock)
| 904 | * Ingest an `@for` block into the given `ViewCompilation`. |
| 905 | */ |
| 906 | function ingestForBlock(unit: ViewCompilationUnit, forBlock: t.ForLoopBlock): void { |
| 907 | const repeaterView = unit.job.allocateView(unit.xref); |
| 908 | |
| 909 | // We copy TemplateDefinitionBuilder's scheme of creating names for `$count` and `$index` |
| 910 | // that are suffixed with special information, to disambiguate which level of nested loop |
| 911 | // the below aliases refer to. |
| 912 | // TODO: We should refactor Template Pipeline's variable phases to gracefully handle |
| 913 | // shadowing, and arbitrarily many levels of variables depending on each other. |
| 914 | const indexName = `ɵ$index_${repeaterView.xref}`; |
| 915 | const countName = `ɵ$count_${repeaterView.xref}`; |
| 916 | const indexVarNames = new Set<string>(); |
| 917 | |
| 918 | // Set all the context variables and aliases available in the repeater. |
| 919 | repeaterView.contextVariables.set(forBlock.item.name, forBlock.item.value); |
| 920 | |
| 921 | for (const variable of forBlock.contextVariables) { |
| 922 | if (variable.value === '$index') { |
| 923 | indexVarNames.add(variable.name); |
| 924 | } |
| 925 | if (variable.name === '$index') { |
| 926 | repeaterView.contextVariables.set('$index', variable.value).set(indexName, variable.value); |
| 927 | } else if (variable.name === '$count') { |
| 928 | repeaterView.contextVariables.set('$count', variable.value).set(countName, variable.value); |
| 929 | } else { |
| 930 | repeaterView.aliases.add({ |
| 931 | kind: ir.SemanticVariableKind.Alias, |
| 932 | name: null, |
| 933 | identifier: variable.name, |
| 934 | expression: getComputedForLoopVariableExpression(variable, indexName, countName), |
| 935 | }); |
| 936 | } |
| 937 | } |
| 938 | |
| 939 | let track: o.Expression; |
| 940 | |
| 941 | if (forBlock.trackBy === null) { |
| 942 | // `@for` without a `track` is invalid and it produces a parser error. |
| 943 | // Put a placeholder here so we don't need to account for it throughout the pipeline. |
| 944 | track = o.variable('$index'); |
| 945 | } else { |
| 946 | track = convertAst( |
| 947 | forBlock.trackBy, |
| 948 | unit.job, |
| 949 | convertSourceSpan(forBlock.trackBy.span, forBlock.sourceSpan), |
| 950 | ); |
| 951 | } |
| 952 | |
| 953 | ingestNodes(repeaterView, forBlock.children); |
| 954 | |
| 955 | let emptyView: ViewCompilationUnit | null = null; |
| 956 | let emptyTagName: string | null = null; |
| 957 | if (forBlock.empty !== null) { |
| 958 | emptyView = unit.job.allocateView(unit.xref); |
| 959 | ingestNodes(emptyView, forBlock.empty.children); |
| 960 | emptyTagName = ingestControlFlowInsertionPoint(unit, emptyView.xref, forBlock.empty); |
| 961 | } |
| 962 | |
| 963 | const varNames: ir.RepeaterVarNames = { |
no test coverage detected