@angular/compiler 16.2.2 → 17.0.0-next.1

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 (40) hide show
  1. package/esm2022/src/compiler.mjs +3 -3
  2. package/esm2022/src/render3/partial/class_metadata.mjs +1 -1
  3. package/esm2022/src/render3/partial/component.mjs +4 -1
  4. package/esm2022/src/render3/partial/directive.mjs +1 -1
  5. package/esm2022/src/render3/partial/factory.mjs +1 -1
  6. package/esm2022/src/render3/partial/injectable.mjs +1 -1
  7. package/esm2022/src/render3/partial/injector.mjs +1 -1
  8. package/esm2022/src/render3/partial/ng_module.mjs +1 -1
  9. package/esm2022/src/render3/partial/pipe.mjs +1 -1
  10. package/esm2022/src/render3/r3_ast.mjs +112 -9
  11. package/esm2022/src/render3/r3_class_metadata_compiler.mjs +55 -1
  12. package/esm2022/src/render3/r3_control_flow.mjs +332 -0
  13. package/esm2022/src/render3/r3_deferred_blocks.mjs +16 -9
  14. package/esm2022/src/render3/r3_deferred_triggers.mjs +33 -19
  15. package/esm2022/src/render3/r3_identifiers.mjs +18 -1
  16. package/esm2022/src/render3/r3_template_transform.mjs +28 -7
  17. package/esm2022/src/render3/view/api.mjs +1 -1
  18. package/esm2022/src/render3/view/compiler.mjs +12 -2
  19. package/esm2022/src/render3/view/t2_binder.mjs +66 -5
  20. package/esm2022/src/render3/view/template.mjs +282 -59
  21. package/esm2022/src/render3/view/util.mjs +4 -2
  22. package/esm2022/src/template/pipeline/ir/index.mjs +2 -3
  23. package/esm2022/src/template/pipeline/ir/src/enums.mjs +41 -3
  24. package/esm2022/src/template/pipeline/ir/src/expression.mjs +11 -1
  25. package/esm2022/src/template/pipeline/ir/src/ops/create.mjs +16 -4
  26. package/esm2022/src/template/pipeline/ir/src/ops/update.mjs +5 -3
  27. package/esm2022/src/template/pipeline/src/emit.mjs +3 -1
  28. package/esm2022/src/template/pipeline/src/ingest.mjs +10 -10
  29. package/esm2022/src/template/pipeline/src/phases/attribute_extraction.mjs +43 -75
  30. package/esm2022/src/template/pipeline/src/phases/binding_specialization.mjs +2 -2
  31. package/esm2022/src/template/pipeline/src/phases/const_collection.mjs +90 -13
  32. package/esm2022/src/template/pipeline/src/phases/parse_extracted_styles.mjs +38 -0
  33. package/esm2022/src/version.mjs +1 -1
  34. package/fesm2022/compiler.mjs +1307 -430
  35. package/fesm2022/compiler.mjs.map +1 -1
  36. package/fesm2022/testing.mjs +1 -1
  37. package/index.d.ts +153 -5
  38. package/package.json +2 -2
  39. package/testing/index.d.ts +1 -1
  40. package/esm2022/src/template/pipeline/ir/src/element.mjs +0 -108
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Angular v16.2.2
2
+ * @license Angular v17.0.0-next.1
3
3
  * (c) 2010-2022 Google LLC. https://angular.io/
4
4
  * License: MIT
5
5
  */
@@ -2614,6 +2614,21 @@ class Identifiers {
2614
2614
  static { this.resetView = { name: 'ɵɵresetView', moduleName: CORE }; }
2615
2615
  static { this.templateCreate = { name: 'ɵɵtemplate', moduleName: CORE }; }
2616
2616
  static { this.defer = { name: 'ɵɵdefer', moduleName: CORE }; }
2617
+ static { this.deferWhen = { name: 'ɵɵdeferWhen', moduleName: CORE }; }
2618
+ static { this.deferOnIdle = { name: 'ɵɵdeferOnIdle', moduleName: CORE }; }
2619
+ static { this.deferOnImmediate = { name: 'ɵɵdeferOnImmediate', moduleName: CORE }; }
2620
+ static { this.deferOnTimer = { name: 'ɵɵdeferOnTimer', moduleName: CORE }; }
2621
+ static { this.deferOnHover = { name: 'ɵɵdeferOnHover', moduleName: CORE }; }
2622
+ static { this.deferOnInteraction = { name: 'ɵɵdeferOnInteraction', moduleName: CORE }; }
2623
+ static { this.deferOnViewport = { name: 'ɵɵdeferOnViewport', moduleName: CORE }; }
2624
+ static { this.deferPrefetchWhen = { name: 'ɵɵdeferPrefetchWhen', moduleName: CORE }; }
2625
+ static { this.deferPrefetchOnIdle = { name: 'ɵɵdeferPrefetchOnIdle', moduleName: CORE }; }
2626
+ static { this.deferPrefetchOnImmediate = { name: 'ɵɵdeferPrefetchOnImmediate', moduleName: CORE }; }
2627
+ static { this.deferPrefetchOnTimer = { name: 'ɵɵdeferPrefetchOnTimer', moduleName: CORE }; }
2628
+ static { this.deferPrefetchOnHover = { name: 'ɵɵdeferPrefetchOnHover', moduleName: CORE }; }
2629
+ static { this.deferPrefetchOnInteraction = { name: 'ɵɵdeferPrefetchOnInteraction', moduleName: CORE }; }
2630
+ static { this.deferPrefetchOnViewport = { name: 'ɵɵdeferPrefetchOnViewport', moduleName: CORE }; }
2631
+ static { this.conditional = { name: 'ɵɵconditional', moduleName: CORE }; }
2617
2632
  static { this.text = { name: 'ɵɵtext', moduleName: CORE }; }
2618
2633
  static { this.enableBindings = { name: 'ɵɵenableBindings', moduleName: CORE }; }
2619
2634
  static { this.disableBindings = { name: 'ɵɵdisableBindings', moduleName: CORE }; }
@@ -2681,6 +2696,7 @@ class Identifiers {
2681
2696
  static { this.resolveWindow = { name: 'ɵɵresolveWindow', moduleName: CORE }; }
2682
2697
  static { this.resolveDocument = { name: 'ɵɵresolveDocument', moduleName: CORE }; }
2683
2698
  static { this.resolveBody = { name: 'ɵɵresolveBody', moduleName: CORE }; }
2699
+ static { this.getComponentDepsFactory = { name: 'ɵɵgetComponentDepsFactory', moduleName: CORE }; }
2684
2700
  static { this.defineComponent = { name: 'ɵɵdefineComponent', moduleName: CORE }; }
2685
2701
  static { this.declareComponent = { name: 'ɵɵngDeclareComponent', moduleName: CORE }; }
2686
2702
  static { this.setComponentScope = { name: 'ɵɵsetComponentScope', moduleName: CORE }; }
@@ -2729,6 +2745,7 @@ class Identifiers {
2729
2745
  static { this.declarePipe = { name: 'ɵɵngDeclarePipe', moduleName: CORE }; }
2730
2746
  static { this.declareClassMetadata = { name: 'ɵɵngDeclareClassMetadata', moduleName: CORE }; }
2731
2747
  static { this.setClassMetadata = { name: 'ɵsetClassMetadata', moduleName: CORE }; }
2748
+ static { this.setClassMetadataAsync = { name: 'ɵsetClassMetadataAsync', moduleName: CORE }; }
2732
2749
  static { this.queryRefresh = { name: 'ɵɵqueryRefresh', moduleName: CORE }; }
2733
2750
  static { this.viewQuery = { name: 'ɵɵviewQuery', moduleName: CORE }; }
2734
2751
  static { this.loadQuery = { name: 'ɵɵloadQuery', moduleName: CORE }; }
@@ -3983,18 +4000,107 @@ class DeferredBlockError {
3983
4000
  class DeferredBlock {
3984
4001
  constructor(children, triggers, prefetchTriggers, placeholder, loading, error, sourceSpan, startSourceSpan, endSourceSpan) {
3985
4002
  this.children = children;
3986
- this.triggers = triggers;
3987
- this.prefetchTriggers = prefetchTriggers;
3988
4003
  this.placeholder = placeholder;
3989
4004
  this.loading = loading;
3990
4005
  this.error = error;
3991
4006
  this.sourceSpan = sourceSpan;
3992
4007
  this.startSourceSpan = startSourceSpan;
3993
4008
  this.endSourceSpan = endSourceSpan;
4009
+ this.triggers = triggers;
4010
+ this.prefetchTriggers = prefetchTriggers;
4011
+ // We cache the keys since we know that they won't change and we
4012
+ // don't want to enumarate them every time we're traversing the AST.
4013
+ this.definedTriggers = Object.keys(triggers);
4014
+ this.definedPrefetchTriggers = Object.keys(prefetchTriggers);
3994
4015
  }
3995
4016
  visit(visitor) {
3996
4017
  return visitor.visitDeferredBlock(this);
3997
4018
  }
4019
+ visitAll(visitor) {
4020
+ this.visitTriggers(this.definedTriggers, this.triggers, visitor);
4021
+ this.visitTriggers(this.definedPrefetchTriggers, this.prefetchTriggers, visitor);
4022
+ visitAll$1(visitor, this.children);
4023
+ this.placeholder && visitor.visitDeferredBlockPlaceholder(this.placeholder);
4024
+ this.loading && visitor.visitDeferredBlockLoading(this.loading);
4025
+ this.error && visitor.visitDeferredBlockError(this.error);
4026
+ }
4027
+ visitTriggers(keys, triggers, visitor) {
4028
+ for (const key of keys) {
4029
+ visitor.visitDeferredTrigger(triggers[key]);
4030
+ }
4031
+ }
4032
+ }
4033
+ class SwitchBlock {
4034
+ constructor(expression, cases, sourceSpan, startSourceSpan, endSourceSpan) {
4035
+ this.expression = expression;
4036
+ this.cases = cases;
4037
+ this.sourceSpan = sourceSpan;
4038
+ this.startSourceSpan = startSourceSpan;
4039
+ this.endSourceSpan = endSourceSpan;
4040
+ }
4041
+ visit(visitor) {
4042
+ return visitor.visitSwitchBlock(this);
4043
+ }
4044
+ }
4045
+ class SwitchBlockCase {
4046
+ constructor(expression, children, sourceSpan, startSourceSpan) {
4047
+ this.expression = expression;
4048
+ this.children = children;
4049
+ this.sourceSpan = sourceSpan;
4050
+ this.startSourceSpan = startSourceSpan;
4051
+ }
4052
+ visit(visitor) {
4053
+ return visitor.visitSwitchBlockCase(this);
4054
+ }
4055
+ }
4056
+ class ForLoopBlock {
4057
+ constructor(itemName, expression, trackBy, contextVariables, children, empty, sourceSpan, startSourceSpan, endSourceSpan) {
4058
+ this.itemName = itemName;
4059
+ this.expression = expression;
4060
+ this.trackBy = trackBy;
4061
+ this.contextVariables = contextVariables;
4062
+ this.children = children;
4063
+ this.empty = empty;
4064
+ this.sourceSpan = sourceSpan;
4065
+ this.startSourceSpan = startSourceSpan;
4066
+ this.endSourceSpan = endSourceSpan;
4067
+ }
4068
+ visit(visitor) {
4069
+ return visitor.visitForLoopBlock(this);
4070
+ }
4071
+ }
4072
+ class ForLoopBlockEmpty {
4073
+ constructor(children, sourceSpan, startSourceSpan) {
4074
+ this.children = children;
4075
+ this.sourceSpan = sourceSpan;
4076
+ this.startSourceSpan = startSourceSpan;
4077
+ }
4078
+ visit(visitor) {
4079
+ return visitor.visitForLoopBlockEmpty(this);
4080
+ }
4081
+ }
4082
+ class IfBlock {
4083
+ constructor(branches, sourceSpan, startSourceSpan, endSourceSpan) {
4084
+ this.branches = branches;
4085
+ this.sourceSpan = sourceSpan;
4086
+ this.startSourceSpan = startSourceSpan;
4087
+ this.endSourceSpan = endSourceSpan;
4088
+ }
4089
+ visit(visitor) {
4090
+ return visitor.visitIfBlock(this);
4091
+ }
4092
+ }
4093
+ class IfBlockBranch {
4094
+ constructor(expression, children, expressionAlias, sourceSpan, startSourceSpan) {
4095
+ this.expression = expression;
4096
+ this.children = children;
4097
+ this.expressionAlias = expressionAlias;
4098
+ this.sourceSpan = sourceSpan;
4099
+ this.startSourceSpan = startSourceSpan;
4100
+ }
4101
+ visit(visitor) {
4102
+ return visitor.visitIfBlockBranch(this);
4103
+ }
3998
4104
  }
3999
4105
  class Template {
4000
4106
  constructor(
@@ -4084,12 +4190,7 @@ class RecursiveVisitor$1 {
4084
4190
  visitAll$1(this, template.variables);
4085
4191
  }
4086
4192
  visitDeferredBlock(deferred) {
4087
- visitAll$1(this, deferred.triggers);
4088
- visitAll$1(this, deferred.prefetchTriggers);
4089
- visitAll$1(this, deferred.children);
4090
- deferred.placeholder?.visit(this);
4091
- deferred.loading?.visit(this);
4092
- deferred.error?.visit(this);
4193
+ deferred.visitAll(this);
4093
4194
  }
4094
4195
  visitDeferredBlockPlaceholder(block) {
4095
4196
  visitAll$1(this, block.children);
@@ -4100,6 +4201,25 @@ class RecursiveVisitor$1 {
4100
4201
  visitDeferredBlockLoading(block) {
4101
4202
  visitAll$1(this, block.children);
4102
4203
  }
4204
+ visitSwitchBlock(block) {
4205
+ visitAll$1(this, block.cases);
4206
+ }
4207
+ visitSwitchBlockCase(block) {
4208
+ visitAll$1(this, block.children);
4209
+ }
4210
+ visitForLoopBlock(block) {
4211
+ visitAll$1(this, block.children);
4212
+ block.empty?.visit(this);
4213
+ }
4214
+ visitForLoopBlockEmpty(block) {
4215
+ visitAll$1(this, block.children);
4216
+ }
4217
+ visitIfBlock(block) {
4218
+ visitAll$1(this, block.branches);
4219
+ }
4220
+ visitIfBlockBranch(block) {
4221
+ visitAll$1(this, block.children);
4222
+ }
4103
4223
  visitContent(content) { }
4104
4224
  visitVariable(variable) { }
4105
4225
  visitReference(reference) { }
@@ -4749,7 +4869,7 @@ function declareI18nVariable(variable) {
4749
4869
  /**
4750
4870
  * Checks whether an object key contains potentially unsafe chars, thus the key should be wrapped in
4751
4871
  * quotes. Note: we do not wrap all keys into quotes, as it may have impact on minification and may
4752
- * bot work in some cases when object keys are mangled by minifier.
4872
+ * not work in some cases when object keys are mangled by a minifier.
4753
4873
  *
4754
4874
  * TODO(FW-1136): this is a temporary solution, we need to come up with a better way of working with
4755
4875
  * inputs that contain potentially unsafe chars.
@@ -4769,6 +4889,8 @@ const IMPLICIT_REFERENCE = '$implicit';
4769
4889
  const NON_BINDABLE_ATTR = 'ngNonBindable';
4770
4890
  /** Name for the variable keeping track of the context returned by `ɵɵrestoreView`. */
4771
4891
  const RESTORED_VIEW_CONTEXT_NAME = 'restoredCtx';
4892
+ /** Special value representing a direct access to a template's context. */
4893
+ const DIRECT_CONTEXT_REFERENCE = '#context';
4772
4894
  /**
4773
4895
  * Maximum length of a single instruction chain. Because our output AST uses recursion, we're
4774
4896
  * limited in how many expressions we can nest before we reach the call stack limit. This
@@ -8615,140 +8737,6 @@ function repeatGroups(groups, multiples) {
8615
8737
  }
8616
8738
  }
8617
8739
 
8618
- var TagContentType;
8619
- (function (TagContentType) {
8620
- TagContentType[TagContentType["RAW_TEXT"] = 0] = "RAW_TEXT";
8621
- TagContentType[TagContentType["ESCAPABLE_RAW_TEXT"] = 1] = "ESCAPABLE_RAW_TEXT";
8622
- TagContentType[TagContentType["PARSABLE_DATA"] = 2] = "PARSABLE_DATA";
8623
- })(TagContentType || (TagContentType = {}));
8624
- function splitNsName(elementName) {
8625
- if (elementName[0] != ':') {
8626
- return [null, elementName];
8627
- }
8628
- const colonIndex = elementName.indexOf(':', 1);
8629
- if (colonIndex === -1) {
8630
- throw new Error(`Unsupported format "${elementName}" expecting ":namespace:name"`);
8631
- }
8632
- return [elementName.slice(1, colonIndex), elementName.slice(colonIndex + 1)];
8633
- }
8634
- // `<ng-container>` tags work the same regardless the namespace
8635
- function isNgContainer(tagName) {
8636
- return splitNsName(tagName)[1] === 'ng-container';
8637
- }
8638
- // `<ng-content>` tags work the same regardless the namespace
8639
- function isNgContent(tagName) {
8640
- return splitNsName(tagName)[1] === 'ng-content';
8641
- }
8642
- // `<ng-template>` tags work the same regardless the namespace
8643
- function isNgTemplate(tagName) {
8644
- return splitNsName(tagName)[1] === 'ng-template';
8645
- }
8646
- function getNsPrefix(fullName) {
8647
- return fullName === null ? null : splitNsName(fullName)[0];
8648
- }
8649
- function mergeNsAndName(prefix, localName) {
8650
- return prefix ? `:${prefix}:${localName}` : localName;
8651
- }
8652
-
8653
- /**
8654
- * Enumeration of the types of attributes which can be applied to an element.
8655
- */
8656
- var BindingKind;
8657
- (function (BindingKind) {
8658
- /**
8659
- * Static attributes.
8660
- */
8661
- BindingKind[BindingKind["Attribute"] = 0] = "Attribute";
8662
- /**
8663
- * Class bindings.
8664
- */
8665
- BindingKind[BindingKind["ClassName"] = 1] = "ClassName";
8666
- /**
8667
- * Style bindings.
8668
- */
8669
- BindingKind[BindingKind["StyleProperty"] = 2] = "StyleProperty";
8670
- /**
8671
- * Dynamic property bindings.
8672
- */
8673
- BindingKind[BindingKind["Property"] = 3] = "Property";
8674
- /**
8675
- * Property or attribute bindings on a template.
8676
- */
8677
- BindingKind[BindingKind["Template"] = 4] = "Template";
8678
- /**
8679
- * Internationalized attributes.
8680
- */
8681
- BindingKind[BindingKind["I18n"] = 5] = "I18n";
8682
- /**
8683
- * TODO: Consider how Animations are handled, and if they should be a distinct BindingKind.
8684
- */
8685
- BindingKind[BindingKind["Animation"] = 6] = "Animation";
8686
- })(BindingKind || (BindingKind = {}));
8687
- const FLYWEIGHT_ARRAY = Object.freeze([]);
8688
- /**
8689
- * Container for all of the various kinds of attributes which are applied on an element.
8690
- */
8691
- class ElementAttributes {
8692
- constructor() {
8693
- this.known = new Set();
8694
- this.byKind = new Map;
8695
- this.projectAs = null;
8696
- }
8697
- get attributes() {
8698
- return this.byKind.get(BindingKind.Attribute) ?? FLYWEIGHT_ARRAY;
8699
- }
8700
- get classes() {
8701
- return this.byKind.get(BindingKind.ClassName) ?? FLYWEIGHT_ARRAY;
8702
- }
8703
- get styles() {
8704
- return this.byKind.get(BindingKind.StyleProperty) ?? FLYWEIGHT_ARRAY;
8705
- }
8706
- get bindings() {
8707
- return this.byKind.get(BindingKind.Property) ?? FLYWEIGHT_ARRAY;
8708
- }
8709
- get template() {
8710
- return this.byKind.get(BindingKind.Template) ?? FLYWEIGHT_ARRAY;
8711
- }
8712
- get i18n() {
8713
- return this.byKind.get(BindingKind.I18n) ?? FLYWEIGHT_ARRAY;
8714
- }
8715
- add(kind, name, value) {
8716
- if (this.known.has(name)) {
8717
- return;
8718
- }
8719
- this.known.add(name);
8720
- const array = this.arrayFor(kind);
8721
- array.push(...getAttributeNameLiterals$1(name));
8722
- if (kind === BindingKind.Attribute || kind === BindingKind.StyleProperty) {
8723
- if (value === null) {
8724
- throw Error('Attribute & style element attributes must have a value');
8725
- }
8726
- array.push(value);
8727
- }
8728
- }
8729
- arrayFor(kind) {
8730
- if (!this.byKind.has(kind)) {
8731
- this.byKind.set(kind, []);
8732
- }
8733
- return this.byKind.get(kind);
8734
- }
8735
- }
8736
- function getAttributeNameLiterals$1(name) {
8737
- const [attributeNamespace, attributeName] = splitNsName(name);
8738
- const nameLiteral = literal(attributeName);
8739
- if (attributeNamespace) {
8740
- return [
8741
- literal(0 /* core.AttributeMarker.NamespaceURI */), literal(attributeNamespace), nameLiteral
8742
- ];
8743
- }
8744
- return [nameLiteral];
8745
- }
8746
- function assertIsElementAttributes(attrs) {
8747
- if (!(attrs instanceof ElementAttributes)) {
8748
- throw new Error(`AssertionError: ElementAttributes has already been coalesced into the view constants`);
8749
- }
8750
- }
8751
-
8752
8740
  /**
8753
8741
  * Distinguishes different kinds of IR operations.
8754
8742
  *
@@ -8855,14 +8843,18 @@ var OpKind;
8855
8843
  * An operation to associate an attribute with an element.
8856
8844
  */
8857
8845
  OpKind[OpKind["Attribute"] = 23] = "Attribute";
8846
+ /**
8847
+ * An attribute that has been extracted for inclusion in the consts array.
8848
+ */
8849
+ OpKind[OpKind["ExtractedAttribute"] = 24] = "ExtractedAttribute";
8858
8850
  /**
8859
8851
  * A host binding property.
8860
8852
  */
8861
- OpKind[OpKind["HostProperty"] = 24] = "HostProperty";
8853
+ OpKind[OpKind["HostProperty"] = 25] = "HostProperty";
8862
8854
  /**
8863
8855
  * A namespace change, which causes the subsequent elements to be processed as either HTML or SVG.
8864
8856
  */
8865
- OpKind[OpKind["Namespace"] = 25] = "Namespace";
8857
+ OpKind[OpKind["Namespace"] = 26] = "Namespace";
8866
8858
  // TODO: Add Host Listeners, and possibly other host ops also.
8867
8859
  })(OpKind || (OpKind = {}));
8868
8860
  /**
@@ -8991,6 +8983,40 @@ var SanitizerFn;
8991
8983
  SanitizerFn[SanitizerFn["ResourceUrl"] = 4] = "ResourceUrl";
8992
8984
  SanitizerFn[SanitizerFn["IframeAttribute"] = 5] = "IframeAttribute";
8993
8985
  })(SanitizerFn || (SanitizerFn = {}));
8986
+ /**
8987
+ * Enumeration of the types of attributes which can be applied to an element.
8988
+ */
8989
+ var BindingKind;
8990
+ (function (BindingKind) {
8991
+ /**
8992
+ * Static attributes.
8993
+ */
8994
+ BindingKind[BindingKind["Attribute"] = 0] = "Attribute";
8995
+ /**
8996
+ * Class bindings.
8997
+ */
8998
+ BindingKind[BindingKind["ClassName"] = 1] = "ClassName";
8999
+ /**
9000
+ * Style bindings.
9001
+ */
9002
+ BindingKind[BindingKind["StyleProperty"] = 2] = "StyleProperty";
9003
+ /**
9004
+ * Dynamic property bindings.
9005
+ */
9006
+ BindingKind[BindingKind["Property"] = 3] = "Property";
9007
+ /**
9008
+ * Property or attribute bindings on a template.
9009
+ */
9010
+ BindingKind[BindingKind["Template"] = 4] = "Template";
9011
+ /**
9012
+ * Internationalized attributes.
9013
+ */
9014
+ BindingKind[BindingKind["I18n"] = 5] = "I18n";
9015
+ /**
9016
+ * Animation property bindings.
9017
+ */
9018
+ BindingKind[BindingKind["Animation"] = 6] = "Animation";
9019
+ })(BindingKind || (BindingKind = {}));
8994
9020
 
8995
9021
  /**
8996
9022
  * Marker symbol for `ConsumesSlotOpTrait`.
@@ -9133,7 +9159,7 @@ class Interpolation {
9133
9159
  /**
9134
9160
  * Create a `BindingOp`, not yet transformed into a particular type of binding.
9135
9161
  */
9136
- function createBindingOp(target, kind, name, expression, unit, securityContext, isTemplate, sourceSpan) {
9162
+ function createBindingOp(target, kind, name, expression, unit, securityContext, isTextAttribute, isTemplate, sourceSpan) {
9137
9163
  return {
9138
9164
  kind: OpKind.Binding,
9139
9165
  bindingKind: kind,
@@ -9142,6 +9168,7 @@ function createBindingOp(target, kind, name, expression, unit, securityContext,
9142
9168
  expression,
9143
9169
  unit,
9144
9170
  securityContext,
9171
+ isTextAttribute,
9145
9172
  isTemplate,
9146
9173
  sourceSpan,
9147
9174
  ...NEW_OP,
@@ -9224,7 +9251,7 @@ function createClassMapOp(xref, expression, sourceSpan) {
9224
9251
  /**
9225
9252
  * Create an `AttributeOp`.
9226
9253
  */
9227
- function createAttributeOp(target, name, expression, securityContext, isTemplate, sourceSpan) {
9254
+ function createAttributeOp(target, name, expression, securityContext, isTextAttribute, isTemplate, sourceSpan) {
9228
9255
  return {
9229
9256
  kind: OpKind.Attribute,
9230
9257
  target,
@@ -9232,6 +9259,7 @@ function createAttributeOp(target, name, expression, securityContext, isTemplate
9232
9259
  expression,
9233
9260
  securityContext,
9234
9261
  sanitizer: null,
9262
+ isTextAttribute,
9235
9263
  isTemplate,
9236
9264
  sourceSpan,
9237
9265
  ...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
@@ -9861,6 +9889,10 @@ function transformExpressionsInOp(op, transform, flags) {
9861
9889
  transformExpressionsInOp(innerOp, transform, flags | VisitorContextFlag.InChildOperation);
9862
9890
  }
9863
9891
  break;
9892
+ case OpKind.ExtractedAttribute:
9893
+ op.expression =
9894
+ op.expression && transformExpressionsInExpression(op.expression, transform, flags);
9895
+ break;
9864
9896
  case OpKind.Element:
9865
9897
  case OpKind.ElementStart:
9866
9898
  case OpKind.ElementEnd:
@@ -9965,6 +9997,12 @@ function transformExpressionsInStatement(stmt, transform, flags) {
9965
9997
  throw new Error(`Unhandled statement kind: ${stmt.constructor.name}`);
9966
9998
  }
9967
9999
  }
10000
+ /**
10001
+ * Checks whether the given expression is a string literal.
10002
+ */
10003
+ function isStringLiteral(expr) {
10004
+ return expr instanceof LiteralExpr && typeof expr.value === 'string';
10005
+ }
9968
10006
 
9969
10007
  /**
9970
10008
  * A linked list of `Op` nodes of a given subtype.
@@ -10227,7 +10265,7 @@ function createElementStartOp(tag, xref, namespace, sourceSpan) {
10227
10265
  kind: OpKind.ElementStart,
10228
10266
  xref,
10229
10267
  tag,
10230
- attributes: new ElementAttributes(),
10268
+ attributes: null,
10231
10269
  localRefs: [],
10232
10270
  nonBindable: false,
10233
10271
  namespace,
@@ -10243,7 +10281,7 @@ function createTemplateOp(xref, tag, namespace, sourceSpan) {
10243
10281
  return {
10244
10282
  kind: OpKind.Template,
10245
10283
  xref,
10246
- attributes: new ElementAttributes(),
10284
+ attributes: null,
10247
10285
  tag,
10248
10286
  decls: null,
10249
10287
  vars: null,
@@ -10354,6 +10392,19 @@ function createNamespaceOp(namespace) {
10354
10392
  ...NEW_OP,
10355
10393
  };
10356
10394
  }
10395
+ /**
10396
+ * Create an `ExtractedAttributeOp`.
10397
+ */
10398
+ function createExtractedAttributeOp(target, bindingKind, name, expression) {
10399
+ return {
10400
+ kind: OpKind.ExtractedAttribute,
10401
+ target,
10402
+ bindingKind,
10403
+ name,
10404
+ expression,
10405
+ ...NEW_OP,
10406
+ };
10407
+ }
10357
10408
 
10358
10409
  function createHostPropertyOp(name, expression, sourceSpan) {
10359
10410
  return {
@@ -10663,86 +10714,7 @@ function removeAnys(e) {
10663
10714
  }
10664
10715
 
10665
10716
  /**
10666
- * Parses string representation of a style and converts it into object literal.
10667
- *
10668
- * @param value string representation of style as used in the `style` attribute in HTML.
10669
- * Example: `color: red; height: auto`.
10670
- * @returns An array of style property name and value pairs, e.g. `['color', 'red', 'height',
10671
- * 'auto']`
10672
- */
10673
- function parse(value) {
10674
- // we use a string array here instead of a string map
10675
- // because a string-map is not guaranteed to retain the
10676
- // order of the entries whereas a string array can be
10677
- // constructed in a [key, value, key, value] format.
10678
- const styles = [];
10679
- let i = 0;
10680
- let parenDepth = 0;
10681
- let quote = 0 /* Char.QuoteNone */;
10682
- let valueStart = 0;
10683
- let propStart = 0;
10684
- let currentProp = null;
10685
- while (i < value.length) {
10686
- const token = value.charCodeAt(i++);
10687
- switch (token) {
10688
- case 40 /* Char.OpenParen */:
10689
- parenDepth++;
10690
- break;
10691
- case 41 /* Char.CloseParen */:
10692
- parenDepth--;
10693
- break;
10694
- case 39 /* Char.QuoteSingle */:
10695
- // valueStart needs to be there since prop values don't
10696
- // have quotes in CSS
10697
- if (quote === 0 /* Char.QuoteNone */) {
10698
- quote = 39 /* Char.QuoteSingle */;
10699
- }
10700
- else if (quote === 39 /* Char.QuoteSingle */ && value.charCodeAt(i - 1) !== 92 /* Char.BackSlash */) {
10701
- quote = 0 /* Char.QuoteNone */;
10702
- }
10703
- break;
10704
- case 34 /* Char.QuoteDouble */:
10705
- // same logic as above
10706
- if (quote === 0 /* Char.QuoteNone */) {
10707
- quote = 34 /* Char.QuoteDouble */;
10708
- }
10709
- else if (quote === 34 /* Char.QuoteDouble */ && value.charCodeAt(i - 1) !== 92 /* Char.BackSlash */) {
10710
- quote = 0 /* Char.QuoteNone */;
10711
- }
10712
- break;
10713
- case 58 /* Char.Colon */:
10714
- if (!currentProp && parenDepth === 0 && quote === 0 /* Char.QuoteNone */) {
10715
- currentProp = hyphenate$1(value.substring(propStart, i - 1).trim());
10716
- valueStart = i;
10717
- }
10718
- break;
10719
- case 59 /* Char.Semicolon */:
10720
- if (currentProp && valueStart > 0 && parenDepth === 0 && quote === 0 /* Char.QuoteNone */) {
10721
- const styleVal = value.substring(valueStart, i - 1).trim();
10722
- styles.push(currentProp, styleVal);
10723
- propStart = i;
10724
- valueStart = 0;
10725
- currentProp = null;
10726
- }
10727
- break;
10728
- }
10729
- }
10730
- if (currentProp && valueStart) {
10731
- const styleVal = value.slice(valueStart).trim();
10732
- styles.push(currentProp, styleVal);
10733
- }
10734
- return styles;
10735
- }
10736
- function hyphenate$1(value) {
10737
- return value
10738
- .replace(/[a-z][A-Z]/g, v => {
10739
- return v.charAt(0) + '-' + v.charAt(1);
10740
- })
10741
- .toLowerCase();
10742
- }
10743
-
10744
- /**
10745
- * Gets a map of all elements in the given view by their xref id.
10717
+ * Gets a map of all elements in the given view by their xref id.
10746
10718
  */
10747
10719
  function getElementsByXrefId(view) {
10748
10720
  const elements = new Map();
@@ -10756,12 +10728,39 @@ function getElementsByXrefId(view) {
10756
10728
  }
10757
10729
 
10758
10730
  /**
10759
- * Find all attribute and binding ops, and collect them into the ElementAttribute structures.
10731
+ * Find all extractable attribute and binding ops, and create ExtractedAttributeOps for them.
10760
10732
  * In cases where no instruction needs to be generated for the attribute or binding, it is removed.
10761
10733
  */
10762
10734
  function phaseAttributeExtraction(cpl) {
10763
10735
  for (const [_, view] of cpl.views) {
10764
- populateElementAttributes(view);
10736
+ const elements = getElementsByXrefId(view);
10737
+ for (const op of view.ops()) {
10738
+ switch (op.kind) {
10739
+ case OpKind.Attribute:
10740
+ extractAttributeOp(view, op, elements);
10741
+ break;
10742
+ case OpKind.Property:
10743
+ if (!op.isAnimationTrigger) {
10744
+ OpList.insertBefore(createExtractedAttributeOp(op.target, op.isTemplate ? BindingKind.Template : BindingKind.Property, op.name, null), lookupElement$2(elements, op.target));
10745
+ }
10746
+ break;
10747
+ case OpKind.StyleProp:
10748
+ case OpKind.ClassProp:
10749
+ // The old compiler treated empty style bindings as regular bindings for the purpose of
10750
+ // directive matching. That behavior is incorrect, but we emulate it in compatibility
10751
+ // mode.
10752
+ if (view.compatibility === CompatibilityMode.TemplateDefinitionBuilder &&
10753
+ op.expression instanceof EmptyExpr) {
10754
+ OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.Property, op.name, null), lookupElement$2(elements, op.target));
10755
+ }
10756
+ break;
10757
+ case OpKind.Listener:
10758
+ if (!op.isAnimationListener) {
10759
+ OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.Property, op.name, null), lookupElement$2(elements, op.target));
10760
+ }
10761
+ break;
10762
+ }
10763
+ }
10765
10764
  }
10766
10765
  }
10767
10766
  /**
@@ -10775,84 +10774,28 @@ function lookupElement$2(elements, xref) {
10775
10774
  return el;
10776
10775
  }
10777
10776
  /**
10778
- * Populates the ElementAttributes map for the given view, and removes ops for any bindings that do
10779
- * not need further processing.
10777
+ * Extracts an attribute binding.
10780
10778
  */
10781
- function populateElementAttributes(view) {
10782
- const elements = getElementsByXrefId(view);
10783
- for (const op of view.ops()) {
10784
- let ownerOp;
10785
- switch (op.kind) {
10786
- case OpKind.Attribute:
10787
- extractAttributeOp(view, op, elements);
10788
- break;
10789
- case OpKind.Property:
10790
- if (op.isAnimationTrigger) {
10791
- continue; // Don't extract animation properties.
10792
- }
10793
- ownerOp = lookupElement$2(elements, op.target);
10794
- assertIsElementAttributes(ownerOp.attributes);
10795
- ownerOp.attributes.add(op.isTemplate ? BindingKind.Template : BindingKind.Property, op.name, null);
10796
- break;
10797
- case OpKind.StyleProp:
10798
- case OpKind.ClassProp:
10799
- ownerOp = lookupElement$2(elements, op.target);
10800
- assertIsElementAttributes(ownerOp.attributes);
10801
- // Empty StyleProperty and ClassName expressions are treated differently depending on
10802
- // compatibility mode.
10803
- if (view.compatibility === CompatibilityMode.TemplateDefinitionBuilder &&
10804
- op.expression instanceof EmptyExpr) {
10805
- // The old compiler treated empty style bindings as regular bindings for the purpose of
10806
- // directive matching. That behavior is incorrect, but we emulate it in compatibility
10807
- // mode.
10808
- ownerOp.attributes.add(BindingKind.Property, op.name, null);
10809
- }
10810
- break;
10811
- case OpKind.Listener:
10812
- if (op.isAnimationListener) {
10813
- continue; // Don't extract animation listeners.
10814
- }
10815
- ownerOp = lookupElement$2(elements, op.target);
10816
- assertIsElementAttributes(ownerOp.attributes);
10817
- ownerOp.attributes.add(BindingKind.Property, op.name, null);
10818
- break;
10819
- }
10820
- }
10821
- }
10822
- function isStringLiteral(expr) {
10823
- return expr instanceof LiteralExpr && typeof expr.value === 'string';
10824
- }
10825
10779
  function extractAttributeOp(view, op, elements) {
10826
10780
  if (op.expression instanceof Interpolation) {
10827
10781
  return;
10828
10782
  }
10829
10783
  const ownerOp = lookupElement$2(elements, op.target);
10830
- assertIsElementAttributes(ownerOp.attributes);
10831
- if (op.name === 'style' && isStringLiteral(op.expression)) {
10832
- // TemplateDefinitionBuilder did not extract style attributes that had a security context.
10833
- if (view.compatibility === CompatibilityMode.TemplateDefinitionBuilder &&
10834
- op.securityContext !== SecurityContext.NONE) {
10835
- return;
10836
- }
10837
- // Extract style attributes.
10838
- const parsedStyles = parse(op.expression.value);
10839
- for (let i = 0; i < parsedStyles.length - 1; i += 2) {
10840
- ownerOp.attributes.add(BindingKind.StyleProperty, parsedStyles[i], literal(parsedStyles[i + 1]));
10841
- }
10784
+ let extractable = op.expression.isConstant();
10785
+ if (view.compatibility === CompatibilityMode.TemplateDefinitionBuilder) {
10786
+ // TemplateDefinitionBuilder only extracted attributes that were string literals.
10787
+ extractable = isStringLiteral(op.expression);
10788
+ if (op.name === 'style' || op.name === 'class') {
10789
+ // For style and class attributes, TemplateDefinitionBuilder only extracted them if they were
10790
+ // text attributes. For example, `[attr.class]="'my-class'"` was not extracted despite being a
10791
+ // string literal, because it is not a text attribute.
10792
+ extractable &&= op.isTextAttribute;
10793
+ }
10794
+ }
10795
+ if (extractable) {
10796
+ OpList.insertBefore(createExtractedAttributeOp(op.target, op.isTemplate ? BindingKind.Template : BindingKind.Attribute, op.name, op.expression), ownerOp);
10842
10797
  OpList.remove(op);
10843
10798
  }
10844
- else {
10845
- // The old compiler only extracted string constants, so we emulate that behavior in
10846
- // compaitiblity mode, otherwise we optimize more aggressively.
10847
- let extractable = view.compatibility === CompatibilityMode.TemplateDefinitionBuilder ?
10848
- (op.expression instanceof LiteralExpr && typeof op.expression.value === 'string') :
10849
- op.expression.isConstant();
10850
- // We don't need to generate instructions for attributes that can be extracted as consts.
10851
- if (extractable) {
10852
- ownerOp.attributes.add(op.isTemplate ? BindingKind.Template : BindingKind.Attribute, op.name, op.expression);
10853
- OpList.remove(op);
10854
- }
10855
- }
10856
10799
  }
10857
10800
 
10858
10801
  /**
@@ -10888,7 +10831,7 @@ function phaseBindingSpecialization(job) {
10888
10831
  target.nonBindable = true;
10889
10832
  }
10890
10833
  else {
10891
- OpList.replace(op, createAttributeOp(op.target, op.name, op.expression, op.securityContext, op.isTemplate, op.sourceSpan));
10834
+ OpList.replace(op, createAttributeOp(op.target, op.name, op.expression, op.securityContext, op.isTextAttribute, op.isTemplate, op.sourceSpan));
10892
10835
  }
10893
10836
  break;
10894
10837
  case BindingKind.Property:
@@ -10997,30 +10940,142 @@ function chainOperationsInList(opList) {
10997
10940
  }
10998
10941
  }
10999
10942
 
10943
+ var TagContentType;
10944
+ (function (TagContentType) {
10945
+ TagContentType[TagContentType["RAW_TEXT"] = 0] = "RAW_TEXT";
10946
+ TagContentType[TagContentType["ESCAPABLE_RAW_TEXT"] = 1] = "ESCAPABLE_RAW_TEXT";
10947
+ TagContentType[TagContentType["PARSABLE_DATA"] = 2] = "PARSABLE_DATA";
10948
+ })(TagContentType || (TagContentType = {}));
10949
+ function splitNsName(elementName) {
10950
+ if (elementName[0] != ':') {
10951
+ return [null, elementName];
10952
+ }
10953
+ const colonIndex = elementName.indexOf(':', 1);
10954
+ if (colonIndex === -1) {
10955
+ throw new Error(`Unsupported format "${elementName}" expecting ":namespace:name"`);
10956
+ }
10957
+ return [elementName.slice(1, colonIndex), elementName.slice(colonIndex + 1)];
10958
+ }
10959
+ // `<ng-container>` tags work the same regardless the namespace
10960
+ function isNgContainer(tagName) {
10961
+ return splitNsName(tagName)[1] === 'ng-container';
10962
+ }
10963
+ // `<ng-content>` tags work the same regardless the namespace
10964
+ function isNgContent(tagName) {
10965
+ return splitNsName(tagName)[1] === 'ng-content';
10966
+ }
10967
+ // `<ng-template>` tags work the same regardless the namespace
10968
+ function isNgTemplate(tagName) {
10969
+ return splitNsName(tagName)[1] === 'ng-template';
10970
+ }
10971
+ function getNsPrefix(fullName) {
10972
+ return fullName === null ? null : splitNsName(fullName)[0];
10973
+ }
10974
+ function mergeNsAndName(prefix, localName) {
10975
+ return prefix ? `:${prefix}:${localName}` : localName;
10976
+ }
10977
+
11000
10978
  /**
11001
10979
  * Converts the semantic attributes of element-like operations (elements, templates) into constant
11002
10980
  * array expressions, and lifts them into the overall component `consts`.
11003
10981
  */
11004
10982
  function phaseConstCollection(cpl) {
10983
+ // Collect all extracted attributes.
10984
+ const elementAttributes = new Map();
11005
10985
  for (const [_, view] of cpl.views) {
11006
10986
  for (const op of view.create) {
11007
- if (op.kind !== OpKind.ElementStart && op.kind !== OpKind.Element &&
11008
- op.kind !== OpKind.Template) {
11009
- continue;
11010
- }
11011
- else if (!(op.attributes instanceof ElementAttributes)) {
11012
- continue;
10987
+ if (op.kind === OpKind.ExtractedAttribute) {
10988
+ const attributes = elementAttributes.get(op.target) || new ElementAttributes();
10989
+ elementAttributes.set(op.target, attributes);
10990
+ attributes.add(op.bindingKind, op.name, op.expression);
10991
+ OpList.remove(op);
11013
10992
  }
11014
- const attrArray = serializeAttributes(op.attributes);
11015
- if (attrArray.entries.length > 0) {
11016
- op.attributes = cpl.addConst(attrArray);
10993
+ }
10994
+ }
10995
+ // Serialize the extracted attributes into the const array.
10996
+ for (const [_, view] of cpl.views) {
10997
+ for (const op of view.create) {
10998
+ if (op.kind === OpKind.Element || op.kind === OpKind.ElementStart ||
10999
+ op.kind === OpKind.Template) {
11000
+ const attributes = elementAttributes.get(op.xref);
11001
+ if (attributes !== undefined) {
11002
+ const attrArray = serializeAttributes(attributes);
11003
+ if (attrArray.entries.length > 0) {
11004
+ op.attributes = cpl.addConst(attrArray);
11005
+ }
11006
+ }
11017
11007
  }
11018
- else {
11019
- op.attributes = null;
11008
+ }
11009
+ }
11010
+ }
11011
+ /**
11012
+ * Shared instance of an empty array to avoid unnecessary array allocations.
11013
+ */
11014
+ const FLYWEIGHT_ARRAY = Object.freeze([]);
11015
+ /**
11016
+ * Container for all of the various kinds of attributes which are applied on an element.
11017
+ */
11018
+ class ElementAttributes {
11019
+ constructor() {
11020
+ this.known = new Set();
11021
+ this.byKind = new Map;
11022
+ this.projectAs = null;
11023
+ }
11024
+ get attributes() {
11025
+ return this.byKind.get(BindingKind.Attribute) ?? FLYWEIGHT_ARRAY;
11026
+ }
11027
+ get classes() {
11028
+ return this.byKind.get(BindingKind.ClassName) ?? FLYWEIGHT_ARRAY;
11029
+ }
11030
+ get styles() {
11031
+ return this.byKind.get(BindingKind.StyleProperty) ?? FLYWEIGHT_ARRAY;
11032
+ }
11033
+ get bindings() {
11034
+ return this.byKind.get(BindingKind.Property) ?? FLYWEIGHT_ARRAY;
11035
+ }
11036
+ get template() {
11037
+ return this.byKind.get(BindingKind.Template) ?? FLYWEIGHT_ARRAY;
11038
+ }
11039
+ get i18n() {
11040
+ return this.byKind.get(BindingKind.I18n) ?? FLYWEIGHT_ARRAY;
11041
+ }
11042
+ add(kind, name, value) {
11043
+ if (this.known.has(name)) {
11044
+ return;
11045
+ }
11046
+ this.known.add(name);
11047
+ const array = this.arrayFor(kind);
11048
+ array.push(...getAttributeNameLiterals$1(name));
11049
+ if (kind === BindingKind.Attribute || kind === BindingKind.StyleProperty) {
11050
+ if (value === null) {
11051
+ throw Error('Attribute & style element attributes must have a value');
11020
11052
  }
11053
+ array.push(value);
11054
+ }
11055
+ }
11056
+ arrayFor(kind) {
11057
+ if (!this.byKind.has(kind)) {
11058
+ this.byKind.set(kind, []);
11021
11059
  }
11060
+ return this.byKind.get(kind);
11061
+ }
11062
+ }
11063
+ /**
11064
+ * Gets an array of literal expressions representing the attribute's namespaced name.
11065
+ */
11066
+ function getAttributeNameLiterals$1(name) {
11067
+ const [attributeNamespace, attributeName] = splitNsName(name);
11068
+ const nameLiteral = literal(attributeName);
11069
+ if (attributeNamespace) {
11070
+ return [
11071
+ literal(0 /* core.AttributeMarker.NamespaceURI */), literal(attributeNamespace), nameLiteral
11072
+ ];
11022
11073
  }
11074
+ return [nameLiteral];
11023
11075
  }
11076
+ /**
11077
+ * Serializes an ElementAttributes object into an array expression.
11078
+ */
11024
11079
  function serializeAttributes({ attributes, bindings, classes, i18n, projectAs, styles, template }) {
11025
11080
  const attrArray = [...attributes];
11026
11081
  if (projectAs !== null) {
@@ -11426,7 +11481,7 @@ function phaseHostStylePropertyParsing(job) {
11426
11481
  op.bindingKind = BindingKind.StyleProperty;
11427
11482
  op.name = op.name.substring(STYLE_DOT.length);
11428
11483
  if (isCssCustomProperty$1(op.name)) {
11429
- op.name = hyphenate(op.name);
11484
+ op.name = hyphenate$1(op.name);
11430
11485
  }
11431
11486
  const { property, suffix } = parseProperty$1(op.name);
11432
11487
  op.name = property;
@@ -11449,7 +11504,7 @@ function phaseHostStylePropertyParsing(job) {
11449
11504
  function isCssCustomProperty$1(name) {
11450
11505
  return name.startsWith('--');
11451
11506
  }
11452
- function hyphenate(value) {
11507
+ function hyphenate$1(value) {
11453
11508
  return value
11454
11509
  .replace(/[a-z][A-Z]/g, v => {
11455
11510
  return v.charAt(0) + '-' + v.charAt(1);
@@ -11524,6 +11579,85 @@ function phaseNamespace(job) {
11524
11579
  }
11525
11580
  }
11526
11581
 
11582
+ /**
11583
+ * Parses string representation of a style and converts it into object literal.
11584
+ *
11585
+ * @param value string representation of style as used in the `style` attribute in HTML.
11586
+ * Example: `color: red; height: auto`.
11587
+ * @returns An array of style property name and value pairs, e.g. `['color', 'red', 'height',
11588
+ * 'auto']`
11589
+ */
11590
+ function parse(value) {
11591
+ // we use a string array here instead of a string map
11592
+ // because a string-map is not guaranteed to retain the
11593
+ // order of the entries whereas a string array can be
11594
+ // constructed in a [key, value, key, value] format.
11595
+ const styles = [];
11596
+ let i = 0;
11597
+ let parenDepth = 0;
11598
+ let quote = 0 /* Char.QuoteNone */;
11599
+ let valueStart = 0;
11600
+ let propStart = 0;
11601
+ let currentProp = null;
11602
+ while (i < value.length) {
11603
+ const token = value.charCodeAt(i++);
11604
+ switch (token) {
11605
+ case 40 /* Char.OpenParen */:
11606
+ parenDepth++;
11607
+ break;
11608
+ case 41 /* Char.CloseParen */:
11609
+ parenDepth--;
11610
+ break;
11611
+ case 39 /* Char.QuoteSingle */:
11612
+ // valueStart needs to be there since prop values don't
11613
+ // have quotes in CSS
11614
+ if (quote === 0 /* Char.QuoteNone */) {
11615
+ quote = 39 /* Char.QuoteSingle */;
11616
+ }
11617
+ else if (quote === 39 /* Char.QuoteSingle */ && value.charCodeAt(i - 1) !== 92 /* Char.BackSlash */) {
11618
+ quote = 0 /* Char.QuoteNone */;
11619
+ }
11620
+ break;
11621
+ case 34 /* Char.QuoteDouble */:
11622
+ // same logic as above
11623
+ if (quote === 0 /* Char.QuoteNone */) {
11624
+ quote = 34 /* Char.QuoteDouble */;
11625
+ }
11626
+ else if (quote === 34 /* Char.QuoteDouble */ && value.charCodeAt(i - 1) !== 92 /* Char.BackSlash */) {
11627
+ quote = 0 /* Char.QuoteNone */;
11628
+ }
11629
+ break;
11630
+ case 58 /* Char.Colon */:
11631
+ if (!currentProp && parenDepth === 0 && quote === 0 /* Char.QuoteNone */) {
11632
+ currentProp = hyphenate(value.substring(propStart, i - 1).trim());
11633
+ valueStart = i;
11634
+ }
11635
+ break;
11636
+ case 59 /* Char.Semicolon */:
11637
+ if (currentProp && valueStart > 0 && parenDepth === 0 && quote === 0 /* Char.QuoteNone */) {
11638
+ const styleVal = value.substring(valueStart, i - 1).trim();
11639
+ styles.push(currentProp, styleVal);
11640
+ propStart = i;
11641
+ valueStart = 0;
11642
+ currentProp = null;
11643
+ }
11644
+ break;
11645
+ }
11646
+ }
11647
+ if (currentProp && valueStart) {
11648
+ const styleVal = value.slice(valueStart).trim();
11649
+ styles.push(currentProp, styleVal);
11650
+ }
11651
+ return styles;
11652
+ }
11653
+ function hyphenate(value) {
11654
+ return value
11655
+ .replace(/[a-z][A-Z]/g, v => {
11656
+ return v.charAt(0) + '-' + v.charAt(1);
11657
+ })
11658
+ .toLowerCase();
11659
+ }
11660
+
11527
11661
  const BINARY_OPERATORS = new Map([
11528
11662
  ['&&', BinaryOperator.And],
11529
11663
  ['>', BinaryOperator.Bigger],
@@ -11664,7 +11798,7 @@ function getVariableName(variable, state) {
11664
11798
  * Normalizes a style prop name by hyphenating it (unless its a CSS variable).
11665
11799
  */
11666
11800
  function normalizeStylePropName(name) {
11667
- return name.startsWith('--') ? name : hyphenate$1(name);
11801
+ return name.startsWith('--') ? name : hyphenate(name);
11668
11802
  }
11669
11803
  /**
11670
11804
  * Strips `!important` out of the given style or class name.
@@ -11846,6 +11980,34 @@ function phaseNullishCoalescing(job) {
11846
11980
  }
11847
11981
  }
11848
11982
 
11983
+ /**
11984
+ * Parses extracted style and class attributes into separate ExtractedAttributeOps per style or
11985
+ * class property.
11986
+ */
11987
+ function phaseParseExtractedStyles(cpl) {
11988
+ for (const [_, view] of cpl.views) {
11989
+ for (const op of view.create) {
11990
+ if (op.kind === OpKind.ExtractedAttribute && op.bindingKind === BindingKind.Attribute &&
11991
+ isStringLiteral(op.expression)) {
11992
+ if (op.name === 'style') {
11993
+ const parsedStyles = parse(op.expression.value);
11994
+ for (let i = 0; i < parsedStyles.length - 1; i += 2) {
11995
+ OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.StyleProperty, parsedStyles[i], literal(parsedStyles[i + 1])), op);
11996
+ }
11997
+ OpList.remove(op);
11998
+ }
11999
+ else if (op.name === 'class') {
12000
+ const parsedClasses = op.expression.value.trim().split(/\s+/g);
12001
+ for (const parsedClass of parsedClasses) {
12002
+ OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.ClassName, parsedClass, null), op);
12003
+ }
12004
+ OpList.remove(op);
12005
+ }
12006
+ }
12007
+ }
12008
+ }
12009
+ }
12010
+
11849
12011
  function phasePipeCreation(cpl) {
11850
12012
  for (const view of cpl.views.values()) {
11851
12013
  processPipeBindingsInView(view);
@@ -13593,6 +13755,7 @@ function transformTemplate(job) {
13593
13755
  phaseStyleBindingSpecialization(job);
13594
13756
  phaseBindingSpecialization(job);
13595
13757
  phaseAttributeExtraction(job);
13758
+ phaseParseExtractedStyles(job);
13596
13759
  phaseRemoveEmptyBindings(job);
13597
13760
  phaseNoListenersOnTemplates(job);
13598
13761
  phasePipeCreation(job);
@@ -13760,7 +13923,7 @@ function ingestComponent(componentName, template, constantPool) {
13760
13923
  function ingestHostBinding(input, bindingParser, constantPool) {
13761
13924
  const job = new HostBindingCompilationJob(input.componentName, constantPool, compatibilityMode);
13762
13925
  for (const property of input.properties ?? []) {
13763
- ingestHostProperty(job, property);
13926
+ ingestHostProperty(job, property, false);
13764
13927
  }
13765
13928
  for (const event of input.events ?? []) {
13766
13929
  ingestHostEvent(job, event);
@@ -13769,7 +13932,7 @@ function ingestHostBinding(input, bindingParser, constantPool) {
13769
13932
  }
13770
13933
  // TODO: We should refactor the parser to use the same types and structures for host bindings as
13771
13934
  // with ordinary components. This would allow us to share a lot more ingestion code.
13772
- function ingestHostProperty(job, property) {
13935
+ function ingestHostProperty(job, property, isTextAttribute) {
13773
13936
  let expression;
13774
13937
  const ast = property.expression.ast;
13775
13938
  if (ast instanceof Interpolation$1) {
@@ -13786,7 +13949,7 @@ function ingestHostProperty(job, property) {
13786
13949
  bindingKind = BindingKind.Attribute;
13787
13950
  }
13788
13951
  job.update.push(createBindingOp(job.root.xref, bindingKind, property.name, expression, null, SecurityContext
13789
- .NONE /* TODO: what should we pass as security context? Passing NONE for now. */, false, property.sourceSpan));
13952
+ .NONE /* TODO: what should we pass as security context? Passing NONE for now. */, isTextAttribute, false, property.sourceSpan));
13790
13953
  }
13791
13954
  function ingestHostEvent(job, event) { }
13792
13955
  /**
@@ -13964,10 +14127,10 @@ function ingestBindings(view, op, element) {
13964
14127
  if (element instanceof Template) {
13965
14128
  for (const attr of element.templateAttrs) {
13966
14129
  if (attr instanceof TextAttribute) {
13967
- ingestBinding(view, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, true);
14130
+ ingestBinding(view, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, true, true);
13968
14131
  }
13969
14132
  else {
13970
- ingestBinding(view, op.xref, attr.name, attr.value, attr.type, attr.unit, attr.securityContext, attr.sourceSpan, true);
14133
+ ingestBinding(view, op.xref, attr.name, attr.value, attr.type, attr.unit, attr.securityContext, attr.sourceSpan, false, true);
13971
14134
  }
13972
14135
  }
13973
14136
  }
@@ -13975,10 +14138,10 @@ function ingestBindings(view, op, element) {
13975
14138
  // This is only attribute TextLiteral bindings, such as `attr.foo="bar"`. This can never be
13976
14139
  // `[attr.foo]="bar"` or `attr.foo="{{bar}}"`, both of which will be handled as inputs with
13977
14140
  // `BindingType.Attribute`.
13978
- ingestBinding(view, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, false);
14141
+ ingestBinding(view, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, true, false);
13979
14142
  }
13980
14143
  for (const input of element.inputs) {
13981
- ingestBinding(view, op.xref, input.name, input.value, input.type, input.unit, input.securityContext, input.sourceSpan, false);
14144
+ ingestBinding(view, op.xref, input.name, input.value, input.type, input.unit, input.securityContext, input.sourceSpan, false, false);
13982
14145
  }
13983
14146
  for (const output of element.outputs) {
13984
14147
  let listenerOp;
@@ -14024,7 +14187,7 @@ const BINDING_KINDS = new Map([
14024
14187
  [3 /* e.BindingType.Style */, BindingKind.StyleProperty],
14025
14188
  [4 /* e.BindingType.Animation */, BindingKind.Animation],
14026
14189
  ]);
14027
- function ingestBinding(view, xref, name, value, type, unit, securityContext, sourceSpan, isTemplateBinding) {
14190
+ function ingestBinding(view, xref, name, value, type, unit, securityContext, sourceSpan, isTextAttribute, isTemplateBinding) {
14028
14191
  if (value instanceof ASTWithSource) {
14029
14192
  value = value.ast;
14030
14193
  }
@@ -14039,7 +14202,7 @@ function ingestBinding(view, xref, name, value, type, unit, securityContext, sou
14039
14202
  expression = value;
14040
14203
  }
14041
14204
  const kind = BINDING_KINDS.get(type);
14042
- view.update.push(createBindingOp(xref, kind, name, expression, unit, securityContext, isTemplateBinding, sourceSpan));
14205
+ view.update.push(createBindingOp(xref, kind, name, expression, unit, securityContext, isTextAttribute, isTemplateBinding, sourceSpan));
14043
14206
  }
14044
14207
  /**
14045
14208
  * Process all of the local references on an element-like structure in the template AST and
@@ -14234,7 +14397,7 @@ class StylingBuilder {
14234
14397
  // CSS custom properties are case-sensitive so we shouldn't normalize them.
14235
14398
  // See: https://www.w3.org/TR/css-variables-1/#defining-variables
14236
14399
  if (!isCssCustomProperty(name)) {
14237
- name = hyphenate$1(name);
14400
+ name = hyphenate(name);
14238
14401
  }
14239
14402
  const { property, hasOverrideFlag, suffix: bindingSuffix } = parseProperty(name);
14240
14403
  suffix = typeof suffix === 'string' && suffix.length !== 0 ? suffix : bindingSuffix;
@@ -21301,6 +21464,328 @@ function normalizeNgContentSelect(selectAttr) {
21301
21464
  return selectAttr;
21302
21465
  }
21303
21466
 
21467
+ /** Pattern for the expression in a for loop block. */
21468
+ const FOR_LOOP_EXPRESSION_PATTERN = /^\s*([0-9A-Za-z_$]*)\s+of\s+(.*)/;
21469
+ /** Pattern for the tracking expression in a for loop block. */
21470
+ const FOR_LOOP_TRACK_PATTERN = /^track\s+(.*)/;
21471
+ /** Pattern for the `as` expression in a conditional block. */
21472
+ const CONDITIONAL_ALIAS_PATTERN = /^as\s+(.*)/;
21473
+ /** Pattern used to identify an `else if` block. */
21474
+ const ELSE_IF_PATTERN = /^if\s/;
21475
+ /** Pattern used to identify a `let` parameter. */
21476
+ const FOR_LOOP_LET_PATTERN = /^let\s+(.*)/;
21477
+ /** Names of variables that are allowed to be used in the `let` expression of a `for` loop. */
21478
+ const ALLOWED_FOR_LOOP_LET_VARIABLES = new Set(['$index', '$first', '$last', '$even', '$odd', '$count']);
21479
+ /** Creates an `if` loop block from an HTML AST node. */
21480
+ function createIfBlock(ast, visitor, bindingParser) {
21481
+ const errors = validateIfBlock(ast);
21482
+ const branches = [];
21483
+ if (errors.length > 0) {
21484
+ return { node: null, errors };
21485
+ }
21486
+ // Assumes that the structure is valid since we validated it above.
21487
+ for (const block of ast.blocks) {
21488
+ const children = visitAll(visitor, block.children);
21489
+ // `{:else}` block.
21490
+ if (block.name === 'else' && block.parameters.length === 0) {
21491
+ branches.push(new IfBlockBranch(null, children, null, block.sourceSpan, block.startSourceSpan));
21492
+ continue;
21493
+ }
21494
+ const params = parseConditionalBlockParameters(block, errors, bindingParser);
21495
+ if (params !== null) {
21496
+ branches.push(new IfBlockBranch(params.expression, children, params.expressionAlias, block.sourceSpan, block.startSourceSpan));
21497
+ }
21498
+ }
21499
+ return {
21500
+ node: new IfBlock(branches, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan),
21501
+ errors,
21502
+ };
21503
+ }
21504
+ /** Creates a `for` loop block from an HTML AST node. */
21505
+ function createForLoop(ast, visitor, bindingParser) {
21506
+ const [primaryBlock, ...secondaryBlocks] = ast.blocks;
21507
+ const errors = [];
21508
+ const params = parseForLoopParameters(primaryBlock, errors, bindingParser);
21509
+ let node = null;
21510
+ let empty = null;
21511
+ for (const block of secondaryBlocks) {
21512
+ if (block.name === 'empty') {
21513
+ if (empty !== null) {
21514
+ errors.push(new ParseError(block.sourceSpan, 'For loop can only have one "empty" block'));
21515
+ }
21516
+ else if (block.parameters.length > 0) {
21517
+ errors.push(new ParseError(block.sourceSpan, 'Empty block cannot have parameters'));
21518
+ }
21519
+ else {
21520
+ empty = new ForLoopBlockEmpty(visitAll(visitor, block.children), block.sourceSpan, block.startSourceSpan);
21521
+ }
21522
+ }
21523
+ else {
21524
+ errors.push(new ParseError(block.sourceSpan, `Unrecognized loop block "${block.name}"`));
21525
+ }
21526
+ }
21527
+ if (params !== null) {
21528
+ if (params.trackBy === null) {
21529
+ errors.push(new ParseError(ast.sourceSpan, 'For loop must have a "track" expression'));
21530
+ }
21531
+ else {
21532
+ node = new ForLoopBlock(params.itemName, params.expression, params.trackBy, params.context, visitAll(visitor, primaryBlock.children), empty, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan);
21533
+ }
21534
+ }
21535
+ return { node, errors };
21536
+ }
21537
+ /** Creates a switch block from an HTML AST node. */
21538
+ function createSwitchBlock(ast, visitor, bindingParser) {
21539
+ const [primaryBlock, ...secondaryBlocks] = ast.blocks;
21540
+ const errors = validateSwitchBlock(ast);
21541
+ if (errors.length > 0) {
21542
+ return { node: null, errors };
21543
+ }
21544
+ const primaryExpression = parseBlockParameterToBinding(primaryBlock.parameters[0], bindingParser);
21545
+ const cases = [];
21546
+ let defaultCase = null;
21547
+ // Here we assume that all the blocks are valid given that we validated them above.
21548
+ for (const block of secondaryBlocks) {
21549
+ const expression = block.name === 'case' ?
21550
+ parseBlockParameterToBinding(block.parameters[0], bindingParser) :
21551
+ null;
21552
+ const ast = new SwitchBlockCase(expression, visitAll(visitor, block.children), block.sourceSpan, block.startSourceSpan);
21553
+ if (expression === null) {
21554
+ defaultCase = ast;
21555
+ }
21556
+ else {
21557
+ cases.push(ast);
21558
+ }
21559
+ }
21560
+ // Ensure that the default case is last in the array.
21561
+ if (defaultCase !== null) {
21562
+ cases.push(defaultCase);
21563
+ }
21564
+ return {
21565
+ node: new SwitchBlock(primaryExpression, cases, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan),
21566
+ errors
21567
+ };
21568
+ }
21569
+ /** Parses the parameters of a `for` loop block. */
21570
+ function parseForLoopParameters(block, errors, bindingParser) {
21571
+ if (block.parameters.length === 0) {
21572
+ errors.push(new ParseError(block.sourceSpan, 'For loop does not have an expression'));
21573
+ return null;
21574
+ }
21575
+ const [expressionParam, ...secondaryParams] = block.parameters;
21576
+ const match = stripOptionalParentheses(expressionParam, errors)?.match(FOR_LOOP_EXPRESSION_PATTERN);
21577
+ if (!match || match[2].trim().length === 0) {
21578
+ errors.push(new ParseError(expressionParam.sourceSpan, 'Cannot parse expression. For loop expression must match the pattern "<identifier> of <expression>"'));
21579
+ return null;
21580
+ }
21581
+ const [, itemName, rawExpression] = match;
21582
+ const result = {
21583
+ itemName,
21584
+ trackBy: null,
21585
+ expression: parseBlockParameterToBinding(expressionParam, bindingParser, rawExpression),
21586
+ context: null,
21587
+ };
21588
+ for (const param of secondaryParams) {
21589
+ const letMatch = param.expression.match(FOR_LOOP_LET_PATTERN);
21590
+ if (letMatch !== null) {
21591
+ result.context = result.context || {};
21592
+ parseLetParameter(param.sourceSpan, letMatch[1], result.context, errors);
21593
+ continue;
21594
+ }
21595
+ const trackMatch = param.expression.match(FOR_LOOP_TRACK_PATTERN);
21596
+ if (trackMatch !== null) {
21597
+ if (result.trackBy !== null) {
21598
+ errors.push(new ParseError(param.sourceSpan, 'For loop can only have one "track" expression'));
21599
+ }
21600
+ else {
21601
+ result.trackBy = parseBlockParameterToBinding(param, bindingParser, trackMatch[1]);
21602
+ }
21603
+ continue;
21604
+ }
21605
+ errors.push(new ParseError(param.sourceSpan, `Unrecognized loop paramater "${param.expression}"`));
21606
+ }
21607
+ return result;
21608
+ }
21609
+ /** Parses the `let` parameter of a `for` loop block. */
21610
+ function parseLetParameter(sourceSpan, expression, context, errors) {
21611
+ const parts = expression.split(',');
21612
+ for (const part of parts) {
21613
+ const expressionParts = part.split('=');
21614
+ const name = expressionParts.length === 2 ? expressionParts[0].trim() : '';
21615
+ const variableName = expressionParts.length === 2 ? expressionParts[1].trim() : '';
21616
+ if (name.length === 0 || variableName.length === 0) {
21617
+ errors.push(new ParseError(sourceSpan, `Invalid for loop "let" parameter. Parameter should match the pattern "<name> = <variable name>"`));
21618
+ }
21619
+ else if (!ALLOWED_FOR_LOOP_LET_VARIABLES.has(variableName)) {
21620
+ errors.push(new ParseError(sourceSpan, `Unknown "let" parameter variable "${variableName}". The allowed variables are: ${Array.from(ALLOWED_FOR_LOOP_LET_VARIABLES).join(', ')}`));
21621
+ }
21622
+ else if (context.hasOwnProperty(variableName)) {
21623
+ errors.push(new ParseError(sourceSpan, `Duplicate "let" parameter variable "${variableName}"`));
21624
+ }
21625
+ else {
21626
+ context[variableName] = name;
21627
+ }
21628
+ }
21629
+ }
21630
+ /** Checks that the shape of a `if` block is valid. Returns an array of errors. */
21631
+ function validateIfBlock(ast) {
21632
+ const errors = [];
21633
+ let hasElse = false;
21634
+ for (let i = 0; i < ast.blocks.length; i++) {
21635
+ const block = ast.blocks[i];
21636
+ // Conditional blocks only allow `if`, `else if` and `else` blocks.
21637
+ if ((block.name !== 'if' || i > 0) && block.name !== 'else') {
21638
+ errors.push(new ParseError(block.sourceSpan, `Unrecognized conditional block "${block.name}"`));
21639
+ continue;
21640
+ }
21641
+ if (block.name === 'if') {
21642
+ continue;
21643
+ }
21644
+ if (block.parameters.length === 0) {
21645
+ if (hasElse) {
21646
+ errors.push(new ParseError(block.sourceSpan, 'Conditional can only have one "else" block'));
21647
+ }
21648
+ else if (ast.blocks.length > 1 && i < ast.blocks.length - 1) {
21649
+ errors.push(new ParseError(block.sourceSpan, 'Else block must be last inside the conditional'));
21650
+ }
21651
+ hasElse = true;
21652
+ // `else if` is an edge case, because it has a space after the block name
21653
+ // which means that the `if` is captured as a part of the parameters.
21654
+ }
21655
+ else if (block.parameters.length > 0 && !ELSE_IF_PATTERN.test(block.parameters[0].expression)) {
21656
+ errors.push(new ParseError(block.sourceSpan, 'Else block cannot have parameters'));
21657
+ }
21658
+ }
21659
+ return errors;
21660
+ }
21661
+ /** Checks that the shape of a `switch` block is valid. Returns an array of errors. */
21662
+ function validateSwitchBlock(ast) {
21663
+ const [primaryBlock, ...secondaryBlocks] = ast.blocks;
21664
+ const errors = [];
21665
+ let hasDefault = false;
21666
+ if (primaryBlock.children.length > 0) {
21667
+ errors.push(new ParseError(primaryBlock.sourceSpan, 'Switch block can only contain "case" and "default" blocks'));
21668
+ }
21669
+ if (primaryBlock.parameters.length !== 1) {
21670
+ errors.push(new ParseError(primaryBlock.sourceSpan, 'Switch block must have exactly one parameter'));
21671
+ }
21672
+ for (const block of secondaryBlocks) {
21673
+ if (block.name === 'case') {
21674
+ if (block.parameters.length !== 1) {
21675
+ errors.push(new ParseError(block.sourceSpan, 'Case block must have exactly one parameter'));
21676
+ }
21677
+ }
21678
+ else if (block.name === 'default') {
21679
+ if (hasDefault) {
21680
+ errors.push(new ParseError(block.sourceSpan, 'Switch block can only have one "default" block'));
21681
+ }
21682
+ else if (block.parameters.length > 0) {
21683
+ errors.push(new ParseError(block.sourceSpan, 'Default block cannot have parameters'));
21684
+ }
21685
+ hasDefault = true;
21686
+ }
21687
+ else {
21688
+ errors.push(new ParseError(block.sourceSpan, 'Switch block can only contain "case" and "default" blocks'));
21689
+ }
21690
+ }
21691
+ return errors;
21692
+ }
21693
+ function parseBlockParameterToBinding(ast, bindingParser, part = 0) {
21694
+ let start;
21695
+ let end;
21696
+ if (typeof part === 'number') {
21697
+ start = part;
21698
+ end = ast.expression.length;
21699
+ }
21700
+ else {
21701
+ // Note: `lastIndexOf` here should be enough to know the start index of the expression,
21702
+ // because we know that it'll be at the end of the param. Ideally we could use the `d`
21703
+ // flag when matching via regex and get the index from `match.indices`, but it's unclear
21704
+ // if we can use it yet since it's a relatively new feature. See:
21705
+ // https://github.com/tc39/proposal-regexp-match-indices
21706
+ start = Math.max(0, ast.expression.lastIndexOf(part));
21707
+ end = start + part.length;
21708
+ }
21709
+ return bindingParser.parseBinding(ast.expression.slice(start, end), false, ast.sourceSpan, ast.sourceSpan.start.offset + start);
21710
+ }
21711
+ /** Parses the parameter of a conditional block (`if` or `else if`). */
21712
+ function parseConditionalBlockParameters(block, errors, bindingParser) {
21713
+ if (block.parameters.length === 0) {
21714
+ errors.push(new ParseError(block.sourceSpan, 'Conditional block does not have an expression'));
21715
+ return null;
21716
+ }
21717
+ const isPrimaryIfBlock = block.name === 'if';
21718
+ const expression =
21719
+ // Expressions for `{:else if}` blocks start at 2 to skip the `if` from the expression.
21720
+ parseBlockParameterToBinding(block.parameters[0], bindingParser, isPrimaryIfBlock ? 0 : 2);
21721
+ let expressionAlias = null;
21722
+ // Start from 1 since we processed the first parameter already.
21723
+ for (let i = 1; i < block.parameters.length; i++) {
21724
+ const param = block.parameters[i];
21725
+ const aliasMatch = param.expression.match(CONDITIONAL_ALIAS_PATTERN);
21726
+ // For now conditionals can only have an `as` parameter.
21727
+ // We may want to rework this later if we add more.
21728
+ if (aliasMatch === null) {
21729
+ errors.push(new ParseError(param.sourceSpan, `Unrecognized conditional paramater "${param.expression}"`));
21730
+ }
21731
+ else if (!isPrimaryIfBlock) {
21732
+ errors.push(new ParseError(param.sourceSpan, '"as" expression is only allowed on the primary "if" block'));
21733
+ }
21734
+ else if (expressionAlias !== null) {
21735
+ errors.push(new ParseError(param.sourceSpan, 'Conditional can only have one "as" expression'));
21736
+ }
21737
+ else {
21738
+ expressionAlias = aliasMatch[1].trim();
21739
+ }
21740
+ }
21741
+ return { expression, expressionAlias };
21742
+ }
21743
+ /** Strips optional parentheses around from a control from expression parameter. */
21744
+ function stripOptionalParentheses(param, errors) {
21745
+ const expression = param.expression;
21746
+ const spaceRegex = /^\s$/;
21747
+ let openParens = 0;
21748
+ let start = 0;
21749
+ let end = expression.length - 1;
21750
+ for (let i = 0; i < expression.length; i++) {
21751
+ const char = expression[i];
21752
+ if (char === '(') {
21753
+ start = i + 1;
21754
+ openParens++;
21755
+ }
21756
+ else if (spaceRegex.test(char)) {
21757
+ continue;
21758
+ }
21759
+ else {
21760
+ break;
21761
+ }
21762
+ }
21763
+ if (openParens === 0) {
21764
+ return expression;
21765
+ }
21766
+ for (let i = expression.length - 1; i > -1; i--) {
21767
+ const char = expression[i];
21768
+ if (char === ')') {
21769
+ end = i;
21770
+ openParens--;
21771
+ if (openParens === 0) {
21772
+ break;
21773
+ }
21774
+ }
21775
+ else if (spaceRegex.test(char)) {
21776
+ continue;
21777
+ }
21778
+ else {
21779
+ break;
21780
+ }
21781
+ }
21782
+ if (openParens !== 0) {
21783
+ errors.push(new ParseError(param.sourceSpan, 'Unclosed parentheses in expression'));
21784
+ return null;
21785
+ }
21786
+ return expression.slice(start, end);
21787
+ }
21788
+
21304
21789
  /** Pattern for a timing value in a trigger. */
21305
21790
  const TIME_PATTERN = /^\d+(ms|s)?$/;
21306
21791
  /** Pattern for a separator between keywords in a trigger expression. */
@@ -21322,38 +21807,41 @@ var OnTriggerType;
21322
21807
  OnTriggerType["VIEWPORT"] = "viewport";
21323
21808
  })(OnTriggerType || (OnTriggerType = {}));
21324
21809
  /** Parses a `when` deferred trigger. */
21325
- function parseWhenTrigger({ expression, sourceSpan }, bindingParser, errors) {
21810
+ function parseWhenTrigger({ expression, sourceSpan }, bindingParser, triggers, errors) {
21326
21811
  const whenIndex = expression.indexOf('when');
21327
21812
  // This is here just to be safe, we shouldn't enter this function
21328
21813
  // in the first place if a block doesn't have the "when" keyword.
21329
21814
  if (whenIndex === -1) {
21330
21815
  errors.push(new ParseError(sourceSpan, `Could not find "when" keyword in expression`));
21331
- return null;
21332
21816
  }
21333
- const start = getTriggerParametersStart(expression, whenIndex + 1);
21334
- const parsed = bindingParser.parseBinding(expression.slice(start), false, sourceSpan, sourceSpan.start.offset + start);
21335
- return new BoundDeferredTrigger(parsed, sourceSpan);
21817
+ else {
21818
+ const start = getTriggerParametersStart(expression, whenIndex + 1);
21819
+ const parsed = bindingParser.parseBinding(expression.slice(start), false, sourceSpan, sourceSpan.start.offset + start);
21820
+ trackTrigger('when', triggers, errors, new BoundDeferredTrigger(parsed, sourceSpan));
21821
+ }
21336
21822
  }
21337
21823
  /** Parses an `on` trigger */
21338
- function parseOnTrigger({ expression, sourceSpan }, errors) {
21824
+ function parseOnTrigger({ expression, sourceSpan }, triggers, errors) {
21339
21825
  const onIndex = expression.indexOf('on');
21340
21826
  // This is here just to be safe, we shouldn't enter this function
21341
21827
  // in the first place if a block doesn't have the "on" keyword.
21342
21828
  if (onIndex === -1) {
21343
21829
  errors.push(new ParseError(sourceSpan, `Could not find "on" keyword in expression`));
21344
- return [];
21345
21830
  }
21346
- const start = getTriggerParametersStart(expression, onIndex + 1);
21347
- return new OnTriggerParser(expression, start, sourceSpan, errors).parse();
21831
+ else {
21832
+ const start = getTriggerParametersStart(expression, onIndex + 1);
21833
+ const parser = new OnTriggerParser(expression, start, sourceSpan, triggers, errors);
21834
+ parser.parse();
21835
+ }
21348
21836
  }
21349
21837
  class OnTriggerParser {
21350
- constructor(expression, start, span, errors) {
21838
+ constructor(expression, start, span, triggers, errors) {
21351
21839
  this.expression = expression;
21352
21840
  this.start = start;
21353
21841
  this.span = span;
21842
+ this.triggers = triggers;
21354
21843
  this.errors = errors;
21355
21844
  this.index = 0;
21356
- this.triggers = [];
21357
21845
  this.tokens = new Lexer().tokenize(expression.slice(start));
21358
21846
  }
21359
21847
  parse() {
@@ -21384,7 +21872,6 @@ class OnTriggerParser {
21384
21872
  }
21385
21873
  this.advance();
21386
21874
  }
21387
- return this.triggers;
21388
21875
  }
21389
21876
  advance() {
21390
21877
  this.index++;
@@ -21405,22 +21892,22 @@ class OnTriggerParser {
21405
21892
  try {
21406
21893
  switch (identifier.toString()) {
21407
21894
  case OnTriggerType.IDLE:
21408
- this.triggers.push(createIdleTrigger(parameters, sourceSpan));
21895
+ this.trackTrigger('idle', createIdleTrigger(parameters, sourceSpan));
21409
21896
  break;
21410
21897
  case OnTriggerType.TIMER:
21411
- this.triggers.push(createTimerTrigger(parameters, sourceSpan));
21898
+ this.trackTrigger('timer', createTimerTrigger(parameters, sourceSpan));
21412
21899
  break;
21413
21900
  case OnTriggerType.INTERACTION:
21414
- this.triggers.push(createInteractionTrigger(parameters, sourceSpan));
21901
+ this.trackTrigger('interaction', createInteractionTrigger(parameters, sourceSpan));
21415
21902
  break;
21416
21903
  case OnTriggerType.IMMEDIATE:
21417
- this.triggers.push(createImmediateTrigger(parameters, sourceSpan));
21904
+ this.trackTrigger('immediate', createImmediateTrigger(parameters, sourceSpan));
21418
21905
  break;
21419
21906
  case OnTriggerType.HOVER:
21420
- this.triggers.push(createHoverTrigger(parameters, sourceSpan));
21907
+ this.trackTrigger('hover', createHoverTrigger(parameters, sourceSpan));
21421
21908
  break;
21422
21909
  case OnTriggerType.VIEWPORT:
21423
- this.triggers.push(createViewportTrigger(parameters, sourceSpan));
21910
+ this.trackTrigger('viewport', createViewportTrigger(parameters, sourceSpan));
21424
21911
  break;
21425
21912
  default:
21426
21913
  throw new Error(`Unrecognized trigger type "${identifier}"`);
@@ -21489,6 +21976,9 @@ class OnTriggerParser {
21489
21976
  // Eventually we could expose this information on the token directly.
21490
21977
  return this.expression.slice(this.start + this.token().index, this.start + this.token().end);
21491
21978
  }
21979
+ trackTrigger(name, trigger) {
21980
+ trackTrigger(name, this.triggers, this.errors, trigger);
21981
+ }
21492
21982
  error(token, message) {
21493
21983
  const newStart = this.span.start.moveBy(this.start + token.index);
21494
21984
  const newEnd = newStart.moveBy(token.end - token.index);
@@ -21498,6 +21988,15 @@ class OnTriggerParser {
21498
21988
  this.error(token, `Unexpected token "${token}"`);
21499
21989
  }
21500
21990
  }
21991
+ /** Adds a trigger to a map of triggers. */
21992
+ function trackTrigger(name, allTriggers, errors, trigger) {
21993
+ if (allTriggers[name]) {
21994
+ errors.push(new ParseError(trigger.sourceSpan, `Duplicate "${name}" trigger is not allowed`));
21995
+ }
21996
+ else {
21997
+ allTriggers[name] = trigger;
21998
+ }
21999
+ }
21501
22000
  function createIdleTrigger(parameters, sourceSpan) {
21502
22001
  if (parameters.length > 0) {
21503
22002
  throw new Error(`"${OnTriggerType.IDLE}" trigger cannot have parameters`);
@@ -21641,6 +22140,9 @@ function parsePlaceholderBlock(ast, visitor) {
21641
22140
  let minimumTime = null;
21642
22141
  for (const param of ast.parameters) {
21643
22142
  if (MINIMUM_PARAMETER_PATTERN.test(param.expression)) {
22143
+ if (minimumTime != null) {
22144
+ throw new Error(`Placeholder block can only have one "minimum" parameter`);
22145
+ }
21644
22146
  const parsedTime = parseDeferredTime(param.expression.slice(getTriggerParametersStart(param.expression)));
21645
22147
  if (parsedTime === null) {
21646
22148
  throw new Error(`Could not parse time value of parameter "minimum"`);
@@ -21658,6 +22160,9 @@ function parseLoadingBlock(ast, visitor) {
21658
22160
  let minimumTime = null;
21659
22161
  for (const param of ast.parameters) {
21660
22162
  if (AFTER_PARAMETER_PATTERN.test(param.expression)) {
22163
+ if (afterTime != null) {
22164
+ throw new Error(`Loading block can only have one "after" parameter`);
22165
+ }
21661
22166
  const parsedTime = parseDeferredTime(param.expression.slice(getTriggerParametersStart(param.expression)));
21662
22167
  if (parsedTime === null) {
21663
22168
  throw new Error(`Could not parse time value of parameter "after"`);
@@ -21665,6 +22170,9 @@ function parseLoadingBlock(ast, visitor) {
21665
22170
  afterTime = parsedTime;
21666
22171
  }
21667
22172
  else if (MINIMUM_PARAMETER_PATTERN.test(param.expression)) {
22173
+ if (minimumTime != null) {
22174
+ throw new Error(`Loading block can only have one "minimum" parameter`);
22175
+ }
21668
22176
  const parsedTime = parseDeferredTime(param.expression.slice(getTriggerParametersStart(param.expression)));
21669
22177
  if (parsedTime === null) {
21670
22178
  throw new Error(`Could not parse time value of parameter "minimum"`);
@@ -21684,24 +22192,22 @@ function parseErrorBlock(ast, visitor) {
21684
22192
  return new DeferredBlockError(visitAll(visitor, ast.children), ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan);
21685
22193
  }
21686
22194
  function parsePrimaryTriggers(params, bindingParser, errors) {
21687
- const triggers = [];
21688
- const prefetchTriggers = [];
22195
+ const triggers = {};
22196
+ const prefetchTriggers = {};
21689
22197
  for (const param of params) {
21690
22198
  // The lexer ignores the leading spaces so we can assume
21691
22199
  // that the expression starts with a keyword.
21692
22200
  if (WHEN_PARAMETER_PATTERN.test(param.expression)) {
21693
- const result = parseWhenTrigger(param, bindingParser, errors);
21694
- result !== null && triggers.push(result);
22201
+ parseWhenTrigger(param, bindingParser, triggers, errors);
21695
22202
  }
21696
22203
  else if (ON_PARAMETER_PATTERN.test(param.expression)) {
21697
- triggers.push(...parseOnTrigger(param, errors));
22204
+ parseOnTrigger(param, triggers, errors);
21698
22205
  }
21699
22206
  else if (PREFETCH_WHEN_PATTERN.test(param.expression)) {
21700
- const result = parseWhenTrigger(param, bindingParser, errors);
21701
- result !== null && prefetchTriggers.push(result);
22207
+ parseWhenTrigger(param, bindingParser, prefetchTriggers, errors);
21702
22208
  }
21703
22209
  else if (PREFETCH_ON_PATTERN.test(param.expression)) {
21704
- prefetchTriggers.push(...parseOnTrigger(param, errors));
22210
+ parseOnTrigger(param, prefetchTriggers, errors);
21705
22211
  }
21706
22212
  else {
21707
22213
  errors.push(new ParseError(param.sourceSpan, 'Unrecognized trigger'));
@@ -21947,13 +22453,33 @@ class HtmlAstToIvyAst {
21947
22453
  this.reportError('Block group must have at least one block.', group.sourceSpan);
21948
22454
  return null;
21949
22455
  }
21950
- if (primaryBlock.name === 'defer' && this.options.enabledBlockTypes.has(primaryBlock.name)) {
21951
- const { node, errors } = createDeferredBlock(group, this, this.bindingParser);
21952
- this.errors.push(...errors);
21953
- return node;
22456
+ if (!this.options.enabledBlockTypes.has(primaryBlock.name)) {
22457
+ this.reportError(`Unrecognized block "${primaryBlock.name}".`, primaryBlock.sourceSpan);
22458
+ return null;
21954
22459
  }
21955
- this.reportError(`Unrecognized block "${primaryBlock.name}".`, primaryBlock.sourceSpan);
21956
- return null;
22460
+ let result = null;
22461
+ switch (primaryBlock.name) {
22462
+ case 'defer':
22463
+ result = createDeferredBlock(group, this, this.bindingParser);
22464
+ break;
22465
+ case 'switch':
22466
+ result = createSwitchBlock(group, this, this.bindingParser);
22467
+ break;
22468
+ case 'for':
22469
+ result = createForLoop(group, this, this.bindingParser);
22470
+ break;
22471
+ case 'if':
22472
+ result = createIfBlock(group, this, this.bindingParser);
22473
+ break;
22474
+ default:
22475
+ result = {
22476
+ node: null,
22477
+ errors: [new ParseError(primaryBlock.sourceSpan, `Unrecognized block "${primaryBlock.name}".`)]
22478
+ };
22479
+ break;
22480
+ }
22481
+ this.errors.push(...result.errors);
22482
+ return result.node;
21957
22483
  }
21958
22484
  visitBlock(block, context) { }
21959
22485
  visitBlockParameter(parameter, context) { }
@@ -23279,6 +23805,11 @@ class TemplateDefinitionBuilder {
23279
23805
  this._currentIndex = 0;
23280
23806
  /** Temporary variable declarations generated from visiting pipes, literals, etc. */
23281
23807
  this._tempVariables = [];
23808
+ /**
23809
+ * Temporary variable used to store state between control flow instructions.
23810
+ * Should be accessed via the `allocateControlFlowTempVariable` method.
23811
+ */
23812
+ this._controlFlowTempVariable = null;
23282
23813
  /**
23283
23814
  * List of callbacks to build nested templates. Nested templates must not be visited until
23284
23815
  * after the parent template has finished visiting all of its nodes. This ensures that all
@@ -23308,6 +23839,12 @@ class TemplateDefinitionBuilder {
23308
23839
  this.visitTextAttribute = invalid;
23309
23840
  this.visitBoundAttribute = invalid;
23310
23841
  this.visitBoundEvent = invalid;
23842
+ this.visitDeferredTrigger = invalid;
23843
+ this.visitDeferredBlockError = invalid;
23844
+ this.visitDeferredBlockLoading = invalid;
23845
+ this.visitDeferredBlockPlaceholder = invalid;
23846
+ this.visitIfBlockBranch = invalid;
23847
+ this.visitSwitchBlockCase = invalid;
23311
23848
  this._bindingScope = parentBindingScope.nestedScope(level);
23312
23849
  // Turn the relative context file path into an identifier by replacing non-alphanumeric
23313
23850
  // characters with underscores.
@@ -23420,8 +23957,16 @@ class TemplateDefinitionBuilder {
23420
23957
  registerContextVariables(variable$1) {
23421
23958
  const scopedName = this._bindingScope.freshReferenceName();
23422
23959
  const retrievalLevel = this.level;
23960
+ const isDirect = variable$1.value === DIRECT_CONTEXT_REFERENCE;
23423
23961
  const lhs = variable(variable$1.name + scopedName);
23424
- this._bindingScope.set(retrievalLevel, variable$1.name, lhs, 1 /* DeclarationPriority.CONTEXT */, (scope, relativeLevel) => {
23962
+ this._bindingScope.set(retrievalLevel, variable$1.name, scope => {
23963
+ // If we're at the top level and we're referring to the context variable directly, we
23964
+ // can do so through the implicit receiver, instead of renaming it. Note that this does
23965
+ // not apply to listeners, because they need to restore the context.
23966
+ return isDirect && scope.bindingLevel === retrievalLevel && !scope.isListenerScope() ?
23967
+ variable(CONTEXT_NAME) :
23968
+ lhs;
23969
+ }, 1 /* DeclarationPriority.CONTEXT */, (scope, relativeLevel) => {
23425
23970
  let rhs;
23426
23971
  if (scope.bindingLevel === retrievalLevel) {
23427
23972
  if (scope.isListenerScope() && scope.hasRestoreViewVariable()) {
@@ -23432,6 +23977,11 @@ class TemplateDefinitionBuilder {
23432
23977
  rhs = variable(RESTORED_VIEW_CONTEXT_NAME);
23433
23978
  scope.notifyRestoredViewContextUse();
23434
23979
  }
23980
+ else if (isDirect) {
23981
+ // If we have a direct read of the context at the top level we don't need to
23982
+ // declare any variables and we can refer to it directly.
23983
+ return [];
23984
+ }
23435
23985
  else {
23436
23986
  // e.g. ctx
23437
23987
  rhs = variable(CONTEXT_NAME);
@@ -23442,8 +23992,11 @@ class TemplateDefinitionBuilder {
23442
23992
  // e.g. ctx_r0 OR x(2);
23443
23993
  rhs = sharedCtxVar ? sharedCtxVar : generateNextContextExpr(relativeLevel);
23444
23994
  }
23445
- // e.g. const $item$ = x(2).$implicit;
23446
- return [lhs.set(rhs.prop(variable$1.value || IMPLICIT_REFERENCE)).toConstDecl()];
23995
+ return [
23996
+ // e.g. const $items$ = x(2) for direct context references and
23997
+ // const $item$ = x(2).$implicit for indirect ones.
23998
+ lhs.set(isDirect ? rhs : rhs.prop(variable$1.value || IMPLICIT_REFERENCE)).toConstDecl()
23999
+ ];
23447
24000
  });
23448
24001
  }
23449
24002
  i18nAppendBindings(expressions) {
@@ -23864,28 +24417,22 @@ class TemplateDefinitionBuilder {
23864
24417
  this.creationInstruction(span, isNgContainer$1 ? Identifiers.elementContainerEnd : Identifiers.elementEnd);
23865
24418
  }
23866
24419
  }
23867
- visitTemplate(template) {
23868
- const NG_TEMPLATE_TAG_NAME = 'ng-template';
24420
+ createEmbeddedTemplateFn(tagName, children, contextNameSuffix, sourceSpan, variables = [], attrsExprs, references, i18n) {
23869
24421
  const templateIndex = this.allocateDataSlot();
23870
- if (this.i18n) {
23871
- this.i18n.appendTemplate(template.i18n, templateIndex);
24422
+ if (this.i18n && i18n) {
24423
+ this.i18n.appendTemplate(i18n, templateIndex);
23872
24424
  }
23873
- const tagNameWithoutNamespace = template.tagName ? splitNsName(template.tagName)[1] : template.tagName;
23874
- const contextName = `${this.contextName}${template.tagName ? '_' + sanitizeIdentifier(template.tagName) : ''}_${templateIndex}`;
24425
+ const contextName = `${this.contextName}${contextNameSuffix}_${templateIndex}`;
23875
24426
  const templateName = `${contextName}_Template`;
23876
24427
  const parameters = [
23877
24428
  literal(templateIndex),
23878
24429
  variable(templateName),
23879
- // We don't care about the tag's namespace here, because we infer
23880
- // it based on the parent nodes inside the template instruction.
23881
- literal(tagNameWithoutNamespace),
24430
+ literal(tagName),
24431
+ this.addAttrsToConsts(attrsExprs || null),
23882
24432
  ];
23883
- // prepare attributes parameter (including attributes used for directive matching)
23884
- const attrsExprs = this.getAttributeExpressions(NG_TEMPLATE_TAG_NAME, template.attributes, template.inputs, template.outputs, undefined /* styles */, template.templateAttrs);
23885
- parameters.push(this.addAttrsToConsts(attrsExprs));
23886
24433
  // local refs (ex.: <ng-template #foo>)
23887
- if (template.references && template.references.length) {
23888
- const refs = this.prepareRefsArray(template.references);
24434
+ if (references && references.length > 0) {
24435
+ const refs = this.prepareRefsArray(references);
23889
24436
  parameters.push(this.addToConsts(refs));
23890
24437
  parameters.push(importExpr(Identifiers.templateRefExtractor));
23891
24438
  }
@@ -23896,17 +24443,28 @@ class TemplateDefinitionBuilder {
23896
24443
  // be able to support bindings in nested templates to local refs that occur after the
23897
24444
  // template definition. e.g. <div *ngIf="showing">{{ foo }}</div> <div #foo></div>
23898
24445
  this._nestedTemplateFns.push(() => {
23899
- const templateFunctionExpr = templateVisitor.buildTemplateFunction(template.children, template.variables, this._ngContentReservedSlots.length + this._ngContentSelectorsOffset, template.i18n);
24446
+ const templateFunctionExpr = templateVisitor.buildTemplateFunction(children, variables, this._ngContentReservedSlots.length + this._ngContentSelectorsOffset, i18n);
23900
24447
  this.constantPool.statements.push(templateFunctionExpr.toDeclStmt(templateName));
23901
24448
  if (templateVisitor._ngContentReservedSlots.length) {
23902
24449
  this._ngContentReservedSlots.push(...templateVisitor._ngContentReservedSlots);
23903
24450
  }
23904
24451
  });
23905
24452
  // e.g. template(1, MyComp_Template_1)
23906
- this.creationInstruction(template.sourceSpan, Identifiers.templateCreate, () => {
24453
+ this.creationInstruction(sourceSpan, Identifiers.templateCreate, () => {
23907
24454
  parameters.splice(2, 0, literal(templateVisitor.getConstCount()), literal(templateVisitor.getVarCount()));
23908
24455
  return trimTrailingNulls(parameters);
23909
24456
  });
24457
+ return templateIndex;
24458
+ }
24459
+ visitTemplate(template) {
24460
+ // We don't care about the tag's namespace here, because we infer
24461
+ // it based on the parent nodes inside the template instruction.
24462
+ const tagNameWithoutNamespace = template.tagName ? splitNsName(template.tagName)[1] : template.tagName;
24463
+ const contextNameSuffix = template.tagName ? '_' + sanitizeIdentifier(template.tagName) : '';
24464
+ const NG_TEMPLATE_TAG_NAME = 'ng-template';
24465
+ // prepare attributes parameter (including attributes used for directive matching)
24466
+ const attrsExprs = this.getAttributeExpressions(NG_TEMPLATE_TAG_NAME, template.attributes, template.inputs, template.outputs, undefined /* styles */, template.templateAttrs);
24467
+ const templateIndex = this.createEmbeddedTemplateFn(tagNameWithoutNamespace, template.children, contextNameSuffix, template.sourceSpan, template.variables, attrsExprs, template.references, template.i18n);
23910
24468
  // handle property bindings e.g. ɵɵproperty('ngForOf', ctx.items), et al;
23911
24469
  this.templatePropertyBindings(templateIndex, template.templateAttrs);
23912
24470
  // Only add normal input/output binding instructions on explicit <ng-template> elements.
@@ -24000,50 +24558,224 @@ class TemplateDefinitionBuilder {
24000
24558
  }
24001
24559
  return null;
24002
24560
  }
24003
- visitDeferredBlock(deferred) {
24004
- const templateIndex = this.allocateDataSlot();
24005
- const deferredDeps = this.deferBlocks.get(deferred);
24006
- const contextName = `${this.contextName}_Defer_${templateIndex}`;
24007
- const depsFnName = `${contextName}_DepsFn`;
24008
- const parameters = [
24009
- literal(templateIndex),
24010
- deferredDeps ? variable(depsFnName) : TYPED_NULL_EXPR,
24011
- ];
24012
- if (deferredDeps) {
24013
- // This defer block has deps for which we need to generate dynamic imports.
24014
- const dependencyExp = [];
24015
- for (const deferredDep of deferredDeps) {
24016
- if (deferredDep.isDeferrable) {
24017
- // Callback function, e.g. `function(m) { return m.MyCmp; }`.
24018
- const innerFn = fn([new FnParam('m', DYNAMIC_TYPE)], [new ReturnStatement(variable('m').prop(deferredDep.symbolName))]);
24019
- const fileName = deferredDep.importPath;
24020
- // Dynamic import, e.g. `import('./a').then(...)`.
24021
- const importExpr = (new DynamicImportExpr(fileName)).prop('then').callFn([innerFn]);
24022
- dependencyExp.push(importExpr);
24561
+ visitIfBlock(block) {
24562
+ // We have to process the block in two steps: once here and again in the update instruction
24563
+ // callback in order to generate the correct expressions when pipes or pure functions are
24564
+ // used inside the branch expressions.
24565
+ const branchData = block.branches.map(({ expression, expressionAlias, children, sourceSpan }) => {
24566
+ let processedExpression = null;
24567
+ if (expression !== null) {
24568
+ processedExpression = expression.visit(this._valueConverter);
24569
+ this.allocateBindingSlots(processedExpression);
24570
+ }
24571
+ // If the branch has an alias, it'll be assigned directly to the container's context.
24572
+ // We define a variable referring directly to the context so that any nested usages can be
24573
+ // rewritten to refer to it.
24574
+ const variables = expressionAlias ?
24575
+ [new Variable(expressionAlias, DIRECT_CONTEXT_REFERENCE, sourceSpan, sourceSpan)] :
24576
+ undefined;
24577
+ return {
24578
+ index: this.createEmbeddedTemplateFn(null, children, '_Conditional', sourceSpan, variables),
24579
+ expression: processedExpression,
24580
+ alias: expressionAlias
24581
+ };
24582
+ });
24583
+ // Use the index of the first block as the index for the entire container.
24584
+ const containerIndex = branchData[0].index;
24585
+ const paramsCallback = () => {
24586
+ let contextVariable = null;
24587
+ const generateBranch = (branchIndex) => {
24588
+ // If we've gone beyond the last branch, return the special -1 value which means that no
24589
+ // view will be rendered. Note that we don't need to reset the context here, because -1
24590
+ // won't render a view so the passed-in context won't be captured.
24591
+ if (branchIndex > branchData.length - 1) {
24592
+ return literal(-1);
24593
+ }
24594
+ const { index, expression, alias } = branchData[branchIndex];
24595
+ // If the branch has no expression, it means that it's the final `else`.
24596
+ // Return its index and stop the recursion. Assumes that there's only one
24597
+ // `else` condition and that it's the last branch.
24598
+ if (expression === null) {
24599
+ return literal(index);
24600
+ }
24601
+ let comparisonTarget;
24602
+ if (alias) {
24603
+ // If the branch is aliased, we need to assign the expression value to the temporary
24604
+ // variable and then pass it into `conditional`. E.g. for the expression:
24605
+ // `{#if foo(); as alias}...{/if}` we have to generate:
24606
+ // ```
24607
+ // let temp;
24608
+ // conditional(0, (temp = ctx.foo()) ? 0 : -1, temp);
24609
+ // ```
24610
+ contextVariable = this.allocateControlFlowTempVariable();
24611
+ comparisonTarget = contextVariable.set(this.convertPropertyBinding(expression));
24023
24612
  }
24024
24613
  else {
24025
- // Non-deferrable symbol, just use a reference to the type.
24026
- dependencyExp.push(deferredDep.type);
24614
+ comparisonTarget = this.convertPropertyBinding(expression);
24027
24615
  }
24616
+ return comparisonTarget.conditional(literal(index), generateBranch(branchIndex + 1));
24617
+ };
24618
+ const params = [literal(containerIndex), generateBranch(0)];
24619
+ if (contextVariable !== null) {
24620
+ params.push(contextVariable);
24028
24621
  }
24029
- const depsFnBody = [];
24030
- depsFnBody.push(new ReturnStatement(literalArr(dependencyExp)));
24031
- const depsFnExpr = fn([] /* args */, depsFnBody, INFERRED_TYPE, null, depsFnName);
24032
- this.constantPool.statements.push(depsFnExpr.toDeclStmt(depsFnName));
24033
- }
24034
- // e.g. `defer(1, MyComp_Defer_1_DepsFn, ...)`
24035
- this.creationInstruction(deferred.sourceSpan, Identifiers.defer, () => {
24036
- return trimTrailingNulls(parameters);
24622
+ return params;
24623
+ };
24624
+ this.updateInstructionWithAdvance(containerIndex, block.branches[0].sourceSpan, Identifiers.conditional, paramsCallback);
24625
+ }
24626
+ visitSwitchBlock(block) {
24627
+ // Allocate slots for the primary block expression.
24628
+ const blockExpression = block.expression.visit(this._valueConverter);
24629
+ this.allocateBindingSlots(blockExpression);
24630
+ // We have to process the block in two steps: once here and again in the update instruction
24631
+ // callback in order to generate the correct expressions when pipes or pure functions are used.
24632
+ const caseData = block.cases.map(currentCase => {
24633
+ const index = this.createEmbeddedTemplateFn(null, currentCase.children, '_Case', currentCase.sourceSpan);
24634
+ let expression = null;
24635
+ if (currentCase.expression !== null) {
24636
+ expression = currentCase.expression.visit(this._valueConverter);
24637
+ this.allocateBindingSlots(expression);
24638
+ }
24639
+ return { index, expression };
24640
+ });
24641
+ // Use the index of the first block as the index for the entire container.
24642
+ const containerIndex = caseData[0].index;
24643
+ this.updateInstructionWithAdvance(containerIndex, block.sourceSpan, Identifiers.conditional, () => {
24644
+ const generateCases = (caseIndex) => {
24645
+ // If we've gone beyond the last branch, return the special -1
24646
+ // value which means that no view will be rendered.
24647
+ if (caseIndex > caseData.length - 1) {
24648
+ return literal(-1);
24649
+ }
24650
+ const { index, expression } = caseData[caseIndex];
24651
+ // If the case has no expression, it means that it's the `default` case.
24652
+ // Return its index and stop the recursion. Assumes that there's only one
24653
+ // `default` condition and that it's defined last.
24654
+ if (expression === null) {
24655
+ return literal(index);
24656
+ }
24657
+ // If this is the very first comparison, we need to assign the value of the primary
24658
+ // expression as a part of the comparison so the remaining cases can reuse it. In practice
24659
+ // this looks as follows:
24660
+ // ```
24661
+ // let temp;
24662
+ // conditional(1, (temp = ctx.foo) === 1 ? 1 : temp === 2 ? 2 : temp === 3 ? 3 : 4);
24663
+ // ```
24664
+ const comparisonTarget = caseIndex === 0 ?
24665
+ this.allocateControlFlowTempVariable().set(this.convertPropertyBinding(blockExpression)) :
24666
+ this.allocateControlFlowTempVariable();
24667
+ return comparisonTarget.identical(this.convertPropertyBinding(expression))
24668
+ .conditional(literal(index), generateCases(caseIndex + 1));
24669
+ };
24670
+ return [literal(containerIndex), generateCases(0)];
24037
24671
  });
24038
24672
  }
24039
- // TODO: implement nested deferred block instructions.
24040
- visitDeferredTrigger(trigger) { }
24041
- visitDeferredBlockPlaceholder(block) { }
24042
- visitDeferredBlockError(block) { }
24043
- visitDeferredBlockLoading(block) { }
24673
+ visitDeferredBlock(deferred) {
24674
+ const { loading, placeholder, error, triggers, prefetchTriggers } = deferred;
24675
+ const primaryTemplateIndex = this.createEmbeddedTemplateFn(null, deferred.children, '_Defer', deferred.sourceSpan);
24676
+ const loadingIndex = loading ?
24677
+ this.createEmbeddedTemplateFn(null, loading.children, '_DeferLoading', loading.sourceSpan) :
24678
+ null;
24679
+ const loadingConsts = loading ?
24680
+ trimTrailingNulls([literal(loading.minimumTime), literal(loading.afterTime)]) :
24681
+ null;
24682
+ const placeholderIndex = placeholder ?
24683
+ this.createEmbeddedTemplateFn(null, placeholder.children, '_DeferPlaceholder', placeholder.sourceSpan) :
24684
+ null;
24685
+ const placeholderConsts = placeholder && placeholder.minimumTime !== null ?
24686
+ // TODO(crisbeto): potentially pass the time directly instead of storing it in the `consts`
24687
+ // since `{:placeholder}` can only have one parameter?
24688
+ literalArr([literal(placeholder.minimumTime)]) :
24689
+ null;
24690
+ const errorIndex = error ?
24691
+ this.createEmbeddedTemplateFn(null, error.children, '_DeferError', error.sourceSpan) :
24692
+ null;
24693
+ // Note: we generate this last so the index matches the instruction order.
24694
+ const deferredIndex = this.allocateDataSlot();
24695
+ const depsFnName = `${this.contextName}_Defer_${deferredIndex}_DepsFn`;
24696
+ // e.g. `defer(1, 0, MyComp_Defer_1_DepsFn, ...)`
24697
+ this.creationInstruction(deferred.sourceSpan, Identifiers.defer, trimTrailingNulls([
24698
+ literal(deferredIndex),
24699
+ literal(primaryTemplateIndex),
24700
+ this.createDeferredDepsFunction(depsFnName, deferred),
24701
+ literal(loadingIndex),
24702
+ literal(placeholderIndex),
24703
+ literal(errorIndex),
24704
+ loadingConsts?.length ? this.addToConsts(literalArr(loadingConsts)) : TYPED_NULL_EXPR,
24705
+ placeholderConsts ? this.addToConsts(placeholderConsts) : TYPED_NULL_EXPR,
24706
+ ]));
24707
+ this.createDeferTriggerInstructions(deferredIndex, triggers, false);
24708
+ this.createDeferTriggerInstructions(deferredIndex, prefetchTriggers, true);
24709
+ }
24710
+ createDeferredDepsFunction(name, deferred) {
24711
+ const deferredDeps = this.deferBlocks.get(deferred);
24712
+ if (!deferredDeps || deferredDeps.length === 0) {
24713
+ return TYPED_NULL_EXPR;
24714
+ }
24715
+ // This defer block has deps for which we need to generate dynamic imports.
24716
+ const dependencyExp = [];
24717
+ for (const deferredDep of deferredDeps) {
24718
+ if (deferredDep.isDeferrable) {
24719
+ // Callback function, e.g. `function(m) { return m.MyCmp; }`.
24720
+ const innerFn = fn([new FnParam('m', DYNAMIC_TYPE)], [new ReturnStatement(variable('m').prop(deferredDep.symbolName))]);
24721
+ // Dynamic import, e.g. `import('./a').then(...)`.
24722
+ const importExpr = (new DynamicImportExpr(deferredDep.importPath)).prop('then').callFn([innerFn]);
24723
+ dependencyExp.push(importExpr);
24724
+ }
24725
+ else {
24726
+ // Non-deferrable symbol, just use a reference to the type.
24727
+ dependencyExp.push(deferredDep.type);
24728
+ }
24729
+ }
24730
+ const depsFnExpr = fn([], [new ReturnStatement(literalArr(dependencyExp))], INFERRED_TYPE, null, name);
24731
+ this.constantPool.statements.push(depsFnExpr.toDeclStmt(name));
24732
+ return variable(name);
24733
+ }
24734
+ createDeferTriggerInstructions(deferredIndex, triggers, prefetch) {
24735
+ const { when, idle, immediate, timer, hover, interaction, viewport } = triggers;
24736
+ // `deferWhen(ctx.someValue)`
24737
+ if (when) {
24738
+ const value = when.value.visit(this._valueConverter);
24739
+ this.allocateBindingSlots(value);
24740
+ this.updateInstructionWithAdvance(deferredIndex, when.sourceSpan, prefetch ? Identifiers.deferPrefetchWhen : Identifiers.deferWhen, () => this.convertPropertyBinding(value));
24741
+ }
24742
+ // Note that we generate an implicit `on idle` if the `deferred` block has no triggers.
24743
+ // TODO(crisbeto): decide if this should be baked into the `defer` instruction.
24744
+ // `deferOnIdle()`
24745
+ if (idle || (!prefetch && Object.keys(triggers).length === 0)) {
24746
+ this.creationInstruction(idle?.sourceSpan || null, prefetch ? Identifiers.deferPrefetchOnIdle : Identifiers.deferOnIdle);
24747
+ }
24748
+ // `deferOnImmediate()`
24749
+ if (immediate) {
24750
+ this.creationInstruction(immediate.sourceSpan, prefetch ? Identifiers.deferPrefetchOnImmediate : Identifiers.deferOnImmediate);
24751
+ }
24752
+ // `deferOnTimer(1337)`
24753
+ if (timer) {
24754
+ this.creationInstruction(timer.sourceSpan, prefetch ? Identifiers.deferPrefetchOnTimer : Identifiers.deferOnTimer, [literal(timer.delay)]);
24755
+ }
24756
+ // `deferOnHover()`
24757
+ if (hover) {
24758
+ this.creationInstruction(hover.sourceSpan, prefetch ? Identifiers.deferPrefetchOnHover : Identifiers.deferOnHover);
24759
+ }
24760
+ // TODO(crisbeto): currently the reference is passed as a string.
24761
+ // Update this once we figure out how we should refer to the target.
24762
+ // `deferOnInteraction(target)`
24763
+ if (interaction) {
24764
+ this.creationInstruction(interaction.sourceSpan, prefetch ? Identifiers.deferPrefetchOnInteraction : Identifiers.deferOnInteraction, [literal(interaction.reference)]);
24765
+ }
24766
+ // TODO(crisbeto): currently the reference is passed as a string.
24767
+ // Update this once we figure out how we should refer to the target.
24768
+ // `deferOnViewport(target)`
24769
+ if (viewport) {
24770
+ this.creationInstruction(viewport.sourceSpan, prefetch ? Identifiers.deferPrefetchOnViewport : Identifiers.deferOnViewport, [literal(viewport.reference)]);
24771
+ }
24772
+ }
24044
24773
  allocateDataSlot() {
24045
24774
  return this._dataIndex++;
24046
24775
  }
24776
+ // TODO: implement for loop instructions
24777
+ visitForLoopBlock(block) { }
24778
+ visitForLoopBlockEmpty(block) { }
24047
24779
  getConstCount() {
24048
24780
  return this._dataIndex;
24049
24781
  }
@@ -24168,6 +24900,21 @@ class TemplateDefinitionBuilder {
24168
24900
  this._tempVariables.push(...stmts);
24169
24901
  return args;
24170
24902
  }
24903
+ /**
24904
+ * Creates and returns a variable that can be used to
24905
+ * store the state between control flow instructions.
24906
+ */
24907
+ allocateControlFlowTempVariable() {
24908
+ // Note: the assumption here is that we'll only need one temporary variable for all control
24909
+ // flow instructions. It's expected that any instructions will overwrite it before passing it
24910
+ // into the parameters.
24911
+ if (this._controlFlowTempVariable === null) {
24912
+ const name = `${this.contextName}_contFlowTmp`;
24913
+ this._tempVariables.push(new DeclareVarStmt(name));
24914
+ this._controlFlowTempVariable = variable(name);
24915
+ }
24916
+ return this._controlFlowTempVariable;
24917
+ }
24171
24918
  /**
24172
24919
  * Prepares all attribute expression values for the `TAttributes` array.
24173
24920
  *
@@ -24293,7 +25040,8 @@ class TemplateDefinitionBuilder {
24293
25040
  return literal(consts.push(expression) - 1);
24294
25041
  }
24295
25042
  addAttrsToConsts(attrs) {
24296
- return attrs.length > 0 ? this.addToConsts(literalArr(attrs)) : TYPED_NULL_EXPR;
25043
+ return attrs !== null && attrs.length > 0 ? this.addToConsts(literalArr(attrs)) :
25044
+ TYPED_NULL_EXPR;
24297
25045
  }
24298
25046
  prepareRefsArray(references) {
24299
25047
  if (!references || references.length === 0) {
@@ -24488,7 +25236,7 @@ class BindingScope {
24488
25236
  if (value.declareLocalCallback && !value.declare) {
24489
25237
  value.declare = true;
24490
25238
  }
24491
- return value.lhs;
25239
+ return typeof value.lhs === 'function' ? value.lhs(this) : value.lhs;
24492
25240
  }
24493
25241
  current = current.parent;
24494
25242
  }
@@ -24592,7 +25340,8 @@ class BindingScope {
24592
25340
  const componentValue = this.map.get(SHARED_CONTEXT_KEY + 0);
24593
25341
  componentValue.declare = true;
24594
25342
  this.maybeRestoreView();
24595
- return componentValue.lhs.prop(name);
25343
+ const lhs = typeof componentValue.lhs === 'function' ? componentValue.lhs(this) : componentValue.lhs;
25344
+ return name === DIRECT_CONTEXT_REFERENCE ? lhs : lhs.prop(name);
24596
25345
  }
24597
25346
  maybeRestoreView() {
24598
25347
  // View restoration is required for listener instructions inside embedded views, because
@@ -25137,9 +25886,17 @@ function compileComponentFromMetadata(meta, constantPool, bindingParser) {
25137
25886
  }
25138
25887
  definitionMap.set('template', templateFn);
25139
25888
  }
25140
- if (meta.declarations.length > 0) {
25889
+ if (meta.declarationListEmitMode !== 3 /* DeclarationListEmitMode.RuntimeResolved */ &&
25890
+ meta.declarations.length > 0) {
25141
25891
  definitionMap.set('dependencies', compileDeclarationList(literalArr(meta.declarations.map(decl => decl.type)), meta.declarationListEmitMode));
25142
25892
  }
25893
+ else if (meta.declarationListEmitMode === 3 /* DeclarationListEmitMode.RuntimeResolved */) {
25894
+ const args = [meta.type.value];
25895
+ if (meta.rawImports) {
25896
+ args.push(meta.rawImports);
25897
+ }
25898
+ definitionMap.set('dependencies', importExpr(Identifiers.getComponentDepsFactory).callFn(args));
25899
+ }
25143
25900
  if (meta.encapsulation === null) {
25144
25901
  meta.encapsulation = ViewEncapsulation.Emulated;
25145
25902
  }
@@ -25211,6 +25968,8 @@ function compileDeclarationList(list, mode) {
25211
25968
  // directives: function () { return [MyDir].map(ng.resolveForwardRef); }
25212
25969
  const resolvedList = list.prop('map').callFn([importExpr(Identifiers.resolveForwardRef)]);
25213
25970
  return fn([], [new ReturnStatement(resolvedList)]);
25971
+ case 3 /* DeclarationListEmitMode.RuntimeResolved */:
25972
+ throw new Error(`Unsupported with an array of pre-resolved dependencies`);
25214
25973
  }
25215
25974
  }
25216
25975
  function prepareQueryParams(query, constantPool) {
@@ -26331,7 +27090,7 @@ function publishFacade(global) {
26331
27090
  * @description
26332
27091
  * Entry point for all public APIs of the compiler package.
26333
27092
  */
26334
- const VERSION = new Version('16.2.2');
27093
+ const VERSION = new Version('17.0.0-next.1');
26335
27094
 
26336
27095
  class CompilerConfig {
26337
27096
  constructor({ defaultEncapsulation = ViewEncapsulation.Emulated, useJit = true, missingTranslation = null, preserveWhitespaces, strictInjectionParameters } = {}) {
@@ -27891,6 +28650,25 @@ class Scope {
27891
28650
  visitDeferredBlockLoading(block) {
27892
28651
  block.children.forEach(node => node.visit(this));
27893
28652
  }
28653
+ visitSwitchBlock(block) {
28654
+ block.cases.forEach(node => node.visit(this));
28655
+ }
28656
+ visitSwitchBlockCase(block) {
28657
+ block.children.forEach(node => node.visit(this));
28658
+ }
28659
+ visitForLoopBlock(block) {
28660
+ block.children.forEach(node => node.visit(this));
28661
+ block.empty?.visit(this);
28662
+ }
28663
+ visitForLoopBlockEmpty(block) {
28664
+ block.children.forEach(node => node.visit(this));
28665
+ }
28666
+ visitIfBlock(block) {
28667
+ block.branches.forEach(node => node.visit(this));
28668
+ }
28669
+ visitIfBlockBranch(block) {
28670
+ block.children.forEach(node => node.visit(this));
28671
+ }
27894
28672
  // Unused visitors.
27895
28673
  visitContent(content) { }
27896
28674
  visitBoundAttribute(attr) { }
@@ -28045,9 +28823,10 @@ class DirectiveBinder {
28045
28823
  node.children.forEach(child => child.visit(this));
28046
28824
  }
28047
28825
  visitDeferredBlock(deferred) {
28826
+ const wasInDeferBlock = this.isInDeferBlock;
28048
28827
  this.isInDeferBlock = true;
28049
28828
  deferred.children.forEach(child => child.visit(this));
28050
- this.isInDeferBlock = false;
28829
+ this.isInDeferBlock = wasInDeferBlock;
28051
28830
  deferred.placeholder?.visit(this);
28052
28831
  deferred.loading?.visit(this);
28053
28832
  deferred.error?.visit(this);
@@ -28061,6 +28840,25 @@ class DirectiveBinder {
28061
28840
  visitDeferredBlockLoading(block) {
28062
28841
  block.children.forEach(child => child.visit(this));
28063
28842
  }
28843
+ visitSwitchBlock(block) {
28844
+ block.cases.forEach(node => node.visit(this));
28845
+ }
28846
+ visitSwitchBlockCase(block) {
28847
+ block.children.forEach(node => node.visit(this));
28848
+ }
28849
+ visitForLoopBlock(block) {
28850
+ block.children.forEach(node => node.visit(this));
28851
+ block.empty?.visit(this);
28852
+ }
28853
+ visitForLoopBlockEmpty(block) {
28854
+ block.children.forEach(node => node.visit(this));
28855
+ }
28856
+ visitIfBlock(block) {
28857
+ block.branches.forEach(node => node.visit(this));
28858
+ }
28859
+ visitIfBlockBranch(block) {
28860
+ block.children.forEach(node => node.visit(this));
28861
+ }
28064
28862
  // Unused visitors.
28065
28863
  visitContent(content) { }
28066
28864
  visitVariable(variable) { }
@@ -28197,11 +28995,10 @@ class TemplateBinder extends RecursiveAstVisitor {
28197
28995
  }
28198
28996
  visitDeferredBlock(deferred) {
28199
28997
  this.deferBlocks.add(deferred);
28998
+ const wasInDeferBlock = this.isInDeferBlock;
28200
28999
  this.isInDeferBlock = true;
28201
29000
  deferred.children.forEach(this.visitNode);
28202
- this.isInDeferBlock = false;
28203
- deferred.triggers.forEach(this.visitNode);
28204
- deferred.prefetchTriggers.forEach(this.visitNode);
29001
+ this.isInDeferBlock = wasInDeferBlock;
28205
29002
  deferred.placeholder && this.visitNode(deferred.placeholder);
28206
29003
  deferred.loading && this.visitNode(deferred.loading);
28207
29004
  deferred.error && this.visitNode(deferred.error);
@@ -28220,6 +29017,29 @@ class TemplateBinder extends RecursiveAstVisitor {
28220
29017
  visitDeferredBlockLoading(block) {
28221
29018
  block.children.forEach(this.visitNode);
28222
29019
  }
29020
+ visitSwitchBlock(block) {
29021
+ block.expression.visit(this);
29022
+ block.cases.forEach(this.visitNode);
29023
+ }
29024
+ visitSwitchBlockCase(block) {
29025
+ block.expression?.visit(this);
29026
+ block.children.forEach(this.visitNode);
29027
+ }
29028
+ visitForLoopBlock(block) {
29029
+ block.expression.visit(this);
29030
+ block.children.forEach(this.visitNode);
29031
+ block.empty?.visit(this);
29032
+ }
29033
+ visitForLoopBlockEmpty(block) {
29034
+ block.children.forEach(this.visitNode);
29035
+ }
29036
+ visitIfBlock(block) {
29037
+ block.branches.forEach(node => node.visit(this));
29038
+ }
29039
+ visitIfBlockBranch(block) {
29040
+ block.expression?.visit(this);
29041
+ block.children.forEach(node => node.visit(this));
29042
+ }
28223
29043
  visitBoundText(text) {
28224
29044
  text.value.visit(this);
28225
29045
  }
@@ -28362,6 +29182,60 @@ function compileClassMetadata(metadata) {
28362
29182
  const iife = fn([], [devOnlyGuardedExpression(fnCall).toStmt()]);
28363
29183
  return iife.callFn([]);
28364
29184
  }
29185
+ /**
29186
+ * Wraps the `setClassMetadata` function with extra logic that dynamically
29187
+ * loads dependencies from `{#defer}` blocks.
29188
+ *
29189
+ * Generates a call like this:
29190
+ * ```
29191
+ * setClassMetadataAsync(type, () => {
29192
+ * return [
29193
+ * import('./cmp-a').then(m => m.CmpA);
29194
+ * import('./cmp-b').then(m => m.CmpB);
29195
+ * ];
29196
+ * }, (CmpA, CmpB) => {
29197
+ * setClassMetadata(type, decorators, ctorParameters, propParameters);
29198
+ * });
29199
+ * ```
29200
+ *
29201
+ * Similar to the `setClassMetadata` call, it's wrapped into the `ngDevMode`
29202
+ * check to tree-shake away this code in production mode.
29203
+ */
29204
+ function compileComponentClassMetadata(metadata, deferrableTypes) {
29205
+ if (deferrableTypes.size === 0) {
29206
+ // If there are no deferrable symbols - just generate a regular `setClassMetadata` call.
29207
+ return compileClassMetadata(metadata);
29208
+ }
29209
+ const dynamicImports = [];
29210
+ const importedSymbols = [];
29211
+ for (const [symbolName, importPath] of deferrableTypes) {
29212
+ // e.g. `function(m) { return m.CmpA; }`
29213
+ const innerFn = fn([new FnParam('m', DYNAMIC_TYPE)], [new ReturnStatement(variable('m').prop(symbolName))]);
29214
+ // e.g. `import('./cmp-a').then(...)`
29215
+ const importExpr = (new DynamicImportExpr(importPath)).prop('then').callFn([innerFn]);
29216
+ dynamicImports.push(importExpr);
29217
+ importedSymbols.push(new FnParam(symbolName, DYNAMIC_TYPE));
29218
+ }
29219
+ // e.g. `function() { return [ ... ]; }`
29220
+ const dependencyLoadingFn = fn([], [new ReturnStatement(literalArr(dynamicImports))]);
29221
+ // e.g. `setClassMetadata(...)`
29222
+ const setClassMetadataCall = importExpr(Identifiers.setClassMetadata).callFn([
29223
+ metadata.type,
29224
+ metadata.decorators,
29225
+ metadata.ctorParameters ?? literal(null),
29226
+ metadata.propDecorators ?? literal(null),
29227
+ ]);
29228
+ // e.g. `function(CmpA) { setClassMetadata(...); }`
29229
+ const setClassMetaWrapper = fn(importedSymbols, [setClassMetadataCall.toStmt()]);
29230
+ // Final `setClassMetadataAsync()` call with all arguments
29231
+ const setClassMetaAsync = importExpr(Identifiers.setClassMetadataAsync).callFn([
29232
+ metadata.type, dependencyLoadingFn, setClassMetaWrapper
29233
+ ]);
29234
+ // Generate an ngDevMode guarded call to `setClassMetadataAsync` with
29235
+ // the class identifier and its metadata, so that this call can be tree-shaken.
29236
+ const iife = fn([], [devOnlyGuardedExpression(setClassMetaAsync).toStmt()]);
29237
+ return iife.callFn([]);
29238
+ }
28365
29239
 
28366
29240
  /**
28367
29241
  * Every time we make a breaking change to the declaration interface or partial-linker behavior, we
@@ -28374,7 +29248,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$6 = '12.0.0';
28374
29248
  function compileDeclareClassMetadata(metadata) {
28375
29249
  const definitionMap = new DefinitionMap();
28376
29250
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$6));
28377
- definitionMap.set('version', literal('16.2.2'));
29251
+ definitionMap.set('version', literal('17.0.0-next.1'));
28378
29252
  definitionMap.set('ngImport', importExpr(Identifiers.core));
28379
29253
  definitionMap.set('type', metadata.type);
28380
29254
  definitionMap.set('decorators', metadata.decorators);
@@ -28482,7 +29356,7 @@ function createDirectiveDefinitionMap(meta) {
28482
29356
  // in 16.1 is actually used.
28483
29357
  const minVersion = hasTransformFunctions ? MINIMUM_PARTIAL_LINKER_VERSION$5 : '14.0.0';
28484
29358
  definitionMap.set('minVersion', literal(minVersion));
28485
- definitionMap.set('version', literal('16.2.2'));
29359
+ definitionMap.set('version', literal('17.0.0-next.1'));
28486
29360
  // e.g. `type: MyDirective`
28487
29361
  definitionMap.set('type', meta.type.value);
28488
29362
  if (meta.isStandalone) {
@@ -28673,6 +29547,9 @@ function compileUsedDependenciesMetadata(meta) {
28673
29547
  const wrapType = meta.declarationListEmitMode !== 0 /* DeclarationListEmitMode.Direct */ ?
28674
29548
  generateForwardRef :
28675
29549
  (expr) => expr;
29550
+ if (meta.declarationListEmitMode === 3 /* DeclarationListEmitMode.RuntimeResolved */) {
29551
+ throw new Error(`Unsupported emit mode`);
29552
+ }
28676
29553
  return toOptionalLiteralArray(meta.declarations, decl => {
28677
29554
  switch (decl.kind) {
28678
29555
  case R3TemplateDependencyKind.Directive:
@@ -28710,7 +29587,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0';
28710
29587
  function compileDeclareFactoryFunction(meta) {
28711
29588
  const definitionMap = new DefinitionMap();
28712
29589
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$4));
28713
- definitionMap.set('version', literal('16.2.2'));
29590
+ definitionMap.set('version', literal('17.0.0-next.1'));
28714
29591
  definitionMap.set('ngImport', importExpr(Identifiers.core));
28715
29592
  definitionMap.set('type', meta.type.value);
28716
29593
  definitionMap.set('deps', compileDependencies(meta.deps));
@@ -28745,7 +29622,7 @@ function compileDeclareInjectableFromMetadata(meta) {
28745
29622
  function createInjectableDefinitionMap(meta) {
28746
29623
  const definitionMap = new DefinitionMap();
28747
29624
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$3));
28748
- definitionMap.set('version', literal('16.2.2'));
29625
+ definitionMap.set('version', literal('17.0.0-next.1'));
28749
29626
  definitionMap.set('ngImport', importExpr(Identifiers.core));
28750
29627
  definitionMap.set('type', meta.type.value);
28751
29628
  // Only generate providedIn property if it has a non-null value
@@ -28796,7 +29673,7 @@ function compileDeclareInjectorFromMetadata(meta) {
28796
29673
  function createInjectorDefinitionMap(meta) {
28797
29674
  const definitionMap = new DefinitionMap();
28798
29675
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$2));
28799
- definitionMap.set('version', literal('16.2.2'));
29676
+ definitionMap.set('version', literal('17.0.0-next.1'));
28800
29677
  definitionMap.set('ngImport', importExpr(Identifiers.core));
28801
29678
  definitionMap.set('type', meta.type.value);
28802
29679
  definitionMap.set('providers', meta.providers);
@@ -28829,7 +29706,7 @@ function createNgModuleDefinitionMap(meta) {
28829
29706
  throw new Error('Invalid path! Local compilation mode should not get into the partial compilation path');
28830
29707
  }
28831
29708
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$1));
28832
- definitionMap.set('version', literal('16.2.2'));
29709
+ definitionMap.set('version', literal('17.0.0-next.1'));
28833
29710
  definitionMap.set('ngImport', importExpr(Identifiers.core));
28834
29711
  definitionMap.set('type', meta.type.value);
28835
29712
  // We only generate the keys in the metadata if the arrays contain values.
@@ -28880,7 +29757,7 @@ function compileDeclarePipeFromMetadata(meta) {
28880
29757
  function createPipeDefinitionMap(meta) {
28881
29758
  const definitionMap = new DefinitionMap();
28882
29759
  definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION));
28883
- definitionMap.set('version', literal('16.2.2'));
29760
+ definitionMap.set('version', literal('17.0.0-next.1'));
28884
29761
  definitionMap.set('ngImport', importExpr(Identifiers.core));
28885
29762
  // e.g. `type: MyPipe`
28886
29763
  definitionMap.set('type', meta.type.value);
@@ -28913,5 +29790,5 @@ publishFacade(_global);
28913
29790
 
28914
29791
  // This file is not used to build this module. It is only used during editing
28915
29792
 
28916
- export { AST, ASTWithName, ASTWithSource, AbsoluteSourceSpan, ArrayType, AstMemoryEfficientTransformer, AstTransformer, Attribute, Binary, BinaryOperator, BinaryOperatorExpr, BindingPipe, Block, BlockGroup, BlockParameter, BoundElementProperty, BuiltinType, BuiltinTypeName, CUSTOM_ELEMENTS_SCHEMA, Call, Chain, ChangeDetectionStrategy, CommaExpr, Comment, CompilerConfig, Conditional, ConditionalExpr, ConstantPool, CssSelector, DEFAULT_INTERPOLATION_CONFIG, DYNAMIC_TYPE, DeclareFunctionStmt, DeclareVarStmt, DomElementSchemaRegistry, DynamicImportExpr, EOF, Element, ElementSchemaRegistry, EmitterVisitorContext, EmptyExpr$1 as EmptyExpr, Expansion, ExpansionCase, Expression, ExpressionBinding, ExpressionStatement, ExpressionType, ExternalExpr, ExternalReference, FactoryTarget$1 as FactoryTarget, FunctionExpr, HtmlParser, HtmlTagDefinition, I18NHtmlParser, IfStmt, ImplicitReceiver, InstantiateExpr, Interpolation$1 as Interpolation, InterpolationConfig, InvokeFunctionExpr, JSDocComment, JitEvaluator, KeyedRead, KeyedWrite, LeadingComment, Lexer, LiteralArray, LiteralArrayExpr, LiteralExpr, LiteralMap, LiteralMapExpr, LiteralPrimitive, LocalizedString, MapType, MessageBundle, NONE_TYPE, NO_ERRORS_SCHEMA, NodeWithI18n, NonNullAssert, NotExpr, ParseError, ParseErrorLevel, ParseLocation, ParseSourceFile, ParseSourceSpan, ParseSpan, ParseTreeResult, ParsedEvent, ParsedProperty, ParsedPropertyType, ParsedVariable, Parser$1 as Parser, ParserError, PrefixNot, PropertyRead, PropertyWrite, R3BoundTarget, Identifiers as R3Identifiers, R3NgModuleMetadataKind, R3SelectorScopeMode, R3TargetBinder, R3TemplateDependencyKind, ReadKeyExpr, ReadPropExpr, ReadVarExpr, RecursiveAstVisitor, RecursiveVisitor, ResourceLoader, ReturnStatement, STRING_TYPE, SafeCall, SafeKeyedRead, SafePropertyRead, SelectorContext, SelectorListContext, SelectorMatcher, Serializer, SplitInterpolation, Statement, StmtModifier, TagContentType, TaggedTemplateExpr, TemplateBindingParseResult, TemplateLiteral, TemplateLiteralElement, Text, ThisReceiver, BoundAttribute as TmplAstBoundAttribute, BoundDeferredTrigger as TmplAstBoundDeferredTrigger, BoundEvent as TmplAstBoundEvent, BoundText as TmplAstBoundText, Content as TmplAstContent, DeferredBlock as TmplAstDeferredBlock, DeferredBlockError as TmplAstDeferredBlockError, DeferredBlockLoading as TmplAstDeferredBlockLoading, DeferredBlockPlaceholder as TmplAstDeferredBlockPlaceholder, DeferredTrigger as TmplAstDeferredTrigger, Element$1 as TmplAstElement, HoverDeferredTrigger as TmplAstHoverDeferredTrigger, Icu$1 as TmplAstIcu, IdleDeferredTrigger as TmplAstIdleDeferredTrigger, ImmediateDeferredTrigger as TmplAstImmediateDeferredTrigger, InteractionDeferredTrigger as TmplAstInteractionDeferredTrigger, RecursiveVisitor$1 as TmplAstRecursiveVisitor, Reference as TmplAstReference, Template as TmplAstTemplate, Text$3 as TmplAstText, TextAttribute as TmplAstTextAttribute, TimerDeferredTrigger as TmplAstTimerDeferredTrigger, Variable as TmplAstVariable, ViewportDeferredTrigger as TmplAstViewportDeferredTrigger, Token, TokenType, TransplantedType, TreeError, Type, TypeModifier, TypeofExpr, Unary, UnaryOperator, UnaryOperatorExpr, VERSION, VariableBinding, Version, ViewEncapsulation, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr, Xliff, Xliff2, Xmb, XmlParser, Xtb, _ParseAST, compileClassMetadata, compileComponentFromMetadata, compileDeclareClassMetadata, compileDeclareComponentFromMetadata, compileDeclareDirectiveFromMetadata, compileDeclareFactoryFunction, compileDeclareInjectableFromMetadata, compileDeclareInjectorFromMetadata, compileDeclareNgModuleFromMetadata, compileDeclarePipeFromMetadata, compileDirectiveFromMetadata, compileFactoryFunction, compileInjectable, compileInjector, compileNgModule, compilePipeFromMetadata, computeMsgId, core, createInjectableType, createMayBeForwardRefExpression, devOnlyGuardedExpression, emitDistinctChangesOnlyDefaultValue, getHtmlTagDefinition, getNsPrefix, getSafePropertyAccessString, identifierName, isIdentifier, isNgContainer, isNgContent, isNgTemplate, jsDocComment, leadingComment, literalMap, makeBindingParser, mergeNsAndName, output_ast as outputAst, parseHostBindings, parseTemplate, preserveWhitespacesDefault, publishFacade, r3JitTypeSourceSpan, sanitizeIdentifier, splitNsName, verifyHostBindings, visitAll };
29793
+ export { AST, ASTWithName, ASTWithSource, AbsoluteSourceSpan, ArrayType, AstMemoryEfficientTransformer, AstTransformer, Attribute, Binary, BinaryOperator, BinaryOperatorExpr, BindingPipe, Block, BlockGroup, BlockParameter, BoundElementProperty, BuiltinType, BuiltinTypeName, CUSTOM_ELEMENTS_SCHEMA, Call, Chain, ChangeDetectionStrategy, CommaExpr, Comment, CompilerConfig, Conditional, ConditionalExpr, ConstantPool, CssSelector, DEFAULT_INTERPOLATION_CONFIG, DYNAMIC_TYPE, DeclareFunctionStmt, DeclareVarStmt, DomElementSchemaRegistry, DynamicImportExpr, EOF, Element, ElementSchemaRegistry, EmitterVisitorContext, EmptyExpr$1 as EmptyExpr, Expansion, ExpansionCase, Expression, ExpressionBinding, ExpressionStatement, ExpressionType, ExternalExpr, ExternalReference, FactoryTarget$1 as FactoryTarget, FunctionExpr, HtmlParser, HtmlTagDefinition, I18NHtmlParser, IfStmt, ImplicitReceiver, InstantiateExpr, Interpolation$1 as Interpolation, InterpolationConfig, InvokeFunctionExpr, JSDocComment, JitEvaluator, KeyedRead, KeyedWrite, LeadingComment, Lexer, LiteralArray, LiteralArrayExpr, LiteralExpr, LiteralMap, LiteralMapExpr, LiteralPrimitive, LocalizedString, MapType, MessageBundle, NONE_TYPE, NO_ERRORS_SCHEMA, NodeWithI18n, NonNullAssert, NotExpr, ParseError, ParseErrorLevel, ParseLocation, ParseSourceFile, ParseSourceSpan, ParseSpan, ParseTreeResult, ParsedEvent, ParsedProperty, ParsedPropertyType, ParsedVariable, Parser$1 as Parser, ParserError, PrefixNot, PropertyRead, PropertyWrite, R3BoundTarget, Identifiers as R3Identifiers, R3NgModuleMetadataKind, R3SelectorScopeMode, R3TargetBinder, R3TemplateDependencyKind, ReadKeyExpr, ReadPropExpr, ReadVarExpr, RecursiveAstVisitor, RecursiveVisitor, ResourceLoader, ReturnStatement, STRING_TYPE, SafeCall, SafeKeyedRead, SafePropertyRead, SelectorContext, SelectorListContext, SelectorMatcher, Serializer, SplitInterpolation, Statement, StmtModifier, TagContentType, TaggedTemplateExpr, TemplateBindingParseResult, TemplateLiteral, TemplateLiteralElement, Text, ThisReceiver, BoundAttribute as TmplAstBoundAttribute, BoundDeferredTrigger as TmplAstBoundDeferredTrigger, BoundEvent as TmplAstBoundEvent, BoundText as TmplAstBoundText, Content as TmplAstContent, DeferredBlock as TmplAstDeferredBlock, DeferredBlockError as TmplAstDeferredBlockError, DeferredBlockLoading as TmplAstDeferredBlockLoading, DeferredBlockPlaceholder as TmplAstDeferredBlockPlaceholder, DeferredTrigger as TmplAstDeferredTrigger, Element$1 as TmplAstElement, ForLoopBlock as TmplAstForLoopBlock, ForLoopBlockEmpty as TmplAstForLoopBlockEmpty, HoverDeferredTrigger as TmplAstHoverDeferredTrigger, Icu$1 as TmplAstIcu, IdleDeferredTrigger as TmplAstIdleDeferredTrigger, IfBlock as TmplAstIfBlock, IfBlockBranch as TmplAstIfBlockBranch, ImmediateDeferredTrigger as TmplAstImmediateDeferredTrigger, InteractionDeferredTrigger as TmplAstInteractionDeferredTrigger, RecursiveVisitor$1 as TmplAstRecursiveVisitor, Reference as TmplAstReference, SwitchBlock as TmplAstSwitchBlock, SwitchBlockCase as TmplAstSwitchBlockCase, Template as TmplAstTemplate, Text$3 as TmplAstText, TextAttribute as TmplAstTextAttribute, TimerDeferredTrigger as TmplAstTimerDeferredTrigger, Variable as TmplAstVariable, ViewportDeferredTrigger as TmplAstViewportDeferredTrigger, Token, TokenType, TransplantedType, TreeError, Type, TypeModifier, TypeofExpr, Unary, UnaryOperator, UnaryOperatorExpr, VERSION, VariableBinding, Version, ViewEncapsulation, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr, Xliff, Xliff2, Xmb, XmlParser, Xtb, _ParseAST, compileClassMetadata, compileComponentClassMetadata, compileComponentFromMetadata, compileDeclareClassMetadata, compileDeclareComponentFromMetadata, compileDeclareDirectiveFromMetadata, compileDeclareFactoryFunction, compileDeclareInjectableFromMetadata, compileDeclareInjectorFromMetadata, compileDeclareNgModuleFromMetadata, compileDeclarePipeFromMetadata, compileDirectiveFromMetadata, compileFactoryFunction, compileInjectable, compileInjector, compileNgModule, compilePipeFromMetadata, computeMsgId, core, createInjectableType, createMayBeForwardRefExpression, devOnlyGuardedExpression, emitDistinctChangesOnlyDefaultValue, getHtmlTagDefinition, getNsPrefix, getSafePropertyAccessString, identifierName, isIdentifier, isNgContainer, isNgContent, isNgTemplate, jsDocComment, leadingComment, literalMap, makeBindingParser, mergeNsAndName, output_ast as outputAst, parseHostBindings, parseTemplate, preserveWhitespacesDefault, publishFacade, r3JitTypeSourceSpan, sanitizeIdentifier, splitNsName, verifyHostBindings, visitAll };
28917
29794
  //# sourceMappingURL=compiler.mjs.map