* Constructs a `Scope` given either a `Template` or a list of `Node`s. * * @param tcb the overall context of TCB generation. * @param parentScope the `Scope` of the parent template (if any) or `null` if this is the root * `Scope`. * @param scopedNode Node that provides the scope aroun
(
tcb: Context,
parentScope: Scope | null,
scopedNode: Template | IfBlockBranch | ForLoopBlock | HostElement | null,
children: Node[] | null,
guard: TcbExpr | null,
)
| 182 | * @param guard an expression that is applied to this scope for type narrowing purposes. |
| 183 | */ |
| 184 | static forNodes( |
| 185 | tcb: Context, |
| 186 | parentScope: Scope | null, |
| 187 | scopedNode: Template | IfBlockBranch | ForLoopBlock | HostElement | null, |
| 188 | children: Node[] | null, |
| 189 | guard: TcbExpr | null, |
| 190 | ): Scope { |
| 191 | const scope = new Scope(tcb, parentScope, guard); |
| 192 | |
| 193 | if (parentScope === null && tcb.env.config.enableTemplateTypeChecker) { |
| 194 | // Add an autocompletion point for the component context. |
| 195 | scope.opQueue.push(new TcbComponentContextCompletionOp(scope)); |
| 196 | } |
| 197 | |
| 198 | // If given an actual `Template` instance, then process any additional information it |
| 199 | // has. |
| 200 | if (scopedNode instanceof Template) { |
| 201 | // The template's variable declarations need to be added as `TcbVariableOp`s. |
| 202 | const varMap = new Map<string, Variable>(); |
| 203 | |
| 204 | for (const v of scopedNode.variables) { |
| 205 | // Validate that variables on the `Template` are only declared once. |
| 206 | if (!varMap.has(v.name)) { |
| 207 | varMap.set(v.name, v); |
| 208 | } else { |
| 209 | const firstDecl = varMap.get(v.name)!; |
| 210 | tcb.oobRecorder.duplicateTemplateVar(tcb.id, v, firstDecl); |
| 211 | } |
| 212 | Scope.registerVariable(scope, v, new TcbTemplateVariableOp(tcb, scope, scopedNode, v)); |
| 213 | } |
| 214 | } else if (scopedNode instanceof IfBlockBranch) { |
| 215 | const {expression, expressionAlias} = scopedNode; |
| 216 | if (expression !== null && expressionAlias !== null) { |
| 217 | Scope.registerVariable( |
| 218 | scope, |
| 219 | expressionAlias, |
| 220 | new TcbBlockVariableOp( |
| 221 | tcb, |
| 222 | scope, |
| 223 | tcbExpression(expression, tcb, scope), |
| 224 | expressionAlias, |
| 225 | ), |
| 226 | ); |
| 227 | } |
| 228 | } else if (scopedNode instanceof ForLoopBlock) { |
| 229 | // Register the variable for the loop so it can be resolved by |
| 230 | // children. It'll be declared once the loop is created. |
| 231 | const loopInitializer = new TcbExpr(tcb.allocateId()); |
| 232 | loopInitializer.addParseSpanInfo(scopedNode.item.sourceSpan); |
| 233 | scope.varMap.set(scopedNode.item, loopInitializer); |
| 234 | |
| 235 | const forLoopContextVariableTypes = Scope.getForLoopContextVariableTypes(); |
| 236 | |
| 237 | for (const variable of scopedNode.contextVariables) { |
| 238 | if (!forLoopContextVariableTypes.has(variable.value)) { |
| 239 | throw new Error(`Unrecognized for loop context variable ${variable.name}`); |
| 240 | } |
| 241 |
no test coverage detected