@angular/core 15.0.0-next.2 → 15.0.0-next.4

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 (44) hide show
  1. package/esm2020/src/core_private_export.mjs +2 -2
  2. package/esm2020/src/debug/debug_node.mjs +1 -1
  3. package/esm2020/src/di/index.mjs +1 -1
  4. package/esm2020/src/di/injector.mjs +1 -1
  5. package/esm2020/src/di/injector_compatibility.mjs +15 -11
  6. package/esm2020/src/di/interface/injector.mjs +1 -1
  7. package/esm2020/src/di/r3_injector.mjs +3 -2
  8. package/esm2020/src/render3/component_ref.mjs +103 -84
  9. package/esm2020/src/render3/context_discovery.mjs +17 -14
  10. package/esm2020/src/render3/definition.mjs +3 -2
  11. package/esm2020/src/render3/di.mjs +3 -2
  12. package/esm2020/src/render3/features/host_directives_feature.mjs +35 -13
  13. package/esm2020/src/render3/instructions/element.mjs +4 -16
  14. package/esm2020/src/render3/instructions/listener.mjs +2 -4
  15. package/esm2020/src/render3/instructions/lview_debug.mjs +9 -9
  16. package/esm2020/src/render3/instructions/projection.mjs +1 -1
  17. package/esm2020/src/render3/instructions/shared.mjs +123 -101
  18. package/esm2020/src/render3/instructions/styling.mjs +2 -2
  19. package/esm2020/src/render3/interfaces/definition.mjs +1 -1
  20. package/esm2020/src/render3/interfaces/node.mjs +3 -3
  21. package/esm2020/src/render3/interfaces/type_checks.mjs +3 -3
  22. package/esm2020/src/render3/jit/directive.mjs +7 -2
  23. package/esm2020/src/render3/ng_module_ref.mjs +1 -1
  24. package/esm2020/src/render3/node_manipulation.mjs +20 -5
  25. package/esm2020/src/render3/node_manipulation_i18n.mjs +2 -2
  26. package/esm2020/src/render3/util/discovery_utils.mjs +2 -2
  27. package/esm2020/src/util/is_dev_mode.mjs +11 -19
  28. package/esm2020/src/version.mjs +1 -1
  29. package/esm2020/src/zone/async-stack-tagging.mjs +28 -0
  30. package/esm2020/src/zone/ng_zone.mjs +8 -3
  31. package/esm2020/testing/src/logger.mjs +3 -3
  32. package/esm2020/testing/src/ng_zone_mock.mjs +3 -3
  33. package/esm2020/testing/src/test_bed.mjs +4 -4
  34. package/fesm2015/core.mjs +1463 -1363
  35. package/fesm2015/core.mjs.map +1 -1
  36. package/fesm2015/testing.mjs +970 -901
  37. package/fesm2015/testing.mjs.map +1 -1
  38. package/fesm2020/core.mjs +1464 -1366
  39. package/fesm2020/core.mjs.map +1 -1
  40. package/fesm2020/testing.mjs +969 -900
  41. package/fesm2020/testing.mjs.map +1 -1
  42. package/index.d.ts +100 -33
  43. package/package.json +1 -1
  44. package/testing/index.d.ts +9 -1
@@ -1,10 +1,10 @@
1
1
  /**
2
- * @license Angular v15.0.0-next.2
2
+ * @license Angular v15.0.0-next.4
3
3
  * (c) 2010-2022 Google LLC. https://angular.io/
4
4
  * License: MIT
5
5
  */
6
6
 
7
- import { getDebugNode as getDebugNode$1, RendererFactory2 as RendererFactory2$1, InjectionToken as InjectionToken$1, ɵstringify, ɵReflectionCapabilities, Directive, Component, Pipe, NgModule, ɵgetInjectableDef, resolveForwardRef as resolveForwardRef$1, ɵNG_COMP_DEF, ɵRender3NgModuleRef, ApplicationInitStatus, LOCALE_ID as LOCALE_ID$1, ɵDEFAULT_LOCALE_ID, ɵsetLocaleId, ɵRender3ComponentFactory, ɵcompileComponent, ɵNG_DIR_DEF, ɵcompileDirective, ɵNG_PIPE_DEF, ɵcompilePipe, ɵNG_MOD_DEF, ɵtransitiveScopesFor, ɵpatchComponentDefWithScope, ɵNG_INJ_DEF, ɵcompileNgModuleDefs, NgZone, Compiler, COMPILER_OPTIONS, ɵNgModuleFactory, ModuleWithComponentFactories, Injector as Injector$1, InjectFlags as InjectFlags$1, ɵsetAllowDuplicateNgModuleIdsForTest, ɵresetCompiledComponents, ɵsetUnknownElementStrictMode as ɵsetUnknownElementStrictMode$1, ɵsetUnknownPropertyStrictMode as ɵsetUnknownPropertyStrictMode$1, ɵgetUnknownElementStrictMode as ɵgetUnknownElementStrictMode$1, ɵgetUnknownPropertyStrictMode as ɵgetUnknownPropertyStrictMode$1, ɵflushModuleScopingQueueAsMuchAsPossible } from '@angular/core';
7
+ import { getDebugNode as getDebugNode$1, RendererFactory2 as RendererFactory2$1, InjectionToken as InjectionToken$1, ɵstringify, ɵReflectionCapabilities, Directive, Component, Pipe, NgModule, ɵgetInjectableDef, resolveForwardRef as resolveForwardRef$1, ɵNG_COMP_DEF, ɵRender3NgModuleRef, ApplicationInitStatus, LOCALE_ID as LOCALE_ID$1, ɵDEFAULT_LOCALE_ID, ɵsetLocaleId, ɵRender3ComponentFactory, ɵcompileComponent, ɵNG_DIR_DEF, ɵcompileDirective, ɵNG_PIPE_DEF, ɵcompilePipe, ɵNG_MOD_DEF, ɵtransitiveScopesFor, ɵpatchComponentDefWithScope, ɵNG_INJ_DEF, ɵcompileNgModuleDefs, NgZone, Compiler, COMPILER_OPTIONS, ɵNgModuleFactory, ModuleWithComponentFactories, ɵconvertToBitFlags, Injector as Injector$1, InjectFlags as InjectFlags$1, ɵsetAllowDuplicateNgModuleIdsForTest, ɵresetCompiledComponents, ɵsetUnknownElementStrictMode as ɵsetUnknownElementStrictMode$1, ɵsetUnknownPropertyStrictMode as ɵsetUnknownPropertyStrictMode$1, ɵgetUnknownElementStrictMode as ɵgetUnknownElementStrictMode$1, ɵgetUnknownPropertyStrictMode as ɵgetUnknownPropertyStrictMode$1, ɵflushModuleScopingQueueAsMuchAsPossible } from '@angular/core';
8
8
  import { ResourceLoader } from '@angular/compiler';
9
9
  import { Subject, Subscription } from 'rxjs';
10
10
 
@@ -2174,17 +2174,21 @@ Please check that 1) the type for the parameter at index ${index} is correct and
2174
2174
  * @publicApi
2175
2175
  */
2176
2176
  function inject$1(token, flags = InjectFlags.Default) {
2177
- if (typeof flags !== 'number') {
2178
- // While TypeScript doesn't accept it without a cast, bitwise OR with false-y values in
2179
- // JavaScript is a no-op. We can use that for a very codesize-efficient conversion from
2180
- // `InjectOptions` to `InjectFlags`.
2181
- flags = (0 /* InternalInjectFlags.Default */ | // comment to force a line break in the formatter
2182
- (flags.optional && 8 /* InternalInjectFlags.Optional */) |
2183
- (flags.host && 1 /* InternalInjectFlags.Host */) |
2184
- (flags.self && 2 /* InternalInjectFlags.Self */) |
2185
- (flags.skipSelf && 4 /* InternalInjectFlags.SkipSelf */));
2186
- }
2187
- return ɵɵinject(token, flags);
2177
+ return ɵɵinject(token, convertToBitFlags(flags));
2178
+ }
2179
+ // Converts object-based DI flags (`InjectOptions`) to bit flags (`InjectFlags`).
2180
+ function convertToBitFlags(flags) {
2181
+ if (typeof flags === 'undefined' || typeof flags === 'number') {
2182
+ return flags;
2183
+ }
2184
+ // While TypeScript doesn't accept it without a cast, bitwise OR with false-y values in
2185
+ // JavaScript is a no-op. We can use that for a very codesize-efficient conversion from
2186
+ // `InjectOptions` to `InjectFlags`.
2187
+ return (0 /* InternalInjectFlags.Default */ | // comment to force a line break in the formatter
2188
+ (flags.optional && 8 /* InternalInjectFlags.Optional */) |
2189
+ (flags.host && 1 /* InternalInjectFlags.Host */) |
2190
+ (flags.self && 2 /* InternalInjectFlags.Self */) |
2191
+ (flags.skipSelf && 4 /* InternalInjectFlags.SkipSelf */));
2188
2192
  }
2189
2193
  function injectArgs(types) {
2190
2194
  const args = [];
@@ -2568,7 +2572,8 @@ function ɵɵdefineComponent(componentDefinition) {
2568
2572
  setInput: null,
2569
2573
  schemas: componentDefinition.schemas || null,
2570
2574
  tView: null,
2571
- applyHostDirectives: null,
2575
+ findHostDirectiveDefs: null,
2576
+ hostDirectives: null,
2572
2577
  };
2573
2578
  const dependencies = componentDefinition.dependencies;
2574
2579
  const feature = componentDefinition.features;
@@ -2909,10 +2914,10 @@ function isLContainer(value) {
2909
2914
  return Array.isArray(value) && value[TYPE] === true;
2910
2915
  }
2911
2916
  function isContentQueryHost(tNode) {
2912
- return (tNode.flags & 8 /* TNodeFlags.hasContentQuery */) !== 0;
2917
+ return (tNode.flags & 4 /* TNodeFlags.hasContentQuery */) !== 0;
2913
2918
  }
2914
2919
  function isComponentHost(tNode) {
2915
- return (tNode.flags & 2 /* TNodeFlags.isComponentHost */) === 2 /* TNodeFlags.isComponentHost */;
2920
+ return tNode.componentOffset > -1;
2916
2921
  }
2917
2922
  function isDirectiveHost(tNode) {
2918
2923
  return (tNode.flags & 1 /* TNodeFlags.isDirectiveHost */) === 1 /* TNodeFlags.isDirectiveHost */;
@@ -4322,7 +4327,7 @@ const unusedValueExportToPlacateAjd$5 = 1;
4322
4327
  * @param tNode
4323
4328
  */
4324
4329
  function hasClassInput(tNode) {
4325
- return (tNode.flags & 16 /* TNodeFlags.hasClassInput */) !== 0;
4330
+ return (tNode.flags & 8 /* TNodeFlags.hasClassInput */) !== 0;
4326
4331
  }
4327
4332
  /**
4328
4333
  * Returns `true` if the `TNode` has a directive which has `@Input()` for `style` binding.
@@ -4346,7 +4351,7 @@ function hasClassInput(tNode) {
4346
4351
  * @param tNode
4347
4352
  */
4348
4353
  function hasStyleInput(tNode) {
4349
- return (tNode.flags & 32 /* TNodeFlags.hasStyleInput */) !== 0;
4354
+ return (tNode.flags & 16 /* TNodeFlags.hasStyleInput */) !== 0;
4350
4355
  }
4351
4356
 
4352
4357
  /**
@@ -5238,7 +5243,7 @@ class NodeInjector {
5238
5243
  this._lView = _lView;
5239
5244
  }
5240
5245
  get(token, notFoundValue, flags) {
5241
- return getOrCreateInjectable(this._tNode, this._lView, token, flags, notFoundValue);
5246
+ return getOrCreateInjectable(this._tNode, this._lView, token, convertToBitFlags(flags), notFoundValue);
5242
5247
  }
5243
5248
  }
5244
5249
  /** Creates a `NodeInjector` for the current node. */
@@ -7135,6 +7140,7 @@ class R3Injector extends EnvironmentInjector {
7135
7140
  }
7136
7141
  get(token, notFoundValue = THROW_IF_NOT_FOUND, flags = InjectFlags.Default) {
7137
7142
  this.assertNotDestroyed();
7143
+ flags = convertToBitFlags(flags);
7138
7144
  // Set the injection context.
7139
7145
  const previousInjector = setCurrentInjector(this);
7140
7146
  const previousInjectImplementation = setInjectImplementation(undefined);
@@ -7636,7 +7642,7 @@ class Version {
7636
7642
  /**
7637
7643
  * @publicApi
7638
7644
  */
7639
- const VERSION = new Version('15.0.0-next.2');
7645
+ const VERSION = new Version('15.0.0-next.4');
7640
7646
 
7641
7647
  /**
7642
7648
  * @license
@@ -7671,29 +7677,70 @@ const NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR = {};
7671
7677
  * Use of this source code is governed by an MIT-style license that can be
7672
7678
  * found in the LICENSE file at https://angular.io/license
7673
7679
  */
7680
+ // Keeps track of the currently-active LViews.
7681
+ const TRACKED_LVIEWS = new Map();
7682
+ // Used for generating unique IDs for LViews.
7683
+ let uniqueIdCounter = 0;
7684
+ /** Gets a unique ID that can be assigned to an LView. */
7685
+ function getUniqueLViewId() {
7686
+ return uniqueIdCounter++;
7687
+ }
7688
+ /** Starts tracking an LView. */
7689
+ function registerLView(lView) {
7690
+ ngDevMode && assertNumber(lView[ID], 'LView must have an ID in order to be registered');
7691
+ TRACKED_LVIEWS.set(lView[ID], lView);
7692
+ }
7693
+ /** Gets an LView by its unique ID. */
7694
+ function getLViewById(id) {
7695
+ ngDevMode && assertNumber(id, 'ID used for LView lookup must be a number');
7696
+ return TRACKED_LVIEWS.get(id) || null;
7697
+ }
7698
+ /** Stops tracking an LView. */
7699
+ function unregisterLView(lView) {
7700
+ ngDevMode && assertNumber(lView[ID], 'Cannot stop tracking an LView that does not have an ID');
7701
+ TRACKED_LVIEWS.delete(lView[ID]);
7702
+ }
7703
+
7674
7704
  /**
7675
- * Defines a schema that allows an NgModule to contain the following:
7676
- * - Non-Angular elements named with dash case (`-`).
7677
- * - Element properties named with dash case (`-`).
7678
- * Dash case is the naming convention for custom elements.
7705
+ * @license
7706
+ * Copyright Google LLC All Rights Reserved.
7679
7707
  *
7680
- * @publicApi
7708
+ * Use of this source code is governed by an MIT-style license that can be
7709
+ * found in the LICENSE file at https://angular.io/license
7681
7710
  */
7682
- const CUSTOM_ELEMENTS_SCHEMA = {
7683
- name: 'custom-elements'
7684
- };
7685
7711
  /**
7686
- * Defines a schema that allows any property on any element.
7687
- *
7688
- * This schema allows you to ignore the errors related to any unknown elements or properties in a
7689
- * template. The usage of this schema is generally discouraged because it prevents useful validation
7690
- * and may hide real errors in your template. Consider using the `CUSTOM_ELEMENTS_SCHEMA` instead.
7712
+ * The internal view context which is specific to a given DOM element, directive or
7713
+ * component instance. Each value in here (besides the LView and element node details)
7714
+ * can be present, null or undefined. If undefined then it implies the value has not been
7715
+ * looked up yet, otherwise, if null, then a lookup was executed and nothing was found.
7691
7716
  *
7692
- * @publicApi
7717
+ * Each value will get filled when the respective value is examined within the getContext
7718
+ * function. The component, element and each directive instance will share the same instance
7719
+ * of the context.
7693
7720
  */
7694
- const NO_ERRORS_SCHEMA = {
7695
- name: 'no-errors-schema'
7696
- };
7721
+ class LContext {
7722
+ constructor(
7723
+ /**
7724
+ * ID of the component's parent view data.
7725
+ */
7726
+ lViewId,
7727
+ /**
7728
+ * The index instance of the node.
7729
+ */
7730
+ nodeIndex,
7731
+ /**
7732
+ * The instance of the DOM node that is attached to the lNode.
7733
+ */
7734
+ native) {
7735
+ this.lViewId = lViewId;
7736
+ this.nodeIndex = nodeIndex;
7737
+ this.native = native;
7738
+ }
7739
+ /** Component's parent view data. */
7740
+ get lView() {
7741
+ return getLViewById(this.lViewId);
7742
+ }
7743
+ }
7697
7744
 
7698
7745
  /**
7699
7746
  * @license
@@ -7702,336 +7749,305 @@ const NO_ERRORS_SCHEMA = {
7702
7749
  * Use of this source code is governed by an MIT-style license that can be
7703
7750
  * found in the LICENSE file at https://angular.io/license
7704
7751
  */
7705
- let shouldThrowErrorOnUnknownElement = false;
7706
- /**
7707
- * Sets a strict mode for JIT-compiled components to throw an error on unknown elements,
7708
- * instead of just logging the error.
7709
- * (for AOT-compiled ones this check happens at build time).
7710
- */
7711
- function ɵsetUnknownElementStrictMode(shouldThrow) {
7712
- shouldThrowErrorOnUnknownElement = shouldThrow;
7713
- }
7714
- /**
7715
- * Gets the current value of the strict mode.
7716
- */
7717
- function ɵgetUnknownElementStrictMode() {
7718
- return shouldThrowErrorOnUnknownElement;
7719
- }
7720
- let shouldThrowErrorOnUnknownProperty = false;
7721
- /**
7722
- * Sets a strict mode for JIT-compiled components to throw an error on unknown properties,
7723
- * instead of just logging the error.
7724
- * (for AOT-compiled ones this check happens at build time).
7725
- */
7726
- function ɵsetUnknownPropertyStrictMode(shouldThrow) {
7727
- shouldThrowErrorOnUnknownProperty = shouldThrow;
7728
- }
7729
- /**
7730
- * Gets the current value of the strict mode.
7731
- */
7732
- function ɵgetUnknownPropertyStrictMode() {
7733
- return shouldThrowErrorOnUnknownProperty;
7734
- }
7735
7752
  /**
7736
- * Validates that the element is known at runtime and produces
7737
- * an error if it's not the case.
7738
- * This check is relevant for JIT-compiled components (for AOT-compiled
7739
- * ones this check happens at build time).
7753
+ * Returns the matching `LContext` data for a given DOM node, directive or component instance.
7740
7754
  *
7741
- * The element is considered known if either:
7742
- * - it's a known HTML element
7743
- * - it's a known custom element
7744
- * - the element matches any directive
7745
- * - the element is allowed by one of the schemas
7755
+ * This function will examine the provided DOM element, component, or directive instance\'s
7756
+ * monkey-patched property to derive the `LContext` data. Once called then the monkey-patched
7757
+ * value will be that of the newly created `LContext`.
7746
7758
  *
7747
- * @param element Element to validate
7748
- * @param lView An `LView` that represents a current component that is being rendered
7749
- * @param tagName Name of the tag to check
7750
- * @param schemas Array of schemas
7751
- * @param hasDirectives Boolean indicating that the element matches any directive
7759
+ * If the monkey-patched value is the `LView` instance then the context value for that
7760
+ * target will be created and the monkey-patch reference will be updated. Therefore when this
7761
+ * function is called it may mutate the provided element\'s, component\'s or any of the associated
7762
+ * directive\'s monkey-patch values.
7763
+ *
7764
+ * If the monkey-patch value is not detected then the code will walk up the DOM until an element
7765
+ * is found which contains a monkey-patch reference. When that occurs then the provided element
7766
+ * will be updated with a new context (which is then returned). If the monkey-patch value is not
7767
+ * detected for a component/directive instance then it will throw an error (all components and
7768
+ * directives should be automatically monkey-patched by ivy).
7769
+ *
7770
+ * @param target Component, Directive or DOM Node.
7752
7771
  */
7753
- function validateElementIsKnown(element, lView, tagName, schemas, hasDirectives) {
7754
- // If `schemas` is set to `null`, that's an indication that this Component was compiled in AOT
7755
- // mode where this check happens at compile time. In JIT mode, `schemas` is always present and
7756
- // defined as an array (as an empty array in case `schemas` field is not defined) and we should
7757
- // execute the check below.
7758
- if (schemas === null)
7759
- return;
7760
- // If the element matches any directive, it's considered as valid.
7761
- if (!hasDirectives && tagName !== null) {
7762
- // The element is unknown if it's an instance of HTMLUnknownElement, or it isn't registered
7763
- // as a custom element. Note that unknown elements with a dash in their name won't be instances
7764
- // of HTMLUnknownElement in browsers that support web components.
7765
- const isUnknown =
7766
- // Note that we can't check for `typeof HTMLUnknownElement === 'function'`,
7767
- // because while most browsers return 'function', IE returns 'object'.
7768
- (typeof HTMLUnknownElement !== 'undefined' && HTMLUnknownElement &&
7769
- element instanceof HTMLUnknownElement) ||
7770
- (typeof customElements !== 'undefined' && tagName.indexOf('-') > -1 &&
7771
- !customElements.get(tagName));
7772
- if (isUnknown && !matchingSchemas(schemas, tagName)) {
7773
- const isHostStandalone = isHostComponentStandalone(lView);
7774
- const templateLocation = getTemplateLocationDetails(lView);
7775
- const schemas = `'${isHostStandalone ? '@Component' : '@NgModule'}.schemas'`;
7776
- let message = `'${tagName}' is not a known element${templateLocation}:\n`;
7777
- message += `1. If '${tagName}' is an Angular component, then verify that it is ${isHostStandalone ? 'included in the \'@Component.imports\' of this component' :
7778
- 'a part of an @NgModule where this component is declared'}.\n`;
7779
- if (tagName && tagName.indexOf('-') > -1) {
7780
- message +=
7781
- `2. If '${tagName}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the ${schemas} of this component to suppress this message.`;
7772
+ function getLContext(target) {
7773
+ let mpValue = readPatchedData(target);
7774
+ if (mpValue) {
7775
+ // only when it's an array is it considered an LView instance
7776
+ // ... otherwise it's an already constructed LContext instance
7777
+ if (isLView(mpValue)) {
7778
+ const lView = mpValue;
7779
+ let nodeIndex;
7780
+ let component = undefined;
7781
+ let directives = undefined;
7782
+ if (isComponentInstance(target)) {
7783
+ nodeIndex = findViaComponent(lView, target);
7784
+ if (nodeIndex == -1) {
7785
+ throw new Error('The provided component was not found in the application');
7786
+ }
7787
+ component = target;
7788
+ }
7789
+ else if (isDirectiveInstance(target)) {
7790
+ nodeIndex = findViaDirective(lView, target);
7791
+ if (nodeIndex == -1) {
7792
+ throw new Error('The provided directive was not found in the application');
7793
+ }
7794
+ directives = getDirectivesAtNodeIndex(nodeIndex, lView);
7782
7795
  }
7783
7796
  else {
7784
- message +=
7785
- `2. To allow any element add 'NO_ERRORS_SCHEMA' to the ${schemas} of this component.`;
7797
+ nodeIndex = findViaNativeElement(lView, target);
7798
+ if (nodeIndex == -1) {
7799
+ return null;
7800
+ }
7786
7801
  }
7787
- if (shouldThrowErrorOnUnknownElement) {
7788
- throw new RuntimeError(304 /* RuntimeErrorCode.UNKNOWN_ELEMENT */, message);
7802
+ // the goal is not to fill the entire context full of data because the lookups
7803
+ // are expensive. Instead, only the target data (the element, component, container, ICU
7804
+ // expression or directive details) are filled into the context. If called multiple times
7805
+ // with different target values then the missing target data will be filled in.
7806
+ const native = unwrapRNode(lView[nodeIndex]);
7807
+ const existingCtx = readPatchedData(native);
7808
+ const context = (existingCtx && !Array.isArray(existingCtx)) ?
7809
+ existingCtx :
7810
+ createLContext(lView, nodeIndex, native);
7811
+ // only when the component has been discovered then update the monkey-patch
7812
+ if (component && context.component === undefined) {
7813
+ context.component = component;
7814
+ attachPatchData(context.component, context);
7789
7815
  }
7790
- else {
7791
- console.error(formatRuntimeError(304 /* RuntimeErrorCode.UNKNOWN_ELEMENT */, message));
7816
+ // only when the directives have been discovered then update the monkey-patch
7817
+ if (directives && context.directives === undefined) {
7818
+ context.directives = directives;
7819
+ for (let i = 0; i < directives.length; i++) {
7820
+ attachPatchData(directives[i], context);
7821
+ }
7822
+ }
7823
+ attachPatchData(context.native, context);
7824
+ mpValue = context;
7825
+ }
7826
+ }
7827
+ else {
7828
+ const rElement = target;
7829
+ ngDevMode && assertDomNode(rElement);
7830
+ // if the context is not found then we need to traverse upwards up the DOM
7831
+ // to find the nearest element that has already been monkey patched with data
7832
+ let parent = rElement;
7833
+ while (parent = parent.parentNode) {
7834
+ const parentContext = readPatchedData(parent);
7835
+ if (parentContext) {
7836
+ const lView = Array.isArray(parentContext) ? parentContext : parentContext.lView;
7837
+ // the edge of the app was also reached here through another means
7838
+ // (maybe because the DOM was changed manually).
7839
+ if (!lView) {
7840
+ return null;
7841
+ }
7842
+ const index = findViaNativeElement(lView, rElement);
7843
+ if (index >= 0) {
7844
+ const native = unwrapRNode(lView[index]);
7845
+ const context = createLContext(lView, index, native);
7846
+ attachPatchData(native, context);
7847
+ mpValue = context;
7848
+ break;
7849
+ }
7792
7850
  }
7793
7851
  }
7794
7852
  }
7853
+ return mpValue || null;
7795
7854
  }
7796
7855
  /**
7797
- * Validates that the property of the element is known at runtime and returns
7798
- * false if it's not the case.
7799
- * This check is relevant for JIT-compiled components (for AOT-compiled
7800
- * ones this check happens at build time).
7856
+ * Creates an empty instance of a `LContext` context
7857
+ */
7858
+ function createLContext(lView, nodeIndex, native) {
7859
+ return new LContext(lView[ID], nodeIndex, native);
7860
+ }
7861
+ /**
7862
+ * Takes a component instance and returns the view for that component.
7801
7863
  *
7802
- * The property is considered known if either:
7803
- * - it's a known property of the element
7804
- * - the element is allowed by one of the schemas
7805
- * - the property is used for animations
7806
- *
7807
- * @param element Element to validate
7808
- * @param propName Name of the property to check
7809
- * @param tagName Name of the tag hosting the property
7810
- * @param schemas Array of schemas
7811
- */
7812
- function isPropertyValid(element, propName, tagName, schemas) {
7813
- // If `schemas` is set to `null`, that's an indication that this Component was compiled in AOT
7814
- // mode where this check happens at compile time. In JIT mode, `schemas` is always present and
7815
- // defined as an array (as an empty array in case `schemas` field is not defined) and we should
7816
- // execute the check below.
7817
- if (schemas === null)
7818
- return true;
7819
- // The property is considered valid if the element matches the schema, it exists on the element,
7820
- // or it is synthetic, and we are in a browser context (web worker nodes should be skipped).
7821
- if (matchingSchemas(schemas, tagName) || propName in element || isAnimationProp(propName)) {
7822
- return true;
7823
- }
7824
- // Note: `typeof Node` returns 'function' in most browsers, but on IE it is 'object' so we
7825
- // need to account for both here, while being careful with `typeof null` also returning 'object'.
7826
- return typeof Node === 'undefined' || Node === null || !(element instanceof Node);
7827
- }
7828
- /**
7829
- * Logs or throws an error that a property is not supported on an element.
7830
- *
7831
- * @param propName Name of the invalid property
7832
- * @param tagName Name of the tag hosting the property
7833
- * @param nodeType Type of the node hosting the property
7834
- * @param lView An `LView` that represents a current component
7864
+ * @param componentInstance
7865
+ * @returns The component's view
7835
7866
  */
7836
- function handleUnknownPropertyError(propName, tagName, nodeType, lView) {
7837
- // Special-case a situation when a structural directive is applied to
7838
- // an `<ng-template>` element, for example: `<ng-template *ngIf="true">`.
7839
- // In this case the compiler generates the `ɵɵtemplate` instruction with
7840
- // the `null` as the tagName. The directive matching logic at runtime relies
7841
- // on this effect (see `isInlineTemplate`), thus using the 'ng-template' as
7842
- // a default value of the `tNode.value` is not feasible at this moment.
7843
- if (!tagName && nodeType === 4 /* TNodeType.Container */) {
7844
- tagName = 'ng-template';
7845
- }
7846
- const isHostStandalone = isHostComponentStandalone(lView);
7847
- const templateLocation = getTemplateLocationDetails(lView);
7848
- let message = `Can't bind to '${propName}' since it isn't a known property of '${tagName}'${templateLocation}.`;
7849
- const schemas = `'${isHostStandalone ? '@Component' : '@NgModule'}.schemas'`;
7850
- const importLocation = isHostStandalone ?
7851
- 'included in the \'@Component.imports\' of this component' :
7852
- 'a part of an @NgModule where this component is declared';
7853
- if (KNOWN_CONTROL_FLOW_DIRECTIVES.has(propName)) {
7854
- // Most likely this is a control flow directive (such as `*ngIf`) used in
7855
- // a template, but the directive or the `CommonModule` is not imported.
7856
- const correspondingImport = KNOWN_CONTROL_FLOW_DIRECTIVES.get(propName);
7857
- message += `\nIf the '${propName}' is an Angular control flow directive, ` +
7858
- `please make sure that either the '${correspondingImport}' directive or the 'CommonModule' is ${importLocation}.`;
7867
+ function getComponentViewByInstance(componentInstance) {
7868
+ let patchedData = readPatchedData(componentInstance);
7869
+ let lView;
7870
+ if (isLView(patchedData)) {
7871
+ const contextLView = patchedData;
7872
+ const nodeIndex = findViaComponent(contextLView, componentInstance);
7873
+ lView = getComponentLViewByIndex(nodeIndex, contextLView);
7874
+ const context = createLContext(contextLView, nodeIndex, lView[HOST]);
7875
+ context.component = componentInstance;
7876
+ attachPatchData(componentInstance, context);
7877
+ attachPatchData(context.native, context);
7859
7878
  }
7860
7879
  else {
7861
- // May be an Angular component, which is not imported/declared?
7862
- message += `\n1. If '${tagName}' is an Angular component and it has the ` +
7863
- `'${propName}' input, then verify that it is ${importLocation}.`;
7864
- // May be a Web Component?
7865
- if (tagName && tagName.indexOf('-') > -1) {
7866
- message += `\n2. If '${tagName}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' ` +
7867
- `to the ${schemas} of this component to suppress this message.`;
7868
- message += `\n3. To allow any property add 'NO_ERRORS_SCHEMA' to ` +
7869
- `the ${schemas} of this component.`;
7870
- }
7871
- else {
7872
- // If it's expected, the error can be suppressed by the `NO_ERRORS_SCHEMA` schema.
7873
- message += `\n2. To allow any property add 'NO_ERRORS_SCHEMA' to ` +
7874
- `the ${schemas} of this component.`;
7875
- }
7880
+ const context = patchedData;
7881
+ const contextLView = context.lView;
7882
+ ngDevMode && assertLView(contextLView);
7883
+ lView = getComponentLViewByIndex(context.nodeIndex, contextLView);
7876
7884
  }
7877
- reportUnknownPropertyError(message);
7885
+ return lView;
7878
7886
  }
7879
- function reportUnknownPropertyError(message) {
7880
- if (shouldThrowErrorOnUnknownProperty) {
7881
- throw new RuntimeError(303 /* RuntimeErrorCode.UNKNOWN_BINDING */, message);
7887
+ /**
7888
+ * This property will be monkey-patched on elements, components and directives.
7889
+ */
7890
+ const MONKEY_PATCH_KEY_NAME = '__ngContext__';
7891
+ /**
7892
+ * Assigns the given data to the given target (which could be a component,
7893
+ * directive or DOM node instance) using monkey-patching.
7894
+ */
7895
+ function attachPatchData(target, data) {
7896
+ ngDevMode && assertDefined(target, 'Target expected');
7897
+ // Only attach the ID of the view in order to avoid memory leaks (see #41047). We only do this
7898
+ // for `LView`, because we have control over when an `LView` is created and destroyed, whereas
7899
+ // we can't know when to remove an `LContext`.
7900
+ if (isLView(data)) {
7901
+ target[MONKEY_PATCH_KEY_NAME] = data[ID];
7902
+ registerLView(data);
7882
7903
  }
7883
7904
  else {
7884
- console.error(formatRuntimeError(303 /* RuntimeErrorCode.UNKNOWN_BINDING */, message));
7905
+ target[MONKEY_PATCH_KEY_NAME] = data;
7885
7906
  }
7886
7907
  }
7887
7908
  /**
7888
- * WARNING: this is a **dev-mode only** function (thus should always be guarded by the `ngDevMode`)
7889
- * and must **not** be used in production bundles. The function makes megamorphic reads, which might
7890
- * be too slow for production mode and also it relies on the constructor function being available.
7891
- *
7892
- * Gets a reference to the host component def (where a current component is declared).
7893
- *
7894
- * @param lView An `LView` that represents a current component that is being rendered.
7909
+ * Returns the monkey-patch value data present on the target (which could be
7910
+ * a component, directive or a DOM node).
7895
7911
  */
7896
- function getDeclarationComponentDef(lView) {
7897
- !ngDevMode && throwError('Must never be called in production mode');
7898
- const declarationLView = lView[DECLARATION_COMPONENT_VIEW];
7899
- const context = declarationLView[CONTEXT];
7900
- // Unable to obtain a context.
7901
- if (!context)
7902
- return null;
7903
- return context.constructor ? getComponentDef$1(context.constructor) : null;
7912
+ function readPatchedData(target) {
7913
+ ngDevMode && assertDefined(target, 'Target expected');
7914
+ const data = target[MONKEY_PATCH_KEY_NAME];
7915
+ return (typeof data === 'number') ? getLViewById(data) : data || null;
7916
+ }
7917
+ function readPatchedLView(target) {
7918
+ const value = readPatchedData(target);
7919
+ if (value) {
7920
+ return (isLView(value) ? value : value.lView);
7921
+ }
7922
+ return null;
7923
+ }
7924
+ function isComponentInstance(instance) {
7925
+ return instance && instance.constructor && instance.constructor.ɵcmp;
7926
+ }
7927
+ function isDirectiveInstance(instance) {
7928
+ return instance && instance.constructor && instance.constructor.ɵdir;
7904
7929
  }
7905
7930
  /**
7906
- * WARNING: this is a **dev-mode only** function (thus should always be guarded by the `ngDevMode`)
7907
- * and must **not** be used in production bundles. The function makes megamorphic reads, which might
7908
- * be too slow for production mode.
7909
- *
7910
- * Checks if the current component is declared inside of a standalone component template.
7911
- *
7912
- * @param lView An `LView` that represents a current component that is being rendered.
7931
+ * Locates the element within the given LView and returns the matching index
7913
7932
  */
7914
- function isHostComponentStandalone(lView) {
7915
- !ngDevMode && throwError('Must never be called in production mode');
7916
- const componentDef = getDeclarationComponentDef(lView);
7917
- // Treat host component as non-standalone if we can't obtain the def.
7918
- return !!componentDef?.standalone;
7933
+ function findViaNativeElement(lView, target) {
7934
+ const tView = lView[TVIEW];
7935
+ for (let i = HEADER_OFFSET; i < tView.bindingStartIndex; i++) {
7936
+ if (unwrapRNode(lView[i]) === target) {
7937
+ return i;
7938
+ }
7939
+ }
7940
+ return -1;
7919
7941
  }
7920
7942
  /**
7921
- * WARNING: this is a **dev-mode only** function (thus should always be guarded by the `ngDevMode`)
7922
- * and must **not** be used in production bundles. The function makes megamorphic reads, which might
7923
- * be too slow for production mode.
7924
- *
7925
- * Constructs a string describing the location of the host component template. The function is used
7926
- * in dev mode to produce error messages.
7927
- *
7928
- * @param lView An `LView` that represents a current component that is being rendered.
7943
+ * Locates the next tNode (child, sibling or parent).
7929
7944
  */
7930
- function getTemplateLocationDetails(lView) {
7931
- !ngDevMode && throwError('Must never be called in production mode');
7932
- const hostComponentDef = getDeclarationComponentDef(lView);
7933
- const componentClassName = hostComponentDef?.type?.name;
7934
- return componentClassName ? ` (used in the '${componentClassName}' component template)` : '';
7945
+ function traverseNextElement(tNode) {
7946
+ if (tNode.child) {
7947
+ return tNode.child;
7948
+ }
7949
+ else if (tNode.next) {
7950
+ return tNode.next;
7951
+ }
7952
+ else {
7953
+ // Let's take the following template: <div><span>text</span></div><component/>
7954
+ // After checking the text node, we need to find the next parent that has a "next" TNode,
7955
+ // in this case the parent `div`, so that we can find the component.
7956
+ while (tNode.parent && !tNode.parent.next) {
7957
+ tNode = tNode.parent;
7958
+ }
7959
+ return tNode.parent && tNode.parent.next;
7960
+ }
7935
7961
  }
7936
7962
  /**
7937
- * The set of known control flow directives and their corresponding imports.
7938
- * We use this set to produce a more precises error message with a note
7939
- * that the `CommonModule` should also be included.
7963
+ * Locates the component within the given LView and returns the matching index
7940
7964
  */
7941
- const KNOWN_CONTROL_FLOW_DIRECTIVES = new Map([
7942
- ['ngIf', 'NgIf'], ['ngFor', 'NgFor'], ['ngSwitchCase', 'NgSwitchCase'],
7943
- ['ngSwitchDefault', 'NgSwitchDefault']
7944
- ]);
7965
+ function findViaComponent(lView, componentInstance) {
7966
+ const componentIndices = lView[TVIEW].components;
7967
+ if (componentIndices) {
7968
+ for (let i = 0; i < componentIndices.length; i++) {
7969
+ const elementComponentIndex = componentIndices[i];
7970
+ const componentView = getComponentLViewByIndex(elementComponentIndex, lView);
7971
+ if (componentView[CONTEXT] === componentInstance) {
7972
+ return elementComponentIndex;
7973
+ }
7974
+ }
7975
+ }
7976
+ else {
7977
+ const rootComponentView = getComponentLViewByIndex(HEADER_OFFSET, lView);
7978
+ const rootComponent = rootComponentView[CONTEXT];
7979
+ if (rootComponent === componentInstance) {
7980
+ // we are dealing with the root element here therefore we know that the
7981
+ // element is the very first element after the HEADER data in the lView
7982
+ return HEADER_OFFSET;
7983
+ }
7984
+ }
7985
+ return -1;
7986
+ }
7945
7987
  /**
7946
- * Returns true if the tag name is allowed by specified schemas.
7947
- * @param schemas Array of schemas
7948
- * @param tagName Name of the tag
7988
+ * Locates the directive within the given LView and returns the matching index
7949
7989
  */
7950
- function matchingSchemas(schemas, tagName) {
7951
- if (schemas !== null) {
7952
- for (let i = 0; i < schemas.length; i++) {
7953
- const schema = schemas[i];
7954
- if (schema === NO_ERRORS_SCHEMA ||
7955
- schema === CUSTOM_ELEMENTS_SCHEMA && tagName && tagName.indexOf('-') > -1) {
7956
- return true;
7990
+ function findViaDirective(lView, directiveInstance) {
7991
+ // if a directive is monkey patched then it will (by default)
7992
+ // have a reference to the LView of the current view. The
7993
+ // element bound to the directive being search lives somewhere
7994
+ // in the view data. We loop through the nodes and check their
7995
+ // list of directives for the instance.
7996
+ let tNode = lView[TVIEW].firstChild;
7997
+ while (tNode) {
7998
+ const directiveIndexStart = tNode.directiveStart;
7999
+ const directiveIndexEnd = tNode.directiveEnd;
8000
+ for (let i = directiveIndexStart; i < directiveIndexEnd; i++) {
8001
+ if (lView[i] === directiveInstance) {
8002
+ return tNode.index;
7957
8003
  }
7958
8004
  }
8005
+ tNode = traverseNextElement(tNode);
7959
8006
  }
7960
- return false;
8007
+ return -1;
7961
8008
  }
7962
-
7963
8009
  /**
7964
- * @license
7965
- * Copyright Google LLC All Rights Reserved.
8010
+ * Returns a list of directives applied to a node at a specific index. The list includes
8011
+ * directives matched by selector and any host directives, but it excludes components.
8012
+ * Use `getComponentAtNodeIndex` to find the component applied to a node.
7966
8013
  *
7967
- * Use of this source code is governed by an MIT-style license that can be
7968
- * found in the LICENSE file at https://angular.io/license
8014
+ * @param nodeIndex The node index
8015
+ * @param lView The target view data
7969
8016
  */
7970
- const ERROR_ORIGINAL_ERROR = 'ngOriginalError';
7971
- function wrappedError(message, originalError) {
7972
- const msg = `${message} caused by: ${originalError instanceof Error ? originalError.message : originalError}`;
7973
- const error = Error(msg);
7974
- error[ERROR_ORIGINAL_ERROR] = originalError;
7975
- return error;
8017
+ function getDirectivesAtNodeIndex(nodeIndex, lView) {
8018
+ const tNode = lView[TVIEW].data[nodeIndex];
8019
+ if (tNode.directiveStart === 0)
8020
+ return EMPTY_ARRAY;
8021
+ const results = [];
8022
+ for (let i = tNode.directiveStart; i < tNode.directiveEnd; i++) {
8023
+ const directiveInstance = lView[i];
8024
+ if (!isComponentInstance(directiveInstance)) {
8025
+ results.push(directiveInstance);
8026
+ }
8027
+ }
8028
+ return results;
7976
8029
  }
7977
- function getOriginalError(error) {
7978
- return error[ERROR_ORIGINAL_ERROR];
8030
+ function getComponentAtNodeIndex(nodeIndex, lView) {
8031
+ const tNode = lView[TVIEW].data[nodeIndex];
8032
+ const { directiveStart, componentOffset } = tNode;
8033
+ return componentOffset > -1 ? lView[directiveStart + componentOffset] : null;
7979
8034
  }
7980
-
7981
- /**
7982
- * @license
7983
- * Copyright Google LLC All Rights Reserved.
7984
- *
7985
- * Use of this source code is governed by an MIT-style license that can be
7986
- * found in the LICENSE file at https://angular.io/license
7987
- */
7988
8035
  /**
7989
- * Provides a hook for centralized exception handling.
7990
- *
7991
- * The default implementation of `ErrorHandler` prints error messages to the `console`. To
7992
- * intercept error handling, write a custom exception handler that replaces this default as
7993
- * appropriate for your app.
7994
- *
7995
- * @usageNotes
7996
- * ### Example
7997
- *
7998
- * ```
7999
- * class MyErrorHandler implements ErrorHandler {
8000
- * handleError(error) {
8001
- * // do something with the exception
8002
- * }
8003
- * }
8004
- *
8005
- * @NgModule({
8006
- * providers: [{provide: ErrorHandler, useClass: MyErrorHandler}]
8007
- * })
8008
- * class MyModule {}
8009
- * ```
8010
- *
8011
- * @publicApi
8036
+ * Returns a map of local references (local reference name => element or directive instance) that
8037
+ * exist on a given element.
8012
8038
  */
8013
- class ErrorHandler {
8014
- constructor() {
8015
- /**
8016
- * @internal
8017
- */
8018
- this._console = console;
8019
- }
8020
- handleError(error) {
8021
- const originalError = this._findOriginalError(error);
8022
- this._console.error('ERROR', error);
8023
- if (originalError) {
8024
- this._console.error('ORIGINAL ERROR', originalError);
8025
- }
8026
- }
8027
- /** @internal */
8028
- _findOriginalError(error) {
8029
- let e = error && getOriginalError(error);
8030
- while (e && getOriginalError(e)) {
8031
- e = getOriginalError(e);
8039
+ function discoverLocalRefs(lView, nodeIndex) {
8040
+ const tNode = lView[TVIEW].data[nodeIndex];
8041
+ if (tNode && tNode.localNames) {
8042
+ const result = {};
8043
+ let localIndex = tNode.index + 1;
8044
+ for (let i = 0; i < tNode.localNames.length; i += 2) {
8045
+ result[tNode.localNames[i]] = lView[localIndex];
8046
+ localIndex++;
8032
8047
  }
8033
- return e || null;
8048
+ return result;
8034
8049
  }
8050
+ return null;
8035
8051
  }
8036
8052
 
8037
8053
  /**
@@ -8042,46 +8058,28 @@ class ErrorHandler {
8042
8058
  * found in the LICENSE file at https://angular.io/license
8043
8059
  */
8044
8060
  /**
8045
- * Disallowed strings in the comment.
8061
+ * Defines a schema that allows an NgModule to contain the following:
8062
+ * - Non-Angular elements named with dash case (`-`).
8063
+ * - Element properties named with dash case (`-`).
8064
+ * Dash case is the naming convention for custom elements.
8046
8065
  *
8047
- * see: https://html.spec.whatwg.org/multipage/syntax.html#comments
8048
- */
8049
- const COMMENT_DISALLOWED = /^>|^->|<!--|-->|--!>|<!-$/g;
8050
- /**
8051
- * Delimiter in the disallowed strings which needs to be wrapped with zero with character.
8066
+ * @publicApi
8052
8067
  */
8053
- const COMMENT_DELIMITER = /(<|>)/;
8054
- const COMMENT_DELIMITER_ESCAPED = '\u200B$1\u200B';
8068
+ const CUSTOM_ELEMENTS_SCHEMA = {
8069
+ name: 'custom-elements'
8070
+ };
8055
8071
  /**
8056
- * Escape the content of comment strings so that it can be safely inserted into a comment node.
8057
- *
8058
- * The issue is that HTML does not specify any way to escape comment end text inside the comment.
8059
- * Consider: `<!-- The way you close a comment is with ">", and "->" at the beginning or by "-->" or
8060
- * "--!>" at the end. -->`. Above the `"-->"` is meant to be text not an end to the comment. This
8061
- * can be created programmatically through DOM APIs. (`<!--` are also disallowed.)
8062
- *
8063
- * see: https://html.spec.whatwg.org/multipage/syntax.html#comments
8064
- *
8065
- * ```
8066
- * div.innerHTML = div.innerHTML
8067
- * ```
8068
- *
8069
- * One would expect that the above code would be safe to do, but it turns out that because comment
8070
- * text is not escaped, the comment may contain text which will prematurely close the comment
8071
- * opening up the application for XSS attack. (In SSR we programmatically create comment nodes which
8072
- * may contain such text and expect them to be safe.)
8072
+ * Defines a schema that allows any property on any element.
8073
8073
  *
8074
- * This function escapes the comment text by looking for comment delimiters (`<` and `>`) and
8075
- * surrounding them with `_>_` where the `_` is a zero width space `\u200B`. The result is that if a
8076
- * comment contains any of the comment start/end delimiters (such as `<!--`, `-->` or `--!>`) the
8077
- * text it will render normally but it will not cause the HTML parser to close/open the comment.
8074
+ * This schema allows you to ignore the errors related to any unknown elements or properties in a
8075
+ * template. The usage of this schema is generally discouraged because it prevents useful validation
8076
+ * and may hide real errors in your template. Consider using the `CUSTOM_ELEMENTS_SCHEMA` instead.
8078
8077
  *
8079
- * @param value text to make safe for comment node by escaping the comment open/close character
8080
- * sequence.
8078
+ * @publicApi
8081
8079
  */
8082
- function escapeCommentText(value) {
8083
- return value.replace(COMMENT_DISALLOWED, (text) => text.replace(COMMENT_DELIMITER, COMMENT_DELIMITER_ESCAPED));
8084
- }
8080
+ const NO_ERRORS_SCHEMA = {
8081
+ name: 'no-errors-schema'
8082
+ };
8085
8083
 
8086
8084
  /**
8087
8085
  * @license
@@ -8090,400 +8088,411 @@ function escapeCommentText(value) {
8090
8088
  * Use of this source code is governed by an MIT-style license that can be
8091
8089
  * found in the LICENSE file at https://angular.io/license
8092
8090
  */
8093
- function normalizeDebugBindingName(name) {
8094
- // Attribute names with `$` (eg `x-y$`) are valid per spec, but unsupported by some browsers
8095
- name = camelCaseToDashCase(name.replace(/[$@]/g, '_'));
8096
- return `ng-reflect-${name}`;
8097
- }
8098
- const CAMEL_CASE_REGEXP = /([A-Z])/g;
8099
- function camelCaseToDashCase(input) {
8100
- return input.replace(CAMEL_CASE_REGEXP, (...m) => '-' + m[1].toLowerCase());
8101
- }
8102
- function normalizeDebugBindingValue(value) {
8103
- try {
8104
- // Limit the size of the value as otherwise the DOM just gets polluted.
8105
- return value != null ? value.toString().slice(0, 30) : value;
8106
- }
8107
- catch (e) {
8108
- return '[ERROR] Exception while trying to serialize the value';
8109
- }
8110
- }
8111
-
8091
+ let shouldThrowErrorOnUnknownElement = false;
8112
8092
  /**
8113
- * @license
8114
- * Copyright Google LLC All Rights Reserved.
8115
- *
8116
- * Use of this source code is governed by an MIT-style license that can be
8117
- * found in the LICENSE file at https://angular.io/license
8093
+ * Sets a strict mode for JIT-compiled components to throw an error on unknown elements,
8094
+ * instead of just logging the error.
8095
+ * (for AOT-compiled ones this check happens at build time).
8118
8096
  */
8119
- // Keeps track of the currently-active LViews.
8120
- const TRACKED_LVIEWS = new Map();
8121
- // Used for generating unique IDs for LViews.
8122
- let uniqueIdCounter = 0;
8123
- /** Gets a unique ID that can be assigned to an LView. */
8124
- function getUniqueLViewId() {
8125
- return uniqueIdCounter++;
8126
- }
8127
- /** Starts tracking an LView. */
8128
- function registerLView(lView) {
8129
- ngDevMode && assertNumber(lView[ID], 'LView must have an ID in order to be registered');
8130
- TRACKED_LVIEWS.set(lView[ID], lView);
8131
- }
8132
- /** Gets an LView by its unique ID. */
8133
- function getLViewById(id) {
8134
- ngDevMode && assertNumber(id, 'ID used for LView lookup must be a number');
8135
- return TRACKED_LVIEWS.get(id) || null;
8136
- }
8137
- /** Stops tracking an LView. */
8138
- function unregisterLView(lView) {
8139
- ngDevMode && assertNumber(lView[ID], 'Cannot stop tracking an LView that does not have an ID');
8140
- TRACKED_LVIEWS.delete(lView[ID]);
8097
+ function ɵsetUnknownElementStrictMode(shouldThrow) {
8098
+ shouldThrowErrorOnUnknownElement = shouldThrow;
8141
8099
  }
8142
-
8143
8100
  /**
8144
- * @license
8145
- * Copyright Google LLC All Rights Reserved.
8146
- *
8147
- * Use of this source code is governed by an MIT-style license that can be
8148
- * found in the LICENSE file at https://angular.io/license
8101
+ * Gets the current value of the strict mode.
8149
8102
  */
8103
+ function ɵgetUnknownElementStrictMode() {
8104
+ return shouldThrowErrorOnUnknownElement;
8105
+ }
8106
+ let shouldThrowErrorOnUnknownProperty = false;
8150
8107
  /**
8151
- * The internal view context which is specific to a given DOM element, directive or
8152
- * component instance. Each value in here (besides the LView and element node details)
8153
- * can be present, null or undefined. If undefined then it implies the value has not been
8154
- * looked up yet, otherwise, if null, then a lookup was executed and nothing was found.
8155
- *
8156
- * Each value will get filled when the respective value is examined within the getContext
8157
- * function. The component, element and each directive instance will share the same instance
8158
- * of the context.
8108
+ * Sets a strict mode for JIT-compiled components to throw an error on unknown properties,
8109
+ * instead of just logging the error.
8110
+ * (for AOT-compiled ones this check happens at build time).
8159
8111
  */
8160
- class LContext {
8161
- constructor(
8162
- /**
8163
- * ID of the component's parent view data.
8164
- */
8165
- lViewId,
8166
- /**
8167
- * The index instance of the node.
8168
- */
8169
- nodeIndex,
8170
- /**
8171
- * The instance of the DOM node that is attached to the lNode.
8172
- */
8173
- native) {
8174
- this.lViewId = lViewId;
8175
- this.nodeIndex = nodeIndex;
8176
- this.native = native;
8177
- }
8178
- /** Component's parent view data. */
8179
- get lView() {
8180
- return getLViewById(this.lViewId);
8181
- }
8112
+ function ɵsetUnknownPropertyStrictMode(shouldThrow) {
8113
+ shouldThrowErrorOnUnknownProperty = shouldThrow;
8182
8114
  }
8183
-
8184
8115
  /**
8185
- * @license
8186
- * Copyright Google LLC All Rights Reserved.
8187
- *
8188
- * Use of this source code is governed by an MIT-style license that can be
8189
- * found in the LICENSE file at https://angular.io/license
8116
+ * Gets the current value of the strict mode.
8190
8117
  */
8118
+ function ɵgetUnknownPropertyStrictMode() {
8119
+ return shouldThrowErrorOnUnknownProperty;
8120
+ }
8191
8121
  /**
8192
- * Returns the matching `LContext` data for a given DOM node, directive or component instance.
8122
+ * Validates that the element is known at runtime and produces
8123
+ * an error if it's not the case.
8124
+ * This check is relevant for JIT-compiled components (for AOT-compiled
8125
+ * ones this check happens at build time).
8193
8126
  *
8194
- * This function will examine the provided DOM element, component, or directive instance\'s
8195
- * monkey-patched property to derive the `LContext` data. Once called then the monkey-patched
8196
- * value will be that of the newly created `LContext`.
8127
+ * The element is considered known if either:
8128
+ * - it's a known HTML element
8129
+ * - it's a known custom element
8130
+ * - the element matches any directive
8131
+ * - the element is allowed by one of the schemas
8197
8132
  *
8198
- * If the monkey-patched value is the `LView` instance then the context value for that
8199
- * target will be created and the monkey-patch reference will be updated. Therefore when this
8200
- * function is called it may mutate the provided element\'s, component\'s or any of the associated
8201
- * directive\'s monkey-patch values.
8202
- *
8203
- * If the monkey-patch value is not detected then the code will walk up the DOM until an element
8204
- * is found which contains a monkey-patch reference. When that occurs then the provided element
8205
- * will be updated with a new context (which is then returned). If the monkey-patch value is not
8206
- * detected for a component/directive instance then it will throw an error (all components and
8207
- * directives should be automatically monkey-patched by ivy).
8208
- *
8209
- * @param target Component, Directive or DOM Node.
8133
+ * @param element Element to validate
8134
+ * @param lView An `LView` that represents a current component that is being rendered
8135
+ * @param tagName Name of the tag to check
8136
+ * @param schemas Array of schemas
8137
+ * @param hasDirectives Boolean indicating that the element matches any directive
8210
8138
  */
8211
- function getLContext(target) {
8212
- let mpValue = readPatchedData(target);
8213
- if (mpValue) {
8214
- // only when it's an array is it considered an LView instance
8215
- // ... otherwise it's an already constructed LContext instance
8216
- if (isLView(mpValue)) {
8217
- const lView = mpValue;
8218
- let nodeIndex;
8219
- let component = undefined;
8220
- let directives = undefined;
8221
- if (isComponentInstance(target)) {
8222
- nodeIndex = findViaComponent(lView, target);
8223
- if (nodeIndex == -1) {
8224
- throw new Error('The provided component was not found in the application');
8225
- }
8226
- component = target;
8227
- }
8228
- else if (isDirectiveInstance(target)) {
8229
- nodeIndex = findViaDirective(lView, target);
8230
- if (nodeIndex == -1) {
8231
- throw new Error('The provided directive was not found in the application');
8232
- }
8233
- directives = getDirectivesAtNodeIndex(nodeIndex, lView, false);
8139
+ function validateElementIsKnown(element, lView, tagName, schemas, hasDirectives) {
8140
+ // If `schemas` is set to `null`, that's an indication that this Component was compiled in AOT
8141
+ // mode where this check happens at compile time. In JIT mode, `schemas` is always present and
8142
+ // defined as an array (as an empty array in case `schemas` field is not defined) and we should
8143
+ // execute the check below.
8144
+ if (schemas === null)
8145
+ return;
8146
+ // If the element matches any directive, it's considered as valid.
8147
+ if (!hasDirectives && tagName !== null) {
8148
+ // The element is unknown if it's an instance of HTMLUnknownElement, or it isn't registered
8149
+ // as a custom element. Note that unknown elements with a dash in their name won't be instances
8150
+ // of HTMLUnknownElement in browsers that support web components.
8151
+ const isUnknown =
8152
+ // Note that we can't check for `typeof HTMLUnknownElement === 'function'`,
8153
+ // because while most browsers return 'function', IE returns 'object'.
8154
+ (typeof HTMLUnknownElement !== 'undefined' && HTMLUnknownElement &&
8155
+ element instanceof HTMLUnknownElement) ||
8156
+ (typeof customElements !== 'undefined' && tagName.indexOf('-') > -1 &&
8157
+ !customElements.get(tagName));
8158
+ if (isUnknown && !matchingSchemas(schemas, tagName)) {
8159
+ const isHostStandalone = isHostComponentStandalone(lView);
8160
+ const templateLocation = getTemplateLocationDetails(lView);
8161
+ const schemas = `'${isHostStandalone ? '@Component' : '@NgModule'}.schemas'`;
8162
+ let message = `'${tagName}' is not a known element${templateLocation}:\n`;
8163
+ message += `1. If '${tagName}' is an Angular component, then verify that it is ${isHostStandalone ? 'included in the \'@Component.imports\' of this component' :
8164
+ 'a part of an @NgModule where this component is declared'}.\n`;
8165
+ if (tagName && tagName.indexOf('-') > -1) {
8166
+ message +=
8167
+ `2. If '${tagName}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the ${schemas} of this component to suppress this message.`;
8234
8168
  }
8235
8169
  else {
8236
- nodeIndex = findViaNativeElement(lView, target);
8237
- if (nodeIndex == -1) {
8238
- return null;
8239
- }
8240
- }
8241
- // the goal is not to fill the entire context full of data because the lookups
8242
- // are expensive. Instead, only the target data (the element, component, container, ICU
8243
- // expression or directive details) are filled into the context. If called multiple times
8244
- // with different target values then the missing target data will be filled in.
8245
- const native = unwrapRNode(lView[nodeIndex]);
8246
- const existingCtx = readPatchedData(native);
8247
- const context = (existingCtx && !Array.isArray(existingCtx)) ?
8248
- existingCtx :
8249
- createLContext(lView, nodeIndex, native);
8250
- // only when the component has been discovered then update the monkey-patch
8251
- if (component && context.component === undefined) {
8252
- context.component = component;
8253
- attachPatchData(context.component, context);
8170
+ message +=
8171
+ `2. To allow any element add 'NO_ERRORS_SCHEMA' to the ${schemas} of this component.`;
8254
8172
  }
8255
- // only when the directives have been discovered then update the monkey-patch
8256
- if (directives && context.directives === undefined) {
8257
- context.directives = directives;
8258
- for (let i = 0; i < directives.length; i++) {
8259
- attachPatchData(directives[i], context);
8260
- }
8173
+ if (shouldThrowErrorOnUnknownElement) {
8174
+ throw new RuntimeError(304 /* RuntimeErrorCode.UNKNOWN_ELEMENT */, message);
8261
8175
  }
8262
- attachPatchData(context.native, context);
8263
- mpValue = context;
8264
- }
8265
- }
8266
- else {
8267
- const rElement = target;
8268
- ngDevMode && assertDomNode(rElement);
8269
- // if the context is not found then we need to traverse upwards up the DOM
8270
- // to find the nearest element that has already been monkey patched with data
8271
- let parent = rElement;
8272
- while (parent = parent.parentNode) {
8273
- const parentContext = readPatchedData(parent);
8274
- if (parentContext) {
8275
- const lView = Array.isArray(parentContext) ? parentContext : parentContext.lView;
8276
- // the edge of the app was also reached here through another means
8277
- // (maybe because the DOM was changed manually).
8278
- if (!lView) {
8279
- return null;
8280
- }
8281
- const index = findViaNativeElement(lView, rElement);
8282
- if (index >= 0) {
8283
- const native = unwrapRNode(lView[index]);
8284
- const context = createLContext(lView, index, native);
8285
- attachPatchData(native, context);
8286
- mpValue = context;
8287
- break;
8288
- }
8176
+ else {
8177
+ console.error(formatRuntimeError(304 /* RuntimeErrorCode.UNKNOWN_ELEMENT */, message));
8289
8178
  }
8290
8179
  }
8291
8180
  }
8292
- return mpValue || null;
8293
8181
  }
8294
8182
  /**
8295
- * Creates an empty instance of a `LContext` context
8183
+ * Validates that the property of the element is known at runtime and returns
8184
+ * false if it's not the case.
8185
+ * This check is relevant for JIT-compiled components (for AOT-compiled
8186
+ * ones this check happens at build time).
8187
+ *
8188
+ * The property is considered known if either:
8189
+ * - it's a known property of the element
8190
+ * - the element is allowed by one of the schemas
8191
+ * - the property is used for animations
8192
+ *
8193
+ * @param element Element to validate
8194
+ * @param propName Name of the property to check
8195
+ * @param tagName Name of the tag hosting the property
8196
+ * @param schemas Array of schemas
8296
8197
  */
8297
- function createLContext(lView, nodeIndex, native) {
8298
- return new LContext(lView[ID], nodeIndex, native);
8198
+ function isPropertyValid(element, propName, tagName, schemas) {
8199
+ // If `schemas` is set to `null`, that's an indication that this Component was compiled in AOT
8200
+ // mode where this check happens at compile time. In JIT mode, `schemas` is always present and
8201
+ // defined as an array (as an empty array in case `schemas` field is not defined) and we should
8202
+ // execute the check below.
8203
+ if (schemas === null)
8204
+ return true;
8205
+ // The property is considered valid if the element matches the schema, it exists on the element,
8206
+ // or it is synthetic, and we are in a browser context (web worker nodes should be skipped).
8207
+ if (matchingSchemas(schemas, tagName) || propName in element || isAnimationProp(propName)) {
8208
+ return true;
8209
+ }
8210
+ // Note: `typeof Node` returns 'function' in most browsers, but on IE it is 'object' so we
8211
+ // need to account for both here, while being careful with `typeof null` also returning 'object'.
8212
+ return typeof Node === 'undefined' || Node === null || !(element instanceof Node);
8299
8213
  }
8300
8214
  /**
8301
- * Takes a component instance and returns the view for that component.
8215
+ * Logs or throws an error that a property is not supported on an element.
8302
8216
  *
8303
- * @param componentInstance
8304
- * @returns The component's view
8217
+ * @param propName Name of the invalid property
8218
+ * @param tagName Name of the tag hosting the property
8219
+ * @param nodeType Type of the node hosting the property
8220
+ * @param lView An `LView` that represents a current component
8305
8221
  */
8306
- function getComponentViewByInstance(componentInstance) {
8307
- let patchedData = readPatchedData(componentInstance);
8308
- let lView;
8309
- if (isLView(patchedData)) {
8310
- const contextLView = patchedData;
8311
- const nodeIndex = findViaComponent(contextLView, componentInstance);
8312
- lView = getComponentLViewByIndex(nodeIndex, contextLView);
8313
- const context = createLContext(contextLView, nodeIndex, lView[HOST]);
8314
- context.component = componentInstance;
8315
- attachPatchData(componentInstance, context);
8316
- attachPatchData(context.native, context);
8222
+ function handleUnknownPropertyError(propName, tagName, nodeType, lView) {
8223
+ // Special-case a situation when a structural directive is applied to
8224
+ // an `<ng-template>` element, for example: `<ng-template *ngIf="true">`.
8225
+ // In this case the compiler generates the `ɵɵtemplate` instruction with
8226
+ // the `null` as the tagName. The directive matching logic at runtime relies
8227
+ // on this effect (see `isInlineTemplate`), thus using the 'ng-template' as
8228
+ // a default value of the `tNode.value` is not feasible at this moment.
8229
+ if (!tagName && nodeType === 4 /* TNodeType.Container */) {
8230
+ tagName = 'ng-template';
8231
+ }
8232
+ const isHostStandalone = isHostComponentStandalone(lView);
8233
+ const templateLocation = getTemplateLocationDetails(lView);
8234
+ let message = `Can't bind to '${propName}' since it isn't a known property of '${tagName}'${templateLocation}.`;
8235
+ const schemas = `'${isHostStandalone ? '@Component' : '@NgModule'}.schemas'`;
8236
+ const importLocation = isHostStandalone ?
8237
+ 'included in the \'@Component.imports\' of this component' :
8238
+ 'a part of an @NgModule where this component is declared';
8239
+ if (KNOWN_CONTROL_FLOW_DIRECTIVES.has(propName)) {
8240
+ // Most likely this is a control flow directive (such as `*ngIf`) used in
8241
+ // a template, but the directive or the `CommonModule` is not imported.
8242
+ const correspondingImport = KNOWN_CONTROL_FLOW_DIRECTIVES.get(propName);
8243
+ message += `\nIf the '${propName}' is an Angular control flow directive, ` +
8244
+ `please make sure that either the '${correspondingImport}' directive or the 'CommonModule' is ${importLocation}.`;
8317
8245
  }
8318
8246
  else {
8319
- const context = patchedData;
8320
- const contextLView = context.lView;
8321
- ngDevMode && assertLView(contextLView);
8322
- lView = getComponentLViewByIndex(context.nodeIndex, contextLView);
8247
+ // May be an Angular component, which is not imported/declared?
8248
+ message += `\n1. If '${tagName}' is an Angular component and it has the ` +
8249
+ `'${propName}' input, then verify that it is ${importLocation}.`;
8250
+ // May be a Web Component?
8251
+ if (tagName && tagName.indexOf('-') > -1) {
8252
+ message += `\n2. If '${tagName}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' ` +
8253
+ `to the ${schemas} of this component to suppress this message.`;
8254
+ message += `\n3. To allow any property add 'NO_ERRORS_SCHEMA' to ` +
8255
+ `the ${schemas} of this component.`;
8256
+ }
8257
+ else {
8258
+ // If it's expected, the error can be suppressed by the `NO_ERRORS_SCHEMA` schema.
8259
+ message += `\n2. To allow any property add 'NO_ERRORS_SCHEMA' to ` +
8260
+ `the ${schemas} of this component.`;
8261
+ }
8323
8262
  }
8324
- return lView;
8263
+ reportUnknownPropertyError(message);
8325
8264
  }
8326
- /**
8327
- * This property will be monkey-patched on elements, components and directives.
8328
- */
8329
- const MONKEY_PATCH_KEY_NAME = '__ngContext__';
8330
- /**
8331
- * Assigns the given data to the given target (which could be a component,
8332
- * directive or DOM node instance) using monkey-patching.
8333
- */
8334
- function attachPatchData(target, data) {
8335
- ngDevMode && assertDefined(target, 'Target expected');
8336
- // Only attach the ID of the view in order to avoid memory leaks (see #41047). We only do this
8337
- // for `LView`, because we have control over when an `LView` is created and destroyed, whereas
8338
- // we can't know when to remove an `LContext`.
8339
- if (isLView(data)) {
8340
- target[MONKEY_PATCH_KEY_NAME] = data[ID];
8341
- registerLView(data);
8265
+ function reportUnknownPropertyError(message) {
8266
+ if (shouldThrowErrorOnUnknownProperty) {
8267
+ throw new RuntimeError(303 /* RuntimeErrorCode.UNKNOWN_BINDING */, message);
8342
8268
  }
8343
8269
  else {
8344
- target[MONKEY_PATCH_KEY_NAME] = data;
8270
+ console.error(formatRuntimeError(303 /* RuntimeErrorCode.UNKNOWN_BINDING */, message));
8345
8271
  }
8346
8272
  }
8347
8273
  /**
8348
- * Returns the monkey-patch value data present on the target (which could be
8349
- * a component, directive or a DOM node).
8274
+ * WARNING: this is a **dev-mode only** function (thus should always be guarded by the `ngDevMode`)
8275
+ * and must **not** be used in production bundles. The function makes megamorphic reads, which might
8276
+ * be too slow for production mode and also it relies on the constructor function being available.
8277
+ *
8278
+ * Gets a reference to the host component def (where a current component is declared).
8279
+ *
8280
+ * @param lView An `LView` that represents a current component that is being rendered.
8350
8281
  */
8351
- function readPatchedData(target) {
8352
- ngDevMode && assertDefined(target, 'Target expected');
8353
- const data = target[MONKEY_PATCH_KEY_NAME];
8354
- return (typeof data === 'number') ? getLViewById(data) : data || null;
8355
- }
8356
- function readPatchedLView(target) {
8357
- const value = readPatchedData(target);
8358
- if (value) {
8359
- return (isLView(value) ? value : value.lView);
8360
- }
8361
- return null;
8282
+ function getDeclarationComponentDef(lView) {
8283
+ !ngDevMode && throwError('Must never be called in production mode');
8284
+ const declarationLView = lView[DECLARATION_COMPONENT_VIEW];
8285
+ const context = declarationLView[CONTEXT];
8286
+ // Unable to obtain a context.
8287
+ if (!context)
8288
+ return null;
8289
+ return context.constructor ? getComponentDef$1(context.constructor) : null;
8362
8290
  }
8363
- function isComponentInstance(instance) {
8364
- return instance && instance.constructor && instance.constructor.ɵcmp;
8291
+ /**
8292
+ * WARNING: this is a **dev-mode only** function (thus should always be guarded by the `ngDevMode`)
8293
+ * and must **not** be used in production bundles. The function makes megamorphic reads, which might
8294
+ * be too slow for production mode.
8295
+ *
8296
+ * Checks if the current component is declared inside of a standalone component template.
8297
+ *
8298
+ * @param lView An `LView` that represents a current component that is being rendered.
8299
+ */
8300
+ function isHostComponentStandalone(lView) {
8301
+ !ngDevMode && throwError('Must never be called in production mode');
8302
+ const componentDef = getDeclarationComponentDef(lView);
8303
+ // Treat host component as non-standalone if we can't obtain the def.
8304
+ return !!componentDef?.standalone;
8365
8305
  }
8366
- function isDirectiveInstance(instance) {
8367
- return instance && instance.constructor && instance.constructor.ɵdir;
8306
+ /**
8307
+ * WARNING: this is a **dev-mode only** function (thus should always be guarded by the `ngDevMode`)
8308
+ * and must **not** be used in production bundles. The function makes megamorphic reads, which might
8309
+ * be too slow for production mode.
8310
+ *
8311
+ * Constructs a string describing the location of the host component template. The function is used
8312
+ * in dev mode to produce error messages.
8313
+ *
8314
+ * @param lView An `LView` that represents a current component that is being rendered.
8315
+ */
8316
+ function getTemplateLocationDetails(lView) {
8317
+ !ngDevMode && throwError('Must never be called in production mode');
8318
+ const hostComponentDef = getDeclarationComponentDef(lView);
8319
+ const componentClassName = hostComponentDef?.type?.name;
8320
+ return componentClassName ? ` (used in the '${componentClassName}' component template)` : '';
8368
8321
  }
8369
8322
  /**
8370
- * Locates the element within the given LView and returns the matching index
8323
+ * The set of known control flow directives and their corresponding imports.
8324
+ * We use this set to produce a more precises error message with a note
8325
+ * that the `CommonModule` should also be included.
8371
8326
  */
8372
- function findViaNativeElement(lView, target) {
8373
- const tView = lView[TVIEW];
8374
- for (let i = HEADER_OFFSET; i < tView.bindingStartIndex; i++) {
8375
- if (unwrapRNode(lView[i]) === target) {
8376
- return i;
8327
+ const KNOWN_CONTROL_FLOW_DIRECTIVES = new Map([
8328
+ ['ngIf', 'NgIf'], ['ngFor', 'NgFor'], ['ngSwitchCase', 'NgSwitchCase'],
8329
+ ['ngSwitchDefault', 'NgSwitchDefault']
8330
+ ]);
8331
+ /**
8332
+ * Returns true if the tag name is allowed by specified schemas.
8333
+ * @param schemas Array of schemas
8334
+ * @param tagName Name of the tag
8335
+ */
8336
+ function matchingSchemas(schemas, tagName) {
8337
+ if (schemas !== null) {
8338
+ for (let i = 0; i < schemas.length; i++) {
8339
+ const schema = schemas[i];
8340
+ if (schema === NO_ERRORS_SCHEMA ||
8341
+ schema === CUSTOM_ELEMENTS_SCHEMA && tagName && tagName.indexOf('-') > -1) {
8342
+ return true;
8343
+ }
8377
8344
  }
8378
8345
  }
8379
- return -1;
8346
+ return false;
8380
8347
  }
8348
+
8381
8349
  /**
8382
- * Locates the next tNode (child, sibling or parent).
8350
+ * @license
8351
+ * Copyright Google LLC All Rights Reserved.
8352
+ *
8353
+ * Use of this source code is governed by an MIT-style license that can be
8354
+ * found in the LICENSE file at https://angular.io/license
8383
8355
  */
8384
- function traverseNextElement(tNode) {
8385
- if (tNode.child) {
8386
- return tNode.child;
8356
+ const ERROR_ORIGINAL_ERROR = 'ngOriginalError';
8357
+ function wrappedError(message, originalError) {
8358
+ const msg = `${message} caused by: ${originalError instanceof Error ? originalError.message : originalError}`;
8359
+ const error = Error(msg);
8360
+ error[ERROR_ORIGINAL_ERROR] = originalError;
8361
+ return error;
8362
+ }
8363
+ function getOriginalError(error) {
8364
+ return error[ERROR_ORIGINAL_ERROR];
8365
+ }
8366
+
8367
+ /**
8368
+ * @license
8369
+ * Copyright Google LLC All Rights Reserved.
8370
+ *
8371
+ * Use of this source code is governed by an MIT-style license that can be
8372
+ * found in the LICENSE file at https://angular.io/license
8373
+ */
8374
+ /**
8375
+ * Provides a hook for centralized exception handling.
8376
+ *
8377
+ * The default implementation of `ErrorHandler` prints error messages to the `console`. To
8378
+ * intercept error handling, write a custom exception handler that replaces this default as
8379
+ * appropriate for your app.
8380
+ *
8381
+ * @usageNotes
8382
+ * ### Example
8383
+ *
8384
+ * ```
8385
+ * class MyErrorHandler implements ErrorHandler {
8386
+ * handleError(error) {
8387
+ * // do something with the exception
8388
+ * }
8389
+ * }
8390
+ *
8391
+ * @NgModule({
8392
+ * providers: [{provide: ErrorHandler, useClass: MyErrorHandler}]
8393
+ * })
8394
+ * class MyModule {}
8395
+ * ```
8396
+ *
8397
+ * @publicApi
8398
+ */
8399
+ class ErrorHandler {
8400
+ constructor() {
8401
+ /**
8402
+ * @internal
8403
+ */
8404
+ this._console = console;
8387
8405
  }
8388
- else if (tNode.next) {
8389
- return tNode.next;
8406
+ handleError(error) {
8407
+ const originalError = this._findOriginalError(error);
8408
+ this._console.error('ERROR', error);
8409
+ if (originalError) {
8410
+ this._console.error('ORIGINAL ERROR', originalError);
8411
+ }
8390
8412
  }
8391
- else {
8392
- // Let's take the following template: <div><span>text</span></div><component/>
8393
- // After checking the text node, we need to find the next parent that has a "next" TNode,
8394
- // in this case the parent `div`, so that we can find the component.
8395
- while (tNode.parent && !tNode.parent.next) {
8396
- tNode = tNode.parent;
8413
+ /** @internal */
8414
+ _findOriginalError(error) {
8415
+ let e = error && getOriginalError(error);
8416
+ while (e && getOriginalError(e)) {
8417
+ e = getOriginalError(e);
8397
8418
  }
8398
- return tNode.parent && tNode.parent.next;
8419
+ return e || null;
8399
8420
  }
8400
8421
  }
8422
+
8401
8423
  /**
8402
- * Locates the component within the given LView and returns the matching index
8424
+ * @license
8425
+ * Copyright Google LLC All Rights Reserved.
8426
+ *
8427
+ * Use of this source code is governed by an MIT-style license that can be
8428
+ * found in the LICENSE file at https://angular.io/license
8403
8429
  */
8404
- function findViaComponent(lView, componentInstance) {
8405
- const componentIndices = lView[TVIEW].components;
8406
- if (componentIndices) {
8407
- for (let i = 0; i < componentIndices.length; i++) {
8408
- const elementComponentIndex = componentIndices[i];
8409
- const componentView = getComponentLViewByIndex(elementComponentIndex, lView);
8410
- if (componentView[CONTEXT] === componentInstance) {
8411
- return elementComponentIndex;
8412
- }
8413
- }
8414
- }
8415
- else {
8416
- const rootComponentView = getComponentLViewByIndex(HEADER_OFFSET, lView);
8417
- const rootComponent = rootComponentView[CONTEXT];
8418
- if (rootComponent === componentInstance) {
8419
- // we are dealing with the root element here therefore we know that the
8420
- // element is the very first element after the HEADER data in the lView
8421
- return HEADER_OFFSET;
8422
- }
8423
- }
8424
- return -1;
8425
- }
8426
8430
  /**
8427
- * Locates the directive within the given LView and returns the matching index
8431
+ * Disallowed strings in the comment.
8432
+ *
8433
+ * see: https://html.spec.whatwg.org/multipage/syntax.html#comments
8428
8434
  */
8429
- function findViaDirective(lView, directiveInstance) {
8430
- // if a directive is monkey patched then it will (by default)
8431
- // have a reference to the LView of the current view. The
8432
- // element bound to the directive being search lives somewhere
8433
- // in the view data. We loop through the nodes and check their
8434
- // list of directives for the instance.
8435
- let tNode = lView[TVIEW].firstChild;
8436
- while (tNode) {
8437
- const directiveIndexStart = tNode.directiveStart;
8438
- const directiveIndexEnd = tNode.directiveEnd;
8439
- for (let i = directiveIndexStart; i < directiveIndexEnd; i++) {
8440
- if (lView[i] === directiveInstance) {
8441
- return tNode.index;
8442
- }
8443
- }
8444
- tNode = traverseNextElement(tNode);
8445
- }
8446
- return -1;
8447
- }
8435
+ const COMMENT_DISALLOWED = /^>|^->|<!--|-->|--!>|<!-$/g;
8436
+ /**
8437
+ * Delimiter in the disallowed strings which needs to be wrapped with zero with character.
8438
+ */
8439
+ const COMMENT_DELIMITER = /(<|>)/;
8440
+ const COMMENT_DELIMITER_ESCAPED = '\u200B$1\u200B';
8448
8441
  /**
8449
- * Returns a list of directives extracted from the given view based on the
8450
- * provided list of directive index values.
8442
+ * Escape the content of comment strings so that it can be safely inserted into a comment node.
8451
8443
  *
8452
- * @param nodeIndex The node index
8453
- * @param lView The target view data
8454
- * @param includeComponents Whether or not to include components in returned directives
8444
+ * The issue is that HTML does not specify any way to escape comment end text inside the comment.
8445
+ * Consider: `<!-- The way you close a comment is with ">", and "->" at the beginning or by "-->" or
8446
+ * "--!>" at the end. -->`. Above the `"-->"` is meant to be text not an end to the comment. This
8447
+ * can be created programmatically through DOM APIs. (`<!--` are also disallowed.)
8448
+ *
8449
+ * see: https://html.spec.whatwg.org/multipage/syntax.html#comments
8450
+ *
8451
+ * ```
8452
+ * div.innerHTML = div.innerHTML
8453
+ * ```
8454
+ *
8455
+ * One would expect that the above code would be safe to do, but it turns out that because comment
8456
+ * text is not escaped, the comment may contain text which will prematurely close the comment
8457
+ * opening up the application for XSS attack. (In SSR we programmatically create comment nodes which
8458
+ * may contain such text and expect them to be safe.)
8459
+ *
8460
+ * This function escapes the comment text by looking for comment delimiters (`<` and `>`) and
8461
+ * surrounding them with `_>_` where the `_` is a zero width space `\u200B`. The result is that if a
8462
+ * comment contains any of the comment start/end delimiters (such as `<!--`, `-->` or `--!>`) the
8463
+ * text it will render normally but it will not cause the HTML parser to close/open the comment.
8464
+ *
8465
+ * @param value text to make safe for comment node by escaping the comment open/close character
8466
+ * sequence.
8455
8467
  */
8456
- function getDirectivesAtNodeIndex(nodeIndex, lView, includeComponents) {
8457
- const tNode = lView[TVIEW].data[nodeIndex];
8458
- let directiveStartIndex = tNode.directiveStart;
8459
- if (directiveStartIndex == 0)
8460
- return EMPTY_ARRAY;
8461
- const directiveEndIndex = tNode.directiveEnd;
8462
- if (!includeComponents && tNode.flags & 2 /* TNodeFlags.isComponentHost */)
8463
- directiveStartIndex++;
8464
- return lView.slice(directiveStartIndex, directiveEndIndex);
8465
- }
8466
- function getComponentAtNodeIndex(nodeIndex, lView) {
8467
- const tNode = lView[TVIEW].data[nodeIndex];
8468
- let directiveStartIndex = tNode.directiveStart;
8469
- return tNode.flags & 2 /* TNodeFlags.isComponentHost */ ? lView[directiveStartIndex] : null;
8468
+ function escapeCommentText(value) {
8469
+ return value.replace(COMMENT_DISALLOWED, (text) => text.replace(COMMENT_DELIMITER, COMMENT_DELIMITER_ESCAPED));
8470
8470
  }
8471
+
8471
8472
  /**
8472
- * Returns a map of local references (local reference name => element or directive instance) that
8473
- * exist on a given element.
8473
+ * @license
8474
+ * Copyright Google LLC All Rights Reserved.
8475
+ *
8476
+ * Use of this source code is governed by an MIT-style license that can be
8477
+ * found in the LICENSE file at https://angular.io/license
8474
8478
  */
8475
- function discoverLocalRefs(lView, nodeIndex) {
8476
- const tNode = lView[TVIEW].data[nodeIndex];
8477
- if (tNode && tNode.localNames) {
8478
- const result = {};
8479
- let localIndex = tNode.index + 1;
8480
- for (let i = 0; i < tNode.localNames.length; i += 2) {
8481
- result[tNode.localNames[i]] = lView[localIndex];
8482
- localIndex++;
8483
- }
8484
- return result;
8479
+ function normalizeDebugBindingName(name) {
8480
+ // Attribute names with `$` (eg `x-y$`) are valid per spec, but unsupported by some browsers
8481
+ name = camelCaseToDashCase(name.replace(/[$@]/g, '_'));
8482
+ return `ng-reflect-${name}`;
8483
+ }
8484
+ const CAMEL_CASE_REGEXP = /([A-Z])/g;
8485
+ function camelCaseToDashCase(input) {
8486
+ return input.replace(CAMEL_CASE_REGEXP, (...m) => '-' + m[1].toLowerCase());
8487
+ }
8488
+ function normalizeDebugBindingValue(value) {
8489
+ try {
8490
+ // Limit the size of the value as otherwise the DOM just gets polluted.
8491
+ return value != null ? value.toString().slice(0, 30) : value;
8492
+ }
8493
+ catch (e) {
8494
+ return '[ERROR] Exception while trying to serialize the value';
8485
8495
  }
8486
- return null;
8487
8496
  }
8488
8497
 
8489
8498
  /**
@@ -9214,9 +9223,10 @@ function getClosestRElement(tView, tNode, lView) {
9214
9223
  }
9215
9224
  else {
9216
9225
  ngDevMode && assertTNodeType(parentTNode, 3 /* TNodeType.AnyRNode */ | 4 /* TNodeType.Container */);
9217
- if (parentTNode.flags & 2 /* TNodeFlags.isComponentHost */) {
9226
+ const { componentOffset } = parentTNode;
9227
+ if (componentOffset > -1) {
9218
9228
  ngDevMode && assertTNodeForLView(parentTNode, lView);
9219
- const encapsulation = tView.data[parentTNode.directiveStart].encapsulation;
9229
+ const { encapsulation } = tView.data[parentTNode.directiveStart + componentOffset];
9220
9230
  // We've got a parent which is an element in the current view. We just need to verify if the
9221
9231
  // parent element is not a component. Component's content nodes are not inserted immediately
9222
9232
  // because they will be projected, and so doing insert at this point would be wasteful.
@@ -9449,10 +9459,10 @@ function applyNodes(renderer, action, tNode, lView, parentRElement, beforeNode,
9449
9459
  if (isProjection) {
9450
9460
  if (action === 0 /* WalkTNodeTreeAction.Create */) {
9451
9461
  rawSlotValue && attachPatchData(unwrapRNode(rawSlotValue), lView);
9452
- tNode.flags |= 4 /* TNodeFlags.isProjected */;
9462
+ tNode.flags |= 2 /* TNodeFlags.isProjected */;
9453
9463
  }
9454
9464
  }
9455
- if ((tNode.flags & 64 /* TNodeFlags.isDetached */) !== 64 /* TNodeFlags.isDetached */) {
9465
+ if ((tNode.flags & 32 /* TNodeFlags.isDetached */) !== 32 /* TNodeFlags.isDetached */) {
9456
9466
  if (tNodeType & 8 /* TNodeType.ElementContainer */) {
9457
9467
  applyNodes(renderer, action, tNode.child, lView, parentRElement, beforeNode, false);
9458
9468
  applyToElementOrContainer(action, renderer, parentRElement, rawSlotValue, beforeNode);
@@ -9646,6 +9656,19 @@ function writeDirectClass(renderer, element, newValue) {
9646
9656
  }
9647
9657
  ngDevMode && ngDevMode.rendererSetClassName++;
9648
9658
  }
9659
+ /** Sets up the static DOM attributes on an `RNode`. */
9660
+ function setupStaticAttributes(renderer, element, tNode) {
9661
+ const { mergedAttrs, classes, styles } = tNode;
9662
+ if (mergedAttrs !== null) {
9663
+ setUpAttributes(renderer, element, mergedAttrs);
9664
+ }
9665
+ if (classes !== null) {
9666
+ writeDirectClass(renderer, element, classes);
9667
+ }
9668
+ if (styles !== null) {
9669
+ writeDirectStyle(renderer, element, styles);
9670
+ }
9671
+ }
9649
9672
 
9650
9673
  /**
9651
9674
  * @license
@@ -11506,6 +11529,7 @@ class TNode {
11506
11529
  index, //
11507
11530
  insertBeforeIndex, //
11508
11531
  injectorIndex, //
11532
+ componentOffset, //
11509
11533
  directiveStart, //
11510
11534
  directiveEnd, //
11511
11535
  directiveStylingLast, //
@@ -11538,6 +11562,7 @@ class TNode {
11538
11562
  this.index = index;
11539
11563
  this.insertBeforeIndex = insertBeforeIndex;
11540
11564
  this.injectorIndex = injectorIndex;
11565
+ this.componentOffset = componentOffset;
11541
11566
  this.directiveStart = directiveStart;
11542
11567
  this.directiveEnd = directiveEnd;
11543
11568
  this.directiveStylingLast = directiveStylingLast;
@@ -11615,21 +11640,19 @@ class TNode {
11615
11640
  }
11616
11641
  get flags_() {
11617
11642
  const flags = [];
11618
- if (this.flags & 16 /* TNodeFlags.hasClassInput */)
11643
+ if (this.flags & 8 /* TNodeFlags.hasClassInput */)
11619
11644
  flags.push('TNodeFlags.hasClassInput');
11620
- if (this.flags & 8 /* TNodeFlags.hasContentQuery */)
11645
+ if (this.flags & 4 /* TNodeFlags.hasContentQuery */)
11621
11646
  flags.push('TNodeFlags.hasContentQuery');
11622
- if (this.flags & 32 /* TNodeFlags.hasStyleInput */)
11647
+ if (this.flags & 16 /* TNodeFlags.hasStyleInput */)
11623
11648
  flags.push('TNodeFlags.hasStyleInput');
11624
- if (this.flags & 128 /* TNodeFlags.hasHostBindings */)
11649
+ if (this.flags & 64 /* TNodeFlags.hasHostBindings */)
11625
11650
  flags.push('TNodeFlags.hasHostBindings');
11626
- if (this.flags & 2 /* TNodeFlags.isComponentHost */)
11627
- flags.push('TNodeFlags.isComponentHost');
11628
11651
  if (this.flags & 1 /* TNodeFlags.isDirectiveHost */)
11629
11652
  flags.push('TNodeFlags.isDirectiveHost');
11630
- if (this.flags & 64 /* TNodeFlags.isDetached */)
11653
+ if (this.flags & 32 /* TNodeFlags.isDetached */)
11631
11654
  flags.push('TNodeFlags.isDetached');
11632
- if (this.flags & 4 /* TNodeFlags.isProjected */)
11655
+ if (this.flags & 2 /* TNodeFlags.isProjected */)
11633
11656
  flags.push('TNodeFlags.isProjected');
11634
11657
  return flags.join('|');
11635
11658
  }
@@ -12139,7 +12162,7 @@ function getOrCreateTNode(tView, index, type, name, attrs) {
12139
12162
  // See `TNodeType.Placeholder` and `LFrame.inI18n` for more context.
12140
12163
  // If the `TNode` was not pre-declared than it means it was not mentioned which means it was
12141
12164
  // removed, so we mark it as detached.
12142
- tNode.flags |= 64 /* TNodeFlags.isDetached */;
12165
+ tNode.flags |= 32 /* TNodeFlags.isDetached */;
12143
12166
  }
12144
12167
  }
12145
12168
  else if (tNode.type & 64 /* TNodeType.Placeholder */) {
@@ -12443,7 +12466,7 @@ function createDirectivesInstances(tView, lView, tNode) {
12443
12466
  if (!getBindingsEnabled())
12444
12467
  return;
12445
12468
  instantiateAllDirectives(tView, lView, tNode, getNativeByTNode(tNode, lView));
12446
- if ((tNode.flags & 128 /* TNodeFlags.hasHostBindings */) === 128 /* TNodeFlags.hasHostBindings */) {
12469
+ if ((tNode.flags & 64 /* TNodeFlags.hasHostBindings */) === 64 /* TNodeFlags.hasHostBindings */) {
12447
12470
  invokeDirectivesHostBindings(tView, lView, tNode);
12448
12471
  }
12449
12472
  }
@@ -12643,6 +12666,7 @@ function createTNode(tView, tParent, type, index, value, attrs) {
12643
12666
  index, // index: number
12644
12667
  null, // insertBeforeIndex: null|-1|number|number[]
12645
12668
  injectorIndex, // injectorIndex: number
12669
+ -1, // componentOffset: number
12646
12670
  -1, // directiveStart: number
12647
12671
  -1, // directiveEnd: number
12648
12672
  -1, // directiveStylingLast: number
@@ -12678,6 +12702,7 @@ function createTNode(tView, tParent, type, index, value, attrs) {
12678
12702
  directiveStart: -1,
12679
12703
  directiveEnd: -1,
12680
12704
  directiveStylingLast: -1,
12705
+ componentOffset: -1,
12681
12706
  propertyBindings: null,
12682
12707
  flags: 0,
12683
12708
  providerIndexes: 0,
@@ -12741,24 +12766,23 @@ function initializeInputAndOutputAliases(tView, tNode) {
12741
12766
  let outputsStore = null;
12742
12767
  for (let i = start; i < end; i++) {
12743
12768
  const directiveDef = tViewData[i];
12744
- const directiveInputs = directiveDef.inputs;
12769
+ inputsStore = generatePropertyAliases(directiveDef.inputs, i, inputsStore);
12770
+ outputsStore = generatePropertyAliases(directiveDef.outputs, i, outputsStore);
12745
12771
  // Do not use unbound attributes as inputs to structural directives, since structural
12746
12772
  // directive inputs can only be set using microsyntax (e.g. `<div *dir="exp">`).
12747
12773
  // TODO(FW-1930): microsyntax expressions may also contain unbound/static attributes, which
12748
12774
  // should be set for inline templates.
12749
- const initialInputs = (tNodeAttrs !== null && !isInlineTemplate(tNode)) ?
12750
- generateInitialInputs(directiveInputs, tNodeAttrs) :
12775
+ const initialInputs = (inputsStore !== null && tNodeAttrs !== null && !isInlineTemplate(tNode)) ?
12776
+ generateInitialInputs(inputsStore, i, tNodeAttrs) :
12751
12777
  null;
12752
12778
  inputsFromAttrs.push(initialInputs);
12753
- inputsStore = generatePropertyAliases(directiveInputs, i, inputsStore);
12754
- outputsStore = generatePropertyAliases(directiveDef.outputs, i, outputsStore);
12755
12779
  }
12756
12780
  if (inputsStore !== null) {
12757
12781
  if (inputsStore.hasOwnProperty('class')) {
12758
- tNode.flags |= 16 /* TNodeFlags.hasClassInput */;
12782
+ tNode.flags |= 8 /* TNodeFlags.hasClassInput */;
12759
12783
  }
12760
12784
  if (inputsStore.hasOwnProperty('style')) {
12761
- tNode.flags |= 32 /* TNodeFlags.hasStyleInput */;
12785
+ tNode.flags |= 16 /* TNodeFlags.hasStyleInput */;
12762
12786
  }
12763
12787
  }
12764
12788
  tNode.initialInputs = inputsFromAttrs;
@@ -12865,28 +12889,6 @@ function setNgReflectProperties(lView, element, type, dataValue, value) {
12865
12889
  }
12866
12890
  }
12867
12891
  }
12868
- /**
12869
- * Instantiate a root component.
12870
- */
12871
- function instantiateRootComponent(tView, lView, def) {
12872
- const rootTNode = getCurrentTNode();
12873
- if (tView.firstCreatePass) {
12874
- if (def.providersResolver)
12875
- def.providersResolver(def);
12876
- const directiveIndex = allocExpando(tView, lView, 1, null);
12877
- ngDevMode &&
12878
- assertEqual(directiveIndex, rootTNode.directiveStart, 'Because this is a root component the allocated expando should match the TNode component.');
12879
- configureViewWithDirective(tView, rootTNode, lView, directiveIndex, def);
12880
- initializeInputAndOutputAliases(tView, rootTNode);
12881
- }
12882
- const directive = getNodeInjectable(lView, tView, rootTNode.directiveStart, rootTNode);
12883
- attachPatchData(directive, lView);
12884
- const native = getNativeByTNode(rootTNode, lView);
12885
- if (native) {
12886
- attachPatchData(native, lView);
12887
- }
12888
- return directive;
12889
- }
12890
12892
  /**
12891
12893
  * Resolve the matched directives on a node.
12892
12894
  */
@@ -12899,53 +12901,13 @@ function resolveDirectives(tView, lView, tNode, localRefs) {
12899
12901
  const directiveDefs = findDirectiveDefMatches(tView, lView, tNode);
12900
12902
  const exportsMap = localRefs === null ? null : { '': -1 };
12901
12903
  if (directiveDefs !== null) {
12902
- hasDirectives = true;
12903
- initTNodeFlags(tNode, tView.data.length, directiveDefs.length);
12904
- // When the same token is provided by several directives on the same node, some rules apply in
12905
- // the viewEngine:
12906
- // - viewProviders have priority over providers
12907
- // - the last directive in NgModule.declarations has priority over the previous one
12908
- // So to match these rules, the order in which providers are added in the arrays is very
12909
- // important.
12910
- for (let i = 0; i < directiveDefs.length; i++) {
12911
- const def = directiveDefs[i];
12912
- if (def.providersResolver)
12913
- def.providersResolver(def);
12914
- }
12915
- let preOrderHooksFound = false;
12916
- let preOrderCheckHooksFound = false;
12917
- let directiveIdx = allocExpando(tView, lView, directiveDefs.length, null);
12918
- ngDevMode &&
12919
- assertSame(directiveIdx, tNode.directiveStart, 'TNode.directiveStart should point to just allocated space');
12904
+ // Publishes the directive types to DI so they can be injected. Needs to
12905
+ // happen in a separate pass before the TNode flags have been initialized.
12920
12906
  for (let i = 0; i < directiveDefs.length; i++) {
12921
- const def = directiveDefs[i];
12922
- // Merge the attrs in the order of matches. This assumes that the first directive is the
12923
- // component itself, so that the component has the least priority.
12924
- tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, def.hostAttrs);
12925
- configureViewWithDirective(tView, tNode, lView, directiveIdx, def);
12926
- saveNameToExportMap(directiveIdx, def, exportsMap);
12927
- if (def.contentQueries !== null)
12928
- tNode.flags |= 8 /* TNodeFlags.hasContentQuery */;
12929
- if (def.hostBindings !== null || def.hostAttrs !== null || def.hostVars !== 0)
12930
- tNode.flags |= 128 /* TNodeFlags.hasHostBindings */;
12931
- const lifeCycleHooks = def.type.prototype;
12932
- // Only push a node index into the preOrderHooks array if this is the first
12933
- // pre-order hook found on this node.
12934
- if (!preOrderHooksFound &&
12935
- (lifeCycleHooks.ngOnChanges || lifeCycleHooks.ngOnInit || lifeCycleHooks.ngDoCheck)) {
12936
- // We will push the actual hook function into this array later during dir instantiation.
12937
- // We cannot do it now because we must ensure hooks are registered in the same
12938
- // order that directives are created (i.e. injection order).
12939
- (tView.preOrderHooks || (tView.preOrderHooks = [])).push(tNode.index);
12940
- preOrderHooksFound = true;
12941
- }
12942
- if (!preOrderCheckHooksFound && (lifeCycleHooks.ngOnChanges || lifeCycleHooks.ngDoCheck)) {
12943
- (tView.preOrderCheckHooks || (tView.preOrderCheckHooks = [])).push(tNode.index);
12944
- preOrderCheckHooksFound = true;
12945
- }
12946
- directiveIdx++;
12907
+ diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, lView), tView, directiveDefs[i].type);
12947
12908
  }
12948
- initializeInputAndOutputAliases(tView, tNode);
12909
+ hasDirectives = true;
12910
+ initializeDirectives(tView, lView, tNode, directiveDefs, exportsMap);
12949
12911
  }
12950
12912
  if (exportsMap)
12951
12913
  cacheMatchingLocalNames(tNode, localRefs, exportsMap);
@@ -12954,17 +12916,66 @@ function resolveDirectives(tView, lView, tNode, localRefs) {
12954
12916
  tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, tNode.attrs);
12955
12917
  return hasDirectives;
12956
12918
  }
12919
+ /** Initializes the data structures necessary for a list of directives to be instantiated. */
12920
+ function initializeDirectives(tView, lView, tNode, directives, exportsMap) {
12921
+ ngDevMode && assertFirstCreatePass(tView);
12922
+ initTNodeFlags(tNode, tView.data.length, directives.length);
12923
+ // When the same token is provided by several directives on the same node, some rules apply in
12924
+ // the viewEngine:
12925
+ // - viewProviders have priority over providers
12926
+ // - the last directive in NgModule.declarations has priority over the previous one
12927
+ // So to match these rules, the order in which providers are added in the arrays is very
12928
+ // important.
12929
+ for (let i = 0; i < directives.length; i++) {
12930
+ const def = directives[i];
12931
+ if (def.providersResolver)
12932
+ def.providersResolver(def);
12933
+ }
12934
+ let preOrderHooksFound = false;
12935
+ let preOrderCheckHooksFound = false;
12936
+ let directiveIdx = allocExpando(tView, lView, directives.length, null);
12937
+ ngDevMode &&
12938
+ assertSame(directiveIdx, tNode.directiveStart, 'TNode.directiveStart should point to just allocated space');
12939
+ for (let i = 0; i < directives.length; i++) {
12940
+ const def = directives[i];
12941
+ // Merge the attrs in the order of matches. This assumes that the first directive is the
12942
+ // component itself, so that the component has the least priority.
12943
+ tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, def.hostAttrs);
12944
+ configureViewWithDirective(tView, tNode, lView, directiveIdx, def);
12945
+ saveNameToExportMap(directiveIdx, def, exportsMap);
12946
+ if (def.contentQueries !== null)
12947
+ tNode.flags |= 4 /* TNodeFlags.hasContentQuery */;
12948
+ if (def.hostBindings !== null || def.hostAttrs !== null || def.hostVars !== 0)
12949
+ tNode.flags |= 64 /* TNodeFlags.hasHostBindings */;
12950
+ const lifeCycleHooks = def.type.prototype;
12951
+ // Only push a node index into the preOrderHooks array if this is the first
12952
+ // pre-order hook found on this node.
12953
+ if (!preOrderHooksFound &&
12954
+ (lifeCycleHooks.ngOnChanges || lifeCycleHooks.ngOnInit || lifeCycleHooks.ngDoCheck)) {
12955
+ // We will push the actual hook function into this array later during dir instantiation.
12956
+ // We cannot do it now because we must ensure hooks are registered in the same
12957
+ // order that directives are created (i.e. injection order).
12958
+ (tView.preOrderHooks || (tView.preOrderHooks = [])).push(tNode.index);
12959
+ preOrderHooksFound = true;
12960
+ }
12961
+ if (!preOrderCheckHooksFound && (lifeCycleHooks.ngOnChanges || lifeCycleHooks.ngDoCheck)) {
12962
+ (tView.preOrderCheckHooks || (tView.preOrderCheckHooks = [])).push(tNode.index);
12963
+ preOrderCheckHooksFound = true;
12964
+ }
12965
+ directiveIdx++;
12966
+ }
12967
+ initializeInputAndOutputAliases(tView, tNode);
12968
+ }
12957
12969
  /**
12958
12970
  * Add `hostBindings` to the `TView.hostBindingOpCodes`.
12959
12971
  *
12960
12972
  * @param tView `TView` to which the `hostBindings` should be added.
12961
12973
  * @param tNode `TNode` the element which contains the directive
12962
- * @param lView `LView` current `LView`
12963
12974
  * @param directiveIdx Directive index in view.
12964
12975
  * @param directiveVarsIdx Where will the directive's vars be stored
12965
12976
  * @param def `ComponentDef`/`DirectiveDef`, which contains the `hostVars`/`hostBindings` to add.
12966
12977
  */
12967
- function registerHostBindingOpCodes(tView, tNode, lView, directiveIdx, directiveVarsIdx, def) {
12978
+ function registerHostBindingOpCodes(tView, tNode, directiveIdx, directiveVarsIdx, def) {
12968
12979
  ngDevMode && assertFirstCreatePass(tView);
12969
12980
  const hostBindings = def.hostBindings;
12970
12981
  if (hostBindings) {
@@ -13065,7 +13076,7 @@ function invokeHostBindingsInCreationMode(def, directive) {
13065
13076
  * Matches the current node against all available selectors.
13066
13077
  * If a component is matched (at most one), it is returned in first position in the array.
13067
13078
  */
13068
- function findDirectiveDefMatches(tView, viewData, tNode) {
13079
+ function findDirectiveDefMatches(tView, lView, tNode) {
13069
13080
  ngDevMode && assertFirstCreatePass(tView);
13070
13081
  ngDevMode && assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */);
13071
13082
  const registry = tView.directiveRegistry;
@@ -13075,25 +13086,47 @@ function findDirectiveDefMatches(tView, viewData, tNode) {
13075
13086
  const def = registry[i];
13076
13087
  if (isNodeMatchingSelectorList(tNode, def.selectors, /* isProjectionMode */ false)) {
13077
13088
  matches || (matches = ngDevMode ? new MatchesArray() : []);
13078
- diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, viewData), tView, def.type);
13079
13089
  if (isComponentDef(def)) {
13080
13090
  if (ngDevMode) {
13081
13091
  assertTNodeType(tNode, 2 /* TNodeType.Element */, `"${tNode.value}" tags cannot be used as component hosts. ` +
13082
13092
  `Please use a different tag to activate the ${stringify(def.type)} component.`);
13083
- if (tNode.flags & 2 /* TNodeFlags.isComponentHost */) {
13084
- // If another component has been matched previously, it's the first element in the
13085
- // `matches` array, see how we store components/directives in `matches` below.
13086
- throwMultipleComponentError(tNode, matches[0].type, def.type);
13093
+ if (isComponentHost(tNode)) {
13094
+ throwMultipleComponentError(tNode, matches.find(isComponentDef).type, def.type);
13087
13095
  }
13088
13096
  }
13089
- markAsComponentHost(tView, tNode);
13090
- // The component is always stored first with directives after.
13091
- matches.unshift(def);
13097
+ // Components are inserted at the front of the matches array so that their lifecycle
13098
+ // hooks run before any directive lifecycle hooks. This appears to be for ViewEngine
13099
+ // compatibility. This logic doesn't make sense with host directives, because it
13100
+ // would allow the host directives to undo any overrides the host may have made.
13101
+ // To handle this case, the host directives of components are inserted at the beginning
13102
+ // of the array, followed by the component. As such, the insertion order is as follows:
13103
+ // 1. Host directives belonging to the selector-matched component.
13104
+ // 2. Selector-matched component.
13105
+ // 3. Host directives belonging to selector-matched directives.
13106
+ // 4. Selector-matched directives.
13107
+ if (def.findHostDirectiveDefs !== null) {
13108
+ const hostDirectiveMatches = [];
13109
+ def.findHostDirectiveDefs(hostDirectiveMatches, def, tView, lView, tNode);
13110
+ // Add all host directives declared on this component, followed by the component itself.
13111
+ // Host directives should execute first so the host has a chance to override changes
13112
+ // to the DOM made by them.
13113
+ matches.unshift(...hostDirectiveMatches, def);
13114
+ // Component is offset starting from the beginning of the host directives array.
13115
+ const componentOffset = hostDirectiveMatches.length;
13116
+ markAsComponentHost(tView, tNode, componentOffset);
13117
+ }
13118
+ else {
13119
+ // No host directives on this component, just add the
13120
+ // component def to the beginning of the matches.
13121
+ matches.unshift(def);
13122
+ markAsComponentHost(tView, tNode, 0);
13123
+ }
13092
13124
  }
13093
13125
  else {
13126
+ // Append any host directives to the matches first.
13127
+ def.findHostDirectiveDefs?.(matches, def, tView, lView, tNode);
13094
13128
  matches.push(def);
13095
13129
  }
13096
- def.applyHostDirectives?.(tView, viewData, tNode, matches);
13097
13130
  }
13098
13131
  }
13099
13132
  }
@@ -13101,12 +13134,13 @@ function findDirectiveDefMatches(tView, viewData, tNode) {
13101
13134
  }
13102
13135
  /**
13103
13136
  * Marks a given TNode as a component's host. This consists of:
13104
- * - setting appropriate TNode flags;
13137
+ * - setting the component offset on the TNode.
13105
13138
  * - storing index of component's host element so it will be queued for view refresh during CD.
13106
13139
  */
13107
- function markAsComponentHost(tView, hostTNode) {
13140
+ function markAsComponentHost(tView, hostTNode, componentOffset) {
13108
13141
  ngDevMode && assertFirstCreatePass(tView);
13109
- hostTNode.flags |= 2 /* TNodeFlags.isComponentHost */;
13142
+ ngDevMode && assertGreaterThan(componentOffset, -1, 'componentOffset must be great than -1');
13143
+ hostTNode.componentOffset = componentOffset;
13110
13144
  (tView.components || (tView.components = ngDevMode ? new TViewComponents() : []))
13111
13145
  .push(hostTNode.index);
13112
13146
  }
@@ -13177,7 +13211,7 @@ function configureViewWithDirective(tView, tNode, lView, directiveIndex, def) {
13177
13211
  const nodeInjectorFactory = new NodeInjectorFactory(directiveFactory, isComponentDef(def), ɵɵdirectiveInject);
13178
13212
  tView.blueprint[directiveIndex] = nodeInjectorFactory;
13179
13213
  lView[directiveIndex] = nodeInjectorFactory;
13180
- registerHostBindingOpCodes(tView, tNode, lView, directiveIndex, allocExpando(tView, lView, def.hostVars, NO_CHANGE), def);
13214
+ registerHostBindingOpCodes(tView, tNode, directiveIndex, allocExpando(tView, lView, def.hostVars, NO_CHANGE), def);
13181
13215
  }
13182
13216
  function addComponentLogic(lView, hostTNode, def) {
13183
13217
  const native = getNativeByTNode(hostTNode, lView);
@@ -13252,10 +13286,11 @@ function setInputsFromAttrs(lView, directiveIndex, instance, def, tNode, initial
13252
13286
  *
13253
13287
  * <my-component name="Bess"></my-component>
13254
13288
  *
13255
- * @param inputs The list of inputs from the directive def
13256
- * @param attrs The static attrs on this node
13289
+ * @param inputs Input alias map that was generated from the directive def inputs.
13290
+ * @param directiveIndex Index of the directive that is currently being processed.
13291
+ * @param attrs Static attrs on this node.
13257
13292
  */
13258
- function generateInitialInputs(inputs, attrs) {
13293
+ function generateInitialInputs(inputs, directiveIndex, attrs) {
13259
13294
  let inputsToStore = null;
13260
13295
  let i = 0;
13261
13296
  while (i < attrs.length) {
@@ -13276,7 +13311,17 @@ function generateInitialInputs(inputs, attrs) {
13276
13311
  if (inputs.hasOwnProperty(attrName)) {
13277
13312
  if (inputsToStore === null)
13278
13313
  inputsToStore = [];
13279
- inputsToStore.push(attrName, inputs[attrName], attrs[i + 1]);
13314
+ // Find the input's public name from the input store. Note that we can be found easier
13315
+ // through the directive def, but we want to do it using the inputs store so that it can
13316
+ // account for host directive aliases.
13317
+ const inputConfig = inputs[attrName];
13318
+ for (let j = 0; j < inputConfig.length; j += 2) {
13319
+ if (inputConfig[j] === directiveIndex) {
13320
+ inputsToStore.push(attrName, inputConfig[j + 1], attrs[i + 1]);
13321
+ // A directive can't have multiple inputs with the same name so we can break here.
13322
+ break;
13323
+ }
13324
+ }
13280
13325
  }
13281
13326
  i += 2;
13282
13327
  }
@@ -14089,6 +14134,7 @@ class ChainedInjector {
14089
14134
  this.parentInjector = parentInjector;
14090
14135
  }
14091
14136
  get(token, notFoundValue, flags) {
14137
+ flags = convertToBitFlags(flags);
14092
14138
  const value = this.injector.get(token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR, flags);
14093
14139
  if (value !== NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR ||
14094
14140
  notFoundValue === NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) {
@@ -14165,42 +14211,23 @@ class ComponentFactory extends ComponentFactory$1 {
14165
14211
  let component;
14166
14212
  let tElementNode;
14167
14213
  try {
14168
- const componentView = createRootComponentView(hostRNode, this.componentDef, rootLView, rendererFactory, hostRenderer);
14214
+ const rootDirectives = [this.componentDef];
14215
+ const hostTNode = createRootComponentTNode(rootLView, hostRNode);
14216
+ const componentView = createRootComponentView(hostTNode, hostRNode, this.componentDef, rootDirectives, rootLView, rendererFactory, hostRenderer);
14217
+ tElementNode = getTNode(rootTView, HEADER_OFFSET);
14218
+ // TODO(crisbeto): in practice `hostRNode` should always be defined, but there are some tests
14219
+ // where the renderer is mocked out and `undefined` is returned. We should update the tests so
14220
+ // that this check can be removed.
14169
14221
  if (hostRNode) {
14170
- if (rootSelectorOrNode) {
14171
- setUpAttributes(hostRenderer, hostRNode, ['ng-version', VERSION.full]);
14172
- }
14173
- else {
14174
- // If host element is created as a part of this function call (i.e. `rootSelectorOrNode`
14175
- // is not defined), also apply attributes and classes extracted from component selector.
14176
- // Extract attributes and classes from the first selector only to match VE behavior.
14177
- const { attrs, classes } = extractAttrsAndClassesFromSelector(this.componentDef.selectors[0]);
14178
- if (attrs) {
14179
- setUpAttributes(hostRenderer, hostRNode, attrs);
14180
- }
14181
- if (classes && classes.length > 0) {
14182
- writeDirectClass(hostRenderer, hostRNode, classes.join(' '));
14183
- }
14184
- }
14222
+ setRootNodeAttributes(hostRenderer, this.componentDef, hostRNode, rootSelectorOrNode);
14185
14223
  }
14186
- tElementNode = getTNode(rootTView, HEADER_OFFSET);
14187
14224
  if (projectableNodes !== undefined) {
14188
- const projection = tElementNode.projection = [];
14189
- for (let i = 0; i < this.ngContentSelectors.length; i++) {
14190
- const nodesforSlot = projectableNodes[i];
14191
- // Projectable nodes can be passed as array of arrays or an array of iterables (ngUpgrade
14192
- // case). Here we do normalize passed data structure to be an array of arrays to avoid
14193
- // complex checks down the line.
14194
- // We also normalize the length of the passed in projectable nodes (to match the number of
14195
- // <ng-container> slots defined by a component).
14196
- projection.push(nodesforSlot != null ? Array.from(nodesforSlot) : null);
14197
- }
14225
+ projectNodes(tElementNode, this.ngContentSelectors, projectableNodes);
14198
14226
  }
14199
14227
  // TODO: should LifecycleHooksFeature and other host features be generated by the compiler and
14200
14228
  // executed here?
14201
14229
  // Angular 5 reference: https://stackblitz.com/edit/lifecycle-hooks-vcref
14202
- component =
14203
- createRootComponent(componentView, this.componentDef, rootLView, [LifecycleHooksFeature]);
14230
+ component = createRootComponent(componentView, this.componentDef, rootDirectives, rootLView, [LifecycleHooksFeature]);
14204
14231
  renderView(rootTView, rootLView, null);
14205
14232
  }
14206
14233
  finally {
@@ -14271,11 +14298,22 @@ const NULL_INJECTOR = {
14271
14298
  throwProviderNotFoundError(token, 'NullInjector');
14272
14299
  }
14273
14300
  };
14301
+ /** Creates a TNode that can be used to instantiate a root component. */
14302
+ function createRootComponentTNode(lView, rNode) {
14303
+ const tView = lView[TVIEW];
14304
+ const index = HEADER_OFFSET;
14305
+ ngDevMode && assertIndexInRange(lView, index);
14306
+ lView[index] = rNode;
14307
+ // '#host' is added here as we don't know the real host DOM name (we don't want to read it) and at
14308
+ // the same time we want to communicate the debug `TNode` that this is a special `TNode`
14309
+ // representing a host element.
14310
+ return getOrCreateTNode(tView, index, 2 /* TNodeType.Element */, '#host', null);
14311
+ }
14274
14312
  /**
14275
14313
  * Creates the root component view and the root component node.
14276
14314
  *
14277
14315
  * @param rNode Render host element.
14278
- * @param def ComponentDef
14316
+ * @param rootComponentDef ComponentDef
14279
14317
  * @param rootView The parent view where the host node is stored
14280
14318
  * @param rendererFactory Factory to be used for creating child renderers.
14281
14319
  * @param hostRenderer The current renderer
@@ -14283,72 +14321,96 @@ const NULL_INJECTOR = {
14283
14321
  *
14284
14322
  * @returns Component view created
14285
14323
  */
14286
- function createRootComponentView(rNode, def, rootView, rendererFactory, hostRenderer, sanitizer) {
14324
+ function createRootComponentView(tNode, rNode, rootComponentDef, rootDirectives, rootView, rendererFactory, hostRenderer, sanitizer) {
14287
14325
  const tView = rootView[TVIEW];
14288
- const index = HEADER_OFFSET;
14289
- ngDevMode && assertIndexInRange(rootView, index);
14290
- rootView[index] = rNode;
14291
- // '#host' is added here as we don't know the real host DOM name (we don't want to read it) and at
14292
- // the same time we want to communicate the debug `TNode` that this is a special `TNode`
14293
- // representing a host element.
14294
- const tNode = getOrCreateTNode(tView, index, 2 /* TNodeType.Element */, '#host', null);
14295
- const mergedAttrs = tNode.mergedAttrs = def.hostAttrs;
14296
- if (mergedAttrs !== null) {
14297
- computeStaticStyling(tNode, mergedAttrs, true);
14298
- if (rNode !== null) {
14299
- setUpAttributes(hostRenderer, rNode, mergedAttrs);
14300
- if (tNode.classes !== null) {
14301
- writeDirectClass(hostRenderer, rNode, tNode.classes);
14302
- }
14303
- if (tNode.styles !== null) {
14304
- writeDirectStyle(hostRenderer, rNode, tNode.styles);
14305
- }
14306
- }
14307
- }
14308
- const viewRenderer = rendererFactory.createRenderer(rNode, def);
14309
- const componentView = createLView(rootView, getOrCreateComponentTView(def), null, def.onPush ? 32 /* LViewFlags.Dirty */ : 16 /* LViewFlags.CheckAlways */, rootView[index], tNode, rendererFactory, viewRenderer, sanitizer || null, null, null);
14326
+ applyRootComponentStyling(rootDirectives, tNode, rNode, hostRenderer);
14327
+ const viewRenderer = rendererFactory.createRenderer(rNode, rootComponentDef);
14328
+ const componentView = createLView(rootView, getOrCreateComponentTView(rootComponentDef), null, rootComponentDef.onPush ? 32 /* LViewFlags.Dirty */ : 16 /* LViewFlags.CheckAlways */, rootView[tNode.index], tNode, rendererFactory, viewRenderer, sanitizer || null, null, null);
14310
14329
  if (tView.firstCreatePass) {
14311
- diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, rootView), tView, def.type);
14312
- markAsComponentHost(tView, tNode);
14313
- initTNodeFlags(tNode, rootView.length, 1);
14330
+ diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, rootView), tView, rootComponentDef.type);
14331
+ markAsComponentHost(tView, tNode, 0);
14314
14332
  }
14315
14333
  addToViewTree(rootView, componentView);
14316
14334
  // Store component view at node index, with node as the HOST
14317
- return rootView[index] = componentView;
14335
+ return rootView[tNode.index] = componentView;
14336
+ }
14337
+ /** Sets up the styling information on a root component. */
14338
+ function applyRootComponentStyling(rootDirectives, tNode, rNode, hostRenderer) {
14339
+ for (const def of rootDirectives) {
14340
+ tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, def.hostAttrs);
14341
+ }
14342
+ if (tNode.mergedAttrs !== null) {
14343
+ computeStaticStyling(tNode, tNode.mergedAttrs, true);
14344
+ if (rNode !== null) {
14345
+ setupStaticAttributes(hostRenderer, rNode, tNode);
14346
+ }
14347
+ }
14318
14348
  }
14319
14349
  /**
14320
14350
  * Creates a root component and sets it up with features and host bindings.Shared by
14321
14351
  * renderComponent() and ViewContainerRef.createComponent().
14322
14352
  */
14323
- function createRootComponent(componentView, componentDef, rootLView, hostFeatures) {
14353
+ function createRootComponent(componentView, rootComponentDef, rootDirectives, rootLView, hostFeatures) {
14354
+ const rootTNode = getCurrentTNode();
14355
+ ngDevMode && assertDefined(rootTNode, 'tNode should have been already created');
14324
14356
  const tView = rootLView[TVIEW];
14325
- // Create directive instance with factory() and store at next index in viewData
14326
- const component = instantiateRootComponent(tView, rootLView, componentDef);
14327
- // Root view only contains an instance of this component,
14328
- // so we use a reference to that component instance as a context.
14357
+ const native = getNativeByTNode(rootTNode, rootLView);
14358
+ initializeDirectives(tView, rootLView, rootTNode, rootDirectives, null);
14359
+ for (let i = 0; i < rootDirectives.length; i++) {
14360
+ const directiveIndex = rootTNode.directiveStart + i;
14361
+ const directiveInstance = getNodeInjectable(rootLView, tView, directiveIndex, rootTNode);
14362
+ attachPatchData(directiveInstance, rootLView);
14363
+ }
14364
+ invokeDirectivesHostBindings(tView, rootLView, rootTNode);
14365
+ if (native) {
14366
+ attachPatchData(native, rootLView);
14367
+ }
14368
+ // We're guaranteed for the `componentOffset` to be positive here
14369
+ // since a root component always matches a component def.
14370
+ ngDevMode &&
14371
+ assertGreaterThan(rootTNode.componentOffset, -1, 'componentOffset must be great than -1');
14372
+ const component = getNodeInjectable(rootLView, tView, rootTNode.directiveStart + rootTNode.componentOffset, rootTNode);
14329
14373
  componentView[CONTEXT] = rootLView[CONTEXT] = component;
14330
14374
  if (hostFeatures !== null) {
14331
14375
  for (const feature of hostFeatures) {
14332
- feature(component, componentDef);
14376
+ feature(component, rootComponentDef);
14333
14377
  }
14334
14378
  }
14335
14379
  // We want to generate an empty QueryList for root content queries for backwards
14336
14380
  // compatibility with ViewEngine.
14337
- if (componentDef.contentQueries) {
14338
- const tNode = getCurrentTNode();
14339
- ngDevMode && assertDefined(tNode, 'TNode expected');
14340
- componentDef.contentQueries(1 /* RenderFlags.Create */, component, tNode.directiveStart);
14381
+ executeContentQueries(tView, rootTNode, componentView);
14382
+ return component;
14383
+ }
14384
+ /** Sets the static attributes on a root component. */
14385
+ function setRootNodeAttributes(hostRenderer, componentDef, hostRNode, rootSelectorOrNode) {
14386
+ if (rootSelectorOrNode) {
14387
+ setUpAttributes(hostRenderer, hostRNode, ['ng-version', VERSION.full]);
14341
14388
  }
14342
- const rootTNode = getCurrentTNode();
14343
- ngDevMode && assertDefined(rootTNode, 'tNode should have been already created');
14344
- if (tView.firstCreatePass &&
14345
- (componentDef.hostBindings !== null || componentDef.hostAttrs !== null)) {
14346
- setSelectedIndex(rootTNode.index);
14347
- const rootTView = rootLView[TVIEW];
14348
- registerHostBindingOpCodes(rootTView, rootTNode, rootLView, rootTNode.directiveStart, rootTNode.directiveEnd, componentDef);
14349
- invokeHostBindingsInCreationMode(componentDef, component);
14389
+ else {
14390
+ // If host element is created as a part of this function call (i.e. `rootSelectorOrNode`
14391
+ // is not defined), also apply attributes and classes extracted from component selector.
14392
+ // Extract attributes and classes from the first selector only to match VE behavior.
14393
+ const { attrs, classes } = extractAttrsAndClassesFromSelector(componentDef.selectors[0]);
14394
+ if (attrs) {
14395
+ setUpAttributes(hostRenderer, hostRNode, attrs);
14396
+ }
14397
+ if (classes && classes.length > 0) {
14398
+ writeDirectClass(hostRenderer, hostRNode, classes.join(' '));
14399
+ }
14400
+ }
14401
+ }
14402
+ /** Projects the `projectableNodes` that were specified when creating a root component. */
14403
+ function projectNodes(tNode, ngContentSelectors, projectableNodes) {
14404
+ const projection = tNode.projection = [];
14405
+ for (let i = 0; i < ngContentSelectors.length; i++) {
14406
+ const nodesforSlot = projectableNodes[i];
14407
+ // Projectable nodes can be passed as array of arrays or an array of iterables (ngUpgrade
14408
+ // case). Here we do normalize passed data structure to be an array of arrays to avoid
14409
+ // complex checks down the line.
14410
+ // We also normalize the length of the passed in projectable nodes (to match the number of
14411
+ // <ng-container> slots defined by a component).
14412
+ projection.push(nodesforSlot != null ? Array.from(nodesforSlot) : null);
14350
14413
  }
14351
- return component;
14352
14414
  }
14353
14415
  /**
14354
14416
  * Used to enable lifecycle hooks on the root component.
@@ -14606,6 +14668,13 @@ function ɵɵCopyDefinitionFeature(definition) {
14606
14668
  }
14607
14669
  }
14608
14670
 
14671
+ /**
14672
+ * @license
14673
+ * Copyright Google LLC All Rights Reserved.
14674
+ *
14675
+ * Use of this source code is governed by an MIT-style license that can be
14676
+ * found in the LICENSE file at https://angular.io/license
14677
+ */
14609
14678
  /**
14610
14679
  * This feature add the host directives behavior to a directive definition by patching a
14611
14680
  * function onto it. The expectation is that the runtime will invoke the function during
@@ -14627,29 +14696,42 @@ function ɵɵCopyDefinitionFeature(definition) {
14627
14696
  * @codeGenApi
14628
14697
  */
14629
14698
  function ɵɵHostDirectivesFeature(rawHostDirectives) {
14630
- const unwrappedHostDirectives = Array.isArray(rawHostDirectives) ? rawHostDirectives : rawHostDirectives();
14631
- const hostDirectives = unwrappedHostDirectives.map(dir => typeof dir === 'function' ? { directive: dir, inputs: EMPTY_OBJ, outputs: EMPTY_OBJ } : {
14632
- directive: dir.directive,
14633
- inputs: bindingArrayToMap(dir.inputs),
14634
- outputs: bindingArrayToMap(dir.outputs)
14635
- });
14636
14699
  return (definition) => {
14637
- // TODO(crisbeto): implement host directive matching logic.
14638
- definition.applyHostDirectives =
14639
- (tView, viewData, tNode, matches) => { };
14700
+ definition.findHostDirectiveDefs = findHostDirectiveDefs;
14701
+ definition.hostDirectives =
14702
+ (Array.isArray(rawHostDirectives) ? rawHostDirectives : rawHostDirectives()).map(dir => {
14703
+ return typeof dir === 'function' ?
14704
+ { directive: resolveForwardRef(dir), inputs: EMPTY_OBJ, outputs: EMPTY_OBJ } :
14705
+ {
14706
+ directive: resolveForwardRef(dir.directive),
14707
+ inputs: bindingArrayToMap(dir.inputs),
14708
+ outputs: bindingArrayToMap(dir.outputs)
14709
+ };
14710
+ });
14640
14711
  };
14641
14712
  }
14713
+ function findHostDirectiveDefs(matches, def, tView, lView, tNode) {
14714
+ if (def.hostDirectives !== null) {
14715
+ for (const hostDirectiveConfig of def.hostDirectives) {
14716
+ const hostDirectiveDef = getDirectiveDef(hostDirectiveConfig.directive);
14717
+ // TODO(crisbeto): assert that the def exists.
14718
+ // Host directives execute before the host so that its host bindings can be overwritten.
14719
+ findHostDirectiveDefs(matches, hostDirectiveDef, tView, lView, tNode);
14720
+ matches.push(hostDirectiveDef);
14721
+ }
14722
+ }
14723
+ }
14642
14724
  /**
14643
14725
  * Converts an array in the form of `['publicName', 'alias', 'otherPublicName', 'otherAlias']` into
14644
14726
  * a map in the form of `{publicName: 'alias', otherPublicName: 'otherAlias'}`.
14645
14727
  */
14646
14728
  function bindingArrayToMap(bindings) {
14647
- if (!bindings || bindings.length === 0) {
14729
+ if (bindings === undefined || bindings.length === 0) {
14648
14730
  return EMPTY_OBJ;
14649
14731
  }
14650
14732
  const result = {};
14651
- for (let i = 1; i < bindings.length; i += 2) {
14652
- result[bindings[i - 1]] = bindings[i];
14733
+ for (let i = 0; i < bindings.length; i += 2) {
14734
+ result[bindings[i]] = bindings[i + 1];
14653
14735
  }
14654
14736
  return result;
14655
14737
  }
@@ -15596,19 +15678,8 @@ function ɵɵelementStart(index, name, attrsIndex, localRefsIndex) {
15596
15678
  elementStartFirstCreatePass(adjustedIndex, tView, lView, native, name, attrsIndex, localRefsIndex) :
15597
15679
  tView.data[adjustedIndex];
15598
15680
  setCurrentTNode(tNode, true);
15599
- const mergedAttrs = tNode.mergedAttrs;
15600
- if (mergedAttrs !== null) {
15601
- setUpAttributes(renderer, native, mergedAttrs);
15602
- }
15603
- const classes = tNode.classes;
15604
- if (classes !== null) {
15605
- writeDirectClass(renderer, native, classes);
15606
- }
15607
- const styles = tNode.styles;
15608
- if (styles !== null) {
15609
- writeDirectStyle(renderer, native, styles);
15610
- }
15611
- if ((tNode.flags & 64 /* TNodeFlags.isDetached */) !== 64 /* TNodeFlags.isDetached */) {
15681
+ setupStaticAttributes(renderer, native, tNode);
15682
+ if ((tNode.flags & 32 /* TNodeFlags.isDetached */) !== 32 /* TNodeFlags.isDetached */) {
15612
15683
  // In the i18n case, the translation may have removed this element, so only add it if it is not
15613
15684
  // detached. See `TNodeType.Placeholder` and `LFrame.inI18n` for more context.
15614
15685
  appendChild(tView, lView, native, tNode);
@@ -16046,9 +16117,7 @@ function wrapListener(tNode, lView, context, listenerFn, wrapWithPreventDefault)
16046
16117
  }
16047
16118
  // In order to be backwards compatible with View Engine, events on component host nodes
16048
16119
  // must also mark the component view itself dirty (i.e. the view that it owns).
16049
- const startView = tNode.flags & 2 /* TNodeFlags.isComponentHost */ ?
16050
- getComponentLViewByIndex(tNode.index, lView) :
16051
- lView;
16120
+ const startView = tNode.componentOffset > -1 ? getComponentLViewByIndex(tNode.index, lView) : lView;
16052
16121
  markViewDirty(startView);
16053
16122
  let result = executeListenerWithErrorHandling(lView, context, listenerFn, e);
16054
16123
  // A just-invoked listener function might have coalesced listeners so we need to check for
@@ -16205,7 +16274,7 @@ function ɵɵprojection(nodeIndex, selectorIndex = 0, attrs) {
16205
16274
  tProjectionNode.projection = selectorIndex;
16206
16275
  // `<ng-content>` has no content
16207
16276
  setCurrentTNodeAsNotParent();
16208
- if ((tProjectionNode.flags & 64 /* TNodeFlags.isDetached */) !== 64 /* TNodeFlags.isDetached */) {
16277
+ if ((tProjectionNode.flags & 32 /* TNodeFlags.isDetached */) !== 32 /* TNodeFlags.isDetached */) {
16209
16278
  // re-distribution of projectable nodes is stored on a component's view level
16210
16279
  applyProjection(tView, lView, tProjectionNode);
16211
16280
  }
@@ -18119,7 +18188,7 @@ function normalizeSuffix(value, suffix) {
18119
18188
  * @param isClassBased `true` if `class` (`false` if `style`)
18120
18189
  */
18121
18190
  function hasStylingInputShadow(tNode, isClassBased) {
18122
- return (tNode.flags & (isClassBased ? 16 /* TNodeFlags.hasClassInput */ : 32 /* TNodeFlags.hasStyleInput */)) !== 0;
18191
+ return (tNode.flags & (isClassBased ? 8 /* TNodeFlags.hasClassInput */ : 16 /* TNodeFlags.hasStyleInput */)) !== 0;
18123
18192
  }
18124
18193
 
18125
18194
  /**
@@ -19811,7 +19880,7 @@ function processI18nInsertBefore(renderer, childTNode, lView, childRNode, parent
19811
19880
  anchorRNode = i18nParent;
19812
19881
  i18nParent = parentRElement;
19813
19882
  }
19814
- if (i18nParent !== null && (childTNode.flags & 2 /* TNodeFlags.isComponentHost */) === 0) {
19883
+ if (i18nParent !== null && childTNode.componentOffset === -1) {
19815
19884
  for (let i = 1; i < tNodeInsertBeforeIndex.length; i++) {
19816
19885
  // No need to `unwrapRNode` because all of the indexes point to i18n text nodes.
19817
19886
  // see `assertDomNode` below.
@@ -22299,7 +22368,7 @@ function getDirectives(node) {
22299
22368
  return [];
22300
22369
  }
22301
22370
  if (context.directives === undefined) {
22302
- context.directives = getDirectivesAtNodeIndex(nodeIndex, lView, false);
22371
+ context.directives = getDirectivesAtNodeIndex(nodeIndex, lView);
22303
22372
  }
22304
22373
  // The `directives` in this case are a named array called `LComponentView`. Clone the
22305
22374
  // result so we don't expose an internal data structure in the user's console.
@@ -26076,7 +26145,7 @@ class TestBedImpl {
26076
26145
  return TestBedImpl.INSTANCE.overrideProvider(token, provider);
26077
26146
  }
26078
26147
  static inject(token, notFoundValue, flags) {
26079
- return TestBedImpl.INSTANCE.inject(token, notFoundValue, flags);
26148
+ return TestBedImpl.INSTANCE.inject(token, notFoundValue, ɵconvertToBitFlags(flags));
26080
26149
  }
26081
26150
  /** @deprecated from v9.0.0 use TestBed.inject */
26082
26151
  static get(token, notFoundValue = Injector$1.THROW_IF_NOT_FOUND, flags = InjectFlags$1.Default) {
@@ -26209,7 +26278,7 @@ class TestBedImpl {
26209
26278
  return this;
26210
26279
  }
26211
26280
  const UNDEFINED = {};
26212
- const result = this.testModuleRef.injector.get(token, UNDEFINED, flags);
26281
+ const result = this.testModuleRef.injector.get(token, UNDEFINED, ɵconvertToBitFlags(flags));
26213
26282
  return result === UNDEFINED ? this.compiler.injector.get(token, notFoundValue, flags) :
26214
26283
  result;
26215
26284
  }