@angular-eslint/bundled-angular-compiler 17.1.2-alpha.5 → 17.1.2-alpha.7

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 (2) hide show
  1. package/dist/index.js +1568 -622
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  /**
4
- * @license Angular v17.0.2
4
+ * @license Angular v17.0.8
5
5
  * (c) 2010-2022 Google LLC. https://angular.io/
6
6
  * License: MIT
7
7
  */
@@ -541,6 +541,9 @@ class _SerializerVisitor {
541
541
  visitIcuPlaceholder(ph, context) {
542
542
  return `<ph icu name="${ph.name}">${ph.value.visit(this)}</ph>`;
543
543
  }
544
+ visitBlockPlaceholder(ph, context) {
545
+ return `<ph block name="${ph.startName}">${ph.children.map(child => child.visit(this)).join(', ')}</ph name="${ph.closeName}">`;
546
+ }
544
547
  }
545
548
  const serializerVisitor$1 = new _SerializerVisitor();
546
549
  function serializeNodes(nodes) {
@@ -2307,7 +2310,9 @@ class ConstantPool {
2307
2310
  }))));
2308
2311
  }
2309
2312
  }
2310
- getSharedFunctionReference(fn, prefix) {
2313
+ // TODO: useUniqueName(false) is necessary for naming compatibility with
2314
+ // TemplateDefinitionBuilder, but should be removed once Template Pipeline is the default.
2315
+ getSharedFunctionReference(fn, prefix, useUniqueName = true) {
2311
2316
  const isArrow = fn instanceof ArrowFunctionExpr;
2312
2317
  for (const current of this.statements) {
2313
2318
  // Arrow functions are saved as variables so we check if the
@@ -2322,7 +2327,7 @@ class ConstantPool {
2322
2327
  }
2323
2328
  }
2324
2329
  // Otherwise declare the function.
2325
- const name = this.uniqueName(prefix);
2330
+ const name = useUniqueName ? this.uniqueName(prefix) : prefix;
2326
2331
  this.statements.push(fn.toDeclStmt(name, exports.StmtModifier.Final));
2327
2332
  return variable(name);
2328
2333
  }
@@ -3667,6 +3672,46 @@ function getInjectFn(target) {
3667
3672
  }
3668
3673
  }
3669
3674
 
3675
+ exports.TagContentType = void 0;
3676
+ (function (TagContentType) {
3677
+ TagContentType[TagContentType["RAW_TEXT"] = 0] = "RAW_TEXT";
3678
+ TagContentType[TagContentType["ESCAPABLE_RAW_TEXT"] = 1] = "ESCAPABLE_RAW_TEXT";
3679
+ TagContentType[TagContentType["PARSABLE_DATA"] = 2] = "PARSABLE_DATA";
3680
+ })(exports.TagContentType || (exports.TagContentType = {}));
3681
+ function splitNsName(elementName, fatal = true) {
3682
+ if (elementName[0] != ':') {
3683
+ return [null, elementName];
3684
+ }
3685
+ const colonIndex = elementName.indexOf(':', 1);
3686
+ if (colonIndex === -1) {
3687
+ if (fatal) {
3688
+ throw new Error(`Unsupported format "${elementName}" expecting ":namespace:name"`);
3689
+ }
3690
+ else {
3691
+ return [null, elementName];
3692
+ }
3693
+ }
3694
+ return [elementName.slice(1, colonIndex), elementName.slice(colonIndex + 1)];
3695
+ }
3696
+ // `<ng-container>` tags work the same regardless the namespace
3697
+ function isNgContainer(tagName) {
3698
+ return splitNsName(tagName)[1] === 'ng-container';
3699
+ }
3700
+ // `<ng-content>` tags work the same regardless the namespace
3701
+ function isNgContent(tagName) {
3702
+ return splitNsName(tagName)[1] === 'ng-content';
3703
+ }
3704
+ // `<ng-template>` tags work the same regardless the namespace
3705
+ function isNgTemplate(tagName) {
3706
+ return splitNsName(tagName)[1] === 'ng-template';
3707
+ }
3708
+ function getNsPrefix(fullName) {
3709
+ return fullName === null ? null : splitNsName(fullName)[0];
3710
+ }
3711
+ function mergeNsAndName(prefix, localName) {
3712
+ return prefix ? `:${prefix}:${localName}` : localName;
3713
+ }
3714
+
3670
3715
  /**
3671
3716
  * This is an R3 `Node`-like wrapper for a raw `html.Comment` node. We do not currently
3672
3717
  * require the implementation of a visitor for Comments as they are only collected at
@@ -3838,43 +3883,47 @@ class BlockNode {
3838
3883
  }
3839
3884
  }
3840
3885
  class DeferredBlockPlaceholder extends BlockNode {
3841
- constructor(children, minimumTime, nameSpan, sourceSpan, startSourceSpan, endSourceSpan) {
3886
+ constructor(children, minimumTime, nameSpan, sourceSpan, startSourceSpan, endSourceSpan, i18n) {
3842
3887
  super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
3843
3888
  this.children = children;
3844
3889
  this.minimumTime = minimumTime;
3890
+ this.i18n = i18n;
3845
3891
  }
3846
3892
  visit(visitor) {
3847
3893
  return visitor.visitDeferredBlockPlaceholder(this);
3848
3894
  }
3849
3895
  }
3850
3896
  class DeferredBlockLoading extends BlockNode {
3851
- constructor(children, afterTime, minimumTime, nameSpan, sourceSpan, startSourceSpan, endSourceSpan) {
3897
+ constructor(children, afterTime, minimumTime, nameSpan, sourceSpan, startSourceSpan, endSourceSpan, i18n) {
3852
3898
  super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
3853
3899
  this.children = children;
3854
3900
  this.afterTime = afterTime;
3855
3901
  this.minimumTime = minimumTime;
3902
+ this.i18n = i18n;
3856
3903
  }
3857
3904
  visit(visitor) {
3858
3905
  return visitor.visitDeferredBlockLoading(this);
3859
3906
  }
3860
3907
  }
3861
3908
  class DeferredBlockError extends BlockNode {
3862
- constructor(children, nameSpan, sourceSpan, startSourceSpan, endSourceSpan) {
3909
+ constructor(children, nameSpan, sourceSpan, startSourceSpan, endSourceSpan, i18n) {
3863
3910
  super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
3864
3911
  this.children = children;
3912
+ this.i18n = i18n;
3865
3913
  }
3866
3914
  visit(visitor) {
3867
3915
  return visitor.visitDeferredBlockError(this);
3868
3916
  }
3869
3917
  }
3870
3918
  class DeferredBlock extends BlockNode {
3871
- constructor(children, triggers, prefetchTriggers, placeholder, loading, error, nameSpan, sourceSpan, mainBlockSpan, startSourceSpan, endSourceSpan) {
3919
+ constructor(children, triggers, prefetchTriggers, placeholder, loading, error, nameSpan, sourceSpan, mainBlockSpan, startSourceSpan, endSourceSpan, i18n) {
3872
3920
  super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
3873
3921
  this.children = children;
3874
3922
  this.placeholder = placeholder;
3875
3923
  this.loading = loading;
3876
3924
  this.error = error;
3877
3925
  this.mainBlockSpan = mainBlockSpan;
3926
+ this.i18n = i18n;
3878
3927
  this.triggers = triggers;
3879
3928
  this.prefetchTriggers = prefetchTriggers;
3880
3929
  // We cache the keys since we know that they won't change and we
@@ -3913,17 +3962,18 @@ class SwitchBlock extends BlockNode {
3913
3962
  }
3914
3963
  }
3915
3964
  class SwitchBlockCase extends BlockNode {
3916
- constructor(expression, children, sourceSpan, startSourceSpan, endSourceSpan, nameSpan) {
3965
+ constructor(expression, children, sourceSpan, startSourceSpan, endSourceSpan, nameSpan, i18n) {
3917
3966
  super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
3918
3967
  this.expression = expression;
3919
3968
  this.children = children;
3969
+ this.i18n = i18n;
3920
3970
  }
3921
3971
  visit(visitor) {
3922
3972
  return visitor.visitSwitchBlockCase(this);
3923
3973
  }
3924
3974
  }
3925
3975
  class ForLoopBlock extends BlockNode {
3926
- constructor(item, expression, trackBy, trackKeywordSpan, contextVariables, children, empty, sourceSpan, mainBlockSpan, startSourceSpan, endSourceSpan, nameSpan) {
3976
+ constructor(item, expression, trackBy, trackKeywordSpan, contextVariables, children, empty, sourceSpan, mainBlockSpan, startSourceSpan, endSourceSpan, nameSpan, i18n) {
3927
3977
  super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
3928
3978
  this.item = item;
3929
3979
  this.expression = expression;
@@ -3933,15 +3983,17 @@ class ForLoopBlock extends BlockNode {
3933
3983
  this.children = children;
3934
3984
  this.empty = empty;
3935
3985
  this.mainBlockSpan = mainBlockSpan;
3986
+ this.i18n = i18n;
3936
3987
  }
3937
3988
  visit(visitor) {
3938
3989
  return visitor.visitForLoopBlock(this);
3939
3990
  }
3940
3991
  }
3941
3992
  class ForLoopBlockEmpty extends BlockNode {
3942
- constructor(children, sourceSpan, startSourceSpan, endSourceSpan, nameSpan) {
3993
+ constructor(children, sourceSpan, startSourceSpan, endSourceSpan, nameSpan, i18n) {
3943
3994
  super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
3944
3995
  this.children = children;
3996
+ this.i18n = i18n;
3945
3997
  }
3946
3998
  visit(visitor) {
3947
3999
  return visitor.visitForLoopBlockEmpty(this);
@@ -3957,11 +4009,12 @@ class IfBlock extends BlockNode {
3957
4009
  }
3958
4010
  }
3959
4011
  class IfBlockBranch extends BlockNode {
3960
- constructor(expression, children, expressionAlias, sourceSpan, startSourceSpan, endSourceSpan, nameSpan) {
4012
+ constructor(expression, children, expressionAlias, sourceSpan, startSourceSpan, endSourceSpan, nameSpan, i18n) {
3961
4013
  super(nameSpan, sourceSpan, startSourceSpan, endSourceSpan);
3962
4014
  this.expression = expression;
3963
4015
  this.children = children;
3964
4016
  this.expressionAlias = expressionAlias;
4017
+ this.i18n = i18n;
3965
4018
  }
3966
4019
  visit(visitor) {
3967
4020
  return visitor.visitIfBlockBranch(this);
@@ -4231,6 +4284,21 @@ class IcuPlaceholder {
4231
4284
  return visitor.visitIcuPlaceholder(this, context);
4232
4285
  }
4233
4286
  }
4287
+ class BlockPlaceholder {
4288
+ constructor(name, parameters, startName, closeName, children, sourceSpan, startSourceSpan, endSourceSpan) {
4289
+ this.name = name;
4290
+ this.parameters = parameters;
4291
+ this.startName = startName;
4292
+ this.closeName = closeName;
4293
+ this.children = children;
4294
+ this.sourceSpan = sourceSpan;
4295
+ this.startSourceSpan = startSourceSpan;
4296
+ this.endSourceSpan = endSourceSpan;
4297
+ }
4298
+ visit(visitor, context) {
4299
+ return visitor.visitBlockPlaceholder(this, context);
4300
+ }
4301
+ }
4234
4302
  // Clone the AST
4235
4303
  class CloneVisitor {
4236
4304
  visitText(text, context) {
@@ -4256,6 +4324,10 @@ class CloneVisitor {
4256
4324
  visitIcuPlaceholder(ph, context) {
4257
4325
  return new IcuPlaceholder(ph.value, ph.name, ph.sourceSpan);
4258
4326
  }
4327
+ visitBlockPlaceholder(ph, context) {
4328
+ const children = ph.children.map(n => n.visit(this, context));
4329
+ return new BlockPlaceholder(ph.name, ph.parameters, ph.startName, ph.closeName, children, ph.sourceSpan, ph.startSourceSpan, ph.endSourceSpan);
4330
+ }
4259
4331
  }
4260
4332
  // Visit all the nodes recursively
4261
4333
  class RecurseVisitor {
@@ -4273,6 +4345,9 @@ class RecurseVisitor {
4273
4345
  }
4274
4346
  visitPlaceholder(ph, context) { }
4275
4347
  visitIcuPlaceholder(ph, context) { }
4348
+ visitBlockPlaceholder(ph, context) {
4349
+ ph.children.forEach(child => child.visit(this));
4350
+ }
4276
4351
  }
4277
4352
  /**
4278
4353
  * Serialize the message to the Localize backtick string format that would appear in compiled code.
@@ -4303,6 +4378,10 @@ class LocalizeMessageStringVisitor {
4303
4378
  visitIcuPlaceholder(ph) {
4304
4379
  return `{$${ph.name}}`;
4305
4380
  }
4381
+ visitBlockPlaceholder(ph) {
4382
+ const children = ph.children.map(child => child.visit(this)).join('');
4383
+ return `{$${ph.startName}}${children}{$${ph.closeName}}`;
4384
+ }
4306
4385
  }
4307
4386
 
4308
4387
  class Serializer {
@@ -4345,6 +4424,11 @@ class SimplePlaceholderMapper extends RecurseVisitor {
4345
4424
  visitPlaceholder(ph, context) {
4346
4425
  this.visitPlaceholderName(ph.name);
4347
4426
  }
4427
+ visitBlockPlaceholder(ph, context) {
4428
+ this.visitPlaceholderName(ph.startName);
4429
+ super.visitBlockPlaceholder(ph, context);
4430
+ this.visitPlaceholderName(ph.closeName);
4431
+ }
4348
4432
  visitIcuPlaceholder(ph, context) {
4349
4433
  this.visitPlaceholderName(ph.name);
4350
4434
  }
@@ -4557,6 +4641,17 @@ class _Visitor$1 {
4557
4641
  new Tag(_PLACEHOLDER_TAG$3, { name: ph.name }, [exTag, interpolationAsText])
4558
4642
  ];
4559
4643
  }
4644
+ visitBlockPlaceholder(ph, context) {
4645
+ const startAsText = new Text$1(`@${ph.name}`);
4646
+ const startEx = new Tag(_EXAMPLE_TAG, {}, [startAsText]);
4647
+ // TC requires PH to have a non empty EX, and uses the text node to show the "original" value.
4648
+ const startTagPh = new Tag(_PLACEHOLDER_TAG$3, { name: ph.startName }, [startEx, startAsText]);
4649
+ const closeAsText = new Text$1(`}`);
4650
+ const closeEx = new Tag(_EXAMPLE_TAG, {}, [closeAsText]);
4651
+ // TC requires PH to have a non empty EX, and uses the text node to show the "original" value.
4652
+ const closeTagPh = new Tag(_PLACEHOLDER_TAG$3, { name: ph.closeName }, [closeEx, closeAsText]);
4653
+ return [startTagPh, ...this.serialize(ph.children), closeTagPh];
4654
+ }
4560
4655
  visitIcuPlaceholder(ph, context) {
4561
4656
  const icuExpression = ph.value.expression;
4562
4657
  const icuType = ph.value.type;
@@ -4615,7 +4710,7 @@ const I18N_ATTR_PREFIX = 'i18n-';
4615
4710
  /** Prefix of var expressions used in ICUs */
4616
4711
  const I18N_ICU_VAR_PREFIX = 'VAR_';
4617
4712
  /** Prefix of ICU expressions for post processing */
4618
- const I18N_ICU_MAPPING_PREFIX = 'I18N_EXP_';
4713
+ const I18N_ICU_MAPPING_PREFIX$1 = 'I18N_EXP_';
4619
4714
  /** Placeholder wrapper for i18n expressions **/
4620
4715
  const I18N_PLACEHOLDER_SYMBOL = '�';
4621
4716
  function isI18nAttribute(name) {
@@ -4957,6 +5052,26 @@ class DefinitionMap {
4957
5052
  return literalMap(this.values);
4958
5053
  }
4959
5054
  }
5055
+ /**
5056
+ * Creates a `CssSelector` from an AST node.
5057
+ */
5058
+ function createCssSelectorFromNode(node) {
5059
+ const elementName = node instanceof Element$1 ? node.name : 'ng-template';
5060
+ const attributes = getAttrsForDirectiveMatching(node);
5061
+ const cssSelector = new CssSelector();
5062
+ const elementNameNoNs = splitNsName(elementName)[1];
5063
+ cssSelector.setElement(elementNameNoNs);
5064
+ Object.getOwnPropertyNames(attributes).forEach((name) => {
5065
+ const nameNoNs = splitNsName(name)[1];
5066
+ const value = attributes[name];
5067
+ cssSelector.addAttribute(nameNoNs, value);
5068
+ if (name.toLowerCase() === 'class') {
5069
+ const classes = value.trim().split(/\s+/);
5070
+ classes.forEach(className => cssSelector.addClassName(className));
5071
+ }
5072
+ });
5073
+ return cssSelector;
5074
+ }
4960
5075
  /**
4961
5076
  * Extract a map of properties to values for a given element or template node, which can be used
4962
5077
  * by the directive matching machinery.
@@ -5202,6 +5317,7 @@ class InterpolationConfig {
5202
5317
  }
5203
5318
  }
5204
5319
  const DEFAULT_INTERPOLATION_CONFIG = new InterpolationConfig('{{', '}}');
5320
+ const DEFAULT_CONTAINER_BLOCKS = new Set(['switch']);
5205
5321
 
5206
5322
  const $EOF = 0;
5207
5323
  const $BSPACE = 8;
@@ -8800,15 +8916,23 @@ var OpKind;
8800
8916
  /**
8801
8917
  * An instruction to create an ICU expression.
8802
8918
  */
8803
- OpKind[OpKind["Icu"] = 41] = "Icu";
8919
+ OpKind[OpKind["IcuStart"] = 41] = "IcuStart";
8804
8920
  /**
8805
8921
  * An instruction to update an ICU expression.
8806
8922
  */
8807
- OpKind[OpKind["IcuUpdate"] = 42] = "IcuUpdate";
8923
+ OpKind[OpKind["IcuEnd"] = 42] = "IcuEnd";
8924
+ /**
8925
+ * An instruction representing a placeholder in an ICU expression.
8926
+ */
8927
+ OpKind[OpKind["IcuPlaceholder"] = 43] = "IcuPlaceholder";
8808
8928
  /**
8809
8929
  * An i18n context containing information needed to generate an i18n message.
8810
8930
  */
8811
- OpKind[OpKind["I18nContext"] = 43] = "I18nContext";
8931
+ OpKind[OpKind["I18nContext"] = 44] = "I18nContext";
8932
+ /**
8933
+ * A creation op that corresponds to i18n attributes on an element.
8934
+ */
8935
+ OpKind[OpKind["I18nAttributes"] = 45] = "I18nAttributes";
8812
8936
  })(OpKind || (OpKind = {}));
8813
8937
  /**
8814
8938
  * Distinguishes different kinds of IR expressions.
@@ -8899,23 +9023,27 @@ var ExpressionKind;
8899
9023
  * An expression representing a sanitizer function.
8900
9024
  */
8901
9025
  ExpressionKind[ExpressionKind["SanitizerExpr"] = 20] = "SanitizerExpr";
9026
+ /**
9027
+ * An expression representing a function to create trusted values.
9028
+ */
9029
+ ExpressionKind[ExpressionKind["TrustedValueFnExpr"] = 21] = "TrustedValueFnExpr";
8902
9030
  /**
8903
9031
  * An expression that will cause a literal slot index to be emitted.
8904
9032
  */
8905
- ExpressionKind[ExpressionKind["SlotLiteralExpr"] = 21] = "SlotLiteralExpr";
9033
+ ExpressionKind[ExpressionKind["SlotLiteralExpr"] = 22] = "SlotLiteralExpr";
8906
9034
  /**
8907
9035
  * A test expression for a conditional op.
8908
9036
  */
8909
- ExpressionKind[ExpressionKind["ConditionalCase"] = 22] = "ConditionalCase";
9037
+ ExpressionKind[ExpressionKind["ConditionalCase"] = 23] = "ConditionalCase";
8910
9038
  /**
8911
9039
  * A variable for use inside a repeater, providing one of the ambiently-available context
8912
9040
  * properties ($even, $first, etc.).
8913
9041
  */
8914
- ExpressionKind[ExpressionKind["DerivedRepeaterVar"] = 23] = "DerivedRepeaterVar";
9042
+ ExpressionKind[ExpressionKind["DerivedRepeaterVar"] = 24] = "DerivedRepeaterVar";
8915
9043
  /**
8916
9044
  * An expression that will be automatically extracted to the component const array.
8917
9045
  */
8918
- ExpressionKind[ExpressionKind["ConstCollected"] = 24] = "ConstCollected";
9046
+ ExpressionKind[ExpressionKind["ConstCollected"] = 25] = "ConstCollected";
8919
9047
  })(ExpressionKind || (ExpressionKind = {}));
8920
9048
  var VariableFlags;
8921
9049
  (function (VariableFlags) {
@@ -8959,18 +9087,6 @@ var CompatibilityMode;
8959
9087
  CompatibilityMode[CompatibilityMode["Normal"] = 0] = "Normal";
8960
9088
  CompatibilityMode[CompatibilityMode["TemplateDefinitionBuilder"] = 1] = "TemplateDefinitionBuilder";
8961
9089
  })(CompatibilityMode || (CompatibilityMode = {}));
8962
- /**
8963
- * Represents functions used to sanitize different pieces of a template.
8964
- */
8965
- var SanitizerFn;
8966
- (function (SanitizerFn) {
8967
- SanitizerFn[SanitizerFn["Html"] = 0] = "Html";
8968
- SanitizerFn[SanitizerFn["Script"] = 1] = "Script";
8969
- SanitizerFn[SanitizerFn["Style"] = 2] = "Style";
8970
- SanitizerFn[SanitizerFn["Url"] = 3] = "Url";
8971
- SanitizerFn[SanitizerFn["ResourceUrl"] = 4] = "ResourceUrl";
8972
- SanitizerFn[SanitizerFn["IframeAttribute"] = 5] = "IframeAttribute";
8973
- })(SanitizerFn || (SanitizerFn = {}));
8974
9090
  /**
8975
9091
  * Enumeration of the different kinds of `@defer` secondary blocks.
8976
9092
  */
@@ -9030,6 +9146,20 @@ var I18nParamResolutionTime;
9030
9146
  */
9031
9147
  I18nParamResolutionTime[I18nParamResolutionTime["Postproccessing"] = 1] = "Postproccessing";
9032
9148
  })(I18nParamResolutionTime || (I18nParamResolutionTime = {}));
9149
+ /**
9150
+ * The contexts in which an i18n expression can be used.
9151
+ */
9152
+ var I18nExpressionFor;
9153
+ (function (I18nExpressionFor) {
9154
+ /**
9155
+ * This expression is used as a value (i.e. inside an i18n block).
9156
+ */
9157
+ I18nExpressionFor[I18nExpressionFor["I18nText"] = 0] = "I18nText";
9158
+ /**
9159
+ * This expression is used in a binding.
9160
+ */
9161
+ I18nExpressionFor[I18nExpressionFor["I18nAttribute"] = 1] = "I18nAttribute";
9162
+ })(I18nExpressionFor || (I18nExpressionFor = {}));
9033
9163
  /**
9034
9164
  * Flags that describe what an i18n param value. These determine how the value is serialized into
9035
9165
  * the final map.
@@ -9053,6 +9183,10 @@ var I18nParamValueFlags;
9053
9183
  * This value represents the closing of a tag.
9054
9184
  */
9055
9185
  I18nParamValueFlags[I18nParamValueFlags["CloseTag"] = 8] = "CloseTag";
9186
+ /**
9187
+ * This value represents an i18n expression index.
9188
+ */
9189
+ I18nParamValueFlags[I18nParamValueFlags["ExpressionIndex"] = 16] = "ExpressionIndex";
9056
9190
  })(I18nParamValueFlags || (I18nParamValueFlags = {}));
9057
9191
  /**
9058
9192
  * Whether the active namespace is HTML, MathML, or SVG mode.
@@ -9085,6 +9219,21 @@ var DerivedRepeaterVarIdentity;
9085
9219
  DerivedRepeaterVarIdentity[DerivedRepeaterVarIdentity["Even"] = 2] = "Even";
9086
9220
  DerivedRepeaterVarIdentity[DerivedRepeaterVarIdentity["Odd"] = 3] = "Odd";
9087
9221
  })(DerivedRepeaterVarIdentity || (DerivedRepeaterVarIdentity = {}));
9222
+ /**
9223
+ * Kinds of i18n contexts. They can be created because of root i18n blocks, or ICUs.
9224
+ */
9225
+ var I18nContextKind;
9226
+ (function (I18nContextKind) {
9227
+ I18nContextKind[I18nContextKind["RootI18n"] = 0] = "RootI18n";
9228
+ I18nContextKind[I18nContextKind["Icu"] = 1] = "Icu";
9229
+ I18nContextKind[I18nContextKind["Attr"] = 2] = "Attr";
9230
+ })(I18nContextKind || (I18nContextKind = {}));
9231
+ var TemplateKind;
9232
+ (function (TemplateKind) {
9233
+ TemplateKind[TemplateKind["NgTemplate"] = 0] = "NgTemplate";
9234
+ TemplateKind[TemplateKind["Structural"] = 1] = "Structural";
9235
+ TemplateKind[TemplateKind["Block"] = 2] = "Block";
9236
+ })(TemplateKind || (TemplateKind = {}));
9088
9237
 
9089
9238
  /**
9090
9239
  * Marker symbol for `ConsumesSlotOpTrait`.
@@ -9181,15 +9330,19 @@ const NEW_OP = {
9181
9330
  next: null,
9182
9331
  };
9183
9332
  class Interpolation {
9184
- constructor(strings, expressions) {
9333
+ constructor(strings, expressions, i18nPlaceholders) {
9185
9334
  this.strings = strings;
9186
9335
  this.expressions = expressions;
9336
+ this.i18nPlaceholders = i18nPlaceholders;
9337
+ if (i18nPlaceholders.length !== 0 && i18nPlaceholders.length !== expressions.length) {
9338
+ throw new Error(`Expected ${expressions.length} placeholders to match interpolation expression count, but got ${i18nPlaceholders.length}`);
9339
+ }
9187
9340
  }
9188
9341
  }
9189
9342
  /**
9190
9343
  * Create a `PropertyOp`.
9191
9344
  */
9192
- function createPropertyOp(target, name, expression, isAnimationTrigger, securityContext, isTemplate, sourceSpan) {
9345
+ function createPropertyOp(target, name, expression, isAnimationTrigger, securityContext, isStructuralTemplateAttribute, templateKind, i18nContext, i18nMessage, sourceSpan) {
9193
9346
  return {
9194
9347
  kind: OpKind.Property,
9195
9348
  target,
@@ -9198,7 +9351,10 @@ function createPropertyOp(target, name, expression, isAnimationTrigger, security
9198
9351
  isAnimationTrigger,
9199
9352
  securityContext,
9200
9353
  sanitizer: null,
9201
- isTemplate,
9354
+ isStructuralTemplateAttribute,
9355
+ templateKind,
9356
+ i18nContext,
9357
+ i18nMessage,
9202
9358
  sourceSpan,
9203
9359
  ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
9204
9360
  ...TRAIT_CONSUMES_VARS,
@@ -9263,7 +9419,7 @@ function createClassMapOp(xref, expression, sourceSpan) {
9263
9419
  /**
9264
9420
  * Create an `AttributeOp`.
9265
9421
  */
9266
- function createAttributeOp(target, name, expression, securityContext, isTextAttribute, isTemplate, sourceSpan) {
9422
+ function createAttributeOp(target, name, expression, securityContext, isTextAttribute, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan) {
9267
9423
  return {
9268
9424
  kind: OpKind.Attribute,
9269
9425
  target,
@@ -9272,7 +9428,10 @@ function createAttributeOp(target, name, expression, securityContext, isTextAttr
9272
9428
  securityContext,
9273
9429
  sanitizer: null,
9274
9430
  isTextAttribute,
9275
- isTemplate,
9431
+ isStructuralTemplateAttribute,
9432
+ templateKind,
9433
+ i18nContext: null,
9434
+ i18nMessage,
9276
9435
  sourceSpan,
9277
9436
  ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
9278
9437
  ...TRAIT_CONSUMES_VARS,
@@ -9293,15 +9452,19 @@ function createAdvanceOp(delta, sourceSpan) {
9293
9452
  /**
9294
9453
  * Create an i18n expression op.
9295
9454
  */
9296
- function createI18nExpressionOp(context, target, handle, expression, i18nPlaceholder, resolutionTime, sourceSpan) {
9455
+ function createI18nExpressionOp(context, target, i18nOwner, handle, expression, icuPlaceholder, i18nPlaceholder, resolutionTime, usage, name, sourceSpan) {
9297
9456
  return {
9298
9457
  kind: OpKind.I18nExpression,
9299
9458
  context,
9300
9459
  target,
9460
+ i18nOwner,
9301
9461
  handle,
9302
9462
  expression,
9463
+ icuPlaceholder,
9303
9464
  i18nPlaceholder,
9304
9465
  resolutionTime,
9466
+ usage,
9467
+ name,
9305
9468
  sourceSpan,
9306
9469
  ...NEW_OP,
9307
9470
  ...TRAIT_CONSUMES_VARS,
@@ -9309,12 +9472,12 @@ function createI18nExpressionOp(context, target, handle, expression, i18nPlaceho
9309
9472
  };
9310
9473
  }
9311
9474
  /**
9312
- *Creates an op to apply i18n expression ops
9475
+ * Creates an op to apply i18n expression ops.
9313
9476
  */
9314
- function createI18nApplyOp(target, handle, sourceSpan) {
9477
+ function createI18nApplyOp(owner, handle, sourceSpan) {
9315
9478
  return {
9316
9479
  kind: OpKind.I18nApply,
9317
- target,
9480
+ owner,
9318
9481
  handle,
9319
9482
  sourceSpan,
9320
9483
  ...NEW_OP,
@@ -9863,24 +10026,6 @@ class ReadTemporaryExpr extends ExpressionBase {
9863
10026
  return r;
9864
10027
  }
9865
10028
  }
9866
- class SanitizerExpr extends ExpressionBase {
9867
- constructor(fn) {
9868
- super();
9869
- this.fn = fn;
9870
- this.kind = ExpressionKind.SanitizerExpr;
9871
- }
9872
- visitExpression(visitor, context) { }
9873
- isEquivalent(e) {
9874
- return e instanceof SanitizerExpr && e.fn === this.fn;
9875
- }
9876
- isConstant() {
9877
- return true;
9878
- }
9879
- clone() {
9880
- return new SanitizerExpr(this.fn);
9881
- }
9882
- transformInternalExpressions() { }
9883
- }
9884
10029
  class SlotLiteralExpr extends ExpressionBase {
9885
10030
  constructor(slot) {
9886
10031
  super();
@@ -9977,7 +10122,6 @@ function transformExpressionsInOp(op, transform, flags) {
9977
10122
  case OpKind.ClassProp:
9978
10123
  case OpKind.ClassMap:
9979
10124
  case OpKind.Binding:
9980
- case OpKind.HostProperty:
9981
10125
  if (op.expression instanceof Interpolation) {
9982
10126
  transformExpressionsInInterpolation(op.expression, transform, flags);
9983
10127
  }
@@ -9986,6 +10130,7 @@ function transformExpressionsInOp(op, transform, flags) {
9986
10130
  }
9987
10131
  break;
9988
10132
  case OpKind.Property:
10133
+ case OpKind.HostProperty:
9989
10134
  case OpKind.Attribute:
9990
10135
  if (op.expression instanceof Interpolation) {
9991
10136
  transformExpressionsInInterpolation(op.expression, transform, flags);
@@ -10031,6 +10176,8 @@ function transformExpressionsInOp(op, transform, flags) {
10031
10176
  case OpKind.ExtractedAttribute:
10032
10177
  op.expression =
10033
10178
  op.expression && transformExpressionsInExpression(op.expression, transform, flags);
10179
+ op.trustedValueFn = op.trustedValueFn &&
10180
+ transformExpressionsInExpression(op.trustedValueFn, transform, flags);
10034
10181
  break;
10035
10182
  case OpKind.RepeaterCreate:
10036
10183
  op.track = transformExpressionsInExpression(op.track, transform, flags);
@@ -10049,6 +10196,9 @@ function transformExpressionsInOp(op, transform, flags) {
10049
10196
  op.placeholderConfig =
10050
10197
  transformExpressionsInExpression(op.placeholderConfig, transform, flags);
10051
10198
  }
10199
+ if (op.resolverFn !== null) {
10200
+ op.resolverFn = transformExpressionsInExpression(op.resolverFn, transform, flags);
10201
+ }
10052
10202
  break;
10053
10203
  case OpKind.I18nMessage:
10054
10204
  for (const [placeholder, expr] of op.params) {
@@ -10076,14 +10226,16 @@ function transformExpressionsInOp(op, transform, flags) {
10076
10226
  case OpKind.I18nContext:
10077
10227
  case OpKind.I18nEnd:
10078
10228
  case OpKind.I18nStart:
10079
- case OpKind.Icu:
10080
- case OpKind.IcuUpdate:
10229
+ case OpKind.IcuEnd:
10230
+ case OpKind.IcuStart:
10081
10231
  case OpKind.Namespace:
10082
10232
  case OpKind.Pipe:
10083
10233
  case OpKind.Projection:
10084
10234
  case OpKind.ProjectionDef:
10085
10235
  case OpKind.Template:
10086
10236
  case OpKind.Text:
10237
+ case OpKind.I18nAttributes:
10238
+ case OpKind.IcuPlaceholder:
10087
10239
  // These operations contain no expressions.
10088
10240
  break;
10089
10241
  default:
@@ -10104,6 +10256,9 @@ function transformExpressionsInExpression(expr, transform, flags) {
10104
10256
  expr.lhs = transformExpressionsInExpression(expr.lhs, transform, flags);
10105
10257
  expr.rhs = transformExpressionsInExpression(expr.rhs, transform, flags);
10106
10258
  }
10259
+ else if (expr instanceof UnaryOperatorExpr) {
10260
+ expr.expr = transformExpressionsInExpression(expr.expr, transform, flags);
10261
+ }
10107
10262
  else if (expr instanceof ReadPropExpr) {
10108
10263
  expr.receiver = transformExpressionsInExpression(expr.receiver, transform, flags);
10109
10264
  }
@@ -10155,6 +10310,15 @@ function transformExpressionsInExpression(expr, transform, flags) {
10155
10310
  expr.expressions[i] = transformExpressionsInExpression(expr.expressions[i], transform, flags);
10156
10311
  }
10157
10312
  }
10313
+ else if (expr instanceof NotExpr) {
10314
+ expr.condition = transformExpressionsInExpression(expr.condition, transform, flags);
10315
+ }
10316
+ else if (expr instanceof TaggedTemplateExpr) {
10317
+ expr.tag = transformExpressionsInExpression(expr.tag, transform, flags);
10318
+ expr.template.expressions =
10319
+ expr.template.expressions.map(e => transformExpressionsInExpression(e, transform, flags));
10320
+ }
10321
+ else if (expr instanceof WrappedNodeExpr) ;
10158
10322
  else if (expr instanceof ReadVarExpr || expr instanceof ExternalExpr ||
10159
10323
  expr instanceof LiteralExpr) ;
10160
10324
  else {
@@ -10513,23 +10677,28 @@ function createProjectionDefOp(def) {
10513
10677
  /**
10514
10678
  * Create an `ExtractedAttributeOp`.
10515
10679
  */
10516
- function createExtractedAttributeOp(target, bindingKind, name, expression) {
10680
+ function createExtractedAttributeOp(target, bindingKind, name, expression, i18nContext, i18nMessage, securityContext) {
10517
10681
  return {
10518
10682
  kind: OpKind.ExtractedAttribute,
10519
10683
  target,
10520
10684
  bindingKind,
10521
10685
  name,
10522
10686
  expression,
10687
+ i18nContext,
10688
+ i18nMessage,
10689
+ securityContext,
10690
+ trustedValueFn: null,
10523
10691
  ...NEW_OP,
10524
10692
  };
10525
10693
  }
10526
10694
  /**
10527
10695
  * Create an `ExtractedMessageOp`.
10528
10696
  */
10529
- function createI18nMessageOp(xref, i18nBlock, message, messagePlaceholder, params, postprocessingParams, needsPostprocessing) {
10697
+ function createI18nMessageOp(xref, i18nContext, i18nBlock, message, messagePlaceholder, params, postprocessingParams, needsPostprocessing) {
10530
10698
  return {
10531
10699
  kind: OpKind.I18nMessage,
10532
10700
  xref,
10701
+ i18nContext,
10533
10702
  i18nBlock,
10534
10703
  message,
10535
10704
  messagePlaceholder,
@@ -10567,9 +10736,26 @@ function createI18nEndOp(xref) {
10567
10736
  ...NEW_OP,
10568
10737
  };
10569
10738
  }
10570
- function createI18nContextOp(xref, i18nBlock, message, sourceSpan) {
10739
+ /**
10740
+ * Creates an ICU placeholder op.
10741
+ */
10742
+ function createIcuPlaceholderOp(xref, name, strings) {
10743
+ return {
10744
+ kind: OpKind.IcuPlaceholder,
10745
+ xref,
10746
+ name,
10747
+ strings,
10748
+ expressionPlaceholders: [],
10749
+ ...NEW_OP,
10750
+ };
10751
+ }
10752
+ function createI18nContextOp(contextKind, xref, i18nBlock, message, sourceSpan) {
10753
+ if (i18nBlock === null && contextKind !== I18nContextKind.Attr) {
10754
+ throw new Error('AssertionError: i18nBlock must be provided for non-attribute contexts.');
10755
+ }
10571
10756
  return {
10572
10757
  kind: OpKind.I18nContext,
10758
+ contextKind,
10573
10759
  xref,
10574
10760
  i18nBlock,
10575
10761
  message,
@@ -10580,12 +10766,15 @@ function createI18nContextOp(xref, i18nBlock, message, sourceSpan) {
10580
10766
  };
10581
10767
  }
10582
10768
 
10583
- function createHostPropertyOp(name, expression, isAnimationTrigger, sourceSpan) {
10769
+ function createHostPropertyOp(name, expression, isAnimationTrigger, i18nContext, securityContext, sourceSpan) {
10584
10770
  return {
10585
10771
  kind: OpKind.HostProperty,
10586
10772
  name,
10587
10773
  expression,
10588
10774
  isAnimationTrigger,
10775
+ i18nContext,
10776
+ securityContext,
10777
+ sanitizer: null,
10589
10778
  sourceSpan,
10590
10779
  ...TRAIT_CONSUMES_VARS,
10591
10780
  ...NEW_OP,
@@ -10809,12 +10998,20 @@ function removeAnys(e) {
10809
10998
  * Adds apply operations after i18n expressions.
10810
10999
  */
10811
11000
  function applyI18nExpressions(job) {
11001
+ const i18nContexts = new Map();
11002
+ for (const unit of job.units) {
11003
+ for (const op of unit.create) {
11004
+ if (op.kind === OpKind.I18nContext) {
11005
+ i18nContexts.set(op.xref, op);
11006
+ }
11007
+ }
11008
+ }
10812
11009
  for (const unit of job.units) {
10813
11010
  for (const op of unit.update) {
10814
11011
  // Only add apply after expressions that are not followed by more expressions.
10815
- if (op.kind === OpKind.I18nExpression && needsApplication(op)) {
11012
+ if (op.kind === OpKind.I18nExpression && needsApplication(i18nContexts, op)) {
10816
11013
  // TODO: what should be the source span for the apply op?
10817
- OpList.insertAfter(createI18nApplyOp(op.target, op.handle, null), op);
11014
+ OpList.insertAfter(createI18nApplyOp(op.i18nOwner, op.handle, null), op);
10818
11015
  }
10819
11016
  }
10820
11017
  }
@@ -10822,50 +11019,85 @@ function applyI18nExpressions(job) {
10822
11019
  /**
10823
11020
  * Checks whether the given expression op needs to be followed with an apply op.
10824
11021
  */
10825
- function needsApplication(op) {
11022
+ function needsApplication(i18nContexts, op) {
10826
11023
  // If the next op is not another expression, we need to apply.
10827
11024
  if (op.next?.kind !== OpKind.I18nExpression) {
10828
11025
  return true;
10829
11026
  }
10830
- // If the next op is an expression targeting a different i18n context, we need to apply.
10831
- if (op.next.context !== op.context) {
11027
+ const context = i18nContexts.get(op.context);
11028
+ const nextContext = i18nContexts.get(op.next.context);
11029
+ if (context === undefined) {
11030
+ throw new Error('AssertionError: expected an I18nContextOp to exist for the I18nExpressionOp\'s context');
11031
+ }
11032
+ if (nextContext === undefined) {
11033
+ throw new Error('AssertionError: expected an I18nContextOp to exist for the next I18nExpressionOp\'s context');
11034
+ }
11035
+ // If the next op is an expression targeting a different i18n block (or different element, in the
11036
+ // case of i18n attributes), we need to apply.
11037
+ // First, handle the case of i18n blocks.
11038
+ if (context.i18nBlock !== null) {
11039
+ // This is a block context. Compare the blocks.
11040
+ if (context.i18nBlock !== nextContext.i18nBlock) {
11041
+ return true;
11042
+ }
11043
+ return false;
11044
+ }
11045
+ // Second, handle the case of i18n attributes.
11046
+ if (op.i18nOwner !== op.next.i18nOwner) {
10832
11047
  return true;
10833
11048
  }
10834
11049
  return false;
10835
11050
  }
10836
11051
 
10837
11052
  /**
10838
- * Updates i18n expression ops to depend on the last slot in their owning i18n block.
11053
+ * Updates i18n expression ops to target the last slot in their owning i18n block, and moves them
11054
+ * after the last update instruction that depends on that slot.
10839
11055
  */
10840
11056
  function assignI18nSlotDependencies(job) {
10841
- const i18nLastSlotConsumers = new Map();
10842
- const i18nContexts = new Map();
10843
- let lastSlotConsumer = null;
10844
- let currentI18nOp = null;
10845
11057
  for (const unit of job.units) {
10846
- // Record the last consumed slot before each i18n end instruction.
10847
- for (const op of unit.create) {
10848
- if (hasConsumesSlotTrait(op)) {
10849
- lastSlotConsumer = op.xref;
11058
+ // The first update op.
11059
+ let updateOp = unit.update.head;
11060
+ // I18n expressions currently being moved during the iteration.
11061
+ let i18nExpressionsInProgress = [];
11062
+ // Non-null while we are iterating through an i18nStart/i18nEnd pair
11063
+ let state = null;
11064
+ for (const createOp of unit.create) {
11065
+ if (createOp.kind === OpKind.I18nStart) {
11066
+ state = {
11067
+ blockXref: createOp.xref,
11068
+ lastSlotConsumer: createOp.xref,
11069
+ };
10850
11070
  }
10851
- switch (op.kind) {
10852
- case OpKind.I18nStart:
10853
- currentI18nOp = op;
10854
- break;
10855
- case OpKind.I18nEnd:
10856
- i18nLastSlotConsumers.set(currentI18nOp.xref, lastSlotConsumer);
10857
- currentI18nOp = null;
10858
- break;
10859
- case OpKind.I18nContext:
10860
- i18nContexts.set(op.xref, op);
10861
- break;
11071
+ else if (createOp.kind === OpKind.I18nEnd) {
11072
+ for (const op of i18nExpressionsInProgress) {
11073
+ op.target = state.lastSlotConsumer;
11074
+ OpList.insertBefore(op, updateOp);
11075
+ }
11076
+ i18nExpressionsInProgress.length = 0;
11077
+ state = null;
10862
11078
  }
10863
- }
10864
- // Assign i18n expressions to target the last slot in its owning block.
10865
- for (const op of unit.update) {
10866
- if (op.kind === OpKind.I18nExpression) {
10867
- const i18nContext = i18nContexts.get(op.context);
10868
- op.target = i18nLastSlotConsumers.get(i18nContext.i18nBlock);
11079
+ if (hasConsumesSlotTrait(createOp)) {
11080
+ if (state !== null) {
11081
+ state.lastSlotConsumer = createOp.xref;
11082
+ }
11083
+ while (true) {
11084
+ if (updateOp.next === null) {
11085
+ break;
11086
+ }
11087
+ if (state !== null && updateOp.kind === OpKind.I18nExpression &&
11088
+ updateOp.usage === I18nExpressionFor.I18nText &&
11089
+ updateOp.i18nOwner === state.blockXref) {
11090
+ const opToRemove = updateOp;
11091
+ updateOp = updateOp.next;
11092
+ OpList.remove(opToRemove);
11093
+ i18nExpressionsInProgress.push(opToRemove);
11094
+ continue;
11095
+ }
11096
+ if (hasDependsOnSlotContextTrait(updateOp) && updateOp.target !== createOp.xref) {
11097
+ break;
11098
+ }
11099
+ updateOp = updateOp.next;
11100
+ }
10869
11101
  }
10870
11102
  }
10871
11103
  }
@@ -10899,22 +11131,42 @@ function extractAttributes(job) {
10899
11131
  break;
10900
11132
  case OpKind.Property:
10901
11133
  if (!op.isAnimationTrigger) {
10902
- OpList.insertBefore(createExtractedAttributeOp(op.target, op.isTemplate ? BindingKind.Template : BindingKind.Property, op.name, null), lookupElement$2(elements, op.target));
11134
+ let bindingKind;
11135
+ if (op.i18nMessage !== null && op.templateKind === null) {
11136
+ // If the binding has an i18n context, it is an i18n attribute, and should have that
11137
+ // kind in the consts array.
11138
+ bindingKind = BindingKind.I18n;
11139
+ }
11140
+ else if (op.isStructuralTemplateAttribute) {
11141
+ bindingKind = BindingKind.Template;
11142
+ }
11143
+ else {
11144
+ bindingKind = BindingKind.Property;
11145
+ }
11146
+ OpList.insertBefore(
11147
+ // Deliberaly null i18nMessage value
11148
+ createExtractedAttributeOp(op.target, bindingKind, op.name, /* expression */ null, /* i18nContext */ null,
11149
+ /* i18nMessage */ null, op.securityContext), lookupElement$2(elements, op.target));
10903
11150
  }
10904
11151
  break;
10905
11152
  case OpKind.StyleProp:
10906
11153
  case OpKind.ClassProp:
11154
+ // TODO: Can style or class bindings be i18n attributes?
10907
11155
  // The old compiler treated empty style bindings as regular bindings for the purpose of
10908
11156
  // directive matching. That behavior is incorrect, but we emulate it in compatibility
10909
11157
  // mode.
10910
11158
  if (unit.job.compatibility === CompatibilityMode.TemplateDefinitionBuilder &&
10911
11159
  op.expression instanceof EmptyExpr) {
10912
- OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.Property, op.name, null), lookupElement$2(elements, op.target));
11160
+ OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.Property, op.name, /* expression */ null,
11161
+ /* i18nContext */ null,
11162
+ /* i18nMessage */ null, SecurityContext.STYLE), lookupElement$2(elements, op.target));
10913
11163
  }
10914
11164
  break;
10915
11165
  case OpKind.Listener:
10916
11166
  if (!op.isAnimationListener) {
10917
- const extractedAttributeOp = createExtractedAttributeOp(op.target, BindingKind.Property, op.name, null);
11167
+ const extractedAttributeOp = createExtractedAttributeOp(op.target, BindingKind.Property, op.name, /* expression */ null,
11168
+ /* i18nContext */ null,
11169
+ /* i18nMessage */ null, SecurityContext.NONE);
10918
11170
  if (job.kind === CompilationJobKind.Host) {
10919
11171
  // This attribute will apply to the enclosing host binding compilation unit, so order
10920
11172
  // doesn't matter.
@@ -10946,24 +11198,14 @@ function extractAttributeOp(unit, op, elements) {
10946
11198
  if (op.expression instanceof Interpolation) {
10947
11199
  return;
10948
11200
  }
10949
- let extractable = op.expression.isConstant();
11201
+ let extractable = op.isTextAttribute || op.expression.isConstant();
10950
11202
  if (unit.job.compatibility === CompatibilityMode.TemplateDefinitionBuilder) {
10951
- // TemplateDefinitionBuilder only extracted attributes that were string literals.
10952
- extractable = isStringLiteral(op.expression);
10953
- if (op.name === 'style' || op.name === 'class') {
10954
- // For style and class attributes, TemplateDefinitionBuilder only extracted them if they were
10955
- // text attributes. For example, `[attr.class]="'my-class'"` was not extracted despite being a
10956
- // string literal, because it is not a text attribute.
10957
- extractable &&= op.isTextAttribute;
10958
- }
10959
- if (unit.job.kind === CompilationJobKind.Host) {
10960
- // TemplateDefinitionBuilder also does not seem to extract string literals if they are part of
10961
- // a host attribute.
10962
- extractable &&= op.isTextAttribute;
10963
- }
11203
+ // TemplateDefinitionBuilder only extracts text attributes. It does not extract attriibute
11204
+ // bindings, even if they are constants.
11205
+ extractable &&= op.isTextAttribute;
10964
11206
  }
10965
11207
  if (extractable) {
10966
- const extractedAttributeOp = createExtractedAttributeOp(op.target, op.isTemplate ? BindingKind.Template : BindingKind.Attribute, op.name, op.expression);
11208
+ const extractedAttributeOp = createExtractedAttributeOp(op.target, op.isStructuralTemplateAttribute ? BindingKind.Template : BindingKind.Attribute, op.name, op.expression, op.i18nContext, op.i18nMessage, op.securityContext);
10967
11209
  if (unit.job.kind === CompilationJobKind.Host) {
10968
11210
  // This attribute will apply to the enclosing host binding compilation unit, so order doesn't
10969
11211
  // matter.
@@ -11010,16 +11252,16 @@ function specializeBindings(job) {
11010
11252
  target.nonBindable = true;
11011
11253
  }
11012
11254
  else {
11013
- OpList.replace(op, createAttributeOp(op.target, op.name, op.expression, op.securityContext, op.isTextAttribute, op.isTemplate, op.sourceSpan));
11255
+ OpList.replace(op, createAttributeOp(op.target, op.name, op.expression, op.securityContext, op.isTextAttribute, op.isStructuralTemplateAttribute, op.templateKind, op.i18nMessage, op.sourceSpan));
11014
11256
  }
11015
11257
  break;
11016
11258
  case BindingKind.Property:
11017
11259
  case BindingKind.Animation:
11018
11260
  if (job.kind === CompilationJobKind.Host) {
11019
- OpList.replace(op, createHostPropertyOp(op.name, op.expression, op.bindingKind === BindingKind.Animation, op.sourceSpan));
11261
+ OpList.replace(op, createHostPropertyOp(op.name, op.expression, op.bindingKind === BindingKind.Animation, op.i18nContext, op.securityContext, op.sourceSpan));
11020
11262
  }
11021
11263
  else {
11022
- OpList.replace(op, createPropertyOp(op.target, op.name, op.expression, op.bindingKind === BindingKind.Animation, op.securityContext, op.isTemplate, op.sourceSpan));
11264
+ OpList.replace(op, createPropertyOp(op.target, op.name, op.expression, op.bindingKind === BindingKind.Animation, op.securityContext, op.isStructuralTemplateAttribute, op.templateKind, op.i18nContext, op.i18nMessage, op.sourceSpan));
11023
11265
  }
11024
11266
  break;
11025
11267
  case BindingKind.I18n:
@@ -11196,41 +11438,6 @@ function generateConditionalExpressions(job) {
11196
11438
  }
11197
11439
  }
11198
11440
 
11199
- exports.TagContentType = void 0;
11200
- (function (TagContentType) {
11201
- TagContentType[TagContentType["RAW_TEXT"] = 0] = "RAW_TEXT";
11202
- TagContentType[TagContentType["ESCAPABLE_RAW_TEXT"] = 1] = "ESCAPABLE_RAW_TEXT";
11203
- TagContentType[TagContentType["PARSABLE_DATA"] = 2] = "PARSABLE_DATA";
11204
- })(exports.TagContentType || (exports.TagContentType = {}));
11205
- function splitNsName(elementName) {
11206
- if (elementName[0] != ':') {
11207
- return [null, elementName];
11208
- }
11209
- const colonIndex = elementName.indexOf(':', 1);
11210
- if (colonIndex === -1) {
11211
- throw new Error(`Unsupported format "${elementName}" expecting ":namespace:name"`);
11212
- }
11213
- return [elementName.slice(1, colonIndex), elementName.slice(colonIndex + 1)];
11214
- }
11215
- // `<ng-container>` tags work the same regardless the namespace
11216
- function isNgContainer(tagName) {
11217
- return splitNsName(tagName)[1] === 'ng-container';
11218
- }
11219
- // `<ng-content>` tags work the same regardless the namespace
11220
- function isNgContent(tagName) {
11221
- return splitNsName(tagName)[1] === 'ng-content';
11222
- }
11223
- // `<ng-template>` tags work the same regardless the namespace
11224
- function isNgTemplate(tagName) {
11225
- return splitNsName(tagName)[1] === 'ng-template';
11226
- }
11227
- function getNsPrefix(fullName) {
11228
- return fullName === null ? null : splitNsName(fullName)[0];
11229
- }
11230
- function mergeNsAndName(prefix, localName) {
11231
- return prefix ? `:${prefix}:${localName}` : localName;
11232
- }
11233
-
11234
11441
  new Map([
11235
11442
  ['&&', exports.BinaryOperator.And],
11236
11443
  ['>', exports.BinaryOperator.Bigger],
@@ -11267,9 +11474,9 @@ function collectElementConsts(job) {
11267
11474
  for (const unit of job.units) {
11268
11475
  for (const op of unit.create) {
11269
11476
  if (op.kind === OpKind.ExtractedAttribute) {
11270
- const attributes = allElementAttributes.get(op.target) || new ElementAttributes();
11477
+ const attributes = allElementAttributes.get(op.target) || new ElementAttributes(job.compatibility);
11271
11478
  allElementAttributes.set(op.target, attributes);
11272
- attributes.add(op.bindingKind, op.name, op.expression);
11479
+ attributes.add(op.bindingKind, op.name, op.expression, op.trustedValueFn);
11273
11480
  OpList.remove(op);
11274
11481
  }
11275
11482
  }
@@ -11312,11 +11519,6 @@ const FLYWEIGHT_ARRAY = Object.freeze([]);
11312
11519
  * Container for all of the various kinds of attributes which are applied on an element.
11313
11520
  */
11314
11521
  class ElementAttributes {
11315
- constructor() {
11316
- this.known = new Set();
11317
- this.byKind = new Map;
11318
- this.projectAs = null;
11319
- }
11320
11522
  get attributes() {
11321
11523
  return this.byKind.get(BindingKind.Attribute) ?? FLYWEIGHT_ARRAY;
11322
11524
  }
@@ -11335,11 +11537,32 @@ class ElementAttributes {
11335
11537
  get i18n() {
11336
11538
  return this.byKind.get(BindingKind.I18n) ?? FLYWEIGHT_ARRAY;
11337
11539
  }
11338
- add(kind, name, value) {
11339
- if (this.known.has(name)) {
11540
+ constructor(compatibility) {
11541
+ this.compatibility = compatibility;
11542
+ this.known = new Map();
11543
+ this.byKind = new Map;
11544
+ this.projectAs = null;
11545
+ }
11546
+ isKnown(kind, name, value) {
11547
+ const nameToValue = this.known.get(kind) ?? new Set();
11548
+ this.known.set(kind, nameToValue);
11549
+ if (nameToValue.has(name)) {
11550
+ return true;
11551
+ }
11552
+ nameToValue.add(name);
11553
+ return false;
11554
+ }
11555
+ add(kind, name, value, trustedValueFn) {
11556
+ // TemplateDefinitionBuilder puts duplicate attribute, class, and style values into the consts
11557
+ // array. This seems inefficient, we can probably keep just the first one or the last value
11558
+ // (whichever actually gets applied when multiple values are listed for the same attribute).
11559
+ const allowDuplicates = this.compatibility === CompatibilityMode.TemplateDefinitionBuilder &&
11560
+ (kind === BindingKind.Attribute || kind === BindingKind.ClassName ||
11561
+ kind === BindingKind.StyleProperty);
11562
+ if (!allowDuplicates && this.isKnown(kind, name, value)) {
11340
11563
  return;
11341
11564
  }
11342
- this.known.add(name);
11565
+ // TODO: Can this be its own phase
11343
11566
  if (name === 'ngProjectAs') {
11344
11567
  if (value === null || !(value instanceof LiteralExpr) || (value.value == null) ||
11345
11568
  (typeof value.value?.toString() !== 'string')) {
@@ -11353,9 +11576,17 @@ class ElementAttributes {
11353
11576
  array.push(...getAttributeNameLiterals$1(name));
11354
11577
  if (kind === BindingKind.Attribute || kind === BindingKind.StyleProperty) {
11355
11578
  if (value === null) {
11356
- throw Error('Attribute & style element attributes must have a value');
11579
+ throw Error('Attribute, i18n attribute, & style element attributes must have a value');
11580
+ }
11581
+ if (trustedValueFn !== null) {
11582
+ if (!isStringLiteral(value)) {
11583
+ throw Error('AssertionError: extracted attribute value should be string literal');
11584
+ }
11585
+ array.push(taggedTemplate(trustedValueFn, new TemplateLiteral([new TemplateLiteralElement(value.value)], []), undefined, value.sourceSpan));
11586
+ }
11587
+ else {
11588
+ array.push(value);
11357
11589
  }
11358
- array.push(value);
11359
11590
  }
11360
11591
  }
11361
11592
  arrayFor(kind) {
@@ -11369,7 +11600,7 @@ class ElementAttributes {
11369
11600
  * Gets an array of literal expressions representing the attribute's namespaced name.
11370
11601
  */
11371
11602
  function getAttributeNameLiterals$1(name) {
11372
- const [attributeNamespace, attributeName] = splitNsName(name);
11603
+ const [attributeNamespace, attributeName] = splitNsName(name, false);
11373
11604
  const nameLiteral = literal(attributeName);
11374
11605
  if (attributeNamespace) {
11375
11606
  return [
@@ -11407,6 +11638,50 @@ function serializeAttributes({ attributes, bindings, classes, i18n, projectAs, s
11407
11638
  return literalArr(attrArray);
11408
11639
  }
11409
11640
 
11641
+ /**
11642
+ * Some binding instructions in the update block may actually correspond to i18n bindings. In that
11643
+ * case, they should be replaced with i18nExp instructions for the dynamic portions.
11644
+ */
11645
+ function convertI18nBindings(job) {
11646
+ const i18nAttributesByElem = new Map();
11647
+ for (const unit of job.units) {
11648
+ for (const op of unit.create) {
11649
+ if (op.kind === OpKind.I18nAttributes) {
11650
+ i18nAttributesByElem.set(op.target, op);
11651
+ }
11652
+ }
11653
+ for (const op of unit.update) {
11654
+ switch (op.kind) {
11655
+ case OpKind.Property:
11656
+ case OpKind.Attribute:
11657
+ if (op.i18nContext === null) {
11658
+ continue;
11659
+ }
11660
+ if (!(op.expression instanceof Interpolation)) {
11661
+ continue;
11662
+ }
11663
+ const i18nAttributesForElem = i18nAttributesByElem.get(op.target);
11664
+ if (i18nAttributesForElem === undefined) {
11665
+ throw new Error('AssertionError: An i18n attribute binding instruction requires the owning element to have an I18nAttributes create instruction');
11666
+ }
11667
+ if (i18nAttributesForElem.target !== op.target) {
11668
+ throw new Error('AssertionError: Expected i18nAttributes target element to match binding target element');
11669
+ }
11670
+ const ops = [];
11671
+ for (let i = 0; i < op.expression.expressions.length; i++) {
11672
+ const expr = op.expression.expressions[i];
11673
+ if (op.expression.i18nPlaceholders.length !== op.expression.expressions.length) {
11674
+ throw new Error(`AssertionError: An i18n attribute binding instruction requires the same number of expressions and placeholders, but found ${op.expression.i18nPlaceholders.length} placeholders and ${op.expression.expressions.length} expressions`);
11675
+ }
11676
+ ops.push(createI18nExpressionOp(op.i18nContext, i18nAttributesForElem.target, i18nAttributesForElem.xref, i18nAttributesForElem.handle, expr, null, op.expression.i18nPlaceholders[i], I18nParamResolutionTime.Creation, I18nExpressionFor.I18nAttribute, op.name, op.sourceSpan));
11677
+ }
11678
+ OpList.replaceWithMany(op, ops);
11679
+ break;
11680
+ }
11681
+ }
11682
+ }
11683
+ }
11684
+
11410
11685
  /**
11411
11686
  * Create extracted deps functions for defer ops.
11412
11687
  */
@@ -11435,7 +11710,8 @@ function createDeferDepsFns(job) {
11435
11710
  if (op.handle.slot === null) {
11436
11711
  throw new Error('AssertionError: slot must be assigned bfore extracting defer deps functions');
11437
11712
  }
11438
- op.resolverFn = job.pool.getSharedFunctionReference(depsFnExpr, `${job.componentName}_Defer_${op.handle.slot}_DepsFn`);
11713
+ op.resolverFn = job.pool.getSharedFunctionReference(depsFnExpr, `${job.componentName}_Defer_${op.handle.slot}_DepsFn`,
11714
+ /* Don't use unique names for TDB compatibility */ false);
11439
11715
  }
11440
11716
  }
11441
11717
  }
@@ -11452,37 +11728,85 @@ function createDeferDepsFns(job) {
11452
11728
  * message.)
11453
11729
  */
11454
11730
  function createI18nContexts(job) {
11731
+ // Create i18n context ops for i18n attrs.
11732
+ const attrContextByMessage = new Map();
11733
+ for (const unit of job.units) {
11734
+ for (const op of unit.ops()) {
11735
+ switch (op.kind) {
11736
+ case OpKind.Binding:
11737
+ case OpKind.Property:
11738
+ case OpKind.Attribute:
11739
+ case OpKind.ExtractedAttribute:
11740
+ if (op.i18nMessage === null) {
11741
+ continue;
11742
+ }
11743
+ if (!attrContextByMessage.has(op.i18nMessage)) {
11744
+ const i18nContext = createI18nContextOp(I18nContextKind.Attr, job.allocateXrefId(), null, op.i18nMessage, null);
11745
+ unit.create.push(i18nContext);
11746
+ attrContextByMessage.set(op.i18nMessage, i18nContext.xref);
11747
+ }
11748
+ op.i18nContext = attrContextByMessage.get(op.i18nMessage);
11749
+ break;
11750
+ }
11751
+ }
11752
+ }
11753
+ // Create i18n context ops for root i18n blocks.
11754
+ const blockContextByI18nBlock = new Map();
11755
+ for (const unit of job.units) {
11756
+ for (const op of unit.create) {
11757
+ switch (op.kind) {
11758
+ case OpKind.I18nStart:
11759
+ if (op.xref === op.root) {
11760
+ const contextOp = createI18nContextOp(I18nContextKind.RootI18n, job.allocateXrefId(), op.xref, op.message, null);
11761
+ unit.create.push(contextOp);
11762
+ op.context = contextOp.xref;
11763
+ blockContextByI18nBlock.set(op.xref, contextOp);
11764
+ }
11765
+ break;
11766
+ }
11767
+ }
11768
+ }
11769
+ // Assign i18n contexts for child i18n blocks. These don't need their own conext, instead they
11770
+ // should inherit from their root i18n block.
11771
+ for (const unit of job.units) {
11772
+ for (const op of unit.create) {
11773
+ if (op.kind === OpKind.I18nStart && op.xref !== op.root) {
11774
+ const rootContext = blockContextByI18nBlock.get(op.root);
11775
+ if (rootContext === undefined) {
11776
+ throw Error('AssertionError: Root i18n block i18n context should have been created.');
11777
+ }
11778
+ op.context = rootContext.xref;
11779
+ blockContextByI18nBlock.set(op.xref, rootContext);
11780
+ }
11781
+ }
11782
+ }
11783
+ // Create or assign i18n contexts for ICUs.
11455
11784
  let currentI18nOp = null;
11456
- let xref;
11457
11785
  for (const unit of job.units) {
11458
11786
  for (const op of unit.create) {
11459
11787
  switch (op.kind) {
11460
11788
  case OpKind.I18nStart:
11461
- // Each i18n block gets its own context.
11462
- xref = job.allocateXrefId();
11463
- unit.create.push(createI18nContextOp(xref, op.xref, op.message, null));
11464
- op.context = xref;
11465
11789
  currentI18nOp = op;
11466
11790
  break;
11467
11791
  case OpKind.I18nEnd:
11468
11792
  currentI18nOp = null;
11469
11793
  break;
11470
- case OpKind.Icu:
11471
- // If an ICU represents a different message than its containing block, we give it its own
11472
- // i18n context.
11794
+ case OpKind.IcuStart:
11473
11795
  if (currentI18nOp === null) {
11474
- throw Error('Unexpected ICU outside of an i18n block.');
11796
+ throw Error('AssertionError: Unexpected ICU outside of an i18n block.');
11475
11797
  }
11476
11798
  if (op.message.id !== currentI18nOp.message.id) {
11477
- // There was an enclosing i18n block around this ICU somewhere.
11478
- xref = job.allocateXrefId();
11479
- unit.create.push(createI18nContextOp(xref, currentI18nOp.xref, op.message, null));
11480
- op.context = xref;
11799
+ // This ICU is a sub-message inside its parent i18n block message. We need to give it
11800
+ // its own context.
11801
+ const contextOp = createI18nContextOp(I18nContextKind.Icu, job.allocateXrefId(), currentI18nOp.xref, op.message, null);
11802
+ unit.create.push(contextOp);
11803
+ op.context = contextOp.xref;
11481
11804
  }
11482
11805
  else {
11483
- // The i18n block was generated because of this ICU, OR it was explicit, but the ICU is
11484
- // the only localizable content inside of it.
11806
+ // This ICU is the only translatable content in its parent i18n block. We need to
11807
+ // convert the parent's context into an ICU context.
11485
11808
  op.context = currentI18nOp.context;
11809
+ blockContextByI18nBlock.get(currentI18nOp.xref).contextKind = I18nContextKind.Icu;
11486
11810
  }
11487
11811
  break;
11488
11812
  }
@@ -11491,44 +11815,27 @@ function createI18nContexts(job) {
11491
11815
  }
11492
11816
 
11493
11817
  /**
11494
- * Replace the ICU update ops with i18n expression ops.
11818
+ * Deduplicate text bindings, e.g. <div class="cls1" class="cls2">
11495
11819
  */
11496
- function createI18nIcuExpressions(job) {
11497
- const icus = new Map();
11498
- const i18nContexts = new Map();
11499
- const i18nBlocks = new Map();
11500
- // Collect maps of ops that need to be referenced to create the I18nExpressionOps.
11820
+ function deduplicateTextBindings(job) {
11821
+ const seen = new Map();
11501
11822
  for (const unit of job.units) {
11502
- for (const op of unit.create) {
11503
- switch (op.kind) {
11504
- case OpKind.Icu:
11505
- icus.set(op.xref, op);
11506
- break;
11507
- case OpKind.I18nContext:
11508
- i18nContexts.set(op.xref, op);
11509
- break;
11510
- case OpKind.I18nStart:
11511
- i18nBlocks.set(op.xref, op);
11512
- break;
11513
- }
11514
- }
11515
- // Replace each IcuUpdateOp with an I18nExpressionOp.
11516
- for (const op of unit.update) {
11517
- switch (op.kind) {
11518
- case OpKind.IcuUpdate:
11519
- const icuOp = icus.get(op.xref);
11520
- if (icuOp?.icu.expressionPlaceholder === undefined) {
11521
- throw Error('ICU should have an i18n placeholder');
11522
- }
11523
- if (icuOp.context === null) {
11524
- throw Error('ICU should have its i18n context set');
11823
+ for (const op of unit.update.reversed()) {
11824
+ if (op.kind === OpKind.Binding && op.isTextAttribute) {
11825
+ const seenForElement = seen.get(op.target) || new Set();
11826
+ if (seenForElement.has(op.name)) {
11827
+ if (job.compatibility === CompatibilityMode.TemplateDefinitionBuilder) {
11828
+ // For most duplicated attributes, TemplateDefinitionBuilder lists all of the values in
11829
+ // the consts array. However, for style and class attributes it only keeps the last one.
11830
+ // We replicate that behavior here since it has actual consequences for apps with
11831
+ // duplicate class or style attrs.
11832
+ if (op.name === 'style' || op.name === 'class') {
11833
+ OpList.remove(op);
11834
+ }
11525
11835
  }
11526
- const i18nContext = i18nContexts.get(icuOp.context);
11527
- const i18nBlock = i18nBlocks.get(i18nContext.i18nBlock);
11528
- OpList.replace(op, createI18nExpressionOp(i18nContext.xref, i18nBlock.xref, i18nBlock.handle, new LexicalReadExpr(icuOp.icu.expression), icuOp.icu.expressionPlaceholder,
11529
- // ICU-based i18n Expressions are resolved during post-processing.
11530
- I18nParamResolutionTime.Postproccessing, null));
11531
- break;
11836
+ }
11837
+ seenForElement.add(op.name);
11838
+ seen.set(op.target, seenForElement);
11532
11839
  }
11533
11840
  }
11534
11841
  }
@@ -11880,7 +12187,7 @@ function ternaryTransform(e) {
11880
12187
  /**
11881
12188
  * The escape sequence used indicate message param values.
11882
12189
  */
11883
- const ESCAPE = '\uFFFD';
12190
+ const ESCAPE$1 = '\uFFFD';
11884
12191
  /**
11885
12192
  * Marker used to indicate an element tag.
11886
12193
  */
@@ -11914,52 +12221,69 @@ const LIST_DELIMITER = '|';
11914
12221
  * used in the final output.
11915
12222
  */
11916
12223
  function extractI18nMessages(job) {
11917
- // Save the i18n context ops for later use.
12224
+ // Create an i18n message for each context.
12225
+ // TODO: Merge the context op with the message op since they're 1:1 anyways.
12226
+ const i18nMessagesByContext = new Map();
12227
+ const i18nBlocks = new Map();
11918
12228
  const i18nContexts = new Map();
11919
- // Record which contexts represent i18n blocks (any other contexts are assumed to have been
11920
- // created from ICUs).
11921
- const i18nBlockContexts = new Set();
11922
12229
  for (const unit of job.units) {
11923
12230
  for (const op of unit.create) {
11924
12231
  switch (op.kind) {
11925
12232
  case OpKind.I18nContext:
12233
+ const i18nMessageOp = createI18nMessage(job, op);
12234
+ unit.create.push(i18nMessageOp);
12235
+ i18nMessagesByContext.set(op.xref, i18nMessageOp);
11926
12236
  i18nContexts.set(op.xref, op);
11927
12237
  break;
11928
12238
  case OpKind.I18nStart:
11929
- i18nBlockContexts.add(op.context);
12239
+ i18nBlocks.set(op.xref, op);
11930
12240
  break;
11931
12241
  }
11932
12242
  }
11933
12243
  }
11934
- // Extract messages from root i18n blocks.
11935
- const i18nBlockMessages = new Map();
11936
- for (const unit of job.units) {
11937
- for (const op of unit.create) {
11938
- if (op.kind === OpKind.I18nStart && op.xref === op.root) {
11939
- if (!op.context) {
11940
- throw Error('I18n start op should have its context set.');
11941
- }
11942
- const i18nMessageOp = createI18nMessage(job, i18nContexts.get(op.context));
11943
- i18nBlockMessages.set(op.xref, i18nMessageOp);
11944
- unit.create.push(i18nMessageOp);
11945
- }
11946
- }
11947
- }
11948
- // Extract messages from ICUs with their own sub-context.
12244
+ // Associate sub-messages for ICUs with their root message. At this point we can also remove the
12245
+ // ICU start/end ops, as they are no longer needed.
12246
+ let currentIcu = null;
11949
12247
  for (const unit of job.units) {
11950
12248
  for (const op of unit.create) {
11951
- if (op.kind === OpKind.Icu) {
11952
- if (!op.context) {
11953
- throw Error('ICU op should have its context set.');
11954
- }
11955
- if (!i18nBlockContexts.has(op.context)) {
11956
- const i18nContext = i18nContexts.get(op.context);
11957
- const subMessage = createI18nMessage(job, i18nContext, op.messagePlaceholder);
11958
- unit.create.push(subMessage);
11959
- const parentMessage = i18nBlockMessages.get(i18nContext.i18nBlock);
11960
- parentMessage?.subMessages.push(subMessage.xref);
11961
- }
11962
- OpList.remove(op);
12249
+ switch (op.kind) {
12250
+ case OpKind.IcuStart:
12251
+ currentIcu = op;
12252
+ OpList.remove(op);
12253
+ // Skip any contexts not associated with an ICU.
12254
+ const icuContext = i18nContexts.get(op.context);
12255
+ if (icuContext.contextKind !== I18nContextKind.Icu) {
12256
+ continue;
12257
+ }
12258
+ // Skip ICUs that share a context with their i18n message. These represent root-level
12259
+ // ICUs, not sub-messages.
12260
+ const i18nBlock = i18nBlocks.get(icuContext.i18nBlock);
12261
+ if (i18nBlock.context === icuContext.xref) {
12262
+ continue;
12263
+ }
12264
+ // Find the root message and push this ICUs message as a sub-message.
12265
+ const rootI18nBlock = i18nBlocks.get(i18nBlock.root);
12266
+ const rootMessage = i18nMessagesByContext.get(rootI18nBlock.context);
12267
+ if (rootMessage === undefined) {
12268
+ throw Error('AssertionError: ICU sub-message should belong to a root message.');
12269
+ }
12270
+ const subMessage = i18nMessagesByContext.get(icuContext.xref);
12271
+ subMessage.messagePlaceholder = op.messagePlaceholder;
12272
+ rootMessage.subMessages.push(subMessage.xref);
12273
+ break;
12274
+ case OpKind.IcuEnd:
12275
+ currentIcu = null;
12276
+ OpList.remove(op);
12277
+ break;
12278
+ case OpKind.IcuPlaceholder:
12279
+ // Add ICU placeholders to the message, then remove the ICU placeholder ops.
12280
+ if (currentIcu === null || currentIcu.context == null) {
12281
+ throw Error('AssertionError: Unexpected ICU placeholder outside of i18n context');
12282
+ }
12283
+ const msg = i18nMessagesByContext.get(currentIcu.context);
12284
+ msg.postprocessingParams.set(op.name, literal(formatIcuPlaceholder(op)));
12285
+ OpList.remove(op);
12286
+ break;
11963
12287
  }
11964
12288
  }
11965
12289
  }
@@ -11968,26 +12292,33 @@ function extractI18nMessages(job) {
11968
12292
  * Create an i18n message op from an i18n context op.
11969
12293
  */
11970
12294
  function createI18nMessage(job, context, messagePlaceholder) {
11971
- let needsPostprocessing = context.postprocessingParams.size > 0;
11972
- for (const values of context.params.values()) {
11973
- if (values.length > 1) {
11974
- needsPostprocessing = true;
11975
- }
12295
+ let formattedParams = formatParams(context.params);
12296
+ const formattedPostprocessingParams = formatParams(context.postprocessingParams);
12297
+ let needsPostprocessing = [...context.params.values()].some(v => v.length > 1);
12298
+ return createI18nMessageOp(job.allocateXrefId(), context.xref, context.i18nBlock, context.message, messagePlaceholder ?? null, formattedParams, formattedPostprocessingParams, needsPostprocessing);
12299
+ }
12300
+ /**
12301
+ * Formats an ICU placeholder into a single string with expression placeholders.
12302
+ */
12303
+ function formatIcuPlaceholder(op) {
12304
+ if (op.strings.length !== op.expressionPlaceholders.length + 1) {
12305
+ throw Error(`AsserionError: Invalid ICU placeholder with ${op.strings.length} strings and ${op.expressionPlaceholders.length} expressions`);
11976
12306
  }
11977
- return createI18nMessageOp(job.allocateXrefId(), context.i18nBlock, context.message, messagePlaceholder ?? null, formatParams(context.params), formatParams(context.postprocessingParams), needsPostprocessing);
12307
+ const values = op.expressionPlaceholders.map(formatValue);
12308
+ return op.strings.flatMap((str, i) => [str, values[i] || '']).join('');
11978
12309
  }
11979
12310
  /**
11980
12311
  * Formats a map of `I18nParamValue[]` values into a map of `Expression` values.
11981
12312
  */
11982
12313
  function formatParams(params) {
11983
- const result = new Map();
11984
- for (const [placeholder, placeholderValues] of [...params].sort()) {
12314
+ const formattedParams = new Map();
12315
+ for (const [placeholder, placeholderValues] of params) {
11985
12316
  const serializedValues = formatParamValues(placeholderValues);
11986
12317
  if (serializedValues !== null) {
11987
- result.set(placeholder, literal(formatParamValues(placeholderValues)));
12318
+ formattedParams.set(placeholder, literal(serializedValues));
11988
12319
  }
11989
12320
  }
11990
- return result;
12321
+ return formattedParams;
11991
12322
  }
11992
12323
  /**
11993
12324
  * Formats an `I18nParamValue[]` into a string (or null for empty array).
@@ -12005,6 +12336,47 @@ function formatParamValues(values) {
12005
12336
  * Formats a single `I18nParamValue` into a string
12006
12337
  */
12007
12338
  function formatValue(value) {
12339
+ // Element tags with a structural directive use a special form that concatenates the element and
12340
+ // template values.
12341
+ if ((value.flags & I18nParamValueFlags.ElementTag) &&
12342
+ (value.flags & I18nParamValueFlags.TemplateTag)) {
12343
+ if (typeof value.value !== 'object') {
12344
+ throw Error('AssertionError: Expected i18n param value to have an element and template slot');
12345
+ }
12346
+ const elementValue = formatValue({
12347
+ ...value,
12348
+ value: value.value.element,
12349
+ flags: value.flags & ~I18nParamValueFlags.TemplateTag
12350
+ });
12351
+ const templateValue = formatValue({
12352
+ ...value,
12353
+ value: value.value.template,
12354
+ flags: value.flags & ~I18nParamValueFlags.ElementTag
12355
+ });
12356
+ // TODO(mmalerba): This is likely a bug in TemplateDefinitionBuilder, we should not need to
12357
+ // record the template value twice. For now I'm re-implementing the behavior here to keep the
12358
+ // output consistent with TemplateDefinitionBuilder.
12359
+ if ((value.flags & I18nParamValueFlags.OpenTag) &&
12360
+ (value.flags & I18nParamValueFlags.CloseTag)) {
12361
+ return `${templateValue}${elementValue}${templateValue}`;
12362
+ }
12363
+ // To match the TemplateDefinitionBuilder output, flip the order depending on whether the
12364
+ // values represent a closing or opening tag (or both).
12365
+ // TODO(mmalerba): Figure out if this makes a difference in terms of either functionality,
12366
+ // or the resulting message ID. If not, we can remove the special-casing in the future.
12367
+ return value.flags & I18nParamValueFlags.CloseTag ? `${elementValue}${templateValue}` :
12368
+ `${templateValue}${elementValue}`;
12369
+ }
12370
+ // Self-closing tags use a special form that concatenates the start and close tag values.
12371
+ if ((value.flags & I18nParamValueFlags.OpenTag) &&
12372
+ (value.flags & I18nParamValueFlags.CloseTag)) {
12373
+ return `${formatValue({ ...value, flags: value.flags & ~I18nParamValueFlags.CloseTag })}${formatValue({ ...value, flags: value.flags & ~I18nParamValueFlags.OpenTag })}`;
12374
+ }
12375
+ // If there are no special flags, just return the raw value.
12376
+ if (value.flags === I18nParamValueFlags.None) {
12377
+ return `${value.value}`;
12378
+ }
12379
+ // Encode the remaining flags as part of the value.
12008
12380
  let tagMarker = '';
12009
12381
  let closeMarker = '';
12010
12382
  if (value.flags & I18nParamValueFlags.ElementTag) {
@@ -12017,12 +12389,7 @@ function formatValue(value) {
12017
12389
  closeMarker = value.flags & I18nParamValueFlags.CloseTag ? TAG_CLOSE_MARKER : '';
12018
12390
  }
12019
12391
  const context = value.subTemplateIndex === null ? '' : `${CONTEXT_MARKER}${value.subTemplateIndex}`;
12020
- // Self-closing tags use a special form that concatenates the start and close tag values.
12021
- if ((value.flags & I18nParamValueFlags.OpenTag) &&
12022
- (value.flags & I18nParamValueFlags.CloseTag)) {
12023
- return `${ESCAPE}${tagMarker}${value.value}${context}${ESCAPE}${ESCAPE}${closeMarker}${tagMarker}${value.value}${context}${ESCAPE}`;
12024
- }
12025
- return `${ESCAPE}${closeMarker}${tagMarker}${value.value}${context}${ESCAPE}`;
12392
+ return `${ESCAPE$1}${closeMarker}${tagMarker}${value.value}${context}${ESCAPE$1}`;
12026
12393
  }
12027
12394
 
12028
12395
  /**
@@ -12056,7 +12423,7 @@ function generateAdvance(job) {
12056
12423
  else if (!slotMap.has(op.target)) {
12057
12424
  // We expect ops that _do_ depend on the slot counter to point at declarations that exist in
12058
12425
  // the `slotMap`.
12059
- throw new Error(`AssertionError: reference to unknown slot for var ${op.target}`);
12426
+ throw new Error(`AssertionError: reference to unknown slot for target ${op.target}`);
12060
12427
  }
12061
12428
  const slot = slotMap.get(op.target);
12062
12429
  // Does the slot counter need to be adjusted?
@@ -12139,9 +12506,15 @@ function recursivelyProcessView(view, parentScope) {
12139
12506
  for (const op of view.create) {
12140
12507
  switch (op.kind) {
12141
12508
  case OpKind.Template:
12509
+ // Descend into child embedded views.
12510
+ recursivelyProcessView(view.job.views.get(op.xref), scope);
12511
+ break;
12142
12512
  case OpKind.RepeaterCreate:
12143
12513
  // Descend into child embedded views.
12144
12514
  recursivelyProcessView(view.job.views.get(op.xref), scope);
12515
+ if (op.emptyView) {
12516
+ recursivelyProcessView(view.job.views.get(op.emptyView), scope);
12517
+ }
12145
12518
  break;
12146
12519
  case OpKind.Listener:
12147
12520
  // Prepend variables to listener handler functions.
@@ -12272,7 +12645,7 @@ const BANG_IMPORTANT = '!important';
12272
12645
  */
12273
12646
  function parseHostStyleProperties(job) {
12274
12647
  for (const op of job.root.update) {
12275
- if (op.kind !== OpKind.Binding) {
12648
+ if (!(op.kind === OpKind.Binding && op.bindingKind === BindingKind.Property)) {
12276
12649
  continue;
12277
12650
  }
12278
12651
  if (op.name.endsWith(BANG_IMPORTANT)) {
@@ -12359,6 +12732,9 @@ class IcuSerializerVisitor {
12359
12732
  visitPlaceholder(ph) {
12360
12733
  return this.formatPh(ph.name);
12361
12734
  }
12735
+ visitBlockPlaceholder(ph) {
12736
+ return `${this.formatPh(ph.startName)}${ph.children.map(child => child.visit(this)).join('')}${this.formatPh(ph.closeName)}`;
12737
+ }
12362
12738
  visitIcuPlaceholder(ph, context) {
12363
12739
  return this.formatPh(ph.name);
12364
12740
  }
@@ -14080,12 +14456,12 @@ class Comment {
14080
14456
  return visitor.visitComment(this, context);
14081
14457
  }
14082
14458
  }
14083
- class Block {
14084
- constructor(name, parameters, children, sourceSpan, nameSpan, startSourceSpan, endSourceSpan = null) {
14459
+ class Block extends NodeWithI18n {
14460
+ constructor(name, parameters, children, sourceSpan, nameSpan, startSourceSpan, endSourceSpan = null, i18n) {
14461
+ super(sourceSpan, i18n);
14085
14462
  this.name = name;
14086
14463
  this.parameters = parameters;
14087
14464
  this.children = children;
14088
- this.sourceSpan = sourceSpan;
14089
14465
  this.nameSpan = nameSpan;
14090
14466
  this.startSourceSpan = startSourceSpan;
14091
14467
  this.endSourceSpan = endSourceSpan;
@@ -14771,6 +15147,24 @@ class PlaceholderRegistry {
14771
15147
  getUniquePlaceholder(name) {
14772
15148
  return this._generateUniqueName(name.toUpperCase());
14773
15149
  }
15150
+ getStartBlockPlaceholderName(name, parameters) {
15151
+ const signature = this._hashBlock(name, parameters);
15152
+ if (this._signatureToName[signature]) {
15153
+ return this._signatureToName[signature];
15154
+ }
15155
+ const placeholder = this._generateUniqueName(`START_BLOCK_${this._toSnakeCase(name)}`);
15156
+ this._signatureToName[signature] = placeholder;
15157
+ return placeholder;
15158
+ }
15159
+ getCloseBlockPlaceholderName(name) {
15160
+ const signature = this._hashClosingBlock(name);
15161
+ if (this._signatureToName[signature]) {
15162
+ return this._signatureToName[signature];
15163
+ }
15164
+ const placeholder = this._generateUniqueName(`CLOSE_BLOCK_${this._toSnakeCase(name)}`);
15165
+ this._signatureToName[signature] = placeholder;
15166
+ return placeholder;
15167
+ }
14774
15168
  // Generate a hash for a tag - does not take attribute order into account
14775
15169
  _hashTag(tag, attrs, isVoid) {
14776
15170
  const start = `<${tag}`;
@@ -14781,6 +15175,16 @@ class PlaceholderRegistry {
14781
15175
  _hashClosingTag(tag) {
14782
15176
  return this._hashTag(`/${tag}`, {}, false);
14783
15177
  }
15178
+ _hashBlock(name, parameters) {
15179
+ const params = parameters.length === 0 ? '' : ` (${parameters.sort().join('; ')})`;
15180
+ return `@${name}${params} {}`;
15181
+ }
15182
+ _hashClosingBlock(name) {
15183
+ return this._hashBlock(`close_${name}`, []);
15184
+ }
15185
+ _toSnakeCase(name) {
15186
+ return name.toUpperCase().replace(/[^A-Z0-9]/g, '_');
15187
+ }
14784
15188
  _generateUniqueName(base) {
14785
15189
  const seen = this._placeHolderNameCounts.hasOwnProperty(base);
14786
15190
  if (!seen) {
@@ -14797,17 +15201,18 @@ const _expParser = new Parser$1(new Lexer());
14797
15201
  /**
14798
15202
  * Returns a function converting html nodes to an i18n Message given an interpolationConfig
14799
15203
  */
14800
- function createI18nMessageFactory(interpolationConfig) {
14801
- const visitor = new _I18nVisitor(_expParser, interpolationConfig);
15204
+ function createI18nMessageFactory(interpolationConfig, containerBlocks) {
15205
+ const visitor = new _I18nVisitor(_expParser, interpolationConfig, containerBlocks);
14802
15206
  return (nodes, meaning, description, customId, visitNodeFn) => visitor.toI18nMessage(nodes, meaning, description, customId, visitNodeFn);
14803
15207
  }
14804
15208
  function noopVisitNodeFn(_html, i18n) {
14805
15209
  return i18n;
14806
15210
  }
14807
15211
  class _I18nVisitor {
14808
- constructor(_expressionParser, _interpolationConfig) {
15212
+ constructor(_expressionParser, _interpolationConfig, _containerBlocks) {
14809
15213
  this._expressionParser = _expressionParser;
14810
15214
  this._interpolationConfig = _interpolationConfig;
15215
+ this._containerBlocks = _containerBlocks;
14811
15216
  }
14812
15217
  toI18nMessage(nodes, meaning = '', description = '', customId = '', visitNodeFn) {
14813
15218
  const context = {
@@ -14894,10 +15299,26 @@ class _I18nVisitor {
14894
15299
  }
14895
15300
  visitBlock(block, context) {
14896
15301
  const children = visitAll(this, block.children, context);
14897
- const node = new Container(children, block.sourceSpan);
15302
+ if (this._containerBlocks.has(block.name)) {
15303
+ return new Container(children, block.sourceSpan);
15304
+ }
15305
+ const parameters = block.parameters.map(param => param.expression);
15306
+ const startPhName = context.placeholderRegistry.getStartBlockPlaceholderName(block.name, parameters);
15307
+ const closePhName = context.placeholderRegistry.getCloseBlockPlaceholderName(block.name);
15308
+ context.placeholderToContent[startPhName] = {
15309
+ text: block.startSourceSpan.toString(),
15310
+ sourceSpan: block.startSourceSpan,
15311
+ };
15312
+ context.placeholderToContent[closePhName] = {
15313
+ text: block.endSourceSpan ? block.endSourceSpan.toString() : '}',
15314
+ sourceSpan: block.endSourceSpan ?? block.sourceSpan,
15315
+ };
15316
+ const node = new BlockPlaceholder(block.name, parameters, startPhName, closePhName, children, block.sourceSpan, block.startSourceSpan, block.endSourceSpan);
14898
15317
  return context.visitNodeFn(block, node);
14899
15318
  }
14900
- visitBlockParameter(_parameter, _context) { }
15319
+ visitBlockParameter(_parameter, _context) {
15320
+ throw new Error('Unreachable code');
15321
+ }
14901
15322
  /**
14902
15323
  * Convert, text and interpolated tokens up into text and placeholder pieces.
14903
15324
  *
@@ -18766,17 +19187,18 @@ const setI18nRefs = (htmlNode, i18nNode) => {
18766
19187
  * stored with other element's and attribute's information.
18767
19188
  */
18768
19189
  class I18nMetaVisitor {
18769
- constructor(interpolationConfig = DEFAULT_INTERPOLATION_CONFIG, keepI18nAttrs = false, enableI18nLegacyMessageIdFormat = false) {
19190
+ constructor(interpolationConfig = DEFAULT_INTERPOLATION_CONFIG, keepI18nAttrs = false, enableI18nLegacyMessageIdFormat = false, containerBlocks = DEFAULT_CONTAINER_BLOCKS) {
18770
19191
  this.interpolationConfig = interpolationConfig;
18771
19192
  this.keepI18nAttrs = keepI18nAttrs;
18772
19193
  this.enableI18nLegacyMessageIdFormat = enableI18nLegacyMessageIdFormat;
19194
+ this.containerBlocks = containerBlocks;
18773
19195
  // whether visited nodes contain i18n information
18774
19196
  this.hasI18nMeta = false;
18775
19197
  this._errors = [];
18776
19198
  }
18777
19199
  _generateI18nMessage(nodes, meta = '', visitNodeFn) {
18778
19200
  const { meaning, description, customId } = this._parseMetadata(meta);
18779
- const createI18nMessage = createI18nMessageFactory(this.interpolationConfig);
19201
+ const createI18nMessage = createI18nMessageFactory(this.interpolationConfig, this.containerBlocks);
18780
19202
  const message = createI18nMessage(nodes, meaning, description, customId, visitNodeFn);
18781
19203
  this._setMessageId(message, meta);
18782
19204
  this._setLegacyIds(message, meta);
@@ -19077,6 +19499,9 @@ class GetMsgSerializerVisitor {
19077
19499
  visitPlaceholder(ph) {
19078
19500
  return this.formatPh(ph.name);
19079
19501
  }
19502
+ visitBlockPlaceholder(ph) {
19503
+ return `${this.formatPh(ph.startName)}${ph.children.map(child => child.visit(this)).join('')}${this.formatPh(ph.closeName)}`;
19504
+ }
19080
19505
  visitIcuPlaceholder(ph, context) {
19081
19506
  return this.formatPh(ph.name);
19082
19507
  }
@@ -19130,6 +19555,11 @@ class LocalizeSerializerVisitor {
19130
19555
  visitPlaceholder(ph) {
19131
19556
  this.pieces.push(this.createPlaceholderPiece(ph.name, ph.sourceSpan));
19132
19557
  }
19558
+ visitBlockPlaceholder(ph) {
19559
+ this.pieces.push(this.createPlaceholderPiece(ph.startName, ph.startSourceSpan ?? ph.sourceSpan));
19560
+ ph.children.forEach(child => child.visit(this));
19561
+ this.pieces.push(this.createPlaceholderPiece(ph.closeName, ph.endSourceSpan ?? ph.sourceSpan));
19562
+ }
19133
19563
  visitIcuPlaceholder(ph) {
19134
19564
  this.pieces.push(this.createPlaceholderPiece(ph.name, ph.sourceSpan, this.placeholderToMessage[ph.name]));
19135
19565
  }
@@ -19203,35 +19633,136 @@ const NG_I18N_CLOSURE_MODE$1 = 'ngI18nClosureMode';
19203
19633
  * considers variables like `I18N_0` as constants and throws an error when their value changes.
19204
19634
  */
19205
19635
  const TRANSLATION_VAR_PREFIX = 'i18n_';
19636
+ /** Prefix of ICU expressions for post processing */
19637
+ const I18N_ICU_MAPPING_PREFIX = 'I18N_EXP_';
19638
+ /**
19639
+ * The escape sequence used for message param values.
19640
+ */
19641
+ const ESCAPE = '\uFFFD';
19206
19642
  /**
19207
19643
  * Lifts i18n properties into the consts array.
19208
19644
  * TODO: Can we use `ConstCollectedExpr`?
19645
+ * TODO: The way the various attributes are linked together is very complex. Perhaps we could
19646
+ * simplify the process, maybe by combining the context and message ops?
19209
19647
  */
19210
19648
  function collectI18nConsts(job) {
19211
19649
  const fileBasedI18nSuffix = job.relativeContextFilePath.replace(/[^A-Za-z0-9]/g, '_').toUpperCase() + '_';
19212
- const messageConstIndices = new Map();
19213
- // Remove all of the i18n message ops into a map.
19650
+ // Step One: Build up various lookup maps we need to collect all the consts.
19651
+ // Context Xref -> Extracted Attribute Ops
19652
+ const extractedAttributesByI18nContext = new Map();
19653
+ // Element/ElementStart Xref -> I18n Attributes config op
19654
+ const i18nAttributesByElement = new Map();
19655
+ // Element/ElementStart Xref -> All I18n Expression ops for attrs on that target
19656
+ const i18nExpressionsByElement = new Map();
19657
+ // I18n Message Xref -> I18n Message Op (TODO: use a central op map)
19214
19658
  const messages = new Map();
19659
+ for (const unit of job.units) {
19660
+ for (const op of unit.ops()) {
19661
+ if (op.kind === OpKind.ExtractedAttribute && op.i18nContext !== null) {
19662
+ const attributes = extractedAttributesByI18nContext.get(op.i18nContext) ?? [];
19663
+ attributes.push(op);
19664
+ extractedAttributesByI18nContext.set(op.i18nContext, attributes);
19665
+ }
19666
+ else if (op.kind === OpKind.I18nAttributes) {
19667
+ i18nAttributesByElement.set(op.target, op);
19668
+ }
19669
+ else if (op.kind === OpKind.I18nExpression && op.usage === I18nExpressionFor.I18nAttribute) {
19670
+ const expressions = i18nExpressionsByElement.get(op.target) ?? [];
19671
+ expressions.push(op);
19672
+ i18nExpressionsByElement.set(op.target, expressions);
19673
+ }
19674
+ else if (op.kind === OpKind.I18nMessage) {
19675
+ messages.set(op.xref, op);
19676
+ }
19677
+ }
19678
+ }
19679
+ // Step Two: Serialize the extracted i18n messages for root i18n blocks and i18n attributes into
19680
+ // the const array.
19681
+ //
19682
+ // Also, each i18n message will have a variable expression that can refer to its
19683
+ // value. Store these expressions in the appropriate place:
19684
+ // 1. For normal i18n content, it also goes in the const array. We save the const index to use
19685
+ // later.
19686
+ // 2. For extracted attributes, it becomes the value of the extracted attribute instruction.
19687
+ // 3. For i18n bindings, it will go in a separate const array instruction below; for now, we just
19688
+ // save it.
19689
+ const i18nValuesByContext = new Map();
19690
+ const messageConstIndices = new Map();
19215
19691
  for (const unit of job.units) {
19216
19692
  for (const op of unit.create) {
19217
19693
  if (op.kind === OpKind.I18nMessage) {
19218
- messages.set(op.xref, op);
19694
+ if (op.messagePlaceholder === null) {
19695
+ const { mainVar, statements } = collectMessage(job, fileBasedI18nSuffix, messages, op);
19696
+ if (op.i18nBlock !== null) {
19697
+ // This is a regular i18n message with a corresponding i18n block. Collect it into the
19698
+ // const array.
19699
+ const i18nConst = job.addConst(mainVar, statements);
19700
+ messageConstIndices.set(op.i18nBlock, i18nConst);
19701
+ }
19702
+ else {
19703
+ // This is an i18n attribute. Extract the initializers into the const pool.
19704
+ job.constsInitializers.push(...statements);
19705
+ // Save the i18n variable value for later.
19706
+ i18nValuesByContext.set(op.i18nContext, mainVar);
19707
+ // This i18n message may correspond to an individual extracted attribute. If so, The
19708
+ // value of that attribute is updated to read the extracted i18n variable.
19709
+ const attributesForMessage = extractedAttributesByI18nContext.get(op.i18nContext);
19710
+ if (attributesForMessage !== undefined) {
19711
+ for (const attr of attributesForMessage) {
19712
+ attr.expression = mainVar.clone();
19713
+ }
19714
+ }
19715
+ }
19716
+ }
19219
19717
  OpList.remove(op);
19220
19718
  }
19221
19719
  }
19222
19720
  }
19223
- // Serialize the extracted messages for root i18n blocks into the const array.
19224
- for (const op of messages.values()) {
19225
- if (op.kind === OpKind.I18nMessage && op.messagePlaceholder === null) {
19226
- const { mainVar, statements } = collectMessage(job, fileBasedI18nSuffix, messages, op);
19227
- messageConstIndices.set(op.i18nBlock, job.addConst(mainVar, statements));
19721
+ // Step Three: Serialize I18nAttributes configurations into the const array. Each I18nAttributes
19722
+ // instruction has a config array, which contains k-v pairs describing each binding name, and the
19723
+ // i18n variable that provides the value.
19724
+ for (const unit of job.units) {
19725
+ for (const elem of unit.create) {
19726
+ if (isElementOrContainerOp(elem)) {
19727
+ const i18nAttributes = i18nAttributesByElement.get(elem.xref);
19728
+ if (i18nAttributes === undefined) {
19729
+ // This element is not associated with an i18n attributes configuration instruction.
19730
+ continue;
19731
+ }
19732
+ let i18nExpressions = i18nExpressionsByElement.get(elem.xref);
19733
+ if (i18nExpressions === undefined) {
19734
+ // Unused i18nAttributes should have already been removed.
19735
+ // TODO: Should the removal of those dead instructions be merged with this phase?
19736
+ throw new Error('AssertionError: Could not find any i18n expressions associated with an I18nAttributes instruction');
19737
+ }
19738
+ // Find expressions for all the unique property names, removing duplicates.
19739
+ const seenPropertyNames = new Set();
19740
+ i18nExpressions = i18nExpressions.filter(i18nExpr => {
19741
+ const seen = (seenPropertyNames.has(i18nExpr.name));
19742
+ seenPropertyNames.add(i18nExpr.name);
19743
+ return !seen;
19744
+ });
19745
+ const i18nAttributeConfig = i18nExpressions.flatMap(i18nExpr => {
19746
+ const i18nExprValue = i18nValuesByContext.get(i18nExpr.context);
19747
+ if (i18nExprValue === undefined) {
19748
+ throw new Error('AssertionError: Could not find i18n expression\'s value');
19749
+ }
19750
+ return [literal(i18nExpr.name), i18nExprValue];
19751
+ });
19752
+ i18nAttributes.i18nAttributesConfig =
19753
+ job.addConst(new LiteralArrayExpr(i18nAttributeConfig));
19754
+ }
19228
19755
  }
19229
19756
  }
19230
- // Assign const index to i18n ops that messages were extracted from.
19757
+ // Step Four: Propagate the extracted const index into i18n ops that messages were extracted from.
19231
19758
  for (const unit of job.units) {
19232
19759
  for (const op of unit.create) {
19233
19760
  if (op.kind === OpKind.I18nStart) {
19234
- op.messageIndex = messageConstIndices.get(op.root);
19761
+ const msgIndex = messageConstIndices.get(op.root);
19762
+ if (msgIndex === undefined) {
19763
+ throw new Error('AssertionError: Could not find corresponding i18n block index for an i18n message op; was an i18n message incorrectly assumed to correspond to an attribute?');
19764
+ }
19765
+ op.messageIndex = msgIndex;
19235
19766
  }
19236
19767
  }
19237
19768
  }
@@ -19241,16 +19772,23 @@ function collectI18nConsts(job) {
19241
19772
  * This will recursively collect any sub-messages referenced from the parent message as well.
19242
19773
  */
19243
19774
  function collectMessage(job, fileBasedI18nSuffix, messages, messageOp) {
19244
- // Recursively collect any sub-messages, and fill in their placeholders in this message.
19775
+ // Recursively collect any sub-messages, record each sub-message's main variable under its
19776
+ // placeholder so that we can add them to the params for the parent message. It is possible
19777
+ // that multiple sub-messages will share the same placeholder, so we need to track an array of
19778
+ // variables for each placeholder.
19245
19779
  const statements = [];
19780
+ const subMessagePlaceholders = new Map();
19246
19781
  for (const subMessageId of messageOp.subMessages) {
19247
19782
  const subMessage = messages.get(subMessageId);
19248
19783
  const { mainVar: subMessageVar, statements: subMessageStatements } = collectMessage(job, fileBasedI18nSuffix, messages, subMessage);
19249
19784
  statements.push(...subMessageStatements);
19250
- messageOp.params.set(subMessage.messagePlaceholder, subMessageVar);
19785
+ const subMessages = subMessagePlaceholders.get(subMessage.messagePlaceholder) ?? [];
19786
+ subMessages.push(subMessageVar);
19787
+ subMessagePlaceholders.set(subMessage.messagePlaceholder, subMessages);
19251
19788
  }
19252
- // Check that the message has all of its parameters filled out.
19253
- assertAllParamsResolved(messageOp);
19789
+ addSubMessageParams(messageOp, subMessagePlaceholders);
19790
+ // Sort the params for consistency with TemaplateDefinitionBuilder output.
19791
+ messageOp.params = new Map([...messageOp.params.entries()].sort());
19254
19792
  const mainVar = variable(job.pool.uniqueName(TRANSLATION_VAR_PREFIX));
19255
19793
  // Closure Compiler requires const names to start with `MSG_` but disallows any other
19256
19794
  // const to start with `MSG_`. We define a variable starting with `MSG_` just for the
@@ -19259,10 +19797,13 @@ function collectMessage(job, fileBasedI18nSuffix, messages, messageOp) {
19259
19797
  let transformFn = undefined;
19260
19798
  // If nescessary, add a post-processing step and resolve any placeholder params that are
19261
19799
  // set in post-processing.
19262
- if (messageOp.needsPostprocessing) {
19800
+ if (messageOp.needsPostprocessing || messageOp.postprocessingParams.size > 0) {
19801
+ // Sort the post-processing params for consistency with TemaplateDefinitionBuilder output.
19802
+ const postprocessingParams = Object.fromEntries([...messageOp.postprocessingParams.entries()].sort());
19803
+ const formattedPostprocessingParams = formatI18nPlaceholderNamesInMap(postprocessingParams, /* useCamelCase */ false);
19263
19804
  const extraTransformFnParams = [];
19264
19805
  if (messageOp.postprocessingParams.size > 0) {
19265
- extraTransformFnParams.push(literalMap([...messageOp.postprocessingParams].map(([key, value]) => ({ key, value, quoted: true }))));
19806
+ extraTransformFnParams.push(mapLiteral(formattedPostprocessingParams, /* quoted */ true));
19266
19807
  }
19267
19808
  transformFn = (expr) => importExpr(Identifiers.i18nPostprocess).callFn([expr, ...extraTransformFnParams]);
19268
19809
  }
@@ -19270,6 +19811,25 @@ function collectMessage(job, fileBasedI18nSuffix, messages, messageOp) {
19270
19811
  statements.push(...getTranslationDeclStmts$1(messageOp.message, mainVar, closureVar, messageOp.params, transformFn));
19271
19812
  return { mainVar, statements };
19272
19813
  }
19814
+ /**
19815
+ * Adds the given subMessage placeholders to the given message op.
19816
+ *
19817
+ * If a placeholder only corresponds to a single sub-message variable, we just set that variable
19818
+ * as the param value. However, if the placeholder corresponds to multiple sub-message
19819
+ * variables, we need to add a special placeholder value that is handled by the post-processing
19820
+ * step. We then add the array of variables as a post-processing param.
19821
+ */
19822
+ function addSubMessageParams(messageOp, subMessagePlaceholders) {
19823
+ for (const [placeholder, subMessages] of subMessagePlaceholders) {
19824
+ if (subMessages.length === 1) {
19825
+ messageOp.params.set(placeholder, subMessages[0]);
19826
+ }
19827
+ else {
19828
+ messageOp.params.set(placeholder, literal(`${ESCAPE}${I18N_ICU_MAPPING_PREFIX}${placeholder}${ESCAPE}`));
19829
+ messageOp.postprocessingParams.set(placeholder, literalArr(subMessages));
19830
+ }
19831
+ }
19832
+ }
19273
19833
  /**
19274
19834
  * Generate statements that define a given translation message.
19275
19835
  *
@@ -19292,7 +19852,8 @@ function collectMessage(job, fileBasedI18nSuffix, messages, messageOp) {
19292
19852
  * @param closureVar The variable for Closure `goog.getMsg` calls, e.g. `MSG_EXTERNAL_XXX`.
19293
19853
  * @param params Object mapping placeholder names to their values (e.g.
19294
19854
  * `{ "interpolation": "\uFFFD0\uFFFD" }`).
19295
- * @param transformFn Optional transformation function that will be applied to the translation (e.g.
19855
+ * @param transformFn Optional transformation function that will be applied to the translation
19856
+ * (e.g.
19296
19857
  * post-processing).
19297
19858
  * @returns An array of statements that defined a given translation.
19298
19859
  */
@@ -19337,31 +19898,21 @@ function i18nGenerateClosureVar(pool, messageId, fileBasedI18nSuffix, useExterna
19337
19898
  }
19338
19899
  return variable(name);
19339
19900
  }
19340
- /**
19341
- * Asserts that all of the message's placeholders have values.
19342
- */
19343
- function assertAllParamsResolved(op) {
19344
- for (const placeholder in op.message.placeholders) {
19345
- if (!op.params.has(placeholder) && !op.postprocessingParams.has(placeholder)) {
19346
- throw Error(`Failed to resolve i18n placeholder: ${placeholder}`);
19347
- }
19348
- }
19349
- for (const placeholder in op.message.placeholderToMessage) {
19350
- if (!op.params.has(placeholder) && !op.postprocessingParams.has(placeholder)) {
19351
- throw Error(`Failed to resolve i18n message placeholder: ${placeholder}`);
19352
- }
19353
- }
19354
- }
19355
19901
 
19356
19902
  /**
19357
19903
  * Removes text nodes within i18n blocks since they are already hardcoded into the i18n message.
19904
+ * Also, replaces interpolations on these text nodes with i18n expressions of the non-text portions,
19905
+ * which will be applied later.
19358
19906
  */
19359
- function extractI18nText(job) {
19907
+ function convertI18nText(job) {
19360
19908
  for (const unit of job.units) {
19361
19909
  // Remove all text nodes within i18n blocks, their content is already captured in the i18n
19362
19910
  // message.
19363
19911
  let currentI18n = null;
19912
+ let currentIcu = null;
19364
19913
  const textNodeI18nBlocks = new Map();
19914
+ const textNodeIcus = new Map();
19915
+ const icuPlaceholderByText = new Map();
19365
19916
  for (const op of unit.create) {
19366
19917
  switch (op.kind) {
19367
19918
  case OpKind.I18nStart:
@@ -19373,10 +19924,32 @@ function extractI18nText(job) {
19373
19924
  case OpKind.I18nEnd:
19374
19925
  currentI18n = null;
19375
19926
  break;
19927
+ case OpKind.IcuStart:
19928
+ if (op.context === null) {
19929
+ throw Error('Icu op should have its context set.');
19930
+ }
19931
+ currentIcu = op;
19932
+ break;
19933
+ case OpKind.IcuEnd:
19934
+ currentIcu = null;
19935
+ break;
19376
19936
  case OpKind.Text:
19377
19937
  if (currentI18n !== null) {
19378
19938
  textNodeI18nBlocks.set(op.xref, currentI18n);
19379
- OpList.remove(op);
19939
+ textNodeIcus.set(op.xref, currentIcu);
19940
+ if (op.icuPlaceholder !== null) {
19941
+ // Create an op to represent the ICU placeholder. Initially set its static text to the
19942
+ // value of the text op, though this may be overwritten later if this text op is a
19943
+ // placeholder for an interpolation.
19944
+ const icuPlaceholderOp = createIcuPlaceholderOp(job.allocateXrefId(), op.icuPlaceholder, [op.initialValue]);
19945
+ OpList.replace(op, icuPlaceholderOp);
19946
+ icuPlaceholderByText.set(op.xref, icuPlaceholderOp);
19947
+ }
19948
+ else {
19949
+ // Otherwise just remove the text op, since its value is already accounted for in the
19950
+ // translated message.
19951
+ OpList.remove(op);
19952
+ }
19380
19953
  }
19381
19954
  break;
19382
19955
  }
@@ -19390,15 +19963,24 @@ function extractI18nText(job) {
19390
19963
  continue;
19391
19964
  }
19392
19965
  const i18nOp = textNodeI18nBlocks.get(op.target);
19966
+ const icuOp = textNodeIcus.get(op.target);
19967
+ const icuPlaceholder = icuPlaceholderByText.get(op.target);
19968
+ const contextId = icuOp ? icuOp.context : i18nOp.context;
19969
+ const resolutionTime = icuOp ? I18nParamResolutionTime.Postproccessing :
19970
+ I18nParamResolutionTime.Creation;
19393
19971
  const ops = [];
19394
19972
  for (let i = 0; i < op.interpolation.expressions.length; i++) {
19395
19973
  const expr = op.interpolation.expressions[i];
19396
- const placeholder = op.i18nPlaceholders[i];
19397
19974
  // For now, this i18nExpression depends on the slot context of the enclosing i18n block.
19398
19975
  // Later, we will modify this, and advance to a different point.
19399
- ops.push(createI18nExpressionOp(i18nOp.context, i18nOp.xref, i18nOp.handle, expr, placeholder.name, I18nParamResolutionTime.Creation, expr.sourceSpan ?? op.sourceSpan));
19976
+ ops.push(createI18nExpressionOp(contextId, i18nOp.xref, i18nOp.xref, i18nOp.handle, expr, icuPlaceholder?.xref ?? null, op.interpolation.i18nPlaceholders[i] ?? null, resolutionTime, I18nExpressionFor.I18nText, '', expr.sourceSpan ?? op.sourceSpan));
19400
19977
  }
19401
19978
  OpList.replaceWithMany(op, ops);
19979
+ // If this interpolation is part of an ICU placeholder, add the strings and expressions to
19980
+ // the placeholder.
19981
+ if (icuPlaceholder !== undefined) {
19982
+ icuPlaceholder.strings = op.interpolation.strings;
19983
+ }
19402
19984
  break;
19403
19985
  }
19404
19986
  }
@@ -19434,60 +20016,9 @@ function liftLocalRefs(job) {
19434
20016
  function serializeLocalRefs(refs) {
19435
20017
  const constRefs = [];
19436
20018
  for (const ref of refs) {
19437
- constRefs.push(literal(ref.name), literal(ref.target));
19438
- }
19439
- return literalArr(constRefs);
19440
- }
19441
-
19442
- /**
19443
- * Merge i18n contexts for child i18n blocks into their ancestor root contexts.
19444
- */
19445
- function mergeI18nContexts(job) {
19446
- // Record all of the i18n and extracted message ops for use later.
19447
- const i18nOps = new Map();
19448
- const i18nContexts = new Map();
19449
- for (const unit of job.units) {
19450
- for (const op of unit.create) {
19451
- switch (op.kind) {
19452
- case OpKind.I18nStart:
19453
- if (!op.context) {
19454
- throw Error('I18n op should have its context set.');
19455
- }
19456
- i18nOps.set(op.xref, op);
19457
- break;
19458
- case OpKind.I18nContext:
19459
- i18nContexts.set(op.xref, op);
19460
- break;
19461
- }
19462
- }
19463
- }
19464
- // For each non-root i18n op, merge its context into the root i18n op's context.
19465
- for (const childI18nOp of i18nOps.values()) {
19466
- if (childI18nOp.xref !== childI18nOp.root) {
19467
- const childContext = i18nContexts.get(childI18nOp.context);
19468
- const rootI18nOp = i18nOps.get(childI18nOp.root);
19469
- const rootContext = i18nContexts.get(rootI18nOp.context);
19470
- mergeParams(rootContext.params, childContext.params);
19471
- mergeParams(rootContext.postprocessingParams, childContext.postprocessingParams);
19472
- }
19473
- }
19474
- }
19475
- /**
19476
- * Merges the params in the `from` map to into the `to` map.
19477
- */
19478
- function mergeParams(to, from) {
19479
- for (const [placeholder, fromValues] of from) {
19480
- const toValues = to.get(placeholder) || [];
19481
- // TODO(mmalerba): Child element close tag params should be prepended to maintain the same order
19482
- // as TemplateDefinitionBuilder. Can be cleaned up when compatibility is no longer required.
19483
- const flags = fromValues[0].flags;
19484
- if ((flags & I18nParamValueFlags.CloseTag) && !(flags & I18nParamValueFlags.OpenTag)) {
19485
- to.set(placeholder, [...fromValues, ...toValues]);
19486
- }
19487
- else {
19488
- to.set(placeholder, [...toValues, ...fromValues]);
19489
- }
20019
+ constRefs.push(literal(ref.name), literal(ref.target));
19490
20020
  }
20021
+ return literalArr(constRefs);
19491
20022
  }
19492
20023
 
19493
20024
  /**
@@ -19978,21 +20509,39 @@ function keepLast(ops) {
19978
20509
  * class property.
19979
20510
  */
19980
20511
  function parseExtractedStyles(job) {
20512
+ const elements = new Map();
20513
+ for (const unit of job.units) {
20514
+ for (const op of unit.create) {
20515
+ if (isElementOrContainerOp(op)) {
20516
+ elements.set(op.xref, op);
20517
+ }
20518
+ }
20519
+ }
19981
20520
  for (const unit of job.units) {
19982
20521
  for (const op of unit.create) {
19983
20522
  if (op.kind === OpKind.ExtractedAttribute && op.bindingKind === BindingKind.Attribute &&
19984
20523
  isStringLiteral(op.expression)) {
20524
+ const target = elements.get(op.target);
20525
+ if (target !== undefined && target.kind === OpKind.Template &&
20526
+ target.templateKind === TemplateKind.Structural) {
20527
+ // TemplateDefinitionBuilder will not apply class and style bindings to structural
20528
+ // directives; instead, it will leave them as attributes.
20529
+ // (It's not clear what that would mean, anyway -- classes and styles on a structural
20530
+ // element should probably be a parse error.)
20531
+ // TODO: We may be able to remove this once Template Pipeline is the default.
20532
+ continue;
20533
+ }
19985
20534
  if (op.name === 'style') {
19986
20535
  const parsedStyles = parse(op.expression.value);
19987
20536
  for (let i = 0; i < parsedStyles.length - 1; i += 2) {
19988
- OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.StyleProperty, parsedStyles[i], literal(parsedStyles[i + 1])), op);
20537
+ OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.StyleProperty, parsedStyles[i], literal(parsedStyles[i + 1]), null, null, SecurityContext.STYLE), op);
19989
20538
  }
19990
20539
  OpList.remove(op);
19991
20540
  }
19992
20541
  else if (op.name === 'class') {
19993
20542
  const parsedClasses = op.expression.value.trim().split(/\s+/g);
19994
20543
  for (const parsedClass of parsedClasses) {
19995
- OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.ClassName, parsedClass, null), op);
20544
+ OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.ClassName, parsedClass, null, null, null, SecurityContext.NONE), op);
19996
20545
  }
19997
20546
  OpList.remove(op);
19998
20547
  }
@@ -20008,18 +20557,30 @@ function parseExtractedStyles(job) {
20008
20557
  function removeContentSelectors(job) {
20009
20558
  for (const unit of job.units) {
20010
20559
  const elements = createOpXrefMap(unit);
20011
- for (const op of unit.update) {
20560
+ for (const op of unit.ops()) {
20012
20561
  switch (op.kind) {
20013
20562
  case OpKind.Binding:
20014
20563
  const target = lookupInXrefMap(elements, op.target);
20015
- if (op.name.toLowerCase() === 'select' && target.kind === OpKind.Projection) {
20564
+ if (isSelectAttribute(op.name) && target.kind === OpKind.Projection) {
20016
20565
  OpList.remove(op);
20017
20566
  }
20018
20567
  break;
20568
+ case OpKind.Projection:
20569
+ // op.attributes is an array of [attr1-name, attr1-value, attr2-name, attr2-value, ...],
20570
+ // find the "select" attribute and remove its name and corresponding value.
20571
+ for (let i = op.attributes.length - 2; i >= 0; i -= 2) {
20572
+ if (isSelectAttribute(op.attributes[i])) {
20573
+ op.attributes.splice(i, 2);
20574
+ }
20575
+ }
20576
+ break;
20019
20577
  }
20020
20578
  }
20021
20579
  }
20022
20580
  }
20581
+ function isSelectAttribute(name) {
20582
+ return name.toLowerCase() === 'select';
20583
+ }
20023
20584
  /**
20024
20585
  * Looks up an element in the given map by xref ID.
20025
20586
  */
@@ -20139,23 +20700,43 @@ function propagateI18nBlocksToTemplates(unit, subTemplateIndex) {
20139
20700
  i18nBlock = op;
20140
20701
  break;
20141
20702
  case OpKind.I18nEnd:
20703
+ // When we exit a root-level i18n block, reset the sub-template index counter.
20704
+ if (i18nBlock.subTemplateIndex === null) {
20705
+ subTemplateIndex = 0;
20706
+ }
20142
20707
  i18nBlock = null;
20143
20708
  break;
20144
20709
  case OpKind.Template:
20145
- const templateView = unit.job.views.get(op.xref);
20146
- // We found an <ng-template> inside an i18n block; increment the sub-template counter and
20147
- // wrap the template's view in a child i18n block.
20148
- if (op.i18nPlaceholder !== undefined) {
20149
- if (i18nBlock === null) {
20150
- throw Error('Expected template with i18n placeholder to be in an i18n block.');
20151
- }
20152
- subTemplateIndex++;
20153
- wrapTemplateWithI18n(templateView, i18nBlock);
20710
+ subTemplateIndex = propagateI18nBlocksForView(unit.job.views.get(op.xref), i18nBlock, op.i18nPlaceholder, subTemplateIndex);
20711
+ break;
20712
+ case OpKind.RepeaterCreate:
20713
+ // Propagate i18n blocks to the @for template.
20714
+ unit.job.views.get(op.xref);
20715
+ subTemplateIndex = propagateI18nBlocksForView(unit.job.views.get(op.xref), i18nBlock, op.i18nPlaceholder, subTemplateIndex);
20716
+ // Then if there's an @empty template, propagate the i18n blocks for it as well.
20717
+ if (op.emptyView !== null) {
20718
+ subTemplateIndex = propagateI18nBlocksForView(unit.job.views.get(op.emptyView), i18nBlock, op.emptyI18nPlaceholder, subTemplateIndex);
20154
20719
  }
20155
- // Continue traversing inside the template's view.
20156
- propagateI18nBlocksToTemplates(templateView, subTemplateIndex);
20720
+ break;
20721
+ }
20722
+ }
20723
+ return subTemplateIndex;
20724
+ }
20725
+ /**
20726
+ * Propagate i18n blocks for a view.
20727
+ */
20728
+ function propagateI18nBlocksForView(view, i18nBlock, i18nPlaceholder, subTemplateIndex) {
20729
+ // We found an <ng-template> inside an i18n block; increment the sub-template counter and
20730
+ // wrap the template's view in a child i18n block.
20731
+ if (i18nPlaceholder !== undefined) {
20732
+ if (i18nBlock === null) {
20733
+ throw Error('Expected template with i18n placeholder to be in an i18n block.');
20157
20734
  }
20735
+ subTemplateIndex++;
20736
+ wrapTemplateWithI18n(view, i18nBlock);
20158
20737
  }
20738
+ // Continue traversing inside the template's view.
20739
+ return propagateI18nBlocksToTemplates(view, subTemplateIndex);
20159
20740
  }
20160
20741
  /**
20161
20742
  * Wraps a template view with i18n start and end ops.
@@ -20322,17 +20903,13 @@ function disableBindings() {
20322
20903
  function enableBindings() {
20323
20904
  return call(Identifiers.enableBindings, [], null);
20324
20905
  }
20325
- function listener(name, handlerFn, sourceSpan) {
20326
- return call(Identifiers.listener, [
20327
- literal(name),
20328
- handlerFn,
20329
- ], sourceSpan);
20330
- }
20331
- function syntheticHostListener(name, handlerFn, sourceSpan) {
20332
- return call(Identifiers.syntheticHostListener, [
20333
- literal(name),
20334
- handlerFn,
20335
- ], sourceSpan);
20906
+ function listener(name, handlerFn, eventTargetResolver, syntheticHost, sourceSpan) {
20907
+ const args = [literal(name), handlerFn];
20908
+ if (eventTargetResolver !== null) {
20909
+ args.push(literal(false)); // `useCapture` flag, defaults to `false`
20910
+ args.push(importExpr(eventTargetResolver));
20911
+ }
20912
+ return call(syntheticHost ? Identifiers.syntheticHostListener : Identifiers.listener, args, sourceSpan);
20336
20913
  }
20337
20914
  function pipe(slot, name) {
20338
20915
  return call(Identifiers.pipe, [
@@ -20463,8 +21040,8 @@ function repeaterCreate(slot, viewFnName, decls, vars, tag, constIndex, trackByF
20463
21040
  }
20464
21041
  return call(Identifiers.repeaterCreate, args, sourceSpan);
20465
21042
  }
20466
- function repeater(metadataSlot, collection, sourceSpan) {
20467
- return call(Identifiers.repeater, [literal(metadataSlot), collection], sourceSpan);
21043
+ function repeater(collection, sourceSpan) {
21044
+ return call(Identifiers.repeater, [collection], sourceSpan);
20468
21045
  }
20469
21046
  function deferWhen(prefetch, expr, sourceSpan) {
20470
21047
  return call(prefetch ? Identifiers.deferPrefetchWhen : Identifiers.deferWhen, [expr], sourceSpan);
@@ -20479,6 +21056,10 @@ function i18n(slot, constIndex, subTemplateIndex) {
20479
21056
  function i18nEnd() {
20480
21057
  return call(Identifiers.i18nEnd, [], null);
20481
21058
  }
21059
+ function i18nAttributes(slot, i18nAttributesConfig) {
21060
+ const args = [literal(slot), literal(i18nAttributesConfig)];
21061
+ return call(Identifiers.i18nAttributes, args, null);
21062
+ }
20482
21063
  function property(name, expression, sanitizer, sourceSpan) {
20483
21064
  const args = [literal(name), expression];
20484
21065
  if (sanitizer !== null) {
@@ -20589,8 +21170,12 @@ function classMapInterpolate(strings, expressions, sourceSpan) {
20589
21170
  const interpolationArgs = collateInterpolationArgs(strings, expressions);
20590
21171
  return callVariadicInstruction(CLASS_MAP_INTERPOLATE_CONFIG, [], interpolationArgs, [], sourceSpan);
20591
21172
  }
20592
- function hostProperty(name, expression, sourceSpan) {
20593
- return call(Identifiers.hostProperty, [literal(name), expression], sourceSpan);
21173
+ function hostProperty(name, expression, sanitizer, sourceSpan) {
21174
+ const args = [literal(name), expression];
21175
+ if (sanitizer !== null) {
21176
+ args.push(sanitizer);
21177
+ }
21178
+ return call(Identifiers.hostProperty, args, sourceSpan);
20594
21179
  }
20595
21180
  function syntheticHostProperty(name, expression, sourceSpan) {
20596
21181
  return call(Identifiers.syntheticHostProperty, [literal(name), expression], sourceSpan);
@@ -20808,14 +21393,12 @@ function callVariadicInstruction(config, baseArgs, interpolationArgs, extraArgs,
20808
21393
  }
20809
21394
 
20810
21395
  /**
20811
- * Map of sanitizers to their identifier.
21396
+ * Map of target resolvers for event listeners.
20812
21397
  */
20813
- const sanitizerIdentifierMap = new Map([
20814
- [SanitizerFn.Html, Identifiers.sanitizeHtml],
20815
- [SanitizerFn.IframeAttribute, Identifiers.validateIframeAttribute],
20816
- [SanitizerFn.ResourceUrl, Identifiers.sanitizeResourceUrl],
20817
- [SanitizerFn.Script, Identifiers.sanitizeScript],
20818
- [SanitizerFn.Style, Identifiers.sanitizeStyle], [SanitizerFn.Url, Identifiers.sanitizeUrl]
21398
+ const GLOBAL_TARGET_RESOLVERS$1 = new Map([
21399
+ ['window', Identifiers.resolveWindow],
21400
+ ['document', Identifiers.resolveDocument],
21401
+ ['body', Identifiers.resolveBody],
20819
21402
  ]);
20820
21403
  /**
20821
21404
  * Compiles semantic operations across all views and generates output `o.Statement`s with actual
@@ -20839,19 +21422,19 @@ function reifyCreateOperations(unit, ops) {
20839
21422
  OpList.replace(op, text(op.handle.slot, op.initialValue, op.sourceSpan));
20840
21423
  break;
20841
21424
  case OpKind.ElementStart:
20842
- OpList.replace(op, elementStart(op.handle.slot, op.tag, op.attributes, op.localRefs, op.sourceSpan));
21425
+ OpList.replace(op, elementStart(op.handle.slot, op.tag, op.attributes, op.localRefs, op.startSourceSpan));
20843
21426
  break;
20844
21427
  case OpKind.Element:
20845
- OpList.replace(op, element(op.handle.slot, op.tag, op.attributes, op.localRefs, op.sourceSpan));
21428
+ OpList.replace(op, element(op.handle.slot, op.tag, op.attributes, op.localRefs, op.wholeSourceSpan));
20846
21429
  break;
20847
21430
  case OpKind.ElementEnd:
20848
21431
  OpList.replace(op, elementEnd(op.sourceSpan));
20849
21432
  break;
20850
21433
  case OpKind.ContainerStart:
20851
- OpList.replace(op, elementContainerStart(op.handle.slot, op.attributes, op.localRefs, op.sourceSpan));
21434
+ OpList.replace(op, elementContainerStart(op.handle.slot, op.attributes, op.localRefs, op.startSourceSpan));
20852
21435
  break;
20853
21436
  case OpKind.Container:
20854
- OpList.replace(op, elementContainer(op.handle.slot, op.attributes, op.localRefs, op.sourceSpan));
21437
+ OpList.replace(op, elementContainer(op.handle.slot, op.attributes, op.localRefs, op.wholeSourceSpan));
20855
21438
  break;
20856
21439
  case OpKind.ContainerEnd:
20857
21440
  OpList.replace(op, elementContainerEnd());
@@ -20865,6 +21448,12 @@ function reifyCreateOperations(unit, ops) {
20865
21448
  case OpKind.I18n:
20866
21449
  OpList.replace(op, i18n(op.handle.slot, op.messageIndex, op.subTemplateIndex));
20867
21450
  break;
21451
+ case OpKind.I18nAttributes:
21452
+ if (op.i18nAttributesConfig === null) {
21453
+ throw new Error(`AssertionError: i18nAttributesConfig was not set`);
21454
+ }
21455
+ OpList.replace(op, i18nAttributes(op.handle.slot, op.i18nAttributesConfig));
21456
+ break;
20868
21457
  case OpKind.Template:
20869
21458
  if (!(unit instanceof ViewCompilationUnit)) {
20870
21459
  throw new Error(`AssertionError: must be compiling a component`);
@@ -20873,7 +21462,7 @@ function reifyCreateOperations(unit, ops) {
20873
21462
  throw new Error(`AssertionError: local refs array should have been extracted into a constant`);
20874
21463
  }
20875
21464
  const childView = unit.job.views.get(op.xref);
20876
- OpList.replace(op, template(op.handle.slot, variable(childView.fnName), childView.decls, childView.vars, op.tag, op.attributes, op.localRefs, op.sourceSpan));
21465
+ OpList.replace(op, template(op.handle.slot, variable(childView.fnName), childView.decls, childView.vars, op.tag, op.attributes, op.localRefs, op.startSourceSpan));
20877
21466
  break;
20878
21467
  case OpKind.DisableBindings:
20879
21468
  OpList.replace(op, disableBindings());
@@ -20886,10 +21475,11 @@ function reifyCreateOperations(unit, ops) {
20886
21475
  break;
20887
21476
  case OpKind.Listener:
20888
21477
  const listenerFn = reifyListenerHandler(unit, op.handlerFnName, op.handlerOps, op.consumesDollarEvent);
20889
- const reified = op.hostListener && op.isAnimationListener ?
20890
- syntheticHostListener(op.name, listenerFn, op.sourceSpan) :
20891
- listener(op.name, listenerFn, op.sourceSpan);
20892
- OpList.replace(op, reified);
21478
+ const eventTargetResolver = op.eventTarget ? GLOBAL_TARGET_RESOLVERS$1.get(op.eventTarget) : null;
21479
+ if (eventTargetResolver === undefined) {
21480
+ throw new Error(`Unexpected global target '${op.eventTarget}' defined for '${op.name}' event. Supported list of global targets: window,document,body.`);
21481
+ }
21482
+ OpList.replace(op, listener(op.name, listenerFn, eventTargetResolver, op.hostListener && op.isAnimationListener, op.sourceSpan));
20893
21483
  break;
20894
21484
  case OpKind.Variable:
20895
21485
  if (op.variable.name === null) {
@@ -20974,7 +21564,7 @@ function reifyCreateOperations(unit, ops) {
20974
21564
  emptyDecls = emptyView.decls;
20975
21565
  emptyVars = emptyView.vars;
20976
21566
  }
20977
- OpList.replace(op, repeaterCreate(op.handle.slot, repeaterView.fnName, op.decls, op.vars, op.tag, op.attributes, op.trackByFn, op.usesComponentInstance, emptyViewFnName, emptyDecls, emptyVars, op.sourceSpan));
21567
+ OpList.replace(op, repeaterCreate(op.handle.slot, repeaterView.fnName, op.decls, op.vars, op.tag, op.attributes, op.trackByFn, op.usesComponentInstance, emptyViewFnName, emptyDecls, emptyVars, op.wholeSourceSpan));
20978
21568
  break;
20979
21569
  case OpKind.Statement:
20980
21570
  // Pass statement operations directly through.
@@ -21052,7 +21642,7 @@ function reifyUpdateOperations(_unit, ops) {
21052
21642
  OpList.replace(op, syntheticHostProperty(op.name, op.expression, op.sourceSpan));
21053
21643
  }
21054
21644
  else {
21055
- OpList.replace(op, hostProperty(op.name, op.expression, op.sourceSpan));
21645
+ OpList.replace(op, hostProperty(op.name, op.expression, op.sanitizer, op.sourceSpan));
21056
21646
  }
21057
21647
  }
21058
21648
  break;
@@ -21072,7 +21662,7 @@ function reifyUpdateOperations(_unit, ops) {
21072
21662
  OpList.replace(op, conditional(op.targetSlot.slot, op.processed, op.contextValue, op.sourceSpan));
21073
21663
  break;
21074
21664
  case OpKind.Repeater:
21075
- OpList.replace(op, repeater(op.targetSlot.slot, op.collection, op.sourceSpan));
21665
+ OpList.replace(op, repeater(op.collection, op.sourceSpan));
21076
21666
  break;
21077
21667
  case OpKind.DeferWhen:
21078
21668
  OpList.replace(op, deferWhen(op.prefetch, op.expr, op.sourceSpan));
@@ -21131,8 +21721,6 @@ function reifyIrExpression(expr) {
21131
21721
  return pipeBind(expr.targetSlot.slot, expr.varOffset, expr.args);
21132
21722
  case ExpressionKind.PipeBindingVariadic:
21133
21723
  return pipeBindV(expr.targetSlot.slot, expr.varOffset, expr.args);
21134
- case ExpressionKind.SanitizerExpr:
21135
- return importExpr(sanitizerIdentifierMap.get(expr.fn));
21136
21724
  case ExpressionKind.SlotLiteralExpr:
21137
21725
  return literal(expr.slot.slot);
21138
21726
  default:
@@ -21206,6 +21794,31 @@ function removeI18nContexts(job) {
21206
21794
  }
21207
21795
  }
21208
21796
 
21797
+ /**
21798
+ * i18nAttributes ops will be generated for each i18n attribute. However, not all i18n attribues
21799
+ * will contain dynamic content, and so some of these i18nAttributes ops may be unnecessary.
21800
+ */
21801
+ function removeUnusedI18nAttributesOps(job) {
21802
+ for (const unit of job.units) {
21803
+ const ownersWithI18nExpressions = new Set();
21804
+ for (const op of unit.update) {
21805
+ switch (op.kind) {
21806
+ case OpKind.I18nExpression:
21807
+ ownersWithI18nExpressions.add(op.i18nOwner);
21808
+ }
21809
+ }
21810
+ for (const op of unit.create) {
21811
+ switch (op.kind) {
21812
+ case OpKind.I18nAttributes:
21813
+ if (ownersWithI18nExpressions.has(op.xref)) {
21814
+ continue;
21815
+ }
21816
+ OpList.remove(op);
21817
+ }
21818
+ }
21819
+ }
21820
+ }
21821
+
21209
21822
  /**
21210
21823
  * Inside the body of a repeater, certain context variables (such as `$first`) are ambiently
21211
21824
  * available. This phase finds those variable usages, and replaces them with the appropriate
@@ -21335,66 +21948,221 @@ function resolveI18nElementPlaceholders(job) {
21335
21948
  }
21336
21949
  }
21337
21950
  }
21338
- for (const unit of job.units) {
21339
- // Track the current i18n op and corresponding i18n context op as we step through the creation
21340
- // IR.
21341
- let currentOps = null;
21342
- for (const op of unit.create) {
21343
- switch (op.kind) {
21344
- case OpKind.I18nStart:
21345
- if (!op.context) {
21346
- throw Error('Could not find i18n context for i18n op');
21951
+ resolvePlaceholdersForView(job, job.root, i18nContexts, elements);
21952
+ }
21953
+ /**
21954
+ * Recursively resolves element and template tag placeholders in the given view.
21955
+ */
21956
+ function resolvePlaceholdersForView(job, unit, i18nContexts, elements, pendingStructuralDirective) {
21957
+ // Track the current i18n op and corresponding i18n context op as we step through the creation
21958
+ // IR.
21959
+ let currentOps = null;
21960
+ let pendingStructuralDirectiveCloses = new Map();
21961
+ for (const op of unit.create) {
21962
+ switch (op.kind) {
21963
+ case OpKind.I18nStart:
21964
+ if (!op.context) {
21965
+ throw Error('Could not find i18n context for i18n op');
21966
+ }
21967
+ currentOps = { i18nBlock: op, i18nContext: i18nContexts.get(op.context) };
21968
+ break;
21969
+ case OpKind.I18nEnd:
21970
+ currentOps = null;
21971
+ break;
21972
+ case OpKind.ElementStart:
21973
+ // For elements with i18n placeholders, record its slot value in the params map under the
21974
+ // corresponding tag start placeholder.
21975
+ if (op.i18nPlaceholder !== undefined) {
21976
+ if (currentOps === null) {
21977
+ throw Error('i18n tag placeholder should only occur inside an i18n block');
21347
21978
  }
21348
- currentOps = { i18nBlock: op, i18nContext: i18nContexts.get(op.context) };
21349
- break;
21350
- case OpKind.I18nEnd:
21351
- currentOps = null;
21352
- break;
21353
- case OpKind.ElementStart:
21354
- // For elements with i18n placeholders, record its slot value in the params map under the
21355
- // corresponding tag start placeholder.
21356
- if (op.i18nPlaceholder !== undefined) {
21357
- if (currentOps === null) {
21358
- throw Error('i18n tag placeholder should only occur inside an i18n block');
21359
- }
21360
- const { startName, closeName } = op.i18nPlaceholder;
21361
- let flags = I18nParamValueFlags.ElementTag | I18nParamValueFlags.OpenTag;
21362
- // For self-closing tags, there is no close tag placeholder. Instead, the start tag
21363
- // placeholder accounts for the start and close of the element.
21364
- if (closeName === '') {
21365
- flags |= I18nParamValueFlags.CloseTag;
21366
- }
21367
- addParam(currentOps.i18nContext.params, startName, op.handle.slot, currentOps.i18nBlock.subTemplateIndex, flags);
21979
+ recordElementStart(op, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
21980
+ // If there is a separate close tag placeholder for this element, save the pending
21981
+ // structural directive so we can pass it to the closing tag as well.
21982
+ if (pendingStructuralDirective && op.i18nPlaceholder.closeName) {
21983
+ pendingStructuralDirectiveCloses.set(op.xref, pendingStructuralDirective);
21368
21984
  }
21369
- break;
21370
- case OpKind.ElementEnd:
21371
- // For elements with i18n placeholders, record its slot value in the params map under the
21372
- // corresponding tag close placeholder.
21373
- const startOp = elements.get(op.xref);
21374
- if (startOp && startOp.i18nPlaceholder !== undefined) {
21375
- if (currentOps === null) {
21376
- throw Error('i18n tag placeholder should only occur inside an i18n block');
21377
- }
21378
- const { closeName } = startOp.i18nPlaceholder;
21379
- // Self-closing tags don't have a closing tag placeholder.
21380
- if (closeName !== '') {
21381
- addParam(currentOps.i18nContext.params, closeName, startOp.handle.slot, currentOps.i18nBlock.subTemplateIndex, I18nParamValueFlags.ElementTag | I18nParamValueFlags.CloseTag);
21382
- }
21985
+ // Clear out the pending structural directive now that its been accounted for.
21986
+ pendingStructuralDirective = undefined;
21987
+ }
21988
+ break;
21989
+ case OpKind.ElementEnd:
21990
+ // For elements with i18n placeholders, record its slot value in the params map under the
21991
+ // corresponding tag close placeholder.
21992
+ const startOp = elements.get(op.xref);
21993
+ if (startOp && startOp.i18nPlaceholder !== undefined) {
21994
+ if (currentOps === null) {
21995
+ throw Error('AssertionError: i18n tag placeholder should only occur inside an i18n block');
21383
21996
  }
21384
- break;
21385
- case OpKind.Template:
21386
- // For templates with i18n placeholders, record its slot value in the params map under the
21387
- // corresponding template start and close placeholders.
21388
- if (op.i18nPlaceholder !== undefined) {
21997
+ recordElementClose(startOp, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirectiveCloses.get(op.xref));
21998
+ // Clear out the pending structural directive close that was accounted for.
21999
+ pendingStructuralDirectiveCloses.delete(op.xref);
22000
+ }
22001
+ break;
22002
+ case OpKind.Projection:
22003
+ // For content projections with i18n placeholders, record its slot value in the params map
22004
+ // under the corresponding tag start and close placeholders.
22005
+ if (op.i18nPlaceholder !== undefined) {
22006
+ if (currentOps === null) {
22007
+ throw Error('i18n tag placeholder should only occur inside an i18n block');
22008
+ }
22009
+ recordElementStart(op, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22010
+ recordElementClose(op, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22011
+ // Clear out the pending structural directive now that its been accounted for.
22012
+ pendingStructuralDirective = undefined;
22013
+ }
22014
+ break;
22015
+ case OpKind.Template:
22016
+ const view = job.views.get(op.xref);
22017
+ if (op.i18nPlaceholder === undefined) {
22018
+ // If there is no i18n placeholder, just recurse into the view in case it contains i18n
22019
+ // blocks.
22020
+ resolvePlaceholdersForView(job, view, i18nContexts, elements);
22021
+ }
22022
+ else {
22023
+ if (currentOps === null) {
22024
+ throw Error('i18n tag placeholder should only occur inside an i18n block');
22025
+ }
22026
+ if (op.templateKind === TemplateKind.Structural) {
22027
+ // If this is a structural directive template, don't record anything yet. Instead pass
22028
+ // the current template as a pending structural directive to be recorded when we find
22029
+ // the element, content, or template it belongs to. This allows us to create combined
22030
+ // values that represent, e.g. the start of a template and element at the same time.
22031
+ resolvePlaceholdersForView(job, view, i18nContexts, elements, op);
22032
+ }
22033
+ else {
22034
+ // If this is some other kind of template, we can record its start, recurse into its
22035
+ // view, and then record its end.
22036
+ recordTemplateStart(job, view, op.handle.slot, op.i18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22037
+ resolvePlaceholdersForView(job, view, i18nContexts, elements);
22038
+ recordTemplateClose(job, view, op.handle.slot, op.i18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22039
+ pendingStructuralDirective = undefined;
22040
+ }
22041
+ }
22042
+ break;
22043
+ case OpKind.RepeaterCreate:
22044
+ if (pendingStructuralDirective !== undefined) {
22045
+ throw Error('AssertionError: Unexpected structural directive associated with @for block');
22046
+ }
22047
+ // RepeaterCreate has 3 slots: the first is for the op itself, the second is for the @for
22048
+ // template and the (optional) third is for the @empty template.
22049
+ const forSlot = op.handle.slot + 1;
22050
+ const forView = job.views.get(op.xref);
22051
+ // First record all of the placeholders for the @for template.
22052
+ if (op.i18nPlaceholder === undefined) {
22053
+ // If there is no i18n placeholder, just recurse into the view in case it contains i18n
22054
+ // blocks.
22055
+ resolvePlaceholdersForView(job, forView, i18nContexts, elements);
22056
+ }
22057
+ else {
22058
+ if (currentOps === null) {
22059
+ throw Error('i18n tag placeholder should only occur inside an i18n block');
22060
+ }
22061
+ recordTemplateStart(job, forView, forSlot, op.i18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22062
+ resolvePlaceholdersForView(job, forView, i18nContexts, elements);
22063
+ recordTemplateClose(job, forView, forSlot, op.i18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22064
+ pendingStructuralDirective = undefined;
22065
+ }
22066
+ // Then if there's an @empty template, add its placeholders as well.
22067
+ if (op.emptyView !== null) {
22068
+ // RepeaterCreate has 3 slots: the first is for the op itself, the second is for the @for
22069
+ // template and the (optional) third is for the @empty template.
22070
+ const emptySlot = op.handle.slot + 2;
22071
+ const emptyView = job.views.get(op.emptyView);
22072
+ if (op.emptyI18nPlaceholder === undefined) {
22073
+ // If there is no i18n placeholder, just recurse into the view in case it contains i18n
22074
+ // blocks.
22075
+ resolvePlaceholdersForView(job, emptyView, i18nContexts, elements);
22076
+ }
22077
+ else {
21389
22078
  if (currentOps === null) {
21390
22079
  throw Error('i18n tag placeholder should only occur inside an i18n block');
21391
22080
  }
21392
- const subTemplateIndex = getSubTemplateIndexForTemplateTag(job, currentOps.i18nBlock, op);
21393
- addParam(currentOps.i18nContext.params, op.i18nPlaceholder.startName, op.handle.slot, subTemplateIndex, I18nParamValueFlags.TemplateTag);
21394
- addParam(currentOps.i18nContext.params, op.i18nPlaceholder.closeName, op.handle.slot, subTemplateIndex, I18nParamValueFlags.TemplateTag | I18nParamValueFlags.CloseTag);
22081
+ recordTemplateStart(job, emptyView, emptySlot, op.emptyI18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22082
+ resolvePlaceholdersForView(job, emptyView, i18nContexts, elements);
22083
+ recordTemplateClose(job, emptyView, emptySlot, op.emptyI18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
22084
+ pendingStructuralDirective = undefined;
21395
22085
  }
21396
- break;
21397
- }
22086
+ }
22087
+ break;
22088
+ }
22089
+ }
22090
+ }
22091
+ /**
22092
+ * Records an i18n param value for the start of an element.
22093
+ */
22094
+ function recordElementStart(op, i18nContext, i18nBlock, structuralDirective) {
22095
+ const { startName, closeName } = op.i18nPlaceholder;
22096
+ let flags = I18nParamValueFlags.ElementTag | I18nParamValueFlags.OpenTag;
22097
+ let value = op.handle.slot;
22098
+ // If the element is associated with a structural directive, start it as well.
22099
+ if (structuralDirective !== undefined) {
22100
+ flags |= I18nParamValueFlags.TemplateTag;
22101
+ value = { element: value, template: structuralDirective.handle.slot };
22102
+ }
22103
+ // For self-closing tags, there is no close tag placeholder. Instead, the start tag
22104
+ // placeholder accounts for the start and close of the element.
22105
+ if (!closeName) {
22106
+ flags |= I18nParamValueFlags.CloseTag;
22107
+ }
22108
+ addParam(i18nContext.params, startName, value, i18nBlock.subTemplateIndex, flags);
22109
+ }
22110
+ /**
22111
+ * Records an i18n param value for the closing of an element.
22112
+ */
22113
+ function recordElementClose(op, i18nContext, i18nBlock, structuralDirective) {
22114
+ const { closeName } = op.i18nPlaceholder;
22115
+ // Self-closing tags don't have a closing tag placeholder, instead the element closing is
22116
+ // recorded via an additional flag on the element start value.
22117
+ if (closeName) {
22118
+ let flags = I18nParamValueFlags.ElementTag | I18nParamValueFlags.CloseTag;
22119
+ let value = op.handle.slot;
22120
+ // If the element is associated with a structural directive, close it as well.
22121
+ if (structuralDirective !== undefined) {
22122
+ flags |= I18nParamValueFlags.TemplateTag;
22123
+ value = { element: value, template: structuralDirective.handle.slot };
22124
+ }
22125
+ addParam(i18nContext.params, closeName, value, i18nBlock.subTemplateIndex, flags);
22126
+ }
22127
+ }
22128
+ /**
22129
+ * Records an i18n param value for the start of a template.
22130
+ */
22131
+ function recordTemplateStart(job, view, slot, i18nPlaceholder, i18nContext, i18nBlock, structuralDirective) {
22132
+ let { startName, closeName } = i18nPlaceholder;
22133
+ let flags = I18nParamValueFlags.TemplateTag | I18nParamValueFlags.OpenTag;
22134
+ // For self-closing tags, there is no close tag placeholder. Instead, the start tag
22135
+ // placeholder accounts for the start and close of the element.
22136
+ if (!closeName) {
22137
+ flags |= I18nParamValueFlags.CloseTag;
22138
+ }
22139
+ // If the template is associated with a structural directive, record the structural directive's
22140
+ // start first. Since this template must be in the structural directive's view, we can just
22141
+ // directly use the current i18n block's sub-template index.
22142
+ if (structuralDirective !== undefined) {
22143
+ addParam(i18nContext.params, startName, structuralDirective.handle.slot, i18nBlock.subTemplateIndex, flags);
22144
+ }
22145
+ // Record the start of the template. For the sub-template index, pass the index for the template's
22146
+ // view, rather than the current i18n block's index.
22147
+ addParam(i18nContext.params, startName, slot, getSubTemplateIndexForTemplateTag(job, i18nBlock, view), flags);
22148
+ }
22149
+ /**
22150
+ * Records an i18n param value for the closing of a template.
22151
+ */
22152
+ function recordTemplateClose(job, view, slot, i18nPlaceholder, i18nContext, i18nBlock, structuralDirective) {
22153
+ const { startName, closeName } = i18nPlaceholder;
22154
+ const flags = I18nParamValueFlags.TemplateTag | I18nParamValueFlags.CloseTag;
22155
+ // Self-closing tags don't have a closing tag placeholder, instead the template's closing is
22156
+ // recorded via an additional flag on the template start value.
22157
+ if (closeName) {
22158
+ // Record the closing of the template. For the sub-template index, pass the index for the
22159
+ // template's view, rather than the current i18n block's index.
22160
+ addParam(i18nContext.params, closeName, slot, getSubTemplateIndexForTemplateTag(job, i18nBlock, view), flags);
22161
+ // If the template is associated with a structural directive, record the structural directive's
22162
+ // closing after. Since this template must be in the structural directive's view, we can just
22163
+ // directly use the current i18n block's sub-template index.
22164
+ if (structuralDirective !== undefined) {
22165
+ addParam(i18nContext.params, closeName, structuralDirective.handle.slot, i18nBlock.subTemplateIndex, flags);
21398
22166
  }
21399
22167
  }
21400
22168
  }
@@ -21402,16 +22170,18 @@ function resolveI18nElementPlaceholders(job) {
21402
22170
  * Get the subTemplateIndex for the given template op. For template ops, use the subTemplateIndex of
21403
22171
  * the child i18n block inside the template.
21404
22172
  */
21405
- function getSubTemplateIndexForTemplateTag(job, i18nOp, op) {
21406
- for (const childOp of job.views.get(op.xref).create) {
22173
+ function getSubTemplateIndexForTemplateTag(job, i18nOp, view) {
22174
+ for (const childOp of view.create) {
21407
22175
  if (childOp.kind === OpKind.I18nStart) {
21408
22176
  return childOp.subTemplateIndex;
21409
22177
  }
21410
22178
  }
21411
22179
  return i18nOp.subTemplateIndex;
21412
22180
  }
21413
- /** Add a param value to the given params map. */
21414
- function addParam(params, placeholder, value, subTemplateIndex, flags = I18nParamValueFlags.None) {
22181
+ /**
22182
+ * Add a param value to the given params map.
22183
+ */
22184
+ function addParam(params, placeholder, value, subTemplateIndex, flags) {
21415
22185
  const values = params.get(placeholder) ?? [];
21416
22186
  values.push({ value, subTemplateIndex, flags });
21417
22187
  params.set(placeholder, values);
@@ -21424,6 +22194,7 @@ function resolveI18nExpressionPlaceholders(job) {
21424
22194
  // Record all of the i18n context ops, and the sub-template index for each i18n op.
21425
22195
  const subTemplateIndicies = new Map();
21426
22196
  const i18nContexts = new Map();
22197
+ const icuPlaceholders = new Map();
21427
22198
  for (const unit of job.units) {
21428
22199
  for (const op of unit.create) {
21429
22200
  switch (op.kind) {
@@ -21433,29 +22204,50 @@ function resolveI18nExpressionPlaceholders(job) {
21433
22204
  case OpKind.I18nContext:
21434
22205
  i18nContexts.set(op.xref, op);
21435
22206
  break;
22207
+ case OpKind.IcuPlaceholder:
22208
+ icuPlaceholders.set(op.xref, op);
22209
+ break;
21436
22210
  }
21437
22211
  }
21438
22212
  }
21439
- // Keep track of the next available expression index per i18n context.
22213
+ // Keep track of the next available expression index for each i18n message.
21440
22214
  const expressionIndices = new Map();
22215
+ // Keep track of a reference index for each expression.
22216
+ // We use different references for normal i18n expressio and attribute i18n expressions. This is
22217
+ // because child i18n blocks in templates don't get their own context, since they're rolled into
22218
+ // the translated message of the parent, but they may target a different slot.
22219
+ const referenceIndex = (op) => op.usage === I18nExpressionFor.I18nText ? op.i18nOwner : op.context;
21441
22220
  for (const unit of job.units) {
21442
22221
  for (const op of unit.update) {
21443
22222
  if (op.kind === OpKind.I18nExpression) {
21444
- const index = expressionIndices.get(op.context) || 0;
21445
- const i18nContext = i18nContexts.get(op.context);
21446
- const subTemplateIndex = subTemplateIndicies.get(i18nContext.i18nBlock);
21447
- // Add the expression index in the appropriate params map.
21448
- const params = op.resolutionTime === I18nParamResolutionTime.Creation ?
21449
- i18nContext.params :
21450
- i18nContext.postprocessingParams;
21451
- const values = params.get(op.i18nPlaceholder) || [];
21452
- values.push({ value: index, subTemplateIndex: subTemplateIndex, flags: I18nParamValueFlags.None });
21453
- params.set(op.i18nPlaceholder, values);
21454
- expressionIndices.set(op.context, index + 1);
22223
+ const index = expressionIndices.get(referenceIndex(op)) || 0;
22224
+ const subTemplateIndex = subTemplateIndicies.get(op.i18nOwner) ?? null;
22225
+ const value = {
22226
+ value: index,
22227
+ subTemplateIndex: subTemplateIndex,
22228
+ flags: I18nParamValueFlags.ExpressionIndex
22229
+ };
22230
+ updatePlaceholder(op, value, i18nContexts, icuPlaceholders);
22231
+ expressionIndices.set(referenceIndex(op), index + 1);
21455
22232
  }
21456
22233
  }
21457
22234
  }
21458
22235
  }
22236
+ function updatePlaceholder(op, value, i18nContexts, icuPlaceholders) {
22237
+ if (op.i18nPlaceholder !== null) {
22238
+ const i18nContext = i18nContexts.get(op.context);
22239
+ const params = op.resolutionTime === I18nParamResolutionTime.Creation ?
22240
+ i18nContext.params :
22241
+ i18nContext.postprocessingParams;
22242
+ const values = params.get(op.i18nPlaceholder) || [];
22243
+ values.push(value);
22244
+ params.set(op.i18nPlaceholder, values);
22245
+ }
22246
+ if (op.icuPlaceholder !== null) {
22247
+ const icuPlaceholderOp = icuPlaceholders.get(op.icuPlaceholder);
22248
+ icuPlaceholderOp?.expressionPlaceholders.push(value);
22249
+ }
22250
+ }
21459
22251
 
21460
22252
  /**
21461
22253
  * Resolves lexical references in views (`ir.LexicalReadExpr`) to either a target variable or to
@@ -21556,12 +22348,20 @@ function processLexicalScope(unit, ops, savedView) {
21556
22348
  }
21557
22349
 
21558
22350
  /**
21559
- * Mapping of security contexts to sanitizer function for that context.
22351
+ * Map of security contexts to their sanitizer function.
22352
+ */
22353
+ const sanitizerFns = new Map([
22354
+ [SecurityContext.HTML, Identifiers.sanitizeHtml],
22355
+ [SecurityContext.RESOURCE_URL, Identifiers.sanitizeResourceUrl],
22356
+ [SecurityContext.SCRIPT, Identifiers.sanitizeScript],
22357
+ [SecurityContext.STYLE, Identifiers.sanitizeStyle], [SecurityContext.URL, Identifiers.sanitizeUrl]
22358
+ ]);
22359
+ /**
22360
+ * Map of security contexts to their trusted value function.
21560
22361
  */
21561
- const sanitizers = new Map([
21562
- [SecurityContext.HTML, SanitizerFn.Html], [SecurityContext.SCRIPT, SanitizerFn.Script],
21563
- [SecurityContext.STYLE, SanitizerFn.Style], [SecurityContext.URL, SanitizerFn.Url],
21564
- [SecurityContext.RESOURCE_URL, SanitizerFn.ResourceUrl]
22362
+ const trustedValueFns = new Map([
22363
+ [SecurityContext.HTML, Identifiers.trustConstantHtml],
22364
+ [SecurityContext.RESOURCE_URL, Identifiers.trustConstantResourceUrl],
21565
22365
  ]);
21566
22366
  /**
21567
22367
  * Resolves sanitization functions for ops that need them.
@@ -21569,24 +22369,61 @@ const sanitizers = new Map([
21569
22369
  function resolveSanitizers(job) {
21570
22370
  for (const unit of job.units) {
21571
22371
  const elements = createOpXrefMap(unit);
21572
- let sanitizerFn;
22372
+ // For normal element bindings we create trusted values for security sensitive constant
22373
+ // attributes. However, for host bindings we skip this step (this matches what
22374
+ // TemplateDefinitionBuilder does).
22375
+ // TODO: Is the TDB behavior correct here?
22376
+ if (job.kind !== CompilationJobKind.Host) {
22377
+ for (const op of unit.create) {
22378
+ if (op.kind === OpKind.ExtractedAttribute) {
22379
+ const trustedValueFn = trustedValueFns.get(getOnlySecurityContext(op.securityContext)) ?? null;
22380
+ op.trustedValueFn = trustedValueFn !== null ? importExpr(trustedValueFn) : null;
22381
+ }
22382
+ }
22383
+ }
21573
22384
  for (const op of unit.update) {
21574
22385
  switch (op.kind) {
21575
22386
  case OpKind.Property:
21576
22387
  case OpKind.Attribute:
21577
- sanitizerFn = sanitizers.get(op.securityContext) || null;
21578
- op.sanitizer = sanitizerFn ? new SanitizerExpr(sanitizerFn) : null;
22388
+ case OpKind.HostProperty:
22389
+ let sanitizerFn = null;
22390
+ if (Array.isArray(op.securityContext) && op.securityContext.length === 2 &&
22391
+ op.securityContext.indexOf(SecurityContext.URL) > -1 &&
22392
+ op.securityContext.indexOf(SecurityContext.RESOURCE_URL) > -1) {
22393
+ // When the host element isn't known, some URL attributes (such as "src" and "href") may
22394
+ // be part of multiple different security contexts. In this case we use special
22395
+ // sanitization function and select the actual sanitizer at runtime based on a tag name
22396
+ // that is provided while invoking sanitization function.
22397
+ sanitizerFn = Identifiers.sanitizeUrlOrResourceUrl;
22398
+ }
22399
+ else {
22400
+ sanitizerFn = sanitizerFns.get(getOnlySecurityContext(op.securityContext)) ?? null;
22401
+ }
22402
+ op.sanitizer = sanitizerFn !== null ? importExpr(sanitizerFn) : null;
21579
22403
  // If there was no sanitization function found based on the security context of an
21580
22404
  // attribute/property, check whether this attribute/property is one of the
21581
22405
  // security-sensitive <iframe> attributes (and that the current element is actually an
21582
22406
  // <iframe>).
21583
22407
  if (op.sanitizer === null) {
21584
- const ownerOp = elements.get(op.target);
21585
- if (ownerOp === undefined || !isElementOrContainerOp(ownerOp)) {
21586
- throw Error('Property should have an element-like owner');
22408
+ let isIframe = false;
22409
+ if (job.kind === CompilationJobKind.Host || op.kind === OpKind.HostProperty) {
22410
+ // Note: for host bindings defined on a directive, we do not try to find all
22411
+ // possible places where it can be matched, so we can not determine whether
22412
+ // the host element is an <iframe>. In this case, we just assume it is and append a
22413
+ // validation function, which is invoked at runtime and would have access to the
22414
+ // underlying DOM element to check if it's an <iframe> and if so - run extra checks.
22415
+ isIframe = true;
22416
+ }
22417
+ else {
22418
+ // For a normal binding we can just check if the element its on is an iframe.
22419
+ const ownerOp = elements.get(op.target);
22420
+ if (ownerOp === undefined || !isElementOrContainerOp(ownerOp)) {
22421
+ throw Error('Property should have an element-like owner');
22422
+ }
22423
+ isIframe = isIframeElement$1(ownerOp);
21587
22424
  }
21588
- if (isIframeElement$1(ownerOp) && isIframeSecuritySensitiveAttr(op.name)) {
21589
- op.sanitizer = new SanitizerExpr(SanitizerFn.IframeAttribute);
22425
+ if (isIframe && isIframeSecuritySensitiveAttr(op.name)) {
22426
+ op.sanitizer = importExpr(Identifiers.validateIframeAttribute);
21590
22427
  }
21591
22428
  }
21592
22429
  break;
@@ -21600,6 +22437,22 @@ function resolveSanitizers(job) {
21600
22437
  function isIframeElement$1(op) {
21601
22438
  return op.kind === OpKind.ElementStart && op.tag?.toLowerCase() === 'iframe';
21602
22439
  }
22440
+ /**
22441
+ * Asserts that there is only a single security context and returns it.
22442
+ */
22443
+ function getOnlySecurityContext(securityContext) {
22444
+ if (Array.isArray(securityContext)) {
22445
+ if (securityContext.length > 1) {
22446
+ // TODO: What should we do here? TDB just took the first one, but this feels like something we
22447
+ // would want to know about and create a special case for like we did for Url/ResourceUrl. My
22448
+ // guess is that, outside of the Url/ResourceUrl case, this never actually happens. If there
22449
+ // do turn out to be other cases, throwing an error until we can address it feels safer.
22450
+ throw Error(`AssertionError: Ambiguous security context`);
22451
+ }
22452
+ return securityContext[0] || SecurityContext.NONE;
22453
+ }
22454
+ return securityContext;
22455
+ }
21603
22456
 
21604
22457
  /**
21605
22458
  * When inside of a listener, we may need access to one or more enclosing views. Therefore, each
@@ -21703,6 +22556,8 @@ function allocateSlots(job) {
21703
22556
  // operation itself, so it can be emitted later.
21704
22557
  const childView = job.views.get(op.xref);
21705
22558
  op.decls = childView.decls;
22559
+ // TODO: currently we handle the decls for the RepeaterCreate empty template in the reify
22560
+ // phase. We should handle that here instead.
21706
22561
  }
21707
22562
  }
21708
22563
  }
@@ -22026,6 +22881,8 @@ function countVariables(job) {
22026
22881
  }
22027
22882
  const childView = job.views.get(op.xref);
22028
22883
  op.vars = childView.vars;
22884
+ // TODO: currently we handle the vars for the RepeaterCreate empty template in the reify
22885
+ // phase. We should handle that here instead.
22029
22886
  }
22030
22887
  }
22031
22888
  }
@@ -22063,7 +22920,14 @@ function varsUsedByOp(op) {
22063
22920
  return op.interpolation.expressions.length;
22064
22921
  case OpKind.I18nExpression:
22065
22922
  case OpKind.Conditional:
22923
+ case OpKind.DeferWhen:
22066
22924
  return 1;
22925
+ case OpKind.RepeaterCreate:
22926
+ // Repeaters may require an extra variable binding slot, if they have an empty view, for the
22927
+ // empty block tracking.
22928
+ // TODO: It's a bit odd to have a create mode instruction consume variable slots. Maybe we can
22929
+ // find a way to use the Repeater update op instead.
22930
+ return op.emptyView ? 1 : 0;
22067
22931
  default:
22068
22932
  throw new Error(`Unhandled op: ${OpKind[op.kind]}`);
22069
22933
  }
@@ -22507,6 +23371,7 @@ function allowConservativeInlining(decl, target) {
22507
23371
  function wrapI18nIcus(job) {
22508
23372
  for (const unit of job.units) {
22509
23373
  let currentI18nOp = null;
23374
+ let addedI18nId = null;
22510
23375
  for (const op of unit.create) {
22511
23376
  switch (op.kind) {
22512
23377
  case OpKind.I18nStart:
@@ -22515,11 +23380,16 @@ function wrapI18nIcus(job) {
22515
23380
  case OpKind.I18nEnd:
22516
23381
  currentI18nOp = null;
22517
23382
  break;
22518
- case OpKind.Icu:
23383
+ case OpKind.IcuStart:
22519
23384
  if (currentI18nOp === null) {
22520
- const id = job.allocateXrefId();
22521
- OpList.insertBefore(createI18nStartOp(id, op.message), op);
22522
- OpList.insertAfter(createI18nEndOp(id), op);
23385
+ addedI18nId = job.allocateXrefId();
23386
+ OpList.insertBefore(createI18nStartOp(addedI18nId, op.message), op);
23387
+ }
23388
+ break;
23389
+ case OpKind.IcuEnd:
23390
+ if (addedI18nId !== null) {
23391
+ OpList.insertAfter(createI18nEndOp(addedI18nId), op);
23392
+ addedI18nId = null;
22523
23393
  }
22524
23394
  break;
22525
23395
  }
@@ -22539,12 +23409,13 @@ function wrapI18nIcus(job) {
22539
23409
  { kind: CompilationJobKind.Tmpl, fn: removeContentSelectors },
22540
23410
  { kind: CompilationJobKind.Host, fn: parseHostStyleProperties },
22541
23411
  { kind: CompilationJobKind.Tmpl, fn: emitNamespaceChanges },
22542
- { kind: CompilationJobKind.Both, fn: specializeStyleBindings },
22543
- { kind: CompilationJobKind.Both, fn: specializeBindings },
22544
23412
  { kind: CompilationJobKind.Tmpl, fn: propagateI18nBlocks },
22545
23413
  { kind: CompilationJobKind.Tmpl, fn: wrapI18nIcus },
22546
- { kind: CompilationJobKind.Tmpl, fn: createI18nContexts },
23414
+ { kind: CompilationJobKind.Both, fn: deduplicateTextBindings },
23415
+ { kind: CompilationJobKind.Both, fn: specializeStyleBindings },
23416
+ { kind: CompilationJobKind.Both, fn: specializeBindings },
22547
23417
  { kind: CompilationJobKind.Both, fn: extractAttributes },
23418
+ { kind: CompilationJobKind.Tmpl, fn: createI18nContexts },
22548
23419
  { kind: CompilationJobKind.Both, fn: parseExtractedStyles },
22549
23420
  { kind: CompilationJobKind.Tmpl, fn: removeEmptyBindings },
22550
23421
  { kind: CompilationJobKind.Both, fn: collapseSingletonInterpolations },
@@ -22552,15 +23423,17 @@ function wrapI18nIcus(job) {
22552
23423
  { kind: CompilationJobKind.Tmpl, fn: generateConditionalExpressions },
22553
23424
  { kind: CompilationJobKind.Tmpl, fn: createPipes },
22554
23425
  { kind: CompilationJobKind.Tmpl, fn: configureDeferInstructions },
22555
- { kind: CompilationJobKind.Tmpl, fn: extractI18nText },
22556
- { kind: CompilationJobKind.Tmpl, fn: createI18nIcuExpressions },
23426
+ { kind: CompilationJobKind.Tmpl, fn: convertI18nText },
23427
+ { kind: CompilationJobKind.Tmpl, fn: convertI18nBindings },
23428
+ { kind: CompilationJobKind.Tmpl, fn: removeUnusedI18nAttributesOps },
23429
+ { kind: CompilationJobKind.Tmpl, fn: assignI18nSlotDependencies },
22557
23430
  { kind: CompilationJobKind.Tmpl, fn: applyI18nExpressions },
22558
23431
  { kind: CompilationJobKind.Tmpl, fn: createVariadicPipes },
22559
23432
  { kind: CompilationJobKind.Both, fn: generatePureLiteralStructures },
22560
23433
  { kind: CompilationJobKind.Tmpl, fn: generateProjectionDefs },
22561
23434
  { kind: CompilationJobKind.Tmpl, fn: generateVariables },
22562
23435
  { kind: CompilationJobKind.Tmpl, fn: saveAndRestoreView },
22563
- { kind: CompilationJobKind.Tmpl, fn: deleteAnyCasts },
23436
+ { kind: CompilationJobKind.Both, fn: deleteAnyCasts },
22564
23437
  { kind: CompilationJobKind.Both, fn: resolveDollarEvent },
22565
23438
  { kind: CompilationJobKind.Tmpl, fn: generateRepeaterDerivedVars },
22566
23439
  { kind: CompilationJobKind.Tmpl, fn: generateTrackVariables },
@@ -22568,7 +23441,7 @@ function wrapI18nIcus(job) {
22568
23441
  { kind: CompilationJobKind.Tmpl, fn: resolveDeferTargetNames },
22569
23442
  { kind: CompilationJobKind.Tmpl, fn: optimizeTrackFns },
22570
23443
  { kind: CompilationJobKind.Both, fn: resolveContexts },
22571
- { kind: CompilationJobKind.Tmpl, fn: resolveSanitizers },
23444
+ { kind: CompilationJobKind.Both, fn: resolveSanitizers },
22572
23445
  { kind: CompilationJobKind.Tmpl, fn: liftLocalRefs },
22573
23446
  { kind: CompilationJobKind.Both, fn: generateNullishCoalesceExpressions },
22574
23447
  { kind: CompilationJobKind.Both, fn: expandSafeReads },
@@ -22577,13 +23450,11 @@ function wrapI18nIcus(job) {
22577
23450
  { kind: CompilationJobKind.Tmpl, fn: createDeferDepsFns },
22578
23451
  { kind: CompilationJobKind.Tmpl, fn: resolveI18nElementPlaceholders },
22579
23452
  { kind: CompilationJobKind.Tmpl, fn: resolveI18nExpressionPlaceholders },
22580
- { kind: CompilationJobKind.Tmpl, fn: mergeI18nContexts },
22581
23453
  { kind: CompilationJobKind.Tmpl, fn: extractI18nMessages },
22582
23454
  { kind: CompilationJobKind.Tmpl, fn: generateTrackFns },
22583
23455
  { kind: CompilationJobKind.Tmpl, fn: collectI18nConsts },
22584
23456
  { kind: CompilationJobKind.Tmpl, fn: collectConstExpressions },
22585
23457
  { kind: CompilationJobKind.Both, fn: collectElementConsts },
22586
- { kind: CompilationJobKind.Tmpl, fn: assignI18nSlotDependencies },
22587
23458
  { kind: CompilationJobKind.Tmpl, fn: removeI18nContexts },
22588
23459
  { kind: CompilationJobKind.Both, fn: countVariables },
22589
23460
  { kind: CompilationJobKind.Tmpl, fn: generateAdvance },
@@ -22599,6 +23470,9 @@ function wrapI18nIcus(job) {
22599
23470
  ];
22600
23471
 
22601
23472
  CompatibilityMode.TemplateDefinitionBuilder;
23473
+ // Schema containing DOM elements and their properties.
23474
+ new DomElementSchemaRegistry();
23475
+ // TODO: Can we populate Template binding kinds in ingest?
22602
23476
  new Map([
22603
23477
  [0 /* e.BindingType.Property */, BindingKind.Property],
22604
23478
  [1 /* e.BindingType.Attribute */, BindingKind.Attribute],
@@ -22606,26 +23480,6 @@ new Map([
22606
23480
  [3 /* e.BindingType.Style */, BindingKind.StyleProperty],
22607
23481
  [4 /* e.BindingType.Animation */, BindingKind.Animation],
22608
23482
  ]);
22609
- var BindingFlags;
22610
- (function (BindingFlags) {
22611
- BindingFlags[BindingFlags["None"] = 0] = "None";
22612
- /**
22613
- * The binding is to a static text literal and not to an expression.
22614
- */
22615
- BindingFlags[BindingFlags["TextValue"] = 1] = "TextValue";
22616
- /**
22617
- * The binding belongs to the `<ng-template>` side of a `t.Template`.
22618
- */
22619
- BindingFlags[BindingFlags["BindingTargetsTemplate"] = 2] = "BindingTargetsTemplate";
22620
- /**
22621
- * The binding is on a structural directive.
22622
- */
22623
- BindingFlags[BindingFlags["IsStructuralTemplateAttribute"] = 4] = "IsStructuralTemplateAttribute";
22624
- /**
22625
- * The binding is on a `t.Template`.
22626
- */
22627
- BindingFlags[BindingFlags["OnNgTemplateElement"] = 8] = "OnNgTemplateElement";
22628
- })(BindingFlags || (BindingFlags = {}));
22629
23483
 
22630
23484
  const IMPORTANT_FLAG = '!important';
22631
23485
  /**
@@ -23776,19 +24630,19 @@ function createIfBlock(ast, connectedBlocks, visitor, bindingParser) {
23776
24630
  const branches = [];
23777
24631
  const mainBlockParams = parseConditionalBlockParameters(ast, errors, bindingParser);
23778
24632
  if (mainBlockParams !== null) {
23779
- branches.push(new IfBlockBranch(mainBlockParams.expression, visitAll(visitor, ast.children, ast.children), mainBlockParams.expressionAlias, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan, ast.nameSpan));
24633
+ branches.push(new IfBlockBranch(mainBlockParams.expression, visitAll(visitor, ast.children, ast.children), mainBlockParams.expressionAlias, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan, ast.nameSpan, ast.i18n));
23780
24634
  }
23781
24635
  for (const block of connectedBlocks) {
23782
24636
  if (ELSE_IF_PATTERN.test(block.name)) {
23783
24637
  const params = parseConditionalBlockParameters(block, errors, bindingParser);
23784
24638
  if (params !== null) {
23785
24639
  const children = visitAll(visitor, block.children, block.children);
23786
- branches.push(new IfBlockBranch(params.expression, children, params.expressionAlias, block.sourceSpan, block.startSourceSpan, block.endSourceSpan, block.nameSpan));
24640
+ branches.push(new IfBlockBranch(params.expression, children, params.expressionAlias, block.sourceSpan, block.startSourceSpan, block.endSourceSpan, block.nameSpan, block.i18n));
23787
24641
  }
23788
24642
  }
23789
24643
  else if (block.name === 'else') {
23790
24644
  const children = visitAll(visitor, block.children, block.children);
23791
- branches.push(new IfBlockBranch(null, children, null, block.sourceSpan, block.startSourceSpan, block.endSourceSpan, block.nameSpan));
24645
+ branches.push(new IfBlockBranch(null, children, null, block.sourceSpan, block.startSourceSpan, block.endSourceSpan, block.nameSpan, block.i18n));
23792
24646
  }
23793
24647
  }
23794
24648
  // The outer IfBlock should have a span that encapsulates all branches.
@@ -23819,7 +24673,7 @@ function createForLoop(ast, connectedBlocks, visitor, bindingParser) {
23819
24673
  errors.push(new ParseError(block.sourceSpan, '@empty block cannot have parameters'));
23820
24674
  }
23821
24675
  else {
23822
- empty = new ForLoopBlockEmpty(visitAll(visitor, block.children, block.children), block.sourceSpan, block.startSourceSpan, block.endSourceSpan, block.nameSpan);
24676
+ empty = new ForLoopBlockEmpty(visitAll(visitor, block.children, block.children), block.sourceSpan, block.startSourceSpan, block.endSourceSpan, block.nameSpan, block.i18n);
23823
24677
  }
23824
24678
  }
23825
24679
  else {
@@ -23837,7 +24691,7 @@ function createForLoop(ast, connectedBlocks, visitor, bindingParser) {
23837
24691
  // main `for` body, use `mainSourceSpan`.
23838
24692
  const endSpan = empty?.endSourceSpan ?? ast.endSourceSpan;
23839
24693
  const sourceSpan = new ParseSourceSpan(ast.sourceSpan.start, endSpan?.end ?? ast.sourceSpan.end);
23840
- node = new ForLoopBlock(params.itemName, params.expression, params.trackBy.expression, params.trackBy.keywordSpan, params.context, visitAll(visitor, ast.children, ast.children), empty, sourceSpan, ast.sourceSpan, ast.startSourceSpan, endSpan, ast.nameSpan);
24694
+ node = new ForLoopBlock(params.itemName, params.expression, params.trackBy.expression, params.trackBy.keywordSpan, params.context, visitAll(visitor, ast.children, ast.children), empty, sourceSpan, ast.sourceSpan, ast.startSourceSpan, endSpan, ast.nameSpan, ast.i18n);
23841
24695
  }
23842
24696
  }
23843
24697
  return { node, errors };
@@ -23863,7 +24717,7 @@ function createSwitchBlock(ast, visitor, bindingParser) {
23863
24717
  const expression = node.name === 'case' ?
23864
24718
  parseBlockParameterToBinding(node.parameters[0], bindingParser) :
23865
24719
  null;
23866
- const ast = new SwitchBlockCase(expression, visitAll(visitor, node.children, node.children), node.sourceSpan, node.startSourceSpan, node.endSourceSpan, node.nameSpan);
24720
+ const ast = new SwitchBlockCase(expression, visitAll(visitor, node.children, node.children), node.sourceSpan, node.startSourceSpan, node.endSourceSpan, node.nameSpan, node.i18n);
23867
24721
  if (expression === null) {
23868
24722
  defaultCase = ast;
23869
24723
  }
@@ -24450,7 +25304,7 @@ function createDeferredBlock(ast, connectedBlocks, visitor, bindingParser) {
24450
25304
  endOfLastSourceSpan = lastConnectedBlock.sourceSpan.end;
24451
25305
  }
24452
25306
  const sourceSpanWithConnectedBlocks = new ParseSourceSpan(ast.sourceSpan.start, endOfLastSourceSpan);
24453
- const node = new DeferredBlock(visitAll(visitor, ast.children, ast.children), triggers, prefetchTriggers, placeholder, loading, error, ast.nameSpan, sourceSpanWithConnectedBlocks, ast.sourceSpan, ast.startSourceSpan, lastEndSourceSpan);
25307
+ const node = new DeferredBlock(visitAll(visitor, ast.children, ast.children), triggers, prefetchTriggers, placeholder, loading, error, ast.nameSpan, sourceSpanWithConnectedBlocks, ast.sourceSpan, ast.startSourceSpan, lastEndSourceSpan, ast.i18n);
24454
25308
  return { node, errors };
24455
25309
  }
24456
25310
  function parseConnectedBlocks(connectedBlocks, errors, visitor) {
@@ -24513,7 +25367,7 @@ function parsePlaceholderBlock(ast, visitor) {
24513
25367
  throw new Error(`Unrecognized parameter in @placeholder block: "${param.expression}"`);
24514
25368
  }
24515
25369
  }
24516
- return new DeferredBlockPlaceholder(visitAll(visitor, ast.children, ast.children), minimumTime, ast.nameSpan, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan);
25370
+ return new DeferredBlockPlaceholder(visitAll(visitor, ast.children, ast.children), minimumTime, ast.nameSpan, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan, ast.i18n);
24517
25371
  }
24518
25372
  function parseLoadingBlock(ast, visitor) {
24519
25373
  let afterTime = null;
@@ -24543,13 +25397,13 @@ function parseLoadingBlock(ast, visitor) {
24543
25397
  throw new Error(`Unrecognized parameter in @loading block: "${param.expression}"`);
24544
25398
  }
24545
25399
  }
24546
- return new DeferredBlockLoading(visitAll(visitor, ast.children, ast.children), afterTime, minimumTime, ast.nameSpan, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan);
25400
+ return new DeferredBlockLoading(visitAll(visitor, ast.children, ast.children), afterTime, minimumTime, ast.nameSpan, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan, ast.i18n);
24547
25401
  }
24548
25402
  function parseErrorBlock(ast, visitor) {
24549
25403
  if (ast.parameters.length > 0) {
24550
25404
  throw new Error(`@error block cannot have parameters`);
24551
25405
  }
24552
- return new DeferredBlockError(visitAll(visitor, ast.children, ast.children), ast.nameSpan, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan);
25406
+ return new DeferredBlockError(visitAll(visitor, ast.children, ast.children), ast.nameSpan, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan, ast.i18n);
24553
25407
  }
24554
25408
  function parsePrimaryTriggers(params, bindingParser, errors, placeholder) {
24555
25409
  const triggers = {};
@@ -25149,6 +26003,11 @@ class I18nContext {
25149
26003
  const content = { type, index, ctx: this.id, isVoid: node.isVoid, closed };
25150
26004
  updatePlaceholderMap(this.placeholders, ph, content);
25151
26005
  }
26006
+ appendBlockPart(node, index, closed) {
26007
+ const ph = closed ? node.closeName : node.startName;
26008
+ const content = { type: TagType.TEMPLATE, index, ctx: this.id, closed };
26009
+ updatePlaceholderMap(this.placeholders, ph, content);
26010
+ }
25152
26011
  get icus() {
25153
26012
  return this._registry.icus;
25154
26013
  }
@@ -25181,6 +26040,13 @@ class I18nContext {
25181
26040
  this.appendTag(TagType.TEMPLATE, node, index, true);
25182
26041
  this._unresolvedCtxCount++;
25183
26042
  }
26043
+ appendBlock(node, index) {
26044
+ // add open and close tags at the same time,
26045
+ // since we process nested templates separately
26046
+ this.appendBlockPart(node, index, false);
26047
+ this.appendBlockPart(node, index, true);
26048
+ this._unresolvedCtxCount++;
26049
+ }
25184
26050
  appendElement(node, index, closed) {
25185
26051
  this.appendTag(TagType.ELEMENT, node, index, closed);
25186
26052
  }
@@ -25459,13 +26325,19 @@ class TemplateDefinitionBuilder {
25459
26325
  this.creationInstruction(null, Identifiers.pipe, [literal(slot), literal(name)]);
25460
26326
  });
25461
26327
  }
25462
- buildTemplateFunction(nodes, variables, ngContentSelectorsOffset = 0, i18n) {
26328
+ buildTemplateFunction(nodes, variables, ngContentSelectorsOffset = 0, i18n, variableAliases) {
25463
26329
  this._ngContentSelectorsOffset = ngContentSelectorsOffset;
25464
26330
  if (this._namespace !== Identifiers.namespaceHTML) {
25465
26331
  this.creationInstruction(null, this._namespace);
25466
26332
  }
25467
26333
  // Create variable bindings
25468
- variables.forEach(v => this.registerContextVariables(v));
26334
+ variables.forEach(v => {
26335
+ const alias = variableAliases?.[v.name];
26336
+ this.registerContextVariables(v.name, v.value);
26337
+ if (alias) {
26338
+ this.registerContextVariables(alias, v.value);
26339
+ }
26340
+ });
25469
26341
  // Initiate i18n context in case:
25470
26342
  // - this template has parent i18n context
25471
26343
  // - or the template has i18n meta associated with it,
@@ -25559,12 +26431,12 @@ class TemplateDefinitionBuilder {
25559
26431
  this._constants.prepareStatements.push(...statements);
25560
26432
  return _ref;
25561
26433
  }
25562
- registerContextVariables(variable$1) {
26434
+ registerContextVariables(name, value) {
25563
26435
  const scopedName = this._bindingScope.freshReferenceName();
25564
26436
  const retrievalLevel = this.level;
25565
- const isDirect = variable$1.value === DIRECT_CONTEXT_REFERENCE;
25566
- const lhs = variable(variable$1.name + scopedName);
25567
- this._bindingScope.set(retrievalLevel, variable$1.name, scope => {
26437
+ const isDirect = value === DIRECT_CONTEXT_REFERENCE;
26438
+ const lhs = variable(name + scopedName);
26439
+ this._bindingScope.set(retrievalLevel, name, scope => {
25568
26440
  // If we're at the top level and we're referring to the context variable directly, we
25569
26441
  // can do so through the implicit receiver, instead of renaming it. Note that this does
25570
26442
  // not apply to listeners, because they need to restore the context.
@@ -25600,7 +26472,7 @@ class TemplateDefinitionBuilder {
25600
26472
  return [
25601
26473
  // e.g. const $items$ = x(2) for direct context references and
25602
26474
  // const $item$ = x(2).$implicit for indirect ones.
25603
- lhs.set(isDirect ? rhs : rhs.prop(variable$1.value || IMPLICIT_REFERENCE)).toConstDecl()
26475
+ lhs.set(isDirect ? rhs : rhs.prop(value || IMPLICIT_REFERENCE)).toConstDecl()
25604
26476
  ];
25605
26477
  });
25606
26478
  }
@@ -25666,7 +26538,7 @@ class TemplateDefinitionBuilder {
25666
26538
  else {
25667
26539
  // ... otherwise we need to activate post-processing
25668
26540
  // to replace ICU placeholders with proper values
25669
- const placeholder = wrapI18nPlaceholder(`${I18N_ICU_MAPPING_PREFIX}${key}`);
26541
+ const placeholder = wrapI18nPlaceholder(`${I18N_ICU_MAPPING_PREFIX$1}${key}`);
25670
26542
  params[key] = literal(placeholder);
25671
26543
  icuMapping[key] = literalArr(refs);
25672
26544
  }
@@ -26023,10 +26895,15 @@ class TemplateDefinitionBuilder {
26023
26895
  this.creationInstruction(span, isNgContainer$1 ? Identifiers.elementContainerEnd : Identifiers.elementEnd);
26024
26896
  }
26025
26897
  }
26026
- prepareEmbeddedTemplateFn(children, contextNameSuffix, variables = [], i18n) {
26898
+ prepareEmbeddedTemplateFn(children, contextNameSuffix, variables = [], i18nMeta, variableAliases) {
26027
26899
  const index = this.allocateDataSlot();
26028
- if (this.i18n && i18n) {
26029
- this.i18n.appendTemplate(i18n, index);
26900
+ if (this.i18n && i18nMeta) {
26901
+ if (i18nMeta instanceof BlockPlaceholder) {
26902
+ this.i18n.appendBlock(i18nMeta, index);
26903
+ }
26904
+ else {
26905
+ this.i18n.appendTemplate(i18nMeta, index);
26906
+ }
26030
26907
  }
26031
26908
  const contextName = `${this.contextName}${contextNameSuffix}_${index}`;
26032
26909
  const name = `${contextName}_Template`;
@@ -26037,7 +26914,7 @@ class TemplateDefinitionBuilder {
26037
26914
  // be able to support bindings in nested templates to local refs that occur after the
26038
26915
  // template definition. e.g. <div *ngIf="showing">{{ foo }}</div> <div #foo></div>
26039
26916
  this._nestedTemplateFns.push(() => {
26040
- const templateFunctionExpr = visitor.buildTemplateFunction(children, variables, this._ngContentReservedSlots.length + this._ngContentSelectorsOffset, i18n);
26917
+ const templateFunctionExpr = visitor.buildTemplateFunction(children, variables, this._ngContentReservedSlots.length + this._ngContentSelectorsOffset, i18nMeta, variableAliases);
26041
26918
  this.constantPool.statements.push(templateFunctionExpr.toDeclStmt(name));
26042
26919
  if (visitor._ngContentReservedSlots.length) {
26043
26920
  this._ngContentReservedSlots.push(...visitor._ngContentReservedSlots);
@@ -26145,7 +27022,9 @@ class TemplateDefinitionBuilder {
26145
27022
  // inside ICUs)
26146
27023
  // - all ICU vars (such as `VAR_SELECT` or `VAR_PLURAL`) are replaced with correct values
26147
27024
  const transformFn = (raw) => {
26148
- const params = { ...vars, ...placeholders };
27025
+ // Sort the map entries in the compiled output. This makes it easy to acheive identical output
27026
+ // in the template pipeline compiler.
27027
+ const params = Object.fromEntries(Object.entries({ ...vars, ...placeholders }).sort());
26149
27028
  const formatted = formatI18nPlaceholderNamesInMap(params, /* useCamelCase */ false);
26150
27029
  return invokeInstruction(null, Identifiers.i18nPostprocess, [raw, mapLiteral(formatted, true)]);
26151
27030
  };
@@ -26192,7 +27071,7 @@ class TemplateDefinitionBuilder {
26192
27071
  }
26193
27072
  // Note: the template needs to be created *before* we process the expression,
26194
27073
  // otherwise pipes injecting some symbols won't work (see #52102).
26195
- const templateIndex = this.createEmbeddedTemplateFn(tagName, children, '_Conditional', sourceSpan, variables, attrsExprs);
27074
+ const templateIndex = this.createEmbeddedTemplateFn(tagName, children, '_Conditional', sourceSpan, variables, attrsExprs, undefined, branch.i18n);
26196
27075
  const processedExpression = expression === null ? null : expression.visit(this._valueConverter);
26197
27076
  return { index: templateIndex, expression: processedExpression, alias: expressionAlias };
26198
27077
  });
@@ -26243,7 +27122,7 @@ class TemplateDefinitionBuilder {
26243
27122
  // We have to process the block in two steps: once here and again in the update instruction
26244
27123
  // callback in order to generate the correct expressions when pipes or pure functions are used.
26245
27124
  const caseData = block.cases.map(currentCase => {
26246
- const index = this.createEmbeddedTemplateFn(null, currentCase.children, '_Case', currentCase.sourceSpan);
27125
+ const index = this.createEmbeddedTemplateFn(null, currentCase.children, '_Case', currentCase.sourceSpan, undefined, undefined, undefined, currentCase.i18n);
26247
27126
  const expression = currentCase.expression === null ?
26248
27127
  null :
26249
27128
  currentCase.expression.visit(this._valueConverter);
@@ -26291,23 +27170,21 @@ class TemplateDefinitionBuilder {
26291
27170
  if (!metadata) {
26292
27171
  throw new Error('Could not resolve `defer` block metadata. Block may need to be analyzed.');
26293
27172
  }
26294
- const primaryTemplateIndex = this.createEmbeddedTemplateFn(null, deferred.children, '_Defer', deferred.sourceSpan);
26295
- const loadingIndex = loading ?
26296
- this.createEmbeddedTemplateFn(null, loading.children, '_DeferLoading', loading.sourceSpan) :
27173
+ const primaryTemplateIndex = this.createEmbeddedTemplateFn(null, deferred.children, '_Defer', deferred.sourceSpan, undefined, undefined, undefined, deferred.i18n);
27174
+ const loadingIndex = loading ? this.createEmbeddedTemplateFn(null, loading.children, '_DeferLoading', loading.sourceSpan, undefined, undefined, undefined, loading.i18n) :
26297
27175
  null;
26298
27176
  const loadingConsts = loading ?
26299
27177
  trimTrailingNulls([literal(loading.minimumTime), literal(loading.afterTime)]) :
26300
27178
  null;
26301
27179
  const placeholderIndex = placeholder ?
26302
- this.createEmbeddedTemplateFn(null, placeholder.children, '_DeferPlaceholder', placeholder.sourceSpan) :
27180
+ this.createEmbeddedTemplateFn(null, placeholder.children, '_DeferPlaceholder', placeholder.sourceSpan, undefined, undefined, undefined, placeholder.i18n) :
26303
27181
  null;
26304
27182
  const placeholderConsts = placeholder && placeholder.minimumTime !== null ?
26305
27183
  // TODO(crisbeto): potentially pass the time directly instead of storing it in the `consts`
26306
27184
  // since the placeholder block can only have one parameter?
26307
27185
  literalArr([literal(placeholder.minimumTime)]) :
26308
27186
  null;
26309
- const errorIndex = error ?
26310
- this.createEmbeddedTemplateFn(null, error.children, '_DeferError', error.sourceSpan) :
27187
+ const errorIndex = error ? this.createEmbeddedTemplateFn(null, error.children, '_DeferError', error.sourceSpan, undefined, undefined, undefined, error.i18n) :
26311
27188
  null;
26312
27189
  // Note: we generate this last so the index matches the instruction order.
26313
27190
  const deferredIndex = this.allocateDataSlot();
@@ -26461,11 +27338,18 @@ class TemplateDefinitionBuilder {
26461
27338
  // are implicitly inferred by the runtime to index + 1 and index + 2.
26462
27339
  const blockIndex = this.allocateDataSlot();
26463
27340
  const { tagName, attrsExprs } = this.inferProjectionDataFromInsertionPoint(block);
26464
- const primaryData = this.prepareEmbeddedTemplateFn(block.children, '_For', [block.item, block.contextVariables.$index, block.contextVariables.$count]);
27341
+ const primaryData = this.prepareEmbeddedTemplateFn(block.children, '_For', [block.item, block.contextVariables.$index, block.contextVariables.$count], block.i18n, {
27342
+ // We need to provide level-specific versions of `$index` and `$count`, because
27343
+ // they're used when deriving the remaining variables (`$odd`, `$even` etc.) while at the
27344
+ // same time being available implicitly. Without these aliases, we wouldn't be able to
27345
+ // access the `$index` of a parent loop from inside of a nested loop.
27346
+ [block.contextVariables.$index.name]: this.getLevelSpecificVariableName('$index', this.level + 1),
27347
+ [block.contextVariables.$count.name]: this.getLevelSpecificVariableName('$count', this.level + 1),
27348
+ });
26465
27349
  const { expression: trackByExpression, usesComponentInstance: trackByUsesComponentInstance } = this.createTrackByFunction(block);
26466
27350
  let emptyData = null;
26467
27351
  if (block.empty !== null) {
26468
- emptyData = this.prepareEmbeddedTemplateFn(block.empty.children, '_ForEmpty');
27352
+ emptyData = this.prepareEmbeddedTemplateFn(block.empty.children, '_ForEmpty', undefined, block.empty.i18n);
26469
27353
  // Allocate an extra slot for the empty block tracking.
26470
27354
  this.allocateBindingSlots(null);
26471
27355
  }
@@ -26495,17 +27379,44 @@ class TemplateDefinitionBuilder {
26495
27379
  // Note: we don't allocate binding slots for this expression,
26496
27380
  // because its value isn't stored in the LView.
26497
27381
  const value = block.expression.visit(this._valueConverter);
26498
- // `repeater(0, iterable)`
26499
- this.updateInstruction(block.sourceSpan, Identifiers.repeater, () => [literal(blockIndex), this.convertPropertyBinding(value)]);
27382
+ // `advance(x); repeater(iterable)`
27383
+ this.updateInstructionWithAdvance(blockIndex, block.sourceSpan, Identifiers.repeater, () => [this.convertPropertyBinding(value)]);
26500
27384
  }
26501
27385
  registerComputedLoopVariables(block, bindingScope) {
26502
- const indexLocalName = block.contextVariables.$index.name;
26503
- const countLocalName = block.contextVariables.$count.name;
26504
27386
  const level = bindingScope.bindingLevel;
26505
- bindingScope.set(level, block.contextVariables.$odd.name, scope => scope.get(indexLocalName).modulo(literal(2)).notIdentical(literal(0)));
26506
- bindingScope.set(level, block.contextVariables.$even.name, scope => scope.get(indexLocalName).modulo(literal(2)).identical(literal(0)));
26507
- bindingScope.set(level, block.contextVariables.$first.name, scope => scope.get(indexLocalName).identical(literal(0)));
26508
- bindingScope.set(level, block.contextVariables.$last.name, scope => scope.get(indexLocalName).identical(scope.get(countLocalName).minus(literal(1))));
27387
+ bindingScope.set(level, block.contextVariables.$odd.name, (scope, retrievalLevel) => {
27388
+ return this.getLevelSpecificForLoopVariable(block, scope, retrievalLevel, '$index')
27389
+ .modulo(literal(2))
27390
+ .notIdentical(literal(0));
27391
+ });
27392
+ bindingScope.set(level, block.contextVariables.$even.name, (scope, retrievalLevel) => {
27393
+ return this.getLevelSpecificForLoopVariable(block, scope, retrievalLevel, '$index')
27394
+ .modulo(literal(2))
27395
+ .identical(literal(0));
27396
+ });
27397
+ bindingScope.set(level, block.contextVariables.$first.name, (scope, retrievalLevel) => {
27398
+ return this.getLevelSpecificForLoopVariable(block, scope, retrievalLevel, '$index')
27399
+ .identical(literal(0));
27400
+ });
27401
+ bindingScope.set(level, block.contextVariables.$last.name, (scope, retrievalLevel) => {
27402
+ const index = this.getLevelSpecificForLoopVariable(block, scope, retrievalLevel, '$index');
27403
+ const count = this.getLevelSpecificForLoopVariable(block, scope, retrievalLevel, '$count');
27404
+ return index.identical(count.minus(literal(1)));
27405
+ });
27406
+ }
27407
+ getLevelSpecificVariableName(name, level) {
27408
+ // We use the `ɵ` here to ensure that there are no name conflicts with user-defined variables.
27409
+ return `ɵ${name}_${level}`;
27410
+ }
27411
+ /**
27412
+ * Gets the name of a for loop variable at a specific binding level. This allows us to look
27413
+ * up implicitly shadowed variables like `$index` and `$count` at a specific level.
27414
+ */
27415
+ getLevelSpecificForLoopVariable(block, scope, retrievalLevel, name) {
27416
+ const scopeName = scope.bindingLevel === retrievalLevel ?
27417
+ block.contextVariables[name].name :
27418
+ this.getLevelSpecificVariableName(name, retrievalLevel);
27419
+ return scope.get(scopeName);
26509
27420
  }
26510
27421
  optimizeTrackByFunction(block) {
26511
27422
  const indexLocalName = block.contextVariables.$index.name;
@@ -27043,7 +27954,7 @@ class BindingScope {
27043
27954
  if (value.declareLocalCallback && !value.declare) {
27044
27955
  value.declare = true;
27045
27956
  }
27046
- return typeof value.lhs === 'function' ? value.lhs(this) : value.lhs;
27957
+ return typeof value.lhs === 'function' ? value.lhs(this, value.retrievalLevel) : value.lhs;
27047
27958
  }
27048
27959
  current = current.parent;
27049
27960
  }
@@ -27151,7 +28062,9 @@ class BindingScope {
27151
28062
  const componentValue = this.map.get(SHARED_CONTEXT_KEY + 0);
27152
28063
  componentValue.declare = true;
27153
28064
  this.maybeRestoreView();
27154
- const lhs = typeof componentValue.lhs === 'function' ? componentValue.lhs(this) : componentValue.lhs;
28065
+ const lhs = typeof componentValue.lhs === 'function' ?
28066
+ componentValue.lhs(this, componentValue.retrievalLevel) :
28067
+ componentValue.lhs;
27155
28068
  return name === DIRECT_CONTEXT_REFERENCE ? lhs : lhs.prop(name);
27156
28069
  }
27157
28070
  maybeRestoreView() {
@@ -27218,12 +28131,16 @@ class BindingScope {
27218
28131
  }
27219
28132
  /** Binding scope of a `track` function inside a `for` loop block. */
27220
28133
  class TrackByBindingScope extends BindingScope {
27221
- constructor(parentScope, globalAliases) {
28134
+ constructor(parentScope, globalOverrides) {
27222
28135
  super(parentScope.bindingLevel + 1, parentScope);
27223
- this.globalAliases = globalAliases;
28136
+ this.globalOverrides = globalOverrides;
27224
28137
  this.componentAccessCount = 0;
27225
28138
  }
27226
28139
  get(name) {
28140
+ // Intercept any overridden globals.
28141
+ if (this.globalOverrides.hasOwnProperty(name)) {
28142
+ return variable(this.globalOverrides[name]);
28143
+ }
27227
28144
  let current = this.parent;
27228
28145
  // Prevent accesses of template variables outside the `for` loop.
27229
28146
  while (current) {
@@ -27232,10 +28149,6 @@ class TrackByBindingScope extends BindingScope {
27232
28149
  }
27233
28150
  current = current.parent;
27234
28151
  }
27235
- // Intercept any aliased globals.
27236
- if (this.globalAliases[name]) {
27237
- return variable(this.globalAliases[name]);
27238
- }
27239
28152
  // When the component scope is accessed, we redirect it through `this`.
27240
28153
  this.componentAccessCount++;
27241
28154
  return variable('this').prop(name);
@@ -27245,24 +28158,6 @@ class TrackByBindingScope extends BindingScope {
27245
28158
  return this.componentAccessCount;
27246
28159
  }
27247
28160
  }
27248
- /**
27249
- * Creates a `CssSelector` given a tag name and a map of attributes
27250
- */
27251
- function createCssSelector(elementName, attributes) {
27252
- const cssSelector = new CssSelector();
27253
- const elementNameNoNs = splitNsName(elementName)[1];
27254
- cssSelector.setElement(elementNameNoNs);
27255
- Object.getOwnPropertyNames(attributes).forEach((name) => {
27256
- const nameNoNs = splitNsName(name)[1];
27257
- const value = attributes[name];
27258
- cssSelector.addAttribute(nameNoNs, value);
27259
- if (name.toLowerCase() === 'class') {
27260
- const classes = value.trim().split(/\s+/);
27261
- classes.forEach(className => cssSelector.addClassName(className));
27262
- }
27263
- });
27264
- return cssSelector;
27265
- }
27266
28161
  /**
27267
28162
  * Creates an array of expressions out of an `ngProjectAs` attributes
27268
28163
  * which can be added to the instruction parameters.
@@ -27623,6 +28518,11 @@ function addFeatures(definitionMap, meta) {
27623
28518
  break;
27624
28519
  }
27625
28520
  }
28521
+ // Note: host directives feature needs to be inserted before the
28522
+ // inheritance feature to ensure the correct execution order.
28523
+ if (meta.hostDirectives?.length) {
28524
+ features.push(importExpr(Identifiers.HostDirectivesFeature).callFn([createHostDirectivesFeatureArg(meta.hostDirectives)]));
28525
+ }
27626
28526
  if (meta.usesInheritance) {
27627
28527
  features.push(importExpr(Identifiers.InheritDefinitionFeature));
27628
28528
  }
@@ -27636,9 +28536,6 @@ function addFeatures(definitionMap, meta) {
27636
28536
  if (meta.hasOwnProperty('template') && meta.isStandalone) {
27637
28537
  features.push(importExpr(Identifiers.StandaloneFeature));
27638
28538
  }
27639
- if (meta.hostDirectives?.length) {
27640
- features.push(importExpr(Identifiers.HostDirectivesFeature).callFn([createHostDirectivesFeatureArg(meta.hostDirectives)]));
27641
- }
27642
28539
  if (features.length) {
27643
28540
  definitionMap.set('features', literalArr(features));
27644
28541
  }
@@ -27949,6 +28846,8 @@ function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindin
27949
28846
  const bindings = bindingParser.createBoundHostProperties(hostBindingsMetadata.properties, typeSourceSpan);
27950
28847
  // Calculate host event bindings
27951
28848
  const eventBindings = bindingParser.createDirectiveHostEventAsts(hostBindingsMetadata.listeners, typeSourceSpan);
28849
+ let bindingId = 0;
28850
+ const getNextBindingId = () => `${bindingId++}`;
27952
28851
  const bindingContext = variable(CONTEXT_NAME);
27953
28852
  const styleBuilder = new StylingBuilder(bindingContext);
27954
28853
  const { styleAttr, classAttr } = hostBindingsMetadata.specialAttributes;
@@ -28001,7 +28900,7 @@ function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindin
28001
28900
  for (const binding of allOtherBindings) {
28002
28901
  // resolve literal arrays and literal objects
28003
28902
  const value = binding.expression.visit(getValueConverter());
28004
- const bindingExpr = bindingFn(bindingContext, value);
28903
+ const bindingExpr = bindingFn(bindingContext, value, getNextBindingId);
28005
28904
  const { bindingName, instruction, isAttribute } = getBindingNameAndInstruction(binding);
28006
28905
  const securityContexts = bindingParser.calcPossibleSecurityContexts(selector, bindingName, isAttribute)
28007
28906
  .filter(context => context !== SecurityContext.NONE);
@@ -28080,10 +28979,12 @@ function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindin
28080
28979
  // at the top of this method when all the input bindings were counted.
28081
28980
  totalHostVarsCount +=
28082
28981
  Math.max(call.allocateBindingSlots - MIN_STYLING_BINDING_SLOTS_REQUIRED, 0);
28982
+ const { params, stmts } = convertStylingCall(call, bindingContext, bindingFn, getNextBindingId);
28983
+ updateVariables.push(...stmts);
28083
28984
  updateInstructions.push({
28084
28985
  reference: instruction.reference,
28085
- paramsOrFn: convertStylingCall(call, bindingContext, bindingFn),
28086
- span: null
28986
+ paramsOrFn: params,
28987
+ span: null,
28087
28988
  });
28088
28989
  }
28089
28990
  });
@@ -28104,11 +29005,19 @@ function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindin
28104
29005
  }
28105
29006
  return null;
28106
29007
  }
28107
- function bindingFn(implicit, value) {
28108
- return convertPropertyBinding(null, implicit, value, 'b');
29008
+ function bindingFn(implicit, value, getNextBindingIdFn) {
29009
+ return convertPropertyBinding(null, implicit, value, getNextBindingIdFn());
28109
29010
  }
28110
- function convertStylingCall(call, bindingContext, bindingFn) {
28111
- return call.params(value => bindingFn(bindingContext, value).currValExpr);
29011
+ function convertStylingCall(call, bindingContext, bindingFn, getNextBindingIdFn) {
29012
+ const stmts = [];
29013
+ const params = call.params(value => {
29014
+ const result = bindingFn(bindingContext, value, getNextBindingIdFn);
29015
+ if (Array.isArray(result.stmts) && result.stmts.length > 0) {
29016
+ stmts.push(...result.stmts);
29017
+ }
29018
+ return result.currValExpr;
29019
+ });
29020
+ return { params, stmts };
28112
29021
  }
28113
29022
  function getBindingNameAndInstruction(binding) {
28114
29023
  let bindingName = binding.name;
@@ -28547,15 +29456,15 @@ class DirectiveBinder {
28547
29456
  template.forEach(node => node.visit(this));
28548
29457
  }
28549
29458
  visitElement(element) {
28550
- this.visitElementOrTemplate(element.name, element);
29459
+ this.visitElementOrTemplate(element);
28551
29460
  }
28552
29461
  visitTemplate(template) {
28553
- this.visitElementOrTemplate('ng-template', template);
29462
+ this.visitElementOrTemplate(template);
28554
29463
  }
28555
- visitElementOrTemplate(elementName, node) {
29464
+ visitElementOrTemplate(node) {
28556
29465
  // First, determine the HTML shape of the node for the purpose of directive matching.
28557
29466
  // Do this by building up a `CssSelector` for the node.
28558
- const cssSelector = createCssSelector(elementName, getAttrsForDirectiveMatching(node));
29467
+ const cssSelector = createCssSelectorFromNode(node);
28559
29468
  // Next, use the `SelectorMatcher` to get the list of directives on the node.
28560
29469
  const directives = [];
28561
29470
  this.matcher.match(cssSelector, (_selector, results) => directives.push(...results));
@@ -29684,7 +30593,7 @@ function publishFacade(global) {
29684
30593
  * @description
29685
30594
  * Entry point for all public APIs of the compiler package.
29686
30595
  */
29687
- const VERSION = new Version('17.0.2');
30596
+ const VERSION = new Version('17.0.8');
29688
30597
 
29689
30598
  class CompilerConfig {
29690
30599
  constructor({ defaultEncapsulation = exports.ViewEncapsulation.Emulated, preserveWhitespaces, strictInjectionParameters } = {}) {
@@ -29917,7 +30826,8 @@ class _Visitor {
29917
30826
  this._errors = [];
29918
30827
  this._messages = [];
29919
30828
  this._inImplicitNode = false;
29920
- this._createI18nMessage = createI18nMessageFactory(interpolationConfig);
30829
+ this._createI18nMessage =
30830
+ createI18nMessageFactory(interpolationConfig, DEFAULT_CONTAINER_BLOCKS);
29921
30831
  }
29922
30832
  // looks for translatable attributes
29923
30833
  _visitAttributesOf(el) {
@@ -30225,6 +31135,12 @@ class _WriteVisitor$1 {
30225
31135
  visitPlaceholder(ph, context) {
30226
31136
  return [new Tag(_PLACEHOLDER_TAG$2, { id: ph.name, 'equiv-text': `{{${ph.value}}}` })];
30227
31137
  }
31138
+ visitBlockPlaceholder(ph, context) {
31139
+ const ctype = `x-${ph.name.toLowerCase().replace(/[^a-z0-9]/g, '-')}`;
31140
+ const startTagPh = new Tag(_PLACEHOLDER_TAG$2, { id: ph.startName, ctype, 'equiv-text': `@${ph.name}` });
31141
+ const closeTagPh = new Tag(_PLACEHOLDER_TAG$2, { id: ph.closeName, ctype, 'equiv-text': `}` });
31142
+ return [startTagPh, ...this.serialize(ph.children), closeTagPh];
31143
+ }
30228
31144
  visitIcuPlaceholder(ph, context) {
30229
31145
  const equivText = `{${ph.value.expression}, ${ph.value.type}, ${Object.keys(ph.value.cases).map((value) => value + ' {...}').join(' ')}}`;
30230
31146
  return [new Tag(_PLACEHOLDER_TAG$2, { id: ph.name, 'equiv-text': equivText })];
@@ -30496,6 +31412,24 @@ class _WriteVisitor {
30496
31412
  disp: `{{${ph.value}}}`,
30497
31413
  })];
30498
31414
  }
31415
+ visitBlockPlaceholder(ph, context) {
31416
+ const tagPc = new Tag(_PLACEHOLDER_SPANNING_TAG, {
31417
+ id: (this._nextPlaceholderId++).toString(),
31418
+ equivStart: ph.startName,
31419
+ equivEnd: ph.closeName,
31420
+ type: 'other',
31421
+ dispStart: `@${ph.name}`,
31422
+ dispEnd: `}`,
31423
+ });
31424
+ const nodes = [].concat(...ph.children.map(node => node.visit(this)));
31425
+ if (nodes.length) {
31426
+ nodes.forEach((node) => tagPc.children.push(node));
31427
+ }
31428
+ else {
31429
+ tagPc.children.push(new Text$1(''));
31430
+ }
31431
+ return [tagPc];
31432
+ }
30499
31433
  visitIcuPlaceholder(ph, context) {
30500
31434
  const cases = Object.keys(ph.value.cases).map((value) => value + ' {...}').join(' ');
30501
31435
  const idStr = (this._nextPlaceholderId++).toString();
@@ -30944,6 +31878,11 @@ class I18nToHtmlVisitor {
30944
31878
  // An ICU placeholder references the source message to be serialized
30945
31879
  return this._convertToText(this._srcMsg.placeholderToMessage[ph.name]);
30946
31880
  }
31881
+ visitBlockPlaceholder(ph, context) {
31882
+ const params = ph.parameters.length === 0 ? '' : ` (${ph.parameters.join('; ')})`;
31883
+ const children = ph.children.map((c) => c.visit(this)).join('');
31884
+ return `@${ph.name}${params} {${children}}`;
31885
+ }
30947
31886
  /**
30948
31887
  * Convert a source message to a translated text string:
30949
31888
  * - text nodes are replaced with their translation,
@@ -31096,6 +32035,12 @@ class MapPlaceholderNames extends CloneVisitor {
31096
32035
  const children = ph.children.map(n => n.visit(this, mapper));
31097
32036
  return new TagPlaceholder(ph.tag, ph.attrs, startName, closeName, children, ph.isVoid, ph.sourceSpan, ph.startSourceSpan, ph.endSourceSpan);
31098
32037
  }
32038
+ visitBlockPlaceholder(ph, mapper) {
32039
+ const startName = mapper.toPublicName(ph.startName);
32040
+ const closeName = ph.closeName ? mapper.toPublicName(ph.closeName) : ph.closeName;
32041
+ const children = ph.children.map(n => n.visit(this, mapper));
32042
+ return new BlockPlaceholder(ph.name, ph.parameters, startName, closeName, children, ph.sourceSpan, ph.startSourceSpan, ph.endSourceSpan);
32043
+ }
31099
32044
  visitPlaceholder(ph, mapper) {
31100
32045
  return new Placeholder(ph.value, mapper.toPublicName(ph.name), ph.sourceSpan);
31101
32046
  }
@@ -31214,7 +32159,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$6 = '12.0.0';
31214
32159
  function compileDeclareClassMetadata(metadata) {
31215
32160
  const definitionMap = new DefinitionMap();
31216
32161
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$6));
31217
- definitionMap.set('version', literal('17.0.2'));
32162
+ definitionMap.set('version', literal('17.0.8'));
31218
32163
  definitionMap.set('ngImport', importExpr(Identifiers.core));
31219
32164
  definitionMap.set('type', metadata.type);
31220
32165
  definitionMap.set('decorators', metadata.decorators);
@@ -31322,7 +32267,7 @@ function createDirectiveDefinitionMap(meta) {
31322
32267
  // in 16.1 is actually used.
31323
32268
  const minVersion = hasTransformFunctions ? MINIMUM_PARTIAL_LINKER_VERSION$5 : '14.0.0';
31324
32269
  definitionMap.set('minVersion', literal(minVersion));
31325
- definitionMap.set('version', literal('17.0.2'));
32270
+ definitionMap.set('version', literal('17.0.8'));
31326
32271
  // e.g. `type: MyDirective`
31327
32272
  definitionMap.set('type', meta.type.value);
31328
32273
  if (meta.isStandalone) {
@@ -31596,7 +32541,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0';
31596
32541
  function compileDeclareFactoryFunction(meta) {
31597
32542
  const definitionMap = new DefinitionMap();
31598
32543
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$4));
31599
- definitionMap.set('version', literal('17.0.2'));
32544
+ definitionMap.set('version', literal('17.0.8'));
31600
32545
  definitionMap.set('ngImport', importExpr(Identifiers.core));
31601
32546
  definitionMap.set('type', meta.type.value);
31602
32547
  definitionMap.set('deps', compileDependencies(meta.deps));
@@ -31631,7 +32576,7 @@ function compileDeclareInjectableFromMetadata(meta) {
31631
32576
  function createInjectableDefinitionMap(meta) {
31632
32577
  const definitionMap = new DefinitionMap();
31633
32578
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$3));
31634
- definitionMap.set('version', literal('17.0.2'));
32579
+ definitionMap.set('version', literal('17.0.8'));
31635
32580
  definitionMap.set('ngImport', importExpr(Identifiers.core));
31636
32581
  definitionMap.set('type', meta.type.value);
31637
32582
  // Only generate providedIn property if it has a non-null value
@@ -31682,7 +32627,7 @@ function compileDeclareInjectorFromMetadata(meta) {
31682
32627
  function createInjectorDefinitionMap(meta) {
31683
32628
  const definitionMap = new DefinitionMap();
31684
32629
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$2));
31685
- definitionMap.set('version', literal('17.0.2'));
32630
+ definitionMap.set('version', literal('17.0.8'));
31686
32631
  definitionMap.set('ngImport', importExpr(Identifiers.core));
31687
32632
  definitionMap.set('type', meta.type.value);
31688
32633
  definitionMap.set('providers', meta.providers);
@@ -31715,7 +32660,7 @@ function createNgModuleDefinitionMap(meta) {
31715
32660
  throw new Error('Invalid path! Local compilation mode should not get into the partial compilation path');
31716
32661
  }
31717
32662
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$1));
31718
- definitionMap.set('version', literal('17.0.2'));
32663
+ definitionMap.set('version', literal('17.0.8'));
31719
32664
  definitionMap.set('ngImport', importExpr(Identifiers.core));
31720
32665
  definitionMap.set('type', meta.type.value);
31721
32666
  // We only generate the keys in the metadata if the arrays contain values.
@@ -31766,7 +32711,7 @@ function compileDeclarePipeFromMetadata(meta) {
31766
32711
  function createPipeDefinitionMap(meta) {
31767
32712
  const definitionMap = new DefinitionMap();
31768
32713
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION));
31769
- definitionMap.set('version', literal('17.0.2'));
32714
+ definitionMap.set('version', literal('17.0.8'));
31770
32715
  definitionMap.set('ngImport', importExpr(Identifiers.core));
31771
32716
  // e.g. `type: MyPipe`
31772
32717
  definitionMap.set('type', meta.type.value);
@@ -31974,6 +32919,7 @@ exports.compileNgModule = compileNgModule;
31974
32919
  exports.compilePipeFromMetadata = compilePipeFromMetadata;
31975
32920
  exports.computeMsgId = computeMsgId;
31976
32921
  exports.core = core;
32922
+ exports.createCssSelectorFromNode = createCssSelectorFromNode;
31977
32923
  exports.createInjectableType = createInjectableType;
31978
32924
  exports.createMayBeForwardRefExpression = createMayBeForwardRefExpression;
31979
32925
  exports.devOnlyGuardedExpression = devOnlyGuardedExpression;