@angular/compiler 16.2.0-next.1 → 16.2.0-next.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/esm2022/src/compiler.mjs +2 -2
  2. package/esm2022/src/expression_parser/ast.mjs +1 -1
  3. package/esm2022/src/i18n/extractor_merger.mjs +8 -1
  4. package/esm2022/src/i18n/i18n_parser.mjs +12 -1
  5. package/esm2022/src/i18n/serializers/xliff.mjs +7 -1
  6. package/esm2022/src/i18n/serializers/xliff2.mjs +7 -1
  7. package/esm2022/src/i18n/serializers/xtb.mjs +7 -1
  8. package/esm2022/src/jit_compiler_facade.mjs +3 -2
  9. package/esm2022/src/ml_parser/ast.mjs +46 -1
  10. package/esm2022/src/ml_parser/html_whitespaces.mjs +10 -1
  11. package/esm2022/src/ml_parser/icu_ast_expander.mjs +10 -1
  12. package/esm2022/src/ml_parser/lexer.mjs +93 -4
  13. package/esm2022/src/ml_parser/parser.mjs +129 -32
  14. package/esm2022/src/ml_parser/tokens.mjs +1 -1
  15. package/esm2022/src/ml_parser/xml_parser.mjs +4 -3
  16. package/esm2022/src/output/output_ast.mjs +9 -1
  17. package/esm2022/src/render3/partial/class_metadata.mjs +1 -1
  18. package/esm2022/src/render3/partial/directive.mjs +1 -1
  19. package/esm2022/src/render3/partial/factory.mjs +1 -1
  20. package/esm2022/src/render3/partial/injectable.mjs +1 -1
  21. package/esm2022/src/render3/partial/injector.mjs +1 -1
  22. package/esm2022/src/render3/partial/ng_module.mjs +6 -3
  23. package/esm2022/src/render3/partial/pipe.mjs +1 -1
  24. package/esm2022/src/render3/r3_module_compiler.mjs +69 -27
  25. package/esm2022/src/render3/r3_template_transform.mjs +19 -1
  26. package/esm2022/src/render3/view/i18n/meta.mjs +12 -1
  27. package/esm2022/src/shadow_css.mjs +2 -2
  28. package/esm2022/src/template/pipeline/ir/src/enums.mjs +13 -1
  29. package/esm2022/src/template/pipeline/ir/src/expression.mjs +80 -6
  30. package/esm2022/src/template/pipeline/ir/src/ops/update.mjs +16 -1
  31. package/esm2022/src/template/pipeline/src/emit.mjs +4 -2
  32. package/esm2022/src/template/pipeline/src/ingest.mjs +18 -1
  33. package/esm2022/src/template/pipeline/src/instruction.mjs +31 -1
  34. package/esm2022/src/template/pipeline/src/phases/chaining.mjs +2 -1
  35. package/esm2022/src/template/pipeline/src/phases/expand_safe_reads.mjs +102 -10
  36. package/esm2022/src/template/pipeline/src/phases/generate_variables.mjs +1 -6
  37. package/esm2022/src/template/pipeline/src/phases/nullish_coalescing.mjs +6 -5
  38. package/esm2022/src/template/pipeline/src/phases/reify.mjs +17 -1
  39. package/esm2022/src/template/pipeline/src/phases/resolve_names.mjs +8 -1
  40. package/esm2022/src/template/pipeline/src/phases/save_restore_view.mjs +32 -19
  41. package/esm2022/src/template/pipeline/src/phases/temporary_variables.mjs +53 -0
  42. package/esm2022/src/template/pipeline/src/phases/var_counting.mjs +7 -1
  43. package/esm2022/src/version.mjs +1 -1
  44. package/fesm2022/compiler.mjs +799 -115
  45. package/fesm2022/compiler.mjs.map +1 -1
  46. package/fesm2022/testing.mjs +1 -1
  47. package/index.d.ts +113 -15
  48. package/package.json +2 -2
  49. package/testing/index.d.ts +1 -1
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Angular v16.2.0-next.1
2
+ * @license Angular v16.2.0-next.2
3
3
  * (c) 2010-2022 Google LLC. https://angular.io/
4
4
  * License: MIT
5
5
  */
@@ -1339,6 +1339,10 @@ class InvokeFunctionExpr extends Expression {
1339
1339
  this.args = args;
1340
1340
  this.pure = pure;
1341
1341
  }
1342
+ // An alias for fn, which allows other logic to handle calls and property reads together.
1343
+ get receiver() {
1344
+ return this.fn;
1345
+ }
1342
1346
  isEquivalent(e) {
1343
1347
  return e instanceof InvokeFunctionExpr && this.fn.isEquivalent(e.fn) &&
1344
1348
  areAllEquivalent(this.args, e.args) && this.pure === e.pure;
@@ -1721,6 +1725,10 @@ class ReadPropExpr extends Expression {
1721
1725
  this.receiver = receiver;
1722
1726
  this.name = name;
1723
1727
  }
1728
+ // An alias for name, which allows other logic to handle property reads and keyed reads together.
1729
+ get index() {
1730
+ return this.name;
1731
+ }
1724
1732
  isEquivalent(e) {
1725
1733
  return e instanceof ReadPropExpr && this.receiver.isEquivalent(e.receiver) &&
1726
1734
  this.name === e.name;
@@ -5717,31 +5725,49 @@ var R3SelectorScopeMode;
5717
5725
  */
5718
5726
  R3SelectorScopeMode[R3SelectorScopeMode["Omit"] = 2] = "Omit";
5719
5727
  })(R3SelectorScopeMode || (R3SelectorScopeMode = {}));
5728
+ /**
5729
+ * The type of the NgModule meta data.
5730
+ * - Global: Used for full and partial compilation modes which mainly includes R3References.
5731
+ * - Local: Used for the local compilation mode which mainly includes the raw expressions as appears
5732
+ * in the NgModule decorator.
5733
+ */
5734
+ var R3NgModuleMetadataKind;
5735
+ (function (R3NgModuleMetadataKind) {
5736
+ R3NgModuleMetadataKind[R3NgModuleMetadataKind["Global"] = 0] = "Global";
5737
+ R3NgModuleMetadataKind[R3NgModuleMetadataKind["Local"] = 1] = "Local";
5738
+ })(R3NgModuleMetadataKind || (R3NgModuleMetadataKind = {}));
5720
5739
  /**
5721
5740
  * Construct an `R3NgModuleDef` for the given `R3NgModuleMetadata`.
5722
5741
  */
5723
5742
  function compileNgModule(meta) {
5724
- const { type: moduleType, bootstrap, declarations, imports, exports, schemas, containsForwardDecls, selectorScopeMode, id } = meta;
5725
5743
  const statements = [];
5726
5744
  const definitionMap = new DefinitionMap();
5727
- definitionMap.set('type', moduleType.value);
5728
- if (bootstrap.length > 0) {
5729
- definitionMap.set('bootstrap', refsToArray(bootstrap, containsForwardDecls));
5745
+ definitionMap.set('type', meta.type.value);
5746
+ // Assign bootstrap definition
5747
+ if (meta.kind === R3NgModuleMetadataKind.Global) {
5748
+ if (meta.bootstrap.length > 0) {
5749
+ definitionMap.set('bootstrap', refsToArray(meta.bootstrap, meta.containsForwardDecls));
5750
+ }
5730
5751
  }
5731
- if (selectorScopeMode === R3SelectorScopeMode.Inline) {
5752
+ else {
5753
+ if (meta.bootstrapExpression) {
5754
+ definitionMap.set('bootstrap', meta.bootstrapExpression);
5755
+ }
5756
+ }
5757
+ if (meta.selectorScopeMode === R3SelectorScopeMode.Inline) {
5732
5758
  // If requested to emit scope information inline, pass the `declarations`, `imports` and
5733
5759
  // `exports` to the `ɵɵdefineNgModule()` call directly.
5734
- if (declarations.length > 0) {
5735
- definitionMap.set('declarations', refsToArray(declarations, containsForwardDecls));
5760
+ if (meta.declarations.length > 0) {
5761
+ definitionMap.set('declarations', refsToArray(meta.declarations, meta.containsForwardDecls));
5736
5762
  }
5737
- if (imports.length > 0) {
5738
- definitionMap.set('imports', refsToArray(imports, containsForwardDecls));
5763
+ if (meta.imports.length > 0) {
5764
+ definitionMap.set('imports', refsToArray(meta.imports, meta.containsForwardDecls));
5739
5765
  }
5740
- if (exports.length > 0) {
5741
- definitionMap.set('exports', refsToArray(exports, containsForwardDecls));
5766
+ if (meta.exports.length > 0) {
5767
+ definitionMap.set('exports', refsToArray(meta.exports, meta.containsForwardDecls));
5742
5768
  }
5743
5769
  }
5744
- else if (selectorScopeMode === R3SelectorScopeMode.SideEffect) {
5770
+ else if (meta.selectorScopeMode === R3SelectorScopeMode.SideEffect) {
5745
5771
  // In this mode, scope information is not passed into `ɵɵdefineNgModule` as it
5746
5772
  // would prevent tree-shaking of the declarations, imports and exports references. Instead, it's
5747
5773
  // patched onto the NgModule definition with a `ɵɵsetNgModuleScope` call that's guarded by the
@@ -5754,14 +5780,14 @@ function compileNgModule(meta) {
5754
5780
  else {
5755
5781
  // Selector scope emit was not requested, so skip it.
5756
5782
  }
5757
- if (schemas !== null && schemas.length > 0) {
5758
- definitionMap.set('schemas', literalArr(schemas.map(ref => ref.value)));
5783
+ if (meta.schemas !== null && meta.schemas.length > 0) {
5784
+ definitionMap.set('schemas', literalArr(meta.schemas.map(ref => ref.value)));
5759
5785
  }
5760
- if (id !== null) {
5761
- definitionMap.set('id', id);
5786
+ if (meta.id !== null) {
5787
+ definitionMap.set('id', meta.id);
5762
5788
  // Generate a side-effectful call to register this NgModule by its id, as per the semantics of
5763
5789
  // NgModule ids.
5764
- statements.push(importExpr(Identifiers.registerNgModuleType).callFn([moduleType.value, id]).toStmt());
5790
+ statements.push(importExpr(Identifiers.registerNgModuleType).callFn([meta.type.value, meta.id]).toStmt());
5765
5791
  }
5766
5792
  const expression = importExpr(Identifiers.defineNgModule).callFn([definitionMap.toLiteralMap()], undefined, true);
5767
5793
  const type = createNgModuleType(meta);
@@ -5794,7 +5820,11 @@ function compileNgModuleDeclarationExpression(meta) {
5794
5820
  }
5795
5821
  return importExpr(Identifiers.defineNgModule).callFn([definitionMap.toLiteralMap()]);
5796
5822
  }
5797
- function createNgModuleType({ type: moduleType, declarations, exports, imports, includeImportTypes, publicDeclarationTypes }) {
5823
+ function createNgModuleType(meta) {
5824
+ if (meta.kind === R3NgModuleMetadataKind.Local) {
5825
+ return new ExpressionType(meta.type.value);
5826
+ }
5827
+ const { type: moduleType, declarations, exports, imports, includeImportTypes, publicDeclarationTypes } = meta;
5798
5828
  return new ExpressionType(importExpr(Identifiers.NgModuleDeclaration, [
5799
5829
  new ExpressionType(moduleType.type),
5800
5830
  publicDeclarationTypes === null ? tupleTypeOf(declarations) :
@@ -5810,16 +5840,36 @@ function createNgModuleType({ type: moduleType, declarations, exports, imports,
5810
5840
  * symbols to become tree-shakeable.
5811
5841
  */
5812
5842
  function generateSetNgModuleScopeCall(meta) {
5813
- const { type: moduleType, declarations, imports, exports, containsForwardDecls } = meta;
5814
5843
  const scopeMap = new DefinitionMap();
5815
- if (declarations.length > 0) {
5816
- scopeMap.set('declarations', refsToArray(declarations, containsForwardDecls));
5844
+ if (meta.kind === R3NgModuleMetadataKind.Global) {
5845
+ if (meta.declarations.length > 0) {
5846
+ scopeMap.set('declarations', refsToArray(meta.declarations, meta.containsForwardDecls));
5847
+ }
5848
+ }
5849
+ else {
5850
+ if (meta.declarationsExpression) {
5851
+ scopeMap.set('declarations', meta.declarationsExpression);
5852
+ }
5853
+ }
5854
+ if (meta.kind === R3NgModuleMetadataKind.Global) {
5855
+ if (meta.imports.length > 0) {
5856
+ scopeMap.set('imports', refsToArray(meta.imports, meta.containsForwardDecls));
5857
+ }
5817
5858
  }
5818
- if (imports.length > 0) {
5819
- scopeMap.set('imports', refsToArray(imports, containsForwardDecls));
5859
+ else {
5860
+ if (meta.importsExpression) {
5861
+ scopeMap.set('imports', meta.importsExpression);
5862
+ }
5863
+ }
5864
+ if (meta.kind === R3NgModuleMetadataKind.Global) {
5865
+ if (meta.exports.length > 0) {
5866
+ scopeMap.set('exports', refsToArray(meta.exports, meta.containsForwardDecls));
5867
+ }
5820
5868
  }
5821
- if (exports.length > 0) {
5822
- scopeMap.set('exports', refsToArray(exports, containsForwardDecls));
5869
+ else {
5870
+ if (meta.exportsExpression) {
5871
+ scopeMap.set('exports', meta.exportsExpression);
5872
+ }
5823
5873
  }
5824
5874
  if (Object.keys(scopeMap.values).length === 0) {
5825
5875
  return null;
@@ -5827,7 +5877,7 @@ function generateSetNgModuleScopeCall(meta) {
5827
5877
  // setNgModuleScope(...)
5828
5878
  const fnCall = new InvokeFunctionExpr(
5829
5879
  /* fn */ importExpr(Identifiers.setNgModuleScope),
5830
- /* args */ [moduleType.value, scopeMap.toLiteralMap()]);
5880
+ /* args */ [meta.type.value, scopeMap.toLiteralMap()]);
5831
5881
  // (ngJitMode guard) && setNgModuleScope(...)
5832
5882
  const guardedCall = jitOnlyGuardedExpression(fnCall);
5833
5883
  // function() { (ngJitMode guard) && setNgModuleScope(...); }
@@ -7946,7 +7996,7 @@ class ShadowCss {
7946
7996
  }
7947
7997
  else if (rule.selector.startsWith('@media') || rule.selector.startsWith('@supports') ||
7948
7998
  rule.selector.startsWith('@document') || rule.selector.startsWith('@layer') ||
7949
- rule.selector.startsWith('@container')) {
7999
+ rule.selector.startsWith('@container') || rule.selector.startsWith('@scope')) {
7950
8000
  content = this._scopeSelectors(rule.content, scopeSelector, hostSelector);
7951
8001
  }
7952
8002
  else if (rule.selector.startsWith('@font-face') || rule.selector.startsWith('@page')) {
@@ -8663,6 +8713,10 @@ var OpKind;
8663
8713
  * An operation to associate an attribute with an element.
8664
8714
  */
8665
8715
  OpKind[OpKind["Attribute"] = 21] = "Attribute";
8716
+ /**
8717
+ * An operation to interpolate text into an attribute binding.
8718
+ */
8719
+ OpKind[OpKind["InterpolateAttribute"] = 22] = "InterpolateAttribute";
8666
8720
  })(OpKind || (OpKind = {}));
8667
8721
  /**
8668
8722
  * Distinguishes different kinds of IR expressions.
@@ -8737,6 +8791,14 @@ var ExpressionKind;
8737
8791
  * An empty expression that will be stipped before generating the final output.
8738
8792
  */
8739
8793
  ExpressionKind[ExpressionKind["EmptyExpr"] = 16] = "EmptyExpr";
8794
+ /*
8795
+ * An assignment to a temporary variable.
8796
+ */
8797
+ ExpressionKind[ExpressionKind["AssignTemporaryExpr"] = 17] = "AssignTemporaryExpr";
8798
+ /**
8799
+ * A reference to a temporary variable.
8800
+ */
8801
+ ExpressionKind[ExpressionKind["ReadTemporaryExpr"] = 18] = "ReadTemporaryExpr";
8740
8802
  })(ExpressionKind || (ExpressionKind = {}));
8741
8803
  /**
8742
8804
  * Distinguishes between different kinds of `SemanticVariable`s.
@@ -9204,7 +9266,13 @@ class SafePropertyReadExpr extends ExpressionBase {
9204
9266
  this.name = name;
9205
9267
  this.kind = ExpressionKind.SafePropertyRead;
9206
9268
  }
9207
- visitExpression(visitor, context) { }
9269
+ // An alias for name, which allows other logic to handle property reads and keyed reads together.
9270
+ get index() {
9271
+ return this.name;
9272
+ }
9273
+ visitExpression(visitor, context) {
9274
+ this.receiver.visitExpression(visitor, context);
9275
+ }
9208
9276
  isEquivalent() {
9209
9277
  return false;
9210
9278
  }
@@ -9225,7 +9293,10 @@ class SafeKeyedReadExpr extends ExpressionBase {
9225
9293
  this.index = index;
9226
9294
  this.kind = ExpressionKind.SafeKeyedRead;
9227
9295
  }
9228
- visitExpression(visitor, context) { }
9296
+ visitExpression(visitor, context) {
9297
+ this.receiver.visitExpression(visitor, context);
9298
+ this.index.visitExpression(visitor, context);
9299
+ }
9229
9300
  isEquivalent() {
9230
9301
  return false;
9231
9302
  }
@@ -9247,7 +9318,12 @@ class SafeInvokeFunctionExpr extends ExpressionBase {
9247
9318
  this.args = args;
9248
9319
  this.kind = ExpressionKind.SafeInvokeFunction;
9249
9320
  }
9250
- visitExpression(visitor, context) { }
9321
+ visitExpression(visitor, context) {
9322
+ this.receiver.visitExpression(visitor, context);
9323
+ for (const a of this.args) {
9324
+ a.visitExpression(visitor, context);
9325
+ }
9326
+ }
9251
9327
  isEquivalent() {
9252
9328
  return false;
9253
9329
  }
@@ -9271,7 +9347,10 @@ class SafeTernaryExpr extends ExpressionBase {
9271
9347
  this.expr = expr;
9272
9348
  this.kind = ExpressionKind.SafeTernaryExpr;
9273
9349
  }
9274
- visitExpression(visitor, context) { }
9350
+ visitExpression(visitor, context) {
9351
+ this.guard.visitExpression(visitor, context);
9352
+ this.expr.visitExpression(visitor, context);
9353
+ }
9275
9354
  isEquivalent() {
9276
9355
  return false;
9277
9356
  }
@@ -9303,6 +9382,53 @@ class EmptyExpr extends ExpressionBase {
9303
9382
  }
9304
9383
  transformInternalExpressions() { }
9305
9384
  }
9385
+ class AssignTemporaryExpr extends ExpressionBase {
9386
+ constructor(expr, xref) {
9387
+ super();
9388
+ this.expr = expr;
9389
+ this.xref = xref;
9390
+ this.kind = ExpressionKind.AssignTemporaryExpr;
9391
+ this.name = null;
9392
+ }
9393
+ visitExpression(visitor, context) {
9394
+ this.expr.visitExpression(visitor, context);
9395
+ }
9396
+ isEquivalent() {
9397
+ return false;
9398
+ }
9399
+ isConstant() {
9400
+ return false;
9401
+ }
9402
+ transformInternalExpressions(transform, flags) {
9403
+ this.expr = transformExpressionsInExpression(this.expr, transform, flags);
9404
+ }
9405
+ clone() {
9406
+ const a = new AssignTemporaryExpr(this.expr, this.xref);
9407
+ a.name = this.name;
9408
+ return a;
9409
+ }
9410
+ }
9411
+ class ReadTemporaryExpr extends ExpressionBase {
9412
+ constructor(xref) {
9413
+ super();
9414
+ this.xref = xref;
9415
+ this.kind = ExpressionKind.ReadTemporaryExpr;
9416
+ this.name = null;
9417
+ }
9418
+ visitExpression(visitor, context) { }
9419
+ isEquivalent() {
9420
+ return this.xref === this.xref;
9421
+ }
9422
+ isConstant() {
9423
+ return false;
9424
+ }
9425
+ transformInternalExpressions(transform, flags) { }
9426
+ clone() {
9427
+ const r = new ReadTemporaryExpr(this.xref);
9428
+ r.name = this.name;
9429
+ return r;
9430
+ }
9431
+ }
9306
9432
  /**
9307
9433
  * Visits all `Expression`s in the AST of `op` with the `visitor` function.
9308
9434
  */
@@ -9343,7 +9469,12 @@ function transformExpressionsInOp(op, transform, flags) {
9343
9469
  break;
9344
9470
  case OpKind.Attribute:
9345
9471
  if (op.value) {
9346
- transformExpressionsInExpression(op.value, transform, flags);
9472
+ op.value = transformExpressionsInExpression(op.value, transform, flags);
9473
+ }
9474
+ break;
9475
+ case OpKind.InterpolateAttribute:
9476
+ for (let i = 0; i < op.expressions.length; i++) {
9477
+ op.expressions[i] = transformExpressionsInExpression(op.expressions[i], transform, flags);
9347
9478
  }
9348
9479
  break;
9349
9480
  case OpKind.Variable:
@@ -9446,6 +9577,11 @@ function transformExpressionsInStatement(stmt, transform, flags) {
9446
9577
  else if (stmt instanceof ReturnStatement) {
9447
9578
  stmt.value = transformExpressionsInExpression(stmt.value, transform, flags);
9448
9579
  }
9580
+ else if (stmt instanceof DeclareVarStmt) {
9581
+ if (stmt.value !== undefined) {
9582
+ stmt.value = transformExpressionsInExpression(stmt.value, transform, flags);
9583
+ }
9584
+ }
9449
9585
  else {
9450
9586
  throw new Error(`Unhandled statement kind: ${stmt.constructor.name}`);
9451
9587
  }
@@ -9862,6 +9998,8 @@ function createAttributeOp(target, attributeKind, name, value) {
9862
9998
  attributeKind,
9863
9999
  name,
9864
10000
  value,
10001
+ ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
10002
+ ...TRAIT_CONSUMES_VARS,
9865
10003
  ...NEW_OP,
9866
10004
  };
9867
10005
  }
@@ -9881,6 +10019,19 @@ function createInterpolatePropertyOp(xref, bindingKind, name, strings, expressio
9881
10019
  ...NEW_OP,
9882
10020
  };
9883
10021
  }
10022
+ function createInterpolateAttributeOp(target, attributeKind, name, strings, expressions) {
10023
+ return {
10024
+ kind: OpKind.InterpolateAttribute,
10025
+ target: target,
10026
+ attributeKind,
10027
+ name,
10028
+ strings,
10029
+ expressions,
10030
+ ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
10031
+ ...TRAIT_CONSUMES_VARS,
10032
+ ...NEW_OP,
10033
+ };
10034
+ }
9884
10035
  /**
9885
10036
  * Create a `InterpolateStyleProp`.
9886
10037
  */
@@ -9972,6 +10123,9 @@ function varsUsedByOp(op) {
9972
10123
  case OpKind.StyleMap:
9973
10124
  // Property bindings use 1 variable slot.
9974
10125
  return 1;
10126
+ case OpKind.Attribute:
10127
+ // Attribute bindings use 1 variable slot.
10128
+ return 1;
9975
10129
  case OpKind.InterpolateText:
9976
10130
  // `ir.InterpolateTextOp`s use a variable slot for each dynamic expression.
9977
10131
  return op.expressions.length;
@@ -9981,6 +10135,9 @@ function varsUsedByOp(op) {
9981
10135
  // `ir.InterpolatePropertyOp`s use a variable slot for each dynamic expression, plus one for
9982
10136
  // the result.
9983
10137
  return 1 + op.expressions.length;
10138
+ case OpKind.InterpolateAttribute:
10139
+ // One variable slot for each dynamic expression, plus one for the result.
10140
+ return 1 + op.expressions.length;
9984
10141
  default:
9985
10142
  throw new Error(`Unhandled op: ${OpKind[op.kind]}`);
9986
10143
  }
@@ -10126,6 +10283,7 @@ const CHAINABLE = new Set([
10126
10283
  Identifiers.elementEnd,
10127
10284
  Identifiers.property,
10128
10285
  Identifiers.styleProp,
10286
+ Identifiers.attribute,
10129
10287
  Identifiers.elementContainerStart,
10130
10288
  Identifiers.elementContainerEnd,
10131
10289
  Identifiers.elementContainer,
@@ -10323,10 +10481,11 @@ function phaseNullishCoalescing(cpl) {
10323
10481
  expr.operator !== BinaryOperator.NullishCoalesce) {
10324
10482
  return expr;
10325
10483
  }
10326
- // TODO: We need to unconditionally emit a temporary variable to match
10327
- // TemplateDefinitionBuilder. (We could also emit one conditionally when not in
10328
- // compatibility mode.)
10329
- return new ConditionalExpr(new BinaryOperatorExpr(BinaryOperator.And, new BinaryOperatorExpr(BinaryOperator.NotIdentical, expr.lhs, NULL_EXPR), new BinaryOperatorExpr(BinaryOperator.NotIdentical, expr.lhs, new LiteralExpr(undefined))), expr.lhs, expr.rhs);
10484
+ const assignment = new AssignTemporaryExpr(expr.lhs.clone(), cpl.allocateXrefId());
10485
+ const read = new ReadTemporaryExpr(assignment.xref);
10486
+ // TODO: When not in compatibility mode for TemplateDefinitionBuilder, we can just emit
10487
+ // `t != null` instead of including an undefined check as well.
10488
+ return new ConditionalExpr(new BinaryOperatorExpr(BinaryOperator.And, new BinaryOperatorExpr(BinaryOperator.NotIdentical, assignment, NULL_EXPR), new BinaryOperatorExpr(BinaryOperator.NotIdentical, read, new LiteralExpr(undefined))), read.clone(), expr.rhs);
10330
10489
  }, VisitorContextFlag.None);
10331
10490
  }
10332
10491
  }
@@ -10359,11 +10518,6 @@ function phaseGenerateVariables(cpl) {
10359
10518
  function recursivelyProcessView(view, parentScope) {
10360
10519
  // Extract a `Scope` from this view.
10361
10520
  const scope = getScopeForView(view, parentScope);
10362
- // Embedded views require an operation to save/restore the view context.
10363
- if (view.parent !== null) {
10364
- // Start the view creation block with an operation to save the current view context. This may be
10365
- // used to restore the view context in any listeners that may be present.
10366
- }
10367
10521
  for (const op of view.create) {
10368
10522
  switch (op.kind) {
10369
10523
  case OpKind.Template:
@@ -10902,6 +11056,9 @@ function property(name, expression) {
10902
11056
  expression,
10903
11057
  ]);
10904
11058
  }
11059
+ function attribute(name, expression) {
11060
+ return call(Identifiers.attribute, [literal(name), expression]);
11061
+ }
10905
11062
  function styleProp(name, expression, unit) {
10906
11063
  const args = [literal(name), expression];
10907
11064
  if (unit !== null) {
@@ -10958,6 +11115,10 @@ function propertyInterpolate(name, strings, expressions) {
10958
11115
  const interpolationArgs = collateInterpolationArgs(strings, expressions);
10959
11116
  return callVariadicInstruction(PROPERTY_INTERPOLATE_CONFIG, [literal(name)], interpolationArgs);
10960
11117
  }
11118
+ function attributeInterpolate(name, strings, expressions) {
11119
+ const interpolationArgs = collateInterpolationArgs(strings, expressions);
11120
+ return callVariadicInstruction(ATTRIBUTE_INTERPOLATE_CONFIG, [literal(name)], interpolationArgs);
11121
+ }
10961
11122
  function stylePropInterpolate(name, strings, expressions, unit) {
10962
11123
  const interpolationArgs = collateInterpolationArgs(strings, expressions);
10963
11124
  const extraArgs = [];
@@ -11072,6 +11233,29 @@ const STYLE_PROP_INTERPOLATE_CONFIG = {
11072
11233
  return (n - 1) / 2;
11073
11234
  },
11074
11235
  };
11236
+ /**
11237
+ * `InterpolationConfig` for the `attributeInterpolate` instruction.
11238
+ */
11239
+ const ATTRIBUTE_INTERPOLATE_CONFIG = {
11240
+ constant: [
11241
+ Identifiers.attribute,
11242
+ Identifiers.attributeInterpolate1,
11243
+ Identifiers.attributeInterpolate2,
11244
+ Identifiers.attributeInterpolate3,
11245
+ Identifiers.attributeInterpolate4,
11246
+ Identifiers.attributeInterpolate5,
11247
+ Identifiers.attributeInterpolate6,
11248
+ Identifiers.attributeInterpolate7,
11249
+ Identifiers.attributeInterpolate8,
11250
+ ],
11251
+ variable: Identifiers.attributeInterpolateV,
11252
+ mapping: n => {
11253
+ if (n % 2 === 0) {
11254
+ throw new Error(`Expected odd number of arguments`);
11255
+ }
11256
+ return (n - 1) / 2;
11257
+ },
11258
+ };
11075
11259
  /**
11076
11260
  * `InterpolationConfig` for the `styleMapInterpolate` instruction.
11077
11261
  */
@@ -11227,6 +11411,12 @@ function reifyUpdateOperations(_view, ops) {
11227
11411
  case OpKind.InterpolateText:
11228
11412
  OpList.replace(op, textInterpolate(op.strings, op.expressions));
11229
11413
  break;
11414
+ case OpKind.Attribute:
11415
+ OpList.replace(op, attribute(op.name, op.value));
11416
+ break;
11417
+ case OpKind.InterpolateAttribute:
11418
+ OpList.replace(op, attributeInterpolate(op.name, op.strings, op.expressions));
11419
+ break;
11230
11420
  case OpKind.Variable:
11231
11421
  if (op.variable.name === null) {
11232
11422
  throw new Error(`AssertionError: unnamed variable ${op.xref}`);
@@ -11266,6 +11456,16 @@ function reifyIrExpression(expr) {
11266
11456
  throw new Error(`Read of unnamed variable ${expr.xref}`);
11267
11457
  }
11268
11458
  return variable(expr.name);
11459
+ case ExpressionKind.ReadTemporaryExpr:
11460
+ if (expr.name === null) {
11461
+ throw new Error(`Read of unnamed temporary ${expr.xref}`);
11462
+ }
11463
+ return variable(expr.name);
11464
+ case ExpressionKind.AssignTemporaryExpr:
11465
+ if (expr.name === null) {
11466
+ throw new Error(`Assign of unnamed temporary ${expr.xref}`);
11467
+ }
11468
+ return variable(expr.name).set(expr.expr);
11269
11469
  case ExpressionKind.PureFunctionExpr:
11270
11470
  if (expr.fn === null) {
11271
11471
  throw new Error(`AssertionError: expected PureFunctions to have been extracted`);
@@ -11453,14 +11653,17 @@ function processLexicalScope(view, ops, savedView) {
11453
11653
  }
11454
11654
  }, VisitorContextFlag.None);
11455
11655
  }
11656
+ for (const op of ops) {
11657
+ visitExpressionsInOp(op, expr => {
11658
+ if (expr instanceof LexicalReadExpr) {
11659
+ throw new Error(`AssertionError: no lexical reads should remain, but found read of ${expr.name}`);
11660
+ }
11661
+ });
11662
+ }
11456
11663
  }
11457
11664
 
11458
11665
  function phaseSaveRestoreView(cpl) {
11459
11666
  for (const view of cpl.views.values()) {
11460
- if (view === cpl.root) {
11461
- // Save/restore operations are not necessary for the root view.
11462
- continue;
11463
- }
11464
11667
  view.create.prepend([
11465
11668
  createVariableOp(view.tpl.allocateXrefId(), {
11466
11669
  kind: SemanticVariableKind.SavedView,
@@ -11472,22 +11675,39 @@ function phaseSaveRestoreView(cpl) {
11472
11675
  if (op.kind !== OpKind.Listener) {
11473
11676
  continue;
11474
11677
  }
11475
- op.handlerOps.prepend([
11476
- createVariableOp(view.tpl.allocateXrefId(), {
11477
- kind: SemanticVariableKind.Context,
11478
- name: null,
11479
- view: view.xref,
11480
- }, new RestoreViewExpr(view.xref)),
11481
- ]);
11482
- // The "restore view" operation in listeners requires a call to `resetView` to reset the
11483
- // context prior to returning from the listener operation. Find any `return` statements in
11484
- // the listener body and wrap them in a call to reset the view.
11485
- for (const handlerOp of op.handlerOps) {
11486
- if (handlerOp.kind === OpKind.Statement &&
11487
- handlerOp.statement instanceof ReturnStatement) {
11488
- handlerOp.statement.value = new ResetViewExpr(handlerOp.statement.value);
11678
+ // Embedded views always need the save/restore view operation.
11679
+ let needsRestoreView = view !== cpl.root;
11680
+ if (!needsRestoreView) {
11681
+ for (const handlerOp of op.handlerOps) {
11682
+ visitExpressionsInOp(handlerOp, expr => {
11683
+ if (expr instanceof ReferenceExpr) {
11684
+ // Listeners that reference() a local ref need the save/restore view operation.
11685
+ needsRestoreView = true;
11686
+ }
11687
+ });
11489
11688
  }
11490
11689
  }
11690
+ if (needsRestoreView) {
11691
+ addSaveRestoreViewOperationToListener(view, op);
11692
+ }
11693
+ }
11694
+ }
11695
+ }
11696
+ function addSaveRestoreViewOperationToListener(view, op) {
11697
+ op.handlerOps.prepend([
11698
+ createVariableOp(view.tpl.allocateXrefId(), {
11699
+ kind: SemanticVariableKind.Context,
11700
+ name: null,
11701
+ view: view.xref,
11702
+ }, new RestoreViewExpr(view.xref)),
11703
+ ]);
11704
+ // The "restore view" operation in listeners requires a call to `resetView` to reset the
11705
+ // context prior to returning from the listener operation. Find any `return` statements in
11706
+ // the listener body and wrap them in a call to reset the view.
11707
+ for (const handlerOp of op.handlerOps) {
11708
+ if (handlerOp.kind === OpKind.Statement &&
11709
+ handlerOp.statement instanceof ReturnStatement) {
11710
+ handlerOp.statement.value = new ResetViewExpr(handlerOp.statement.value);
11491
11711
  }
11492
11712
  }
11493
11713
  }
@@ -11936,19 +12156,107 @@ function allowConservativeInlining(decl, target) {
11936
12156
  * Finds all unresolved safe read expressions, and converts them into the appropriate output AST
11937
12157
  * reads, guarded by null checks.
11938
12158
  */
11939
- function phaseExpandSafeReads(cpl) {
12159
+ function phaseExpandSafeReads(cpl, compatibility) {
11940
12160
  for (const [_, view] of cpl.views) {
11941
12161
  for (const op of view.ops()) {
11942
- transformExpressionsInOp(op, safeTransform, VisitorContextFlag.None);
12162
+ transformExpressionsInOp(op, e => safeTransform(e, { cpl, compatibility }), VisitorContextFlag.None);
11943
12163
  transformExpressionsInOp(op, ternaryTransform, VisitorContextFlag.None);
11944
12164
  }
11945
12165
  }
11946
12166
  }
12167
+ // A lookup set of all the expression kinds that require a temporary variable to be generated.
12168
+ const requiresTemporary = [
12169
+ InvokeFunctionExpr, LiteralArrayExpr, LiteralMapExpr, SafeInvokeFunctionExpr,
12170
+ PipeBindingExpr
12171
+ ].map(e => e.constructor.name);
12172
+ function needsTemporaryInSafeAccess(e) {
12173
+ // TODO: We probably want to use an expression visitor to recursively visit all descendents.
12174
+ // However, that would potentially do a lot of extra work (because it cannot short circuit), so we
12175
+ // implement the logic ourselves for now.
12176
+ if (e instanceof UnaryOperatorExpr) {
12177
+ return needsTemporaryInSafeAccess(e.expr);
12178
+ }
12179
+ else if (e instanceof BinaryOperatorExpr) {
12180
+ return needsTemporaryInSafeAccess(e.lhs) || needsTemporaryInSafeAccess(e.rhs);
12181
+ }
12182
+ else if (e instanceof ConditionalExpr) {
12183
+ if (e.falseCase && needsTemporaryInSafeAccess(e.falseCase))
12184
+ return true;
12185
+ return needsTemporaryInSafeAccess(e.condition) || needsTemporaryInSafeAccess(e.trueCase);
12186
+ }
12187
+ else if (e instanceof NotExpr) {
12188
+ return needsTemporaryInSafeAccess(e.condition);
12189
+ }
12190
+ else if (e instanceof AssignTemporaryExpr) {
12191
+ return needsTemporaryInSafeAccess(e.expr);
12192
+ }
12193
+ else if (e instanceof ReadPropExpr) {
12194
+ return needsTemporaryInSafeAccess(e.receiver);
12195
+ }
12196
+ else if (e instanceof ReadKeyExpr) {
12197
+ return needsTemporaryInSafeAccess(e.receiver) || needsTemporaryInSafeAccess(e.index);
12198
+ }
12199
+ // TODO: Switch to a method which is exhaustive of newly added expression subtypes.
12200
+ return e instanceof InvokeFunctionExpr || e instanceof LiteralArrayExpr ||
12201
+ e instanceof LiteralMapExpr || e instanceof SafeInvokeFunctionExpr ||
12202
+ e instanceof PipeBindingExpr;
12203
+ }
12204
+ function temporariesIn(e) {
12205
+ const temporaries = new Set();
12206
+ // TODO: Although it's not currently supported by the transform helper, we should be able to
12207
+ // short-circuit exploring the tree to do less work. In particular, we don't have to penetrate
12208
+ // into the subexpressions of temporary assignments.
12209
+ transformExpressionsInExpression(e, e => {
12210
+ if (e instanceof AssignTemporaryExpr) {
12211
+ temporaries.add(e.xref);
12212
+ }
12213
+ return e;
12214
+ }, VisitorContextFlag.None);
12215
+ return temporaries;
12216
+ }
12217
+ function eliminateTemporaryAssignments(e, tmps, ctx) {
12218
+ // TODO: We can be more efficient than the transform helper here. We don't need to visit any
12219
+ // descendents of temporary assignments.
12220
+ transformExpressionsInExpression(e, e => {
12221
+ if (e instanceof AssignTemporaryExpr && tmps.has(e.xref)) {
12222
+ const read = new ReadTemporaryExpr(e.xref);
12223
+ // `TemplateDefinitionBuilder` has the (accidental?) behavior of generating assignments of
12224
+ // temporary variables to themselves. This happens because some subexpression that the
12225
+ // temporary refers to, possibly through nested temporaries, has a function call. We copy that
12226
+ // behavior here.
12227
+ return ctx.compatibility ? new AssignTemporaryExpr(read, read.xref) : read;
12228
+ }
12229
+ return e;
12230
+ }, VisitorContextFlag.None);
12231
+ return e;
12232
+ }
12233
+ /**
12234
+ * Creates a safe ternary guarded by the input expression, and with a body generated by the provided
12235
+ * callback on the input expression. Generates a temporary variable assignment if needed, and
12236
+ * deduplicates nested temporary assignments if needed.
12237
+ */
12238
+ function safeTernaryWithTemporary(guard, body, ctx) {
12239
+ let result;
12240
+ if (needsTemporaryInSafeAccess(guard)) {
12241
+ const xref = ctx.cpl.allocateXrefId();
12242
+ result = [new AssignTemporaryExpr(guard, xref), new ReadTemporaryExpr(xref)];
12243
+ }
12244
+ else {
12245
+ result = [guard, guard.clone()];
12246
+ // Consider an expression like `a?.[b?.c()]?.d`. The `b?.c()` will be transformed first,
12247
+ // introducing a temporary assignment into the key. Then, as part of expanding the `?.d`. That
12248
+ // assignment will be duplicated into both the guard and expression sides. We de-duplicate it,
12249
+ // by transforming it from an assignment into a read on the expression side.
12250
+ eliminateTemporaryAssignments(result[1], temporariesIn(result[0]), ctx);
12251
+ }
12252
+ return new SafeTernaryExpr(result[0], body(result[1]));
12253
+ }
11947
12254
  function isSafeAccessExpression(e) {
11948
12255
  return e instanceof SafePropertyReadExpr || e instanceof SafeKeyedReadExpr;
11949
12256
  }
11950
12257
  function isUnsafeAccessExpression(e) {
11951
- return e instanceof ReadPropExpr || e instanceof ReadKeyExpr;
12258
+ return e instanceof ReadPropExpr || e instanceof ReadKeyExpr ||
12259
+ e instanceof InvokeFunctionExpr;
11952
12260
  }
11953
12261
  function isAccessExpression(e) {
11954
12262
  return isSafeAccessExpression(e) || isUnsafeAccessExpression(e);
@@ -11964,8 +12272,8 @@ function deepestSafeTernary(e) {
11964
12272
  return null;
11965
12273
  }
11966
12274
  // TODO: When strict compatibility with TemplateDefinitionBuilder is not required, we can use `&&`
11967
- // instead.
11968
- function safeTransform(e) {
12275
+ // instead to save some code size.
12276
+ function safeTransform(e, ctx) {
11969
12277
  if (e instanceof SafeInvokeFunctionExpr) {
11970
12278
  // TODO: Implement safe function calls in a subsequent commit.
11971
12279
  return new InvokeFunctionExpr(e.receiver, e.args);
@@ -11975,6 +12283,10 @@ function safeTransform(e) {
11975
12283
  }
11976
12284
  const dst = deepestSafeTernary(e);
11977
12285
  if (dst) {
12286
+ if (e instanceof InvokeFunctionExpr) {
12287
+ dst.expr = dst.expr.callFn(e.args);
12288
+ return e.receiver;
12289
+ }
11978
12290
  if (e instanceof ReadPropExpr) {
11979
12291
  dst.expr = dst.expr.prop(e.name);
11980
12292
  return e.receiver;
@@ -11984,20 +12296,20 @@ function safeTransform(e) {
11984
12296
  return e.receiver;
11985
12297
  }
11986
12298
  if (e instanceof SafePropertyReadExpr) {
11987
- dst.expr = new SafeTernaryExpr(dst.expr.clone(), dst.expr.prop(e.name));
12299
+ dst.expr = safeTernaryWithTemporary(dst.expr, (r) => r.prop(e.name), ctx);
11988
12300
  return e.receiver;
11989
12301
  }
11990
12302
  if (e instanceof SafeKeyedReadExpr) {
11991
- dst.expr = new SafeTernaryExpr(dst.expr.clone(), dst.expr.key(e.index));
12303
+ dst.expr = safeTernaryWithTemporary(dst.expr, (r) => r.key(e.index), ctx);
11992
12304
  return e.receiver;
11993
12305
  }
11994
12306
  }
11995
12307
  else {
11996
12308
  if (e instanceof SafePropertyReadExpr) {
11997
- return new SafeTernaryExpr(e.receiver.clone(), e.receiver.prop(e.name));
12309
+ return safeTernaryWithTemporary(e.receiver, (r) => r.prop(e.name), ctx);
11998
12310
  }
11999
12311
  if (e instanceof SafeKeyedReadExpr) {
12000
- return new SafeTernaryExpr(e.receiver.clone(), e.receiver.key(e.index));
12312
+ return safeTernaryWithTemporary(e.receiver, (r) => r.key(e.index), ctx);
12001
12313
  }
12002
12314
  }
12003
12315
  return e;
@@ -12009,6 +12321,50 @@ function ternaryTransform(e) {
12009
12321
  return new ConditionalExpr(new BinaryOperatorExpr(BinaryOperator.Equals, e.guard, NULL_EXPR), NULL_EXPR, e.expr);
12010
12322
  }
12011
12323
 
12324
+ /**
12325
+ * Find all assignments and usages of temporary variables, which are linked to each other with cross
12326
+ * references. Generate names for each cross-reference, and add a `DeclareVarStmt` to initialize
12327
+ * them at the beginning of the update block.
12328
+ *
12329
+ * TODO: Sometimes, it will be possible to reuse names across different subexpressions. For example,
12330
+ * in the double keyed read `a?.[f()]?.[f()]`, the two function calls have non-overlapping scopes.
12331
+ * Implement an algorithm for reuse.
12332
+ */
12333
+ function phaseTemporaryVariables(cpl) {
12334
+ for (const view of cpl.views.values()) {
12335
+ let opCount = 0;
12336
+ let generatedStatements = [];
12337
+ for (const op of view.ops()) {
12338
+ let count = 0;
12339
+ let xrefs = new Set();
12340
+ let defs = new Map();
12341
+ visitExpressionsInOp(op, expr => {
12342
+ if (expr instanceof ReadTemporaryExpr || expr instanceof AssignTemporaryExpr) {
12343
+ xrefs.add(expr.xref);
12344
+ }
12345
+ });
12346
+ for (const xref of xrefs) {
12347
+ // TODO: Exactly replicate the naming scheme used by `TemplateDefinitionBuilder`. It seems
12348
+ // to rely on an expression index instead of an op index.
12349
+ defs.set(xref, `tmp_${opCount}_${count++}`);
12350
+ }
12351
+ visitExpressionsInOp(op, expr => {
12352
+ if (expr instanceof ReadTemporaryExpr || expr instanceof AssignTemporaryExpr) {
12353
+ const name = defs.get(expr.xref);
12354
+ if (name === undefined) {
12355
+ throw new Error('Found xref with unassigned name');
12356
+ }
12357
+ expr.name = name;
12358
+ }
12359
+ });
12360
+ generatedStatements.push(...Array.from(defs.values())
12361
+ .map(name => createStatementOp(new DeclareVarStmt(name))));
12362
+ opCount++;
12363
+ }
12364
+ view.update.prepend(generatedStatements);
12365
+ }
12366
+ }
12367
+
12012
12368
  /**
12013
12369
  * Run all transformation phases in the correct order against a `ComponentCompilation`. After this
12014
12370
  * processing, the compilation should be in a state where it can be emitted via `emitTemplateFn`.s
@@ -12025,7 +12381,8 @@ function transformTemplate(cpl) {
12025
12381
  phaseLocalRefs(cpl);
12026
12382
  phaseConstCollection(cpl);
12027
12383
  phaseNullishCoalescing(cpl);
12028
- phaseExpandSafeReads(cpl);
12384
+ phaseExpandSafeReads(cpl, true);
12385
+ phaseTemporaryVariables(cpl);
12029
12386
  phaseSlotAllocation(cpl);
12030
12387
  phaseVarCounting(cpl);
12031
12388
  phaseGenerateAdvance(cpl);
@@ -12422,6 +12779,9 @@ function ingestBindings(view, op, element) {
12422
12779
  }
12423
12780
  }
12424
12781
  for (const attr of element.attributes) {
12782
+ // This is only attribute TextLiteral bindings, such as `attr.foo="bar'`. This can never be
12783
+ // `[attr.foo]="bar"` or `attr.foo="{{bar}}"`, both of which will be handled as inputs with
12784
+ // `BindingType.Attribute`.
12425
12785
  view.update.push(createAttributeOp(op.xref, ElementAttributeKind.Attribute, attr.name, literal(attr.value)));
12426
12786
  }
12427
12787
  for (const input of element.inputs) {
@@ -12478,6 +12838,13 @@ function ingestPropertyBinding(view, xref, bindingKind, { name, value, type, uni
12478
12838
  }
12479
12839
  view.update.push(createInterpolateStylePropOp(xref, name, value.strings, value.expressions.map(expr => convertAst(expr, view.tpl)), unit));
12480
12840
  break;
12841
+ case 1 /* e.BindingType.Attribute */:
12842
+ if (bindingKind !== ElementAttributeKind.Binding) {
12843
+ throw new Error('Attribute bindings on templates are not expected to be valid');
12844
+ }
12845
+ const attributeInterpolate = createInterpolateAttributeOp(xref, bindingKind, name, value.strings, value.expressions.map(expr => convertAst(expr, view.tpl)));
12846
+ view.update.push(attributeInterpolate);
12847
+ break;
12481
12848
  default:
12482
12849
  // TODO: implement remaining binding types.
12483
12850
  throw Error(`Interpolated property binding type not handled: ${type}`);
@@ -12503,6 +12870,13 @@ function ingestPropertyBinding(view, xref, bindingKind, { name, value, type, uni
12503
12870
  }
12504
12871
  view.update.push(createStylePropOp(xref, name, convertAst(value, view.tpl), unit));
12505
12872
  break;
12873
+ case 1 /* e.BindingType.Attribute */:
12874
+ if (bindingKind !== ElementAttributeKind.Binding) {
12875
+ throw new Error('Attribute bindings on templates are not expected to be valid');
12876
+ }
12877
+ const attrOp = createAttributeOp(xref, bindingKind, name, convertAst(value, view.tpl));
12878
+ view.update.push(attrOp);
12879
+ break;
12506
12880
  default:
12507
12881
  // TODO: implement remaining binding types.
12508
12882
  throw Error(`Property binding type not handled: ${type}`);
@@ -14851,6 +15225,39 @@ class Comment {
14851
15225
  return visitor.visitComment(this, context);
14852
15226
  }
14853
15227
  }
15228
+ class BlockGroup {
15229
+ constructor(blocks, sourceSpan, startSourceSpan, endSourceSpan = null) {
15230
+ this.blocks = blocks;
15231
+ this.sourceSpan = sourceSpan;
15232
+ this.startSourceSpan = startSourceSpan;
15233
+ this.endSourceSpan = endSourceSpan;
15234
+ }
15235
+ visit(visitor, context) {
15236
+ return visitor.visitBlockGroup(this, context);
15237
+ }
15238
+ }
15239
+ class Block {
15240
+ constructor(name, parameters, children, sourceSpan, startSourceSpan, endSourceSpan = null) {
15241
+ this.name = name;
15242
+ this.parameters = parameters;
15243
+ this.children = children;
15244
+ this.sourceSpan = sourceSpan;
15245
+ this.startSourceSpan = startSourceSpan;
15246
+ this.endSourceSpan = endSourceSpan;
15247
+ }
15248
+ visit(visitor, context) {
15249
+ return visitor.visitBlock(this, context);
15250
+ }
15251
+ }
15252
+ class BlockParameter {
15253
+ constructor(expression, sourceSpan) {
15254
+ this.expression = expression;
15255
+ this.sourceSpan = sourceSpan;
15256
+ }
15257
+ visit(visitor, context) {
15258
+ return visitor.visitBlockParameter(this, context);
15259
+ }
15260
+ }
14854
15261
  function visitAll(visitor, nodes, context = null) {
14855
15262
  const result = [];
14856
15263
  const visit = visitor.visit ?
@@ -14881,6 +15288,18 @@ class RecursiveVisitor {
14881
15288
  });
14882
15289
  }
14883
15290
  visitExpansionCase(ast, context) { }
15291
+ visitBlockGroup(ast, context) {
15292
+ this.visitChildren(context, visit => {
15293
+ visit(ast.blocks);
15294
+ });
15295
+ }
15296
+ visitBlock(block, context) {
15297
+ this.visitChildren(context, visit => {
15298
+ visit(block.parameters);
15299
+ visit(block.children);
15300
+ });
15301
+ }
15302
+ visitBlockParameter(ast, context) { }
14884
15303
  visitChildren(context, cb) {
14885
15304
  let results = [];
14886
15305
  let t = this;
@@ -17633,8 +18052,8 @@ class _Tokenizer {
17633
18052
  this._cursor = options.escapedString ? new EscapedCharacterCursor(_file, range) :
17634
18053
  new PlainCharacterCursor(_file, range);
17635
18054
  this._preserveLineEndings = options.preserveLineEndings || false;
17636
- this._escapedString = options.escapedString || false;
17637
18055
  this._i18nNormalizeLineEndingsInICUs = options.i18nNormalizeLineEndingsInICUs || false;
18056
+ this._tokenizeBlocks = options.tokenizeBlocks || false;
17638
18057
  try {
17639
18058
  this._cursor.init();
17640
18059
  }
@@ -17675,6 +18094,15 @@ class _Tokenizer {
17675
18094
  this._consumeTagOpen(start);
17676
18095
  }
17677
18096
  }
18097
+ else if (this._tokenizeBlocks && this._attemptStr('{#')) {
18098
+ this._consumeBlockGroupOpen(start);
18099
+ }
18100
+ else if (this._tokenizeBlocks && this._attemptStr('{/')) {
18101
+ this._consumeBlockGroupClose(start);
18102
+ }
18103
+ else if (this._tokenizeBlocks && this._attemptStr('{:')) {
18104
+ this._consumeBlock(start);
18105
+ }
17678
18106
  else if (!(this._tokenizeIcu && this._tokenizeExpansionForm())) {
17679
18107
  // In (possibly interpolated) text the end of the text is given by `isTextEnd()`, while
17680
18108
  // the premature end of an interpolation is given by the start of a new HTML element.
@@ -17688,6 +18116,64 @@ class _Tokenizer {
17688
18116
  this._beginToken(24 /* TokenType.EOF */);
17689
18117
  this._endToken([]);
17690
18118
  }
18119
+ _consumeBlockGroupOpen(start) {
18120
+ this._beginToken(25 /* TokenType.BLOCK_GROUP_OPEN_START */, start);
18121
+ const nameCursor = this._cursor.clone();
18122
+ this._attemptCharCodeUntilFn(code => !isBlockNameChar(code));
18123
+ this._endToken([this._cursor.getChars(nameCursor)]);
18124
+ this._consumeBlockParameters();
18125
+ this._beginToken(26 /* TokenType.BLOCK_GROUP_OPEN_END */);
18126
+ this._requireCharCode($RBRACE);
18127
+ this._endToken([]);
18128
+ }
18129
+ _consumeBlockGroupClose(start) {
18130
+ this._beginToken(27 /* TokenType.BLOCK_GROUP_CLOSE */, start);
18131
+ const nameCursor = this._cursor.clone();
18132
+ this._attemptCharCodeUntilFn(code => !isBlockNameChar(code));
18133
+ const name = this._cursor.getChars(nameCursor);
18134
+ this._requireCharCode($RBRACE);
18135
+ this._endToken([name]);
18136
+ }
18137
+ _consumeBlock(start) {
18138
+ this._beginToken(29 /* TokenType.BLOCK_OPEN_START */, start);
18139
+ const nameCursor = this._cursor.clone();
18140
+ this._attemptCharCodeUntilFn(code => !isBlockNameChar(code));
18141
+ this._endToken([this._cursor.getChars(nameCursor)]);
18142
+ this._consumeBlockParameters();
18143
+ this._beginToken(30 /* TokenType.BLOCK_OPEN_END */);
18144
+ this._requireCharCode($RBRACE);
18145
+ this._endToken([]);
18146
+ }
18147
+ _consumeBlockParameters() {
18148
+ // Trim the whitespace until the first parameter.
18149
+ this._attemptCharCodeUntilFn(isBlockParameterChar);
18150
+ while (this._cursor.peek() !== $RBRACE && this._cursor.peek() !== $EOF) {
18151
+ this._beginToken(28 /* TokenType.BLOCK_PARAMETER */);
18152
+ const start = this._cursor.clone();
18153
+ let inQuote = null;
18154
+ // Consume the parameter until the next semicolon or brace.
18155
+ // Note that we skip over semicolons/braces inside of strings.
18156
+ while ((this._cursor.peek() !== $SEMICOLON && this._cursor.peek() !== $RBRACE &&
18157
+ this._cursor.peek() !== $EOF) ||
18158
+ inQuote !== null) {
18159
+ const char = this._cursor.peek();
18160
+ // Skip to the next character if it was escaped.
18161
+ if (char === $BACKSLASH) {
18162
+ this._cursor.advance();
18163
+ }
18164
+ else if (char === inQuote) {
18165
+ inQuote = null;
18166
+ }
18167
+ else if (inQuote === null && isQuote(char)) {
18168
+ inQuote = char;
18169
+ }
18170
+ this._cursor.advance();
18171
+ }
18172
+ this._endToken([this._cursor.getChars(start)]);
18173
+ // Skip to the next parameter.
18174
+ this._attemptCharCodeUntilFn(isBlockParameterChar);
18175
+ }
18176
+ }
17691
18177
  /**
17692
18178
  * @returns whether an ICU token has been created
17693
18179
  * @internal
@@ -18021,7 +18507,6 @@ class _Tokenizer {
18021
18507
  this._endToken(prefixAndName);
18022
18508
  }
18023
18509
  _consumeAttributeValue() {
18024
- let value;
18025
18510
  if (this._cursor.peek() === $SQ || this._cursor.peek() === $DQ) {
18026
18511
  const quoteChar = this._cursor.peek();
18027
18512
  this._consumeQuote(quoteChar);
@@ -18210,7 +18695,7 @@ class _Tokenizer {
18210
18695
  return this._processCarriageReturns(end.getChars(start));
18211
18696
  }
18212
18697
  _isTextEnd() {
18213
- if (this._isTagStart() || this._cursor.peek() === $EOF) {
18698
+ if (this._isTagStart() || this._isBlockStart() || this._cursor.peek() === $EOF) {
18214
18699
  return true;
18215
18700
  }
18216
18701
  if (this._tokenizeIcu && !this._inInterpolation) {
@@ -18243,6 +18728,23 @@ class _Tokenizer {
18243
18728
  }
18244
18729
  return false;
18245
18730
  }
18731
+ _isBlockStart() {
18732
+ if (this._tokenizeBlocks && this._cursor.peek() === $LBRACE) {
18733
+ const tmp = this._cursor.clone();
18734
+ // Check that the cursor is on a `{#`, `{/` or `{:`.
18735
+ tmp.advance();
18736
+ const next = tmp.peek();
18737
+ if (next !== $BANG && next !== $SLASH && next !== $COLON) {
18738
+ return false;
18739
+ }
18740
+ // If it is, also verify that the next character is a valid block identifier.
18741
+ tmp.advance();
18742
+ if (isBlockNameChar(tmp.peek())) {
18743
+ return true;
18744
+ }
18745
+ }
18746
+ return false;
18747
+ }
18246
18748
  _readUntil(char) {
18247
18749
  const start = this._cursor.clone();
18248
18750
  this._attemptUntilChar(char);
@@ -18298,6 +18800,12 @@ function compareCharCodeCaseInsensitive(code1, code2) {
18298
18800
  function toUpperCaseCharCode(code) {
18299
18801
  return code >= $a && code <= $z ? code - $a + $A : code;
18300
18802
  }
18803
+ function isBlockNameChar(code) {
18804
+ return isAsciiLetter(code) || isDigit(code) || code === $_;
18805
+ }
18806
+ function isBlockParameterChar(code) {
18807
+ return code !== $SEMICOLON && isNotWhitespace(code);
18808
+ }
18301
18809
  function mergeTextTokens(srcTokens) {
18302
18810
  const dstTokens = [];
18303
18811
  let lastDstToken = undefined;
@@ -18585,7 +19093,7 @@ class _TreeBuilder {
18585
19093
  this.tokens = tokens;
18586
19094
  this.getTagDefinition = getTagDefinition;
18587
19095
  this._index = -1;
18588
- this._elementStack = [];
19096
+ this._containerStack = [];
18589
19097
  this.rootNodes = [];
18590
19098
  this.errors = [];
18591
19099
  this._advance();
@@ -18615,6 +19123,18 @@ class _TreeBuilder {
18615
19123
  else if (this._peek.type === 19 /* TokenType.EXPANSION_FORM_START */) {
18616
19124
  this._consumeExpansion(this._advance());
18617
19125
  }
19126
+ else if (this._peek.type === 25 /* TokenType.BLOCK_GROUP_OPEN_START */) {
19127
+ this._closeVoidElement();
19128
+ this._consumeBlockGroupOpen(this._advance());
19129
+ }
19130
+ else if (this._peek.type === 29 /* TokenType.BLOCK_OPEN_START */) {
19131
+ this._closeVoidElement();
19132
+ this._consumeBlock(this._advance(), 30 /* TokenType.BLOCK_OPEN_END */);
19133
+ }
19134
+ else if (this._peek.type === 27 /* TokenType.BLOCK_GROUP_CLOSE */) {
19135
+ this._closeVoidElement();
19136
+ this._consumeBlockGroupClose(this._advance());
19137
+ }
18618
19138
  else {
18619
19139
  // Skip all other tokens...
18620
19140
  this._advance();
@@ -18731,7 +19251,12 @@ class _TreeBuilder {
18731
19251
  const startSpan = token.sourceSpan;
18732
19252
  let text = token.parts[0];
18733
19253
  if (text.length > 0 && text[0] === '\n') {
18734
- const parent = this._getParentElement();
19254
+ const parent = this._getContainer();
19255
+ // This is unlikely to happen, but we have an assertion just in case.
19256
+ if (parent instanceof BlockGroup) {
19257
+ this.errors.push(TreeError.create(null, startSpan, 'Text cannot be placed directly inside of a block group.'));
19258
+ return null;
19259
+ }
18735
19260
  if (parent != null && parent.children.length === 0 &&
18736
19261
  this.getTagDefinition(parent.name).ignoreFirstLf) {
18737
19262
  text = text.substring(1);
@@ -18762,9 +19287,9 @@ class _TreeBuilder {
18762
19287
  }
18763
19288
  }
18764
19289
  _closeVoidElement() {
18765
- const el = this._getParentElement();
18766
- if (el && this.getTagDefinition(el.name).isVoid) {
18767
- this._elementStack.pop();
19290
+ const el = this._getContainer();
19291
+ if (el instanceof Element && this.getTagDefinition(el.name).isVoid) {
19292
+ this._containerStack.pop();
18768
19293
  }
18769
19294
  }
18770
19295
  _consumeStartTag(startTagToken) {
@@ -18773,7 +19298,7 @@ class _TreeBuilder {
18773
19298
  while (this._peek.type === 14 /* TokenType.ATTR_NAME */) {
18774
19299
  attrs.push(this._consumeAttr(this._advance()));
18775
19300
  }
18776
- const fullName = this._getElementFullName(prefix, name, this._getParentElement());
19301
+ const fullName = this._getElementFullName(prefix, name, this._getClosestParentElement());
18777
19302
  let selfClosing = false;
18778
19303
  // Note: There could have been a tokenizer error
18779
19304
  // so that we don't get a token for the end tag...
@@ -18794,33 +19319,34 @@ class _TreeBuilder {
18794
19319
  // Create a separate `startSpan` because `span` will be modified when there is an `end` span.
18795
19320
  const startSpan = new ParseSourceSpan(startTagToken.sourceSpan.start, end, startTagToken.sourceSpan.fullStart);
18796
19321
  const el = new Element(fullName, attrs, [], span, startSpan, undefined);
18797
- this._pushElement(el);
19322
+ const parentEl = this._getContainer();
19323
+ this._pushContainer(el, parentEl instanceof Element &&
19324
+ this.getTagDefinition(parentEl.name).isClosedByChild(el.name));
18798
19325
  if (selfClosing) {
18799
19326
  // Elements that are self-closed have their `endSourceSpan` set to the full span, as the
18800
19327
  // element start tag also represents the end tag.
18801
- this._popElement(fullName, span);
19328
+ this._popContainer(fullName, Element, span);
18802
19329
  }
18803
19330
  else if (startTagToken.type === 4 /* TokenType.INCOMPLETE_TAG_OPEN */) {
18804
19331
  // We already know the opening tag is not complete, so it is unlikely it has a corresponding
18805
19332
  // close tag. Let's optimistically parse it as a full element and emit an error.
18806
- this._popElement(fullName, null);
19333
+ this._popContainer(fullName, Element, null);
18807
19334
  this.errors.push(TreeError.create(fullName, span, `Opening tag "${fullName}" not terminated.`));
18808
19335
  }
18809
19336
  }
18810
- _pushElement(el) {
18811
- const parentEl = this._getParentElement();
18812
- if (parentEl && this.getTagDefinition(parentEl.name).isClosedByChild(el.name)) {
18813
- this._elementStack.pop();
19337
+ _pushContainer(node, isClosedByChild) {
19338
+ if (isClosedByChild) {
19339
+ this._containerStack.pop();
18814
19340
  }
18815
- this._addToParent(el);
18816
- this._elementStack.push(el);
19341
+ this._addToParent(node);
19342
+ this._containerStack.push(node);
18817
19343
  }
18818
19344
  _consumeEndTag(endTagToken) {
18819
- const fullName = this._getElementFullName(endTagToken.parts[0], endTagToken.parts[1], this._getParentElement());
19345
+ const fullName = this._getElementFullName(endTagToken.parts[0], endTagToken.parts[1], this._getClosestParentElement());
18820
19346
  if (this.getTagDefinition(fullName).isVoid) {
18821
19347
  this.errors.push(TreeError.create(fullName, endTagToken.sourceSpan, `Void elements do not have end tags "${endTagToken.parts[1]}"`));
18822
19348
  }
18823
- else if (!this._popElement(fullName, endTagToken.sourceSpan)) {
19349
+ else if (!this._popContainer(fullName, Element, endTagToken.sourceSpan)) {
18824
19350
  const errMsg = `Unexpected closing tag "${fullName}". It may happen when the tag has already been closed by another tag. For more info see https://www.w3.org/TR/html5/syntax.html#closing-elements-that-have-implied-end-tags`;
18825
19351
  this.errors.push(TreeError.create(fullName, endTagToken.sourceSpan, errMsg));
18826
19352
  }
@@ -18831,20 +19357,23 @@ class _TreeBuilder {
18831
19357
  * not have a closing tag (for example, this happens when an incomplete
18832
19358
  * opening tag is recovered).
18833
19359
  */
18834
- _popElement(fullName, endSourceSpan) {
19360
+ _popContainer(fullName, expectedType, endSourceSpan) {
18835
19361
  let unexpectedCloseTagDetected = false;
18836
- for (let stackIndex = this._elementStack.length - 1; stackIndex >= 0; stackIndex--) {
18837
- const el = this._elementStack[stackIndex];
18838
- if (el.name === fullName) {
19362
+ for (let stackIndex = this._containerStack.length - 1; stackIndex >= 0; stackIndex--) {
19363
+ const node = this._containerStack[stackIndex];
19364
+ const name = node instanceof BlockGroup ? node.blocks[0]?.name : node.name;
19365
+ if (name === fullName && node instanceof expectedType) {
18839
19366
  // Record the parse span with the element that is being closed. Any elements that are
18840
19367
  // removed from the element stack at this point are closed implicitly, so they won't get
18841
19368
  // an end source span (as there is no explicit closing element).
18842
- el.endSourceSpan = endSourceSpan;
18843
- el.sourceSpan.end = endSourceSpan !== null ? endSourceSpan.end : el.sourceSpan.end;
18844
- this._elementStack.splice(stackIndex, this._elementStack.length - stackIndex);
19369
+ node.endSourceSpan = endSourceSpan;
19370
+ node.sourceSpan.end = endSourceSpan !== null ? endSourceSpan.end : node.sourceSpan.end;
19371
+ this._containerStack.splice(stackIndex, this._containerStack.length - stackIndex);
18845
19372
  return !unexpectedCloseTagDetected;
18846
19373
  }
18847
- if (!this.getTagDefinition(el.name).closedByParent) {
19374
+ // Blocks are self-closing while block groups and (most times) elements are not.
19375
+ if (node instanceof BlockGroup ||
19376
+ node instanceof Element && !this.getTagDefinition(node.name).closedByParent) {
18848
19377
  // Note that we encountered an unexpected close tag but continue processing the element
18849
19378
  // stack so we can assign an `endSourceSpan` if there is a corresponding start tag for this
18850
19379
  // end tag in the stack.
@@ -18903,16 +19432,92 @@ class _TreeBuilder {
18903
19432
  new ParseSourceSpan(valueStartSpan.start, valueEnd, valueStartSpan.fullStart);
18904
19433
  return new Attribute(fullName, value, new ParseSourceSpan(attrName.sourceSpan.start, attrEnd, attrName.sourceSpan.fullStart), attrName.sourceSpan, valueSpan, valueTokens.length > 0 ? valueTokens : undefined, undefined);
18905
19434
  }
18906
- _getParentElement() {
18907
- return this._elementStack.length > 0 ? this._elementStack[this._elementStack.length - 1] : null;
19435
+ _consumeBlockGroupOpen(token) {
19436
+ const end = this._peek.sourceSpan.fullStart;
19437
+ const span = new ParseSourceSpan(token.sourceSpan.start, end, token.sourceSpan.fullStart);
19438
+ // Create a separate `startSpan` because `span` will be modified when there is an `end` span.
19439
+ const startSpan = new ParseSourceSpan(token.sourceSpan.start, end, token.sourceSpan.fullStart);
19440
+ const blockGroup = new BlockGroup([], span, startSpan, null);
19441
+ this._pushContainer(blockGroup, false);
19442
+ const implicitBlock = this._consumeBlock(token, 26 /* TokenType.BLOCK_GROUP_OPEN_END */);
19443
+ // Block parameters are consumed as a part of the implicit block so we need to expand the
19444
+ // start source span once the block is parsed to include the full opening tag.
19445
+ startSpan.end = implicitBlock.startSourceSpan.end;
19446
+ }
19447
+ _consumeBlock(token, closeToken) {
19448
+ // The start of a block implicitly closes the previous block.
19449
+ this._conditionallyClosePreviousBlock();
19450
+ const parameters = [];
19451
+ while (this._peek.type === 28 /* TokenType.BLOCK_PARAMETER */) {
19452
+ const paramToken = this._advance();
19453
+ parameters.push(new BlockParameter(paramToken.parts[0], paramToken.sourceSpan));
19454
+ }
19455
+ if (this._peek.type === closeToken) {
19456
+ this._advance();
19457
+ }
19458
+ const end = this._peek.sourceSpan.fullStart;
19459
+ const span = new ParseSourceSpan(token.sourceSpan.start, end, token.sourceSpan.fullStart);
19460
+ // Create a separate `startSpan` because `span` will be modified when there is an `end` span.
19461
+ const startSpan = new ParseSourceSpan(token.sourceSpan.start, end, token.sourceSpan.fullStart);
19462
+ const block = new Block(token.parts[0], parameters, [], span, startSpan);
19463
+ const parent = this._getContainer();
19464
+ if (!(parent instanceof BlockGroup)) {
19465
+ this.errors.push(TreeError.create(block.name, block.sourceSpan, 'Blocks can only be placed inside of block groups.'));
19466
+ }
19467
+ else {
19468
+ parent.blocks.push(block);
19469
+ this._containerStack.push(block);
19470
+ }
19471
+ return block;
19472
+ }
19473
+ _consumeBlockGroupClose(token) {
19474
+ const name = token.parts[0];
19475
+ const previousContainer = this._getContainer();
19476
+ // Blocks are implcitly closed by the block group.
19477
+ this._conditionallyClosePreviousBlock();
19478
+ if (!this._popContainer(name, BlockGroup, token.sourceSpan)) {
19479
+ const context = previousContainer instanceof Element ?
19480
+ `There is an unclosed "${previousContainer.name}" HTML tag named that may have to be closed first.` :
19481
+ `The block may have been closed earlier.`;
19482
+ this.errors.push(TreeError.create(name, token.sourceSpan, `Unexpected closing block "${name}". ${context}`));
19483
+ }
19484
+ }
19485
+ _conditionallyClosePreviousBlock() {
19486
+ const container = this._getContainer();
19487
+ if (container instanceof Block) {
19488
+ // Blocks don't have an explicit closing tag, they're closed either by the next block or
19489
+ // the end of the block group. Infer the end span from the last child node.
19490
+ const lastChild = container.children.length ? container.children[container.children.length - 1] : null;
19491
+ const endSpan = lastChild === null ?
19492
+ null :
19493
+ new ParseSourceSpan(lastChild.sourceSpan.end, lastChild.sourceSpan.end);
19494
+ this._popContainer(container.name, Block, endSpan);
19495
+ }
19496
+ }
19497
+ _getContainer() {
19498
+ return this._containerStack.length > 0 ? this._containerStack[this._containerStack.length - 1] :
19499
+ null;
19500
+ }
19501
+ _getClosestParentElement() {
19502
+ for (let i = this._containerStack.length - 1; i > -1; i--) {
19503
+ if (this._containerStack[i] instanceof Element) {
19504
+ return this._containerStack[i];
19505
+ }
19506
+ }
19507
+ return null;
18908
19508
  }
18909
19509
  _addToParent(node) {
18910
- const parent = this._getParentElement();
18911
- if (parent != null) {
18912
- parent.children.push(node);
19510
+ const parent = this._getContainer();
19511
+ if (parent === null) {
19512
+ this.rootNodes.push(node);
19513
+ }
19514
+ else if (parent instanceof BlockGroup) {
19515
+ // Due to how parsing is set up, we're unlikely to hit this code path, but we
19516
+ // have the assertion here just in case and to satisfy the type checker.
19517
+ this.errors.push(TreeError.create(null, node.sourceSpan, 'Block groups can only contain blocks.'));
18913
19518
  }
18914
19519
  else {
18915
- this.rootNodes.push(node);
19520
+ parent.children.push(node);
18916
19521
  }
18917
19522
  }
18918
19523
  _getElementFullName(prefix, localName, parentElement) {
@@ -19026,6 +19631,15 @@ class WhitespaceVisitor {
19026
19631
  visitExpansionCase(expansionCase, context) {
19027
19632
  return expansionCase;
19028
19633
  }
19634
+ visitBlockGroup(group, context) {
19635
+ return new BlockGroup(visitAllWithSiblings(this, group.blocks), group.sourceSpan, group.startSourceSpan, group.endSourceSpan);
19636
+ }
19637
+ visitBlock(block, context) {
19638
+ return new Block(block.name, block.parameters, visitAllWithSiblings(this, block.children), block.sourceSpan, block.startSourceSpan);
19639
+ }
19640
+ visitBlockParameter(parameter, context) {
19641
+ return parameter;
19642
+ }
19029
19643
  }
19030
19644
  function createWhitespaceProcessedTextToken({ type, parts, sourceSpan }) {
19031
19645
  return { type, parts: [processWhitespace(parts[0])], sourceSpan };
@@ -19814,6 +20428,15 @@ class HtmlAstToIvyAst {
19814
20428
  }
19815
20429
  return null;
19816
20430
  }
20431
+ visitBlockGroup(group, context) {
20432
+ throw new Error('TODO');
20433
+ }
20434
+ visitBlock(block, context) {
20435
+ throw new Error('TODO');
20436
+ }
20437
+ visitBlockParameter(parameter, context) {
20438
+ throw new Error('TODO');
20439
+ }
19817
20440
  // convert view engine `ParsedProperty` to a format suitable for IVY
19818
20441
  extractAttributes(elementName, properties, i18nPropsMeta) {
19819
20442
  const bound = [];
@@ -19991,6 +20614,15 @@ class NonBindableVisitor {
19991
20614
  visitExpansionCase(expansionCase) {
19992
20615
  return null;
19993
20616
  }
20617
+ visitBlockGroup(group, context) {
20618
+ throw new Error('TODO');
20619
+ }
20620
+ visitBlock(block, context) {
20621
+ throw new Error('TODO');
20622
+ }
20623
+ visitBlockParameter(parameter, context) {
20624
+ throw new Error('TODO');
20625
+ }
19994
20626
  }
19995
20627
  const NON_BINDABLE_VISITOR = new NonBindableVisitor();
19996
20628
  function normalizeAttributeName(attrName) {
@@ -20438,6 +21070,17 @@ class _I18nVisitor {
20438
21070
  visitExpansionCase(_icuCase, _context) {
20439
21071
  throw new Error('Unreachable code');
20440
21072
  }
21073
+ visitBlockGroup(group, context) {
21074
+ const children = visitAll(this, group.blocks, context);
21075
+ const node = new Container(children, group.sourceSpan);
21076
+ return context.visitNodeFn(group, node);
21077
+ }
21078
+ visitBlock(block, context) {
21079
+ const children = visitAll(this, block.children, context);
21080
+ const node = new Container(children, block.sourceSpan);
21081
+ return context.visitNodeFn(block, node);
21082
+ }
21083
+ visitBlockParameter(_parameter, _context) { }
20441
21084
  /**
20442
21085
  * Convert, text and interpolated tokens up into text and placeholder pieces.
20443
21086
  *
@@ -20684,6 +21327,17 @@ class I18nMetaVisitor {
20684
21327
  visitExpansionCase(expansionCase) {
20685
21328
  return expansionCase;
20686
21329
  }
21330
+ visitBlockGroup(group, context) {
21331
+ visitAll(this, group.blocks, context);
21332
+ return group;
21333
+ }
21334
+ visitBlock(block, context) {
21335
+ visitAll(this, block.children, context);
21336
+ return block;
21337
+ }
21338
+ visitBlockParameter(parameter, context) {
21339
+ return parameter;
21340
+ }
20687
21341
  /**
20688
21342
  * Parse the general form `meta` passed into extract the explicit metadata needed to create a
20689
21343
  * `Message`.
@@ -23556,6 +24210,7 @@ class CompilerFacadeImpl {
23556
24210
  }
23557
24211
  compileNgModule(angularCoreEnv, sourceMapUrl, facade) {
23558
24212
  const meta = {
24213
+ kind: R3NgModuleMetadataKind.Global,
23559
24214
  type: wrapReference(facade.type),
23560
24215
  bootstrap: facade.bootstrap.map(wrapReference),
23561
24216
  declarations: facade.declarations.map(wrapReference),
@@ -24067,7 +24722,7 @@ function publishFacade(global) {
24067
24722
  * @description
24068
24723
  * Entry point for all public APIs of the compiler package.
24069
24724
  */
24070
- const VERSION = new Version('16.2.0-next.1');
24725
+ const VERSION = new Version('16.2.0-next.2');
24071
24726
 
24072
24727
  class CompilerConfig {
24073
24728
  constructor({ defaultEncapsulation = ViewEncapsulation.Emulated, useJit = true, missingTranslation = null, preserveWhitespaces, strictInjectionParameters } = {}) {
@@ -24288,6 +24943,13 @@ class _Visitor {
24288
24943
  visitAttribute(attribute, context) {
24289
24944
  throw new Error('unreachable code');
24290
24945
  }
24946
+ visitBlockGroup(group, context) {
24947
+ visitAll(this, group.blocks, context);
24948
+ }
24949
+ visitBlock(block, context) {
24950
+ visitAll(this, block.children, context);
24951
+ }
24952
+ visitBlockParameter(parameter, context) { }
24291
24953
  _init(mode, interpolationConfig) {
24292
24954
  this._mode = mode;
24293
24955
  this._inI18nBlock = false;
@@ -24500,8 +25162,9 @@ class XmlParser extends Parser {
24500
25162
  constructor() {
24501
25163
  super(getXmlTagDefinition);
24502
25164
  }
24503
- parse(source, url, options) {
24504
- return super.parse(source, url, options);
25165
+ parse(source, url, options = {}) {
25166
+ // Blocks aren't supported in an XML context.
25167
+ return super.parse(source, url, { ...options, tokenizeBlocks: false });
24505
25168
  }
24506
25169
  }
24507
25170
 
@@ -24685,6 +25348,9 @@ class XliffParser {
24685
25348
  visitComment(comment, context) { }
24686
25349
  visitExpansion(expansion, context) { }
24687
25350
  visitExpansionCase(expansionCase, context) { }
25351
+ visitBlockGroup(group, context) { }
25352
+ visitBlock(block, context) { }
25353
+ visitBlockParameter(parameter, context) { }
24688
25354
  _addError(node, message) {
24689
25355
  this._errors.push(new I18nError(node.sourceSpan, message));
24690
25356
  }
@@ -24735,6 +25401,9 @@ class XmlToI18n$2 {
24735
25401
  }
24736
25402
  visitComment(comment, context) { }
24737
25403
  visitAttribute(attribute, context) { }
25404
+ visitBlockGroup(group, context) { }
25405
+ visitBlock(block, context) { }
25406
+ visitBlockParameter(parameter, context) { }
24738
25407
  _addError(node, message) {
24739
25408
  this._errors.push(new I18nError(node.sourceSpan, message));
24740
25409
  }
@@ -24958,6 +25627,9 @@ class Xliff2Parser {
24958
25627
  visitComment(comment, context) { }
24959
25628
  visitExpansion(expansion, context) { }
24960
25629
  visitExpansionCase(expansionCase, context) { }
25630
+ visitBlockGroup(group, context) { }
25631
+ visitBlock(block, context) { }
25632
+ visitBlockParameter(parameter, context) { }
24961
25633
  _addError(node, message) {
24962
25634
  this._errors.push(new I18nError(node.sourceSpan, message));
24963
25635
  }
@@ -25025,6 +25697,9 @@ class XmlToI18n$1 {
25025
25697
  }
25026
25698
  visitComment(comment, context) { }
25027
25699
  visitAttribute(attribute, context) { }
25700
+ visitBlockGroup(group, context) { }
25701
+ visitBlock(block, context) { }
25702
+ visitBlockParameter(parameter, context) { }
25028
25703
  _addError(node, message) {
25029
25704
  this._errors.push(new I18nError(node.sourceSpan, message));
25030
25705
  }
@@ -25159,6 +25834,9 @@ class XtbParser {
25159
25834
  visitComment(comment, context) { }
25160
25835
  visitExpansion(expansion, context) { }
25161
25836
  visitExpansionCase(expansionCase, context) { }
25837
+ visitBlockGroup(group, context) { }
25838
+ visitBlock(block, context) { }
25839
+ visitBlockParameter(block, context) { }
25162
25840
  _addError(node, message) {
25163
25841
  this._errors.push(new I18nError(node.sourceSpan, message));
25164
25842
  }
@@ -25207,6 +25885,9 @@ class XmlToI18n {
25207
25885
  }
25208
25886
  visitComment(comment, context) { }
25209
25887
  visitAttribute(attribute, context) { }
25888
+ visitBlockGroup(group, context) { }
25889
+ visitBlock(block, context) { }
25890
+ visitBlockParameter(block, context) { }
25210
25891
  _addError(node, message) {
25211
25892
  this._errors.push(new I18nError(node.sourceSpan, message));
25212
25893
  }
@@ -25995,7 +26676,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$6 = '12.0.0';
25995
26676
  function compileDeclareClassMetadata(metadata) {
25996
26677
  const definitionMap = new DefinitionMap();
25997
26678
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$6));
25998
- definitionMap.set('version', literal('16.2.0-next.1'));
26679
+ definitionMap.set('version', literal('16.2.0-next.2'));
25999
26680
  definitionMap.set('ngImport', importExpr(Identifiers.core));
26000
26681
  definitionMap.set('type', metadata.type);
26001
26682
  definitionMap.set('decorators', metadata.decorators);
@@ -26098,7 +26779,7 @@ function compileDeclareDirectiveFromMetadata(meta) {
26098
26779
  function createDirectiveDefinitionMap(meta) {
26099
26780
  const definitionMap = new DefinitionMap();
26100
26781
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$5));
26101
- definitionMap.set('version', literal('16.2.0-next.1'));
26782
+ definitionMap.set('version', literal('16.2.0-next.2'));
26102
26783
  // e.g. `type: MyDirective`
26103
26784
  definitionMap.set('type', meta.type.value);
26104
26785
  if (meta.isStandalone) {
@@ -26326,7 +27007,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0';
26326
27007
  function compileDeclareFactoryFunction(meta) {
26327
27008
  const definitionMap = new DefinitionMap();
26328
27009
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$4));
26329
- definitionMap.set('version', literal('16.2.0-next.1'));
27010
+ definitionMap.set('version', literal('16.2.0-next.2'));
26330
27011
  definitionMap.set('ngImport', importExpr(Identifiers.core));
26331
27012
  definitionMap.set('type', meta.type.value);
26332
27013
  definitionMap.set('deps', compileDependencies(meta.deps));
@@ -26361,7 +27042,7 @@ function compileDeclareInjectableFromMetadata(meta) {
26361
27042
  function createInjectableDefinitionMap(meta) {
26362
27043
  const definitionMap = new DefinitionMap();
26363
27044
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$3));
26364
- definitionMap.set('version', literal('16.2.0-next.1'));
27045
+ definitionMap.set('version', literal('16.2.0-next.2'));
26365
27046
  definitionMap.set('ngImport', importExpr(Identifiers.core));
26366
27047
  definitionMap.set('type', meta.type.value);
26367
27048
  // Only generate providedIn property if it has a non-null value
@@ -26412,7 +27093,7 @@ function compileDeclareInjectorFromMetadata(meta) {
26412
27093
  function createInjectorDefinitionMap(meta) {
26413
27094
  const definitionMap = new DefinitionMap();
26414
27095
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$2));
26415
- definitionMap.set('version', literal('16.2.0-next.1'));
27096
+ definitionMap.set('version', literal('16.2.0-next.2'));
26416
27097
  definitionMap.set('ngImport', importExpr(Identifiers.core));
26417
27098
  definitionMap.set('type', meta.type.value);
26418
27099
  definitionMap.set('providers', meta.providers);
@@ -26441,8 +27122,11 @@ function compileDeclareNgModuleFromMetadata(meta) {
26441
27122
  */
26442
27123
  function createNgModuleDefinitionMap(meta) {
26443
27124
  const definitionMap = new DefinitionMap();
27125
+ if (meta.kind === R3NgModuleMetadataKind.Local) {
27126
+ throw new Error('Invalid path! Local compilation mode should not get into the partial compilation path');
27127
+ }
26444
27128
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$1));
26445
- definitionMap.set('version', literal('16.2.0-next.1'));
27129
+ definitionMap.set('version', literal('16.2.0-next.2'));
26446
27130
  definitionMap.set('ngImport', importExpr(Identifiers.core));
26447
27131
  definitionMap.set('type', meta.type.value);
26448
27132
  // We only generate the keys in the metadata if the arrays contain values.
@@ -26493,7 +27177,7 @@ function compileDeclarePipeFromMetadata(meta) {
26493
27177
  function createPipeDefinitionMap(meta) {
26494
27178
  const definitionMap = new DefinitionMap();
26495
27179
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION));
26496
- definitionMap.set('version', literal('16.2.0-next.1'));
27180
+ definitionMap.set('version', literal('16.2.0-next.2'));
26497
27181
  definitionMap.set('ngImport', importExpr(Identifiers.core));
26498
27182
  // e.g. `type: MyPipe`
26499
27183
  definitionMap.set('type', meta.type.value);
@@ -26526,5 +27210,5 @@ publishFacade(_global);
26526
27210
 
26527
27211
  // This file is not used to build this module. It is only used during editing
26528
27212
 
26529
- export { AST, ASTWithName, ASTWithSource, AbsoluteSourceSpan, ArrayType, AstMemoryEfficientTransformer, AstTransformer, Attribute, Binary, BinaryOperator, BinaryOperatorExpr, BindingPipe, BoundElementProperty, BuiltinType, BuiltinTypeName, CUSTOM_ELEMENTS_SCHEMA, Call, Chain, ChangeDetectionStrategy, CommaExpr, Comment, CompilerConfig, Conditional, ConditionalExpr, ConstantPool, CssSelector, DEFAULT_INTERPOLATION_CONFIG, DYNAMIC_TYPE, DeclareFunctionStmt, DeclareVarStmt, DomElementSchemaRegistry, EOF, Element, ElementSchemaRegistry, EmitterVisitorContext, EmptyExpr$1 as EmptyExpr, Expansion, ExpansionCase, Expression, ExpressionBinding, ExpressionStatement, ExpressionType, ExternalExpr, ExternalReference, FactoryTarget$1 as FactoryTarget, FunctionExpr, HtmlParser, HtmlTagDefinition, I18NHtmlParser, IfStmt, ImplicitReceiver, InstantiateExpr, Interpolation, InterpolationConfig, InvokeFunctionExpr, JSDocComment, JitEvaluator, KeyedRead, KeyedWrite, LeadingComment, Lexer, LiteralArray, LiteralArrayExpr, LiteralExpr, LiteralMap, LiteralMapExpr, LiteralPrimitive, LocalizedString, MapType, MessageBundle, NONE_TYPE, NO_ERRORS_SCHEMA, NodeWithI18n, NonNullAssert, NotExpr, ParseError, ParseErrorLevel, ParseLocation, ParseSourceFile, ParseSourceSpan, ParseSpan, ParseTreeResult, ParsedEvent, ParsedProperty, ParsedPropertyType, ParsedVariable, Parser$1 as Parser, ParserError, PrefixNot, PropertyRead, PropertyWrite, R3BoundTarget, Identifiers as R3Identifiers, R3SelectorScopeMode, R3TargetBinder, R3TemplateDependencyKind, ReadKeyExpr, ReadPropExpr, ReadVarExpr, RecursiveAstVisitor, RecursiveVisitor, ResourceLoader, ReturnStatement, STRING_TYPE, SafeCall, SafeKeyedRead, SafePropertyRead, SelectorContext, SelectorListContext, SelectorMatcher, Serializer, SplitInterpolation, Statement, StmtModifier, TagContentType, TaggedTemplateExpr, TemplateBindingParseResult, TemplateLiteral, TemplateLiteralElement, Text, ThisReceiver, BoundAttribute as TmplAstBoundAttribute, BoundEvent as TmplAstBoundEvent, BoundText as TmplAstBoundText, Content as TmplAstContent, Element$1 as TmplAstElement, Icu$1 as TmplAstIcu, RecursiveVisitor$1 as TmplAstRecursiveVisitor, Reference as TmplAstReference, Template as TmplAstTemplate, Text$3 as TmplAstText, TextAttribute as TmplAstTextAttribute, Variable as TmplAstVariable, Token, TokenType, TransplantedType, TreeError, Type, TypeModifier, TypeofExpr, Unary, UnaryOperator, UnaryOperatorExpr, VERSION, VariableBinding, Version, ViewEncapsulation, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr, Xliff, Xliff2, Xmb, XmlParser, Xtb, _ParseAST, compileClassMetadata, compileComponentFromMetadata, compileDeclareClassMetadata, compileDeclareComponentFromMetadata, compileDeclareDirectiveFromMetadata, compileDeclareFactoryFunction, compileDeclareInjectableFromMetadata, compileDeclareInjectorFromMetadata, compileDeclareNgModuleFromMetadata, compileDeclarePipeFromMetadata, compileDirectiveFromMetadata, compileFactoryFunction, compileInjectable, compileInjector, compileNgModule, compilePipeFromMetadata, computeMsgId, core, createInjectableType, createMayBeForwardRefExpression, devOnlyGuardedExpression, emitDistinctChangesOnlyDefaultValue, getHtmlTagDefinition, getNsPrefix, getSafePropertyAccessString, identifierName, isIdentifier, isNgContainer, isNgContent, isNgTemplate, jsDocComment, leadingComment, literalMap, makeBindingParser, mergeNsAndName, output_ast as outputAst, parseHostBindings, parseTemplate, preserveWhitespacesDefault, publishFacade, r3JitTypeSourceSpan, sanitizeIdentifier, splitNsName, verifyHostBindings, visitAll };
27213
+ export { AST, ASTWithName, ASTWithSource, AbsoluteSourceSpan, ArrayType, AstMemoryEfficientTransformer, AstTransformer, Attribute, Binary, BinaryOperator, BinaryOperatorExpr, BindingPipe, Block, BlockGroup, BlockParameter, BoundElementProperty, BuiltinType, BuiltinTypeName, CUSTOM_ELEMENTS_SCHEMA, Call, Chain, ChangeDetectionStrategy, CommaExpr, Comment, CompilerConfig, Conditional, ConditionalExpr, ConstantPool, CssSelector, DEFAULT_INTERPOLATION_CONFIG, DYNAMIC_TYPE, DeclareFunctionStmt, DeclareVarStmt, DomElementSchemaRegistry, EOF, Element, ElementSchemaRegistry, EmitterVisitorContext, EmptyExpr$1 as EmptyExpr, Expansion, ExpansionCase, Expression, ExpressionBinding, ExpressionStatement, ExpressionType, ExternalExpr, ExternalReference, FactoryTarget$1 as FactoryTarget, FunctionExpr, HtmlParser, HtmlTagDefinition, I18NHtmlParser, IfStmt, ImplicitReceiver, InstantiateExpr, Interpolation, InterpolationConfig, InvokeFunctionExpr, JSDocComment, JitEvaluator, KeyedRead, KeyedWrite, LeadingComment, Lexer, LiteralArray, LiteralArrayExpr, LiteralExpr, LiteralMap, LiteralMapExpr, LiteralPrimitive, LocalizedString, MapType, MessageBundle, NONE_TYPE, NO_ERRORS_SCHEMA, NodeWithI18n, NonNullAssert, NotExpr, ParseError, ParseErrorLevel, ParseLocation, ParseSourceFile, ParseSourceSpan, ParseSpan, ParseTreeResult, ParsedEvent, ParsedProperty, ParsedPropertyType, ParsedVariable, Parser$1 as Parser, ParserError, PrefixNot, PropertyRead, PropertyWrite, R3BoundTarget, Identifiers as R3Identifiers, R3NgModuleMetadataKind, R3SelectorScopeMode, R3TargetBinder, R3TemplateDependencyKind, ReadKeyExpr, ReadPropExpr, ReadVarExpr, RecursiveAstVisitor, RecursiveVisitor, ResourceLoader, ReturnStatement, STRING_TYPE, SafeCall, SafeKeyedRead, SafePropertyRead, SelectorContext, SelectorListContext, SelectorMatcher, Serializer, SplitInterpolation, Statement, StmtModifier, TagContentType, TaggedTemplateExpr, TemplateBindingParseResult, TemplateLiteral, TemplateLiteralElement, Text, ThisReceiver, BoundAttribute as TmplAstBoundAttribute, BoundEvent as TmplAstBoundEvent, BoundText as TmplAstBoundText, Content as TmplAstContent, Element$1 as TmplAstElement, Icu$1 as TmplAstIcu, RecursiveVisitor$1 as TmplAstRecursiveVisitor, Reference as TmplAstReference, Template as TmplAstTemplate, Text$3 as TmplAstText, TextAttribute as TmplAstTextAttribute, Variable as TmplAstVariable, Token, TokenType, TransplantedType, TreeError, Type, TypeModifier, TypeofExpr, Unary, UnaryOperator, UnaryOperatorExpr, VERSION, VariableBinding, Version, ViewEncapsulation, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr, Xliff, Xliff2, Xmb, XmlParser, Xtb, _ParseAST, compileClassMetadata, compileComponentFromMetadata, compileDeclareClassMetadata, compileDeclareComponentFromMetadata, compileDeclareDirectiveFromMetadata, compileDeclareFactoryFunction, compileDeclareInjectableFromMetadata, compileDeclareInjectorFromMetadata, compileDeclareNgModuleFromMetadata, compileDeclarePipeFromMetadata, compileDirectiveFromMetadata, compileFactoryFunction, compileInjectable, compileInjector, compileNgModule, compilePipeFromMetadata, computeMsgId, core, createInjectableType, createMayBeForwardRefExpression, devOnlyGuardedExpression, emitDistinctChangesOnlyDefaultValue, getHtmlTagDefinition, getNsPrefix, getSafePropertyAccessString, identifierName, isIdentifier, isNgContainer, isNgContent, isNgTemplate, jsDocComment, leadingComment, literalMap, makeBindingParser, mergeNsAndName, output_ast as outputAst, parseHostBindings, parseTemplate, preserveWhitespacesDefault, publishFacade, r3JitTypeSourceSpan, sanitizeIdentifier, splitNsName, verifyHostBindings, visitAll };
26530
27214
  //# sourceMappingURL=compiler.mjs.map