@angular/core 15.0.0-next.3 → 15.0.0-next.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/esm2020/src/core_private_export.mjs +2 -2
  2. package/esm2020/src/di/index.mjs +1 -1
  3. package/esm2020/src/di/injectable.mjs +1 -1
  4. package/esm2020/src/di/injection_token.mjs +6 -3
  5. package/esm2020/src/di/injector.mjs +1 -1
  6. package/esm2020/src/di/injector_compatibility.mjs +15 -11
  7. package/esm2020/src/di/interface/injector.mjs +1 -1
  8. package/esm2020/src/di/r3_injector.mjs +3 -2
  9. package/esm2020/src/errors.mjs +1 -1
  10. package/esm2020/src/metadata/directives.mjs +1 -1
  11. package/esm2020/src/render3/component_ref.mjs +113 -84
  12. package/esm2020/src/render3/context_discovery.mjs +7 -7
  13. package/esm2020/src/render3/di.mjs +3 -2
  14. package/esm2020/src/render3/features/host_directives_feature.mjs +101 -10
  15. package/esm2020/src/render3/features/ng_onchanges_feature.mjs +4 -2
  16. package/esm2020/src/render3/instructions/element.mjs +3 -15
  17. package/esm2020/src/render3/instructions/shared.mjs +151 -128
  18. package/esm2020/src/render3/interfaces/definition.mjs +1 -1
  19. package/esm2020/src/render3/jit/module.mjs +2 -2
  20. package/esm2020/src/render3/ng_module_ref.mjs +1 -1
  21. package/esm2020/src/render3/node_manipulation.mjs +15 -1
  22. package/esm2020/src/render3/util/discovery_utils.mjs +2 -2
  23. package/esm2020/src/util/is_dev_mode.mjs +11 -19
  24. package/esm2020/src/version.mjs +1 -1
  25. package/esm2020/src/zone/async-stack-tagging.mjs +28 -0
  26. package/esm2020/src/zone/ng_zone.mjs +8 -3
  27. package/esm2020/testing/src/logger.mjs +3 -3
  28. package/esm2020/testing/src/ng_zone_mock.mjs +3 -3
  29. package/esm2020/testing/src/test_bed.mjs +4 -4
  30. package/fesm2015/core.mjs +1789 -1613
  31. package/fesm2015/core.mjs.map +1 -1
  32. package/fesm2015/testing.mjs +1055 -904
  33. package/fesm2015/testing.mjs.map +1 -1
  34. package/fesm2020/core.mjs +1788 -1614
  35. package/fesm2020/core.mjs.map +1 -1
  36. package/fesm2020/testing.mjs +1052 -902
  37. package/fesm2020/testing.mjs.map +1 -1
  38. package/index.d.ts +105 -24
  39. package/package.json +1 -1
  40. package/testing/index.d.ts +9 -1
@@ -1,10 +1,10 @@
1
1
  /**
2
- * @license Angular v15.0.0-next.3
2
+ * @license Angular v15.0.0-next.5
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 = [];
@@ -3166,11 +3170,12 @@ function rememberChangeHistoryAndInvokeOnChangesHook() {
3166
3170
  }
3167
3171
  }
3168
3172
  function ngOnChangesSetInput(instance, value, publicName, privateName) {
3173
+ const declaredName = this.declaredInputs[publicName];
3174
+ ngDevMode && assertString(declaredName, 'Name of input in ngOnChanges has to be a string');
3169
3175
  const simpleChangesStore = getSimpleChangesStore(instance) ||
3170
3176
  setSimpleChangesStore(instance, { previous: EMPTY_OBJ, current: null });
3171
3177
  const current = simpleChangesStore.current || (simpleChangesStore.current = {});
3172
3178
  const previous = simpleChangesStore.previous;
3173
- const declaredName = this.declaredInputs[publicName];
3174
3179
  const previousChange = previous[declaredName];
3175
3180
  current[declaredName] = new SimpleChange(previousChange && previousChange.currentValue, value, previous === EMPTY_OBJ);
3176
3181
  instance[privateName] = value;
@@ -5239,7 +5244,7 @@ class NodeInjector {
5239
5244
  this._lView = _lView;
5240
5245
  }
5241
5246
  get(token, notFoundValue, flags) {
5242
- return getOrCreateInjectable(this._tNode, this._lView, token, flags, notFoundValue);
5247
+ return getOrCreateInjectable(this._tNode, this._lView, token, convertToBitFlags(flags), notFoundValue);
5243
5248
  }
5244
5249
  }
5245
5250
  /** Creates a `NodeInjector` for the current node. */
@@ -6635,8 +6640,11 @@ function getSanitizer() {
6635
6640
  * As you can see in the Tree-shakable InjectionToken example below.
6636
6641
  *
6637
6642
  * Additionally, if a `factory` is specified you can also specify the `providedIn` option, which
6638
- * overrides the above behavior and marks the token as belonging to a particular `@NgModule`. As
6639
- * mentioned above, `'root'` is the default value for `providedIn`.
6643
+ * overrides the above behavior and marks the token as belonging to a particular `@NgModule` (note:
6644
+ * this option is now deprecated). As mentioned above, `'root'` is the default value for
6645
+ * `providedIn`.
6646
+ *
6647
+ * The `providedIn: NgModule` and `providedIn: 'any'` options are deprecated.
6640
6648
  *
6641
6649
  * @usageNotes
6642
6650
  * ### Basic Examples
@@ -7136,6 +7144,7 @@ class R3Injector extends EnvironmentInjector {
7136
7144
  }
7137
7145
  get(token, notFoundValue = THROW_IF_NOT_FOUND, flags = InjectFlags.Default) {
7138
7146
  this.assertNotDestroyed();
7147
+ flags = convertToBitFlags(flags);
7139
7148
  // Set the injection context.
7140
7149
  const previousInjector = setCurrentInjector(this);
7141
7150
  const previousInjectImplementation = setInjectImplementation(undefined);
@@ -7637,7 +7646,7 @@ class Version {
7637
7646
  /**
7638
7647
  * @publicApi
7639
7648
  */
7640
- const VERSION = new Version('15.0.0-next.3');
7649
+ const VERSION = new Version('15.0.0-next.5');
7641
7650
 
7642
7651
  /**
7643
7652
  * @license
@@ -7672,29 +7681,70 @@ const NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR = {};
7672
7681
  * Use of this source code is governed by an MIT-style license that can be
7673
7682
  * found in the LICENSE file at https://angular.io/license
7674
7683
  */
7684
+ // Keeps track of the currently-active LViews.
7685
+ const TRACKED_LVIEWS = new Map();
7686
+ // Used for generating unique IDs for LViews.
7687
+ let uniqueIdCounter = 0;
7688
+ /** Gets a unique ID that can be assigned to an LView. */
7689
+ function getUniqueLViewId() {
7690
+ return uniqueIdCounter++;
7691
+ }
7692
+ /** Starts tracking an LView. */
7693
+ function registerLView(lView) {
7694
+ ngDevMode && assertNumber(lView[ID], 'LView must have an ID in order to be registered');
7695
+ TRACKED_LVIEWS.set(lView[ID], lView);
7696
+ }
7697
+ /** Gets an LView by its unique ID. */
7698
+ function getLViewById(id) {
7699
+ ngDevMode && assertNumber(id, 'ID used for LView lookup must be a number');
7700
+ return TRACKED_LVIEWS.get(id) || null;
7701
+ }
7702
+ /** Stops tracking an LView. */
7703
+ function unregisterLView(lView) {
7704
+ ngDevMode && assertNumber(lView[ID], 'Cannot stop tracking an LView that does not have an ID');
7705
+ TRACKED_LVIEWS.delete(lView[ID]);
7706
+ }
7707
+
7675
7708
  /**
7676
- * Defines a schema that allows an NgModule to contain the following:
7677
- * - Non-Angular elements named with dash case (`-`).
7678
- * - Element properties named with dash case (`-`).
7679
- * Dash case is the naming convention for custom elements.
7709
+ * @license
7710
+ * Copyright Google LLC All Rights Reserved.
7680
7711
  *
7681
- * @publicApi
7712
+ * Use of this source code is governed by an MIT-style license that can be
7713
+ * found in the LICENSE file at https://angular.io/license
7682
7714
  */
7683
- const CUSTOM_ELEMENTS_SCHEMA = {
7684
- name: 'custom-elements'
7685
- };
7686
7715
  /**
7687
- * Defines a schema that allows any property on any element.
7688
- *
7689
- * This schema allows you to ignore the errors related to any unknown elements or properties in a
7690
- * template. The usage of this schema is generally discouraged because it prevents useful validation
7691
- * and may hide real errors in your template. Consider using the `CUSTOM_ELEMENTS_SCHEMA` instead.
7716
+ * The internal view context which is specific to a given DOM element, directive or
7717
+ * component instance. Each value in here (besides the LView and element node details)
7718
+ * can be present, null or undefined. If undefined then it implies the value has not been
7719
+ * looked up yet, otherwise, if null, then a lookup was executed and nothing was found.
7692
7720
  *
7693
- * @publicApi
7721
+ * Each value will get filled when the respective value is examined within the getContext
7722
+ * function. The component, element and each directive instance will share the same instance
7723
+ * of the context.
7694
7724
  */
7695
- const NO_ERRORS_SCHEMA = {
7696
- name: 'no-errors-schema'
7697
- };
7725
+ class LContext {
7726
+ constructor(
7727
+ /**
7728
+ * ID of the component's parent view data.
7729
+ */
7730
+ lViewId,
7731
+ /**
7732
+ * The index instance of the node.
7733
+ */
7734
+ nodeIndex,
7735
+ /**
7736
+ * The instance of the DOM node that is attached to the lNode.
7737
+ */
7738
+ native) {
7739
+ this.lViewId = lViewId;
7740
+ this.nodeIndex = nodeIndex;
7741
+ this.native = native;
7742
+ }
7743
+ /** Component's parent view data. */
7744
+ get lView() {
7745
+ return getLViewById(this.lViewId);
7746
+ }
7747
+ }
7698
7748
 
7699
7749
  /**
7700
7750
  * @license
@@ -7703,336 +7753,305 @@ const NO_ERRORS_SCHEMA = {
7703
7753
  * Use of this source code is governed by an MIT-style license that can be
7704
7754
  * found in the LICENSE file at https://angular.io/license
7705
7755
  */
7706
- let shouldThrowErrorOnUnknownElement = false;
7707
- /**
7708
- * Sets a strict mode for JIT-compiled components to throw an error on unknown elements,
7709
- * instead of just logging the error.
7710
- * (for AOT-compiled ones this check happens at build time).
7711
- */
7712
- function ɵsetUnknownElementStrictMode(shouldThrow) {
7713
- shouldThrowErrorOnUnknownElement = shouldThrow;
7714
- }
7715
- /**
7716
- * Gets the current value of the strict mode.
7717
- */
7718
- function ɵgetUnknownElementStrictMode() {
7719
- return shouldThrowErrorOnUnknownElement;
7720
- }
7721
- let shouldThrowErrorOnUnknownProperty = false;
7722
- /**
7723
- * Sets a strict mode for JIT-compiled components to throw an error on unknown properties,
7724
- * instead of just logging the error.
7725
- * (for AOT-compiled ones this check happens at build time).
7726
- */
7727
- function ɵsetUnknownPropertyStrictMode(shouldThrow) {
7728
- shouldThrowErrorOnUnknownProperty = shouldThrow;
7729
- }
7730
- /**
7731
- * Gets the current value of the strict mode.
7732
- */
7733
- function ɵgetUnknownPropertyStrictMode() {
7734
- return shouldThrowErrorOnUnknownProperty;
7735
- }
7736
7756
  /**
7737
- * Validates that the element is known at runtime and produces
7738
- * an error if it's not the case.
7739
- * This check is relevant for JIT-compiled components (for AOT-compiled
7740
- * ones this check happens at build time).
7757
+ * Returns the matching `LContext` data for a given DOM node, directive or component instance.
7741
7758
  *
7742
- * The element is considered known if either:
7743
- * - it's a known HTML element
7744
- * - it's a known custom element
7745
- * - the element matches any directive
7746
- * - the element is allowed by one of the schemas
7759
+ * This function will examine the provided DOM element, component, or directive instance\'s
7760
+ * monkey-patched property to derive the `LContext` data. Once called then the monkey-patched
7761
+ * value will be that of the newly created `LContext`.
7747
7762
  *
7748
- * @param element Element to validate
7749
- * @param lView An `LView` that represents a current component that is being rendered
7750
- * @param tagName Name of the tag to check
7751
- * @param schemas Array of schemas
7752
- * @param hasDirectives Boolean indicating that the element matches any directive
7763
+ * If the monkey-patched value is the `LView` instance then the context value for that
7764
+ * target will be created and the monkey-patch reference will be updated. Therefore when this
7765
+ * function is called it may mutate the provided element\'s, component\'s or any of the associated
7766
+ * directive\'s monkey-patch values.
7767
+ *
7768
+ * If the monkey-patch value is not detected then the code will walk up the DOM until an element
7769
+ * is found which contains a monkey-patch reference. When that occurs then the provided element
7770
+ * will be updated with a new context (which is then returned). If the monkey-patch value is not
7771
+ * detected for a component/directive instance then it will throw an error (all components and
7772
+ * directives should be automatically monkey-patched by ivy).
7773
+ *
7774
+ * @param target Component, Directive or DOM Node.
7753
7775
  */
7754
- function validateElementIsKnown(element, lView, tagName, schemas, hasDirectives) {
7755
- // If `schemas` is set to `null`, that's an indication that this Component was compiled in AOT
7756
- // mode where this check happens at compile time. In JIT mode, `schemas` is always present and
7757
- // defined as an array (as an empty array in case `schemas` field is not defined) and we should
7758
- // execute the check below.
7759
- if (schemas === null)
7760
- return;
7761
- // If the element matches any directive, it's considered as valid.
7762
- if (!hasDirectives && tagName !== null) {
7763
- // The element is unknown if it's an instance of HTMLUnknownElement, or it isn't registered
7764
- // as a custom element. Note that unknown elements with a dash in their name won't be instances
7765
- // of HTMLUnknownElement in browsers that support web components.
7766
- const isUnknown =
7767
- // Note that we can't check for `typeof HTMLUnknownElement === 'function'`,
7768
- // because while most browsers return 'function', IE returns 'object'.
7769
- (typeof HTMLUnknownElement !== 'undefined' && HTMLUnknownElement &&
7770
- element instanceof HTMLUnknownElement) ||
7771
- (typeof customElements !== 'undefined' && tagName.indexOf('-') > -1 &&
7772
- !customElements.get(tagName));
7773
- if (isUnknown && !matchingSchemas(schemas, tagName)) {
7774
- const isHostStandalone = isHostComponentStandalone(lView);
7775
- const templateLocation = getTemplateLocationDetails(lView);
7776
- const schemas = `'${isHostStandalone ? '@Component' : '@NgModule'}.schemas'`;
7777
- let message = `'${tagName}' is not a known element${templateLocation}:\n`;
7778
- message += `1. If '${tagName}' is an Angular component, then verify that it is ${isHostStandalone ? 'included in the \'@Component.imports\' of this component' :
7779
- 'a part of an @NgModule where this component is declared'}.\n`;
7780
- if (tagName && tagName.indexOf('-') > -1) {
7781
- message +=
7782
- `2. If '${tagName}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the ${schemas} of this component to suppress this message.`;
7776
+ function getLContext(target) {
7777
+ let mpValue = readPatchedData(target);
7778
+ if (mpValue) {
7779
+ // only when it's an array is it considered an LView instance
7780
+ // ... otherwise it's an already constructed LContext instance
7781
+ if (isLView(mpValue)) {
7782
+ const lView = mpValue;
7783
+ let nodeIndex;
7784
+ let component = undefined;
7785
+ let directives = undefined;
7786
+ if (isComponentInstance(target)) {
7787
+ nodeIndex = findViaComponent(lView, target);
7788
+ if (nodeIndex == -1) {
7789
+ throw new Error('The provided component was not found in the application');
7790
+ }
7791
+ component = target;
7792
+ }
7793
+ else if (isDirectiveInstance(target)) {
7794
+ nodeIndex = findViaDirective(lView, target);
7795
+ if (nodeIndex == -1) {
7796
+ throw new Error('The provided directive was not found in the application');
7797
+ }
7798
+ directives = getDirectivesAtNodeIndex(nodeIndex, lView);
7783
7799
  }
7784
7800
  else {
7785
- message +=
7786
- `2. To allow any element add 'NO_ERRORS_SCHEMA' to the ${schemas} of this component.`;
7801
+ nodeIndex = findViaNativeElement(lView, target);
7802
+ if (nodeIndex == -1) {
7803
+ return null;
7804
+ }
7787
7805
  }
7788
- if (shouldThrowErrorOnUnknownElement) {
7789
- throw new RuntimeError(304 /* RuntimeErrorCode.UNKNOWN_ELEMENT */, message);
7806
+ // the goal is not to fill the entire context full of data because the lookups
7807
+ // are expensive. Instead, only the target data (the element, component, container, ICU
7808
+ // expression or directive details) are filled into the context. If called multiple times
7809
+ // with different target values then the missing target data will be filled in.
7810
+ const native = unwrapRNode(lView[nodeIndex]);
7811
+ const existingCtx = readPatchedData(native);
7812
+ const context = (existingCtx && !Array.isArray(existingCtx)) ?
7813
+ existingCtx :
7814
+ createLContext(lView, nodeIndex, native);
7815
+ // only when the component has been discovered then update the monkey-patch
7816
+ if (component && context.component === undefined) {
7817
+ context.component = component;
7818
+ attachPatchData(context.component, context);
7790
7819
  }
7791
- else {
7792
- console.error(formatRuntimeError(304 /* RuntimeErrorCode.UNKNOWN_ELEMENT */, message));
7820
+ // only when the directives have been discovered then update the monkey-patch
7821
+ if (directives && context.directives === undefined) {
7822
+ context.directives = directives;
7823
+ for (let i = 0; i < directives.length; i++) {
7824
+ attachPatchData(directives[i], context);
7825
+ }
7826
+ }
7827
+ attachPatchData(context.native, context);
7828
+ mpValue = context;
7829
+ }
7830
+ }
7831
+ else {
7832
+ const rElement = target;
7833
+ ngDevMode && assertDomNode(rElement);
7834
+ // if the context is not found then we need to traverse upwards up the DOM
7835
+ // to find the nearest element that has already been monkey patched with data
7836
+ let parent = rElement;
7837
+ while (parent = parent.parentNode) {
7838
+ const parentContext = readPatchedData(parent);
7839
+ if (parentContext) {
7840
+ const lView = Array.isArray(parentContext) ? parentContext : parentContext.lView;
7841
+ // the edge of the app was also reached here through another means
7842
+ // (maybe because the DOM was changed manually).
7843
+ if (!lView) {
7844
+ return null;
7845
+ }
7846
+ const index = findViaNativeElement(lView, rElement);
7847
+ if (index >= 0) {
7848
+ const native = unwrapRNode(lView[index]);
7849
+ const context = createLContext(lView, index, native);
7850
+ attachPatchData(native, context);
7851
+ mpValue = context;
7852
+ break;
7853
+ }
7793
7854
  }
7794
7855
  }
7795
7856
  }
7857
+ return mpValue || null;
7796
7858
  }
7797
7859
  /**
7798
- * Validates that the property of the element is known at runtime and returns
7799
- * false if it's not the case.
7800
- * This check is relevant for JIT-compiled components (for AOT-compiled
7801
- * ones this check happens at build time).
7802
- *
7803
- * The property is considered known if either:
7804
- * - it's a known property of the element
7805
- * - the element is allowed by one of the schemas
7806
- * - the property is used for animations
7807
- *
7808
- * @param element Element to validate
7809
- * @param propName Name of the property to check
7810
- * @param tagName Name of the tag hosting the property
7811
- * @param schemas Array of schemas
7860
+ * Creates an empty instance of a `LContext` context
7812
7861
  */
7813
- function isPropertyValid(element, propName, tagName, schemas) {
7814
- // If `schemas` is set to `null`, that's an indication that this Component was compiled in AOT
7815
- // mode where this check happens at compile time. In JIT mode, `schemas` is always present and
7816
- // defined as an array (as an empty array in case `schemas` field is not defined) and we should
7817
- // execute the check below.
7818
- if (schemas === null)
7819
- return true;
7820
- // The property is considered valid if the element matches the schema, it exists on the element,
7821
- // or it is synthetic, and we are in a browser context (web worker nodes should be skipped).
7822
- if (matchingSchemas(schemas, tagName) || propName in element || isAnimationProp(propName)) {
7823
- return true;
7824
- }
7825
- // Note: `typeof Node` returns 'function' in most browsers, but on IE it is 'object' so we
7826
- // need to account for both here, while being careful with `typeof null` also returning 'object'.
7827
- return typeof Node === 'undefined' || Node === null || !(element instanceof Node);
7862
+ function createLContext(lView, nodeIndex, native) {
7863
+ return new LContext(lView[ID], nodeIndex, native);
7828
7864
  }
7829
7865
  /**
7830
- * Logs or throws an error that a property is not supported on an element.
7866
+ * Takes a component instance and returns the view for that component.
7831
7867
  *
7832
- * @param propName Name of the invalid property
7833
- * @param tagName Name of the tag hosting the property
7834
- * @param nodeType Type of the node hosting the property
7835
- * @param lView An `LView` that represents a current component
7868
+ * @param componentInstance
7869
+ * @returns The component's view
7836
7870
  */
7837
- function handleUnknownPropertyError(propName, tagName, nodeType, lView) {
7838
- // Special-case a situation when a structural directive is applied to
7839
- // an `<ng-template>` element, for example: `<ng-template *ngIf="true">`.
7840
- // In this case the compiler generates the `ɵɵtemplate` instruction with
7841
- // the `null` as the tagName. The directive matching logic at runtime relies
7842
- // on this effect (see `isInlineTemplate`), thus using the 'ng-template' as
7843
- // a default value of the `tNode.value` is not feasible at this moment.
7844
- if (!tagName && nodeType === 4 /* TNodeType.Container */) {
7845
- tagName = 'ng-template';
7846
- }
7847
- const isHostStandalone = isHostComponentStandalone(lView);
7848
- const templateLocation = getTemplateLocationDetails(lView);
7849
- let message = `Can't bind to '${propName}' since it isn't a known property of '${tagName}'${templateLocation}.`;
7850
- const schemas = `'${isHostStandalone ? '@Component' : '@NgModule'}.schemas'`;
7851
- const importLocation = isHostStandalone ?
7852
- 'included in the \'@Component.imports\' of this component' :
7853
- 'a part of an @NgModule where this component is declared';
7854
- if (KNOWN_CONTROL_FLOW_DIRECTIVES.has(propName)) {
7855
- // Most likely this is a control flow directive (such as `*ngIf`) used in
7856
- // a template, but the directive or the `CommonModule` is not imported.
7857
- const correspondingImport = KNOWN_CONTROL_FLOW_DIRECTIVES.get(propName);
7858
- message += `\nIf the '${propName}' is an Angular control flow directive, ` +
7859
- `please make sure that either the '${correspondingImport}' directive or the 'CommonModule' is ${importLocation}.`;
7871
+ function getComponentViewByInstance(componentInstance) {
7872
+ let patchedData = readPatchedData(componentInstance);
7873
+ let lView;
7874
+ if (isLView(patchedData)) {
7875
+ const contextLView = patchedData;
7876
+ const nodeIndex = findViaComponent(contextLView, componentInstance);
7877
+ lView = getComponentLViewByIndex(nodeIndex, contextLView);
7878
+ const context = createLContext(contextLView, nodeIndex, lView[HOST]);
7879
+ context.component = componentInstance;
7880
+ attachPatchData(componentInstance, context);
7881
+ attachPatchData(context.native, context);
7860
7882
  }
7861
7883
  else {
7862
- // May be an Angular component, which is not imported/declared?
7863
- message += `\n1. If '${tagName}' is an Angular component and it has the ` +
7864
- `'${propName}' input, then verify that it is ${importLocation}.`;
7865
- // May be a Web Component?
7866
- if (tagName && tagName.indexOf('-') > -1) {
7867
- message += `\n2. If '${tagName}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' ` +
7868
- `to the ${schemas} of this component to suppress this message.`;
7869
- message += `\n3. To allow any property add 'NO_ERRORS_SCHEMA' to ` +
7870
- `the ${schemas} of this component.`;
7871
- }
7872
- else {
7873
- // If it's expected, the error can be suppressed by the `NO_ERRORS_SCHEMA` schema.
7874
- message += `\n2. To allow any property add 'NO_ERRORS_SCHEMA' to ` +
7875
- `the ${schemas} of this component.`;
7876
- }
7884
+ const context = patchedData;
7885
+ const contextLView = context.lView;
7886
+ ngDevMode && assertLView(contextLView);
7887
+ lView = getComponentLViewByIndex(context.nodeIndex, contextLView);
7877
7888
  }
7878
- reportUnknownPropertyError(message);
7889
+ return lView;
7879
7890
  }
7880
- function reportUnknownPropertyError(message) {
7881
- if (shouldThrowErrorOnUnknownProperty) {
7882
- throw new RuntimeError(303 /* RuntimeErrorCode.UNKNOWN_BINDING */, message);
7891
+ /**
7892
+ * This property will be monkey-patched on elements, components and directives.
7893
+ */
7894
+ const MONKEY_PATCH_KEY_NAME = '__ngContext__';
7895
+ /**
7896
+ * Assigns the given data to the given target (which could be a component,
7897
+ * directive or DOM node instance) using monkey-patching.
7898
+ */
7899
+ function attachPatchData(target, data) {
7900
+ ngDevMode && assertDefined(target, 'Target expected');
7901
+ // Only attach the ID of the view in order to avoid memory leaks (see #41047). We only do this
7902
+ // for `LView`, because we have control over when an `LView` is created and destroyed, whereas
7903
+ // we can't know when to remove an `LContext`.
7904
+ if (isLView(data)) {
7905
+ target[MONKEY_PATCH_KEY_NAME] = data[ID];
7906
+ registerLView(data);
7883
7907
  }
7884
7908
  else {
7885
- console.error(formatRuntimeError(303 /* RuntimeErrorCode.UNKNOWN_BINDING */, message));
7909
+ target[MONKEY_PATCH_KEY_NAME] = data;
7886
7910
  }
7887
7911
  }
7888
7912
  /**
7889
- * WARNING: this is a **dev-mode only** function (thus should always be guarded by the `ngDevMode`)
7890
- * and must **not** be used in production bundles. The function makes megamorphic reads, which might
7891
- * be too slow for production mode and also it relies on the constructor function being available.
7892
- *
7893
- * Gets a reference to the host component def (where a current component is declared).
7894
- *
7895
- * @param lView An `LView` that represents a current component that is being rendered.
7913
+ * Returns the monkey-patch value data present on the target (which could be
7914
+ * a component, directive or a DOM node).
7896
7915
  */
7897
- function getDeclarationComponentDef(lView) {
7898
- !ngDevMode && throwError('Must never be called in production mode');
7899
- const declarationLView = lView[DECLARATION_COMPONENT_VIEW];
7900
- const context = declarationLView[CONTEXT];
7901
- // Unable to obtain a context.
7902
- if (!context)
7903
- return null;
7904
- return context.constructor ? getComponentDef$1(context.constructor) : null;
7916
+ function readPatchedData(target) {
7917
+ ngDevMode && assertDefined(target, 'Target expected');
7918
+ const data = target[MONKEY_PATCH_KEY_NAME];
7919
+ return (typeof data === 'number') ? getLViewById(data) : data || null;
7920
+ }
7921
+ function readPatchedLView(target) {
7922
+ const value = readPatchedData(target);
7923
+ if (value) {
7924
+ return (isLView(value) ? value : value.lView);
7925
+ }
7926
+ return null;
7927
+ }
7928
+ function isComponentInstance(instance) {
7929
+ return instance && instance.constructor && instance.constructor.ɵcmp;
7930
+ }
7931
+ function isDirectiveInstance(instance) {
7932
+ return instance && instance.constructor && instance.constructor.ɵdir;
7905
7933
  }
7906
7934
  /**
7907
- * WARNING: this is a **dev-mode only** function (thus should always be guarded by the `ngDevMode`)
7908
- * and must **not** be used in production bundles. The function makes megamorphic reads, which might
7909
- * be too slow for production mode.
7910
- *
7911
- * Checks if the current component is declared inside of a standalone component template.
7912
- *
7913
- * @param lView An `LView` that represents a current component that is being rendered.
7935
+ * Locates the element within the given LView and returns the matching index
7914
7936
  */
7915
- function isHostComponentStandalone(lView) {
7916
- !ngDevMode && throwError('Must never be called in production mode');
7917
- const componentDef = getDeclarationComponentDef(lView);
7918
- // Treat host component as non-standalone if we can't obtain the def.
7919
- return !!componentDef?.standalone;
7937
+ function findViaNativeElement(lView, target) {
7938
+ const tView = lView[TVIEW];
7939
+ for (let i = HEADER_OFFSET; i < tView.bindingStartIndex; i++) {
7940
+ if (unwrapRNode(lView[i]) === target) {
7941
+ return i;
7942
+ }
7943
+ }
7944
+ return -1;
7920
7945
  }
7921
7946
  /**
7922
- * WARNING: this is a **dev-mode only** function (thus should always be guarded by the `ngDevMode`)
7923
- * and must **not** be used in production bundles. The function makes megamorphic reads, which might
7924
- * be too slow for production mode.
7925
- *
7926
- * Constructs a string describing the location of the host component template. The function is used
7927
- * in dev mode to produce error messages.
7928
- *
7929
- * @param lView An `LView` that represents a current component that is being rendered.
7947
+ * Locates the next tNode (child, sibling or parent).
7930
7948
  */
7931
- function getTemplateLocationDetails(lView) {
7932
- !ngDevMode && throwError('Must never be called in production mode');
7933
- const hostComponentDef = getDeclarationComponentDef(lView);
7934
- const componentClassName = hostComponentDef?.type?.name;
7935
- return componentClassName ? ` (used in the '${componentClassName}' component template)` : '';
7949
+ function traverseNextElement(tNode) {
7950
+ if (tNode.child) {
7951
+ return tNode.child;
7952
+ }
7953
+ else if (tNode.next) {
7954
+ return tNode.next;
7955
+ }
7956
+ else {
7957
+ // Let's take the following template: <div><span>text</span></div><component/>
7958
+ // After checking the text node, we need to find the next parent that has a "next" TNode,
7959
+ // in this case the parent `div`, so that we can find the component.
7960
+ while (tNode.parent && !tNode.parent.next) {
7961
+ tNode = tNode.parent;
7962
+ }
7963
+ return tNode.parent && tNode.parent.next;
7964
+ }
7936
7965
  }
7937
7966
  /**
7938
- * The set of known control flow directives and their corresponding imports.
7939
- * We use this set to produce a more precises error message with a note
7940
- * that the `CommonModule` should also be included.
7967
+ * Locates the component within the given LView and returns the matching index
7941
7968
  */
7942
- const KNOWN_CONTROL_FLOW_DIRECTIVES = new Map([
7943
- ['ngIf', 'NgIf'], ['ngFor', 'NgFor'], ['ngSwitchCase', 'NgSwitchCase'],
7944
- ['ngSwitchDefault', 'NgSwitchDefault']
7945
- ]);
7969
+ function findViaComponent(lView, componentInstance) {
7970
+ const componentIndices = lView[TVIEW].components;
7971
+ if (componentIndices) {
7972
+ for (let i = 0; i < componentIndices.length; i++) {
7973
+ const elementComponentIndex = componentIndices[i];
7974
+ const componentView = getComponentLViewByIndex(elementComponentIndex, lView);
7975
+ if (componentView[CONTEXT] === componentInstance) {
7976
+ return elementComponentIndex;
7977
+ }
7978
+ }
7979
+ }
7980
+ else {
7981
+ const rootComponentView = getComponentLViewByIndex(HEADER_OFFSET, lView);
7982
+ const rootComponent = rootComponentView[CONTEXT];
7983
+ if (rootComponent === componentInstance) {
7984
+ // we are dealing with the root element here therefore we know that the
7985
+ // element is the very first element after the HEADER data in the lView
7986
+ return HEADER_OFFSET;
7987
+ }
7988
+ }
7989
+ return -1;
7990
+ }
7946
7991
  /**
7947
- * Returns true if the tag name is allowed by specified schemas.
7948
- * @param schemas Array of schemas
7949
- * @param tagName Name of the tag
7992
+ * Locates the directive within the given LView and returns the matching index
7950
7993
  */
7951
- function matchingSchemas(schemas, tagName) {
7952
- if (schemas !== null) {
7953
- for (let i = 0; i < schemas.length; i++) {
7954
- const schema = schemas[i];
7955
- if (schema === NO_ERRORS_SCHEMA ||
7956
- schema === CUSTOM_ELEMENTS_SCHEMA && tagName && tagName.indexOf('-') > -1) {
7957
- return true;
7994
+ function findViaDirective(lView, directiveInstance) {
7995
+ // if a directive is monkey patched then it will (by default)
7996
+ // have a reference to the LView of the current view. The
7997
+ // element bound to the directive being search lives somewhere
7998
+ // in the view data. We loop through the nodes and check their
7999
+ // list of directives for the instance.
8000
+ let tNode = lView[TVIEW].firstChild;
8001
+ while (tNode) {
8002
+ const directiveIndexStart = tNode.directiveStart;
8003
+ const directiveIndexEnd = tNode.directiveEnd;
8004
+ for (let i = directiveIndexStart; i < directiveIndexEnd; i++) {
8005
+ if (lView[i] === directiveInstance) {
8006
+ return tNode.index;
7958
8007
  }
7959
8008
  }
8009
+ tNode = traverseNextElement(tNode);
7960
8010
  }
7961
- return false;
8011
+ return -1;
7962
8012
  }
7963
-
7964
8013
  /**
7965
- * @license
7966
- * Copyright Google LLC All Rights Reserved.
8014
+ * Returns a list of directives applied to a node at a specific index. The list includes
8015
+ * directives matched by selector and any host directives, but it excludes components.
8016
+ * Use `getComponentAtNodeIndex` to find the component applied to a node.
7967
8017
  *
7968
- * Use of this source code is governed by an MIT-style license that can be
7969
- * found in the LICENSE file at https://angular.io/license
8018
+ * @param nodeIndex The node index
8019
+ * @param lView The target view data
7970
8020
  */
7971
- const ERROR_ORIGINAL_ERROR = 'ngOriginalError';
7972
- function wrappedError(message, originalError) {
7973
- const msg = `${message} caused by: ${originalError instanceof Error ? originalError.message : originalError}`;
7974
- const error = Error(msg);
7975
- error[ERROR_ORIGINAL_ERROR] = originalError;
7976
- return error;
8021
+ function getDirectivesAtNodeIndex(nodeIndex, lView) {
8022
+ const tNode = lView[TVIEW].data[nodeIndex];
8023
+ if (tNode.directiveStart === 0)
8024
+ return EMPTY_ARRAY;
8025
+ const results = [];
8026
+ for (let i = tNode.directiveStart; i < tNode.directiveEnd; i++) {
8027
+ const directiveInstance = lView[i];
8028
+ if (!isComponentInstance(directiveInstance)) {
8029
+ results.push(directiveInstance);
8030
+ }
8031
+ }
8032
+ return results;
7977
8033
  }
7978
- function getOriginalError(error) {
7979
- return error[ERROR_ORIGINAL_ERROR];
8034
+ function getComponentAtNodeIndex(nodeIndex, lView) {
8035
+ const tNode = lView[TVIEW].data[nodeIndex];
8036
+ const { directiveStart, componentOffset } = tNode;
8037
+ return componentOffset > -1 ? lView[directiveStart + componentOffset] : null;
7980
8038
  }
7981
-
7982
- /**
7983
- * @license
7984
- * Copyright Google LLC All Rights Reserved.
7985
- *
7986
- * Use of this source code is governed by an MIT-style license that can be
7987
- * found in the LICENSE file at https://angular.io/license
7988
- */
7989
8039
  /**
7990
- * Provides a hook for centralized exception handling.
7991
- *
7992
- * The default implementation of `ErrorHandler` prints error messages to the `console`. To
7993
- * intercept error handling, write a custom exception handler that replaces this default as
7994
- * appropriate for your app.
7995
- *
7996
- * @usageNotes
7997
- * ### Example
7998
- *
7999
- * ```
8000
- * class MyErrorHandler implements ErrorHandler {
8001
- * handleError(error) {
8002
- * // do something with the exception
8003
- * }
8004
- * }
8005
- *
8006
- * @NgModule({
8007
- * providers: [{provide: ErrorHandler, useClass: MyErrorHandler}]
8008
- * })
8009
- * class MyModule {}
8010
- * ```
8011
- *
8012
- * @publicApi
8040
+ * Returns a map of local references (local reference name => element or directive instance) that
8041
+ * exist on a given element.
8013
8042
  */
8014
- class ErrorHandler {
8015
- constructor() {
8016
- /**
8017
- * @internal
8018
- */
8019
- this._console = console;
8020
- }
8021
- handleError(error) {
8022
- const originalError = this._findOriginalError(error);
8023
- this._console.error('ERROR', error);
8024
- if (originalError) {
8025
- this._console.error('ORIGINAL ERROR', originalError);
8026
- }
8027
- }
8028
- /** @internal */
8029
- _findOriginalError(error) {
8030
- let e = error && getOriginalError(error);
8031
- while (e && getOriginalError(e)) {
8032
- e = getOriginalError(e);
8043
+ function discoverLocalRefs(lView, nodeIndex) {
8044
+ const tNode = lView[TVIEW].data[nodeIndex];
8045
+ if (tNode && tNode.localNames) {
8046
+ const result = {};
8047
+ let localIndex = tNode.index + 1;
8048
+ for (let i = 0; i < tNode.localNames.length; i += 2) {
8049
+ result[tNode.localNames[i]] = lView[localIndex];
8050
+ localIndex++;
8033
8051
  }
8034
- return e || null;
8052
+ return result;
8035
8053
  }
8054
+ return null;
8036
8055
  }
8037
8056
 
8038
8057
  /**
@@ -8043,46 +8062,28 @@ class ErrorHandler {
8043
8062
  * found in the LICENSE file at https://angular.io/license
8044
8063
  */
8045
8064
  /**
8046
- * Disallowed strings in the comment.
8065
+ * Defines a schema that allows an NgModule to contain the following:
8066
+ * - Non-Angular elements named with dash case (`-`).
8067
+ * - Element properties named with dash case (`-`).
8068
+ * Dash case is the naming convention for custom elements.
8047
8069
  *
8048
- * see: https://html.spec.whatwg.org/multipage/syntax.html#comments
8049
- */
8050
- const COMMENT_DISALLOWED = /^>|^->|<!--|-->|--!>|<!-$/g;
8051
- /**
8052
- * Delimiter in the disallowed strings which needs to be wrapped with zero with character.
8070
+ * @publicApi
8053
8071
  */
8054
- const COMMENT_DELIMITER = /(<|>)/;
8055
- const COMMENT_DELIMITER_ESCAPED = '\u200B$1\u200B';
8072
+ const CUSTOM_ELEMENTS_SCHEMA = {
8073
+ name: 'custom-elements'
8074
+ };
8056
8075
  /**
8057
- * Escape the content of comment strings so that it can be safely inserted into a comment node.
8058
- *
8059
- * The issue is that HTML does not specify any way to escape comment end text inside the comment.
8060
- * Consider: `<!-- The way you close a comment is with ">", and "->" at the beginning or by "-->" or
8061
- * "--!>" at the end. -->`. Above the `"-->"` is meant to be text not an end to the comment. This
8062
- * can be created programmatically through DOM APIs. (`<!--` are also disallowed.)
8063
- *
8064
- * see: https://html.spec.whatwg.org/multipage/syntax.html#comments
8065
- *
8066
- * ```
8067
- * div.innerHTML = div.innerHTML
8068
- * ```
8069
- *
8070
- * One would expect that the above code would be safe to do, but it turns out that because comment
8071
- * text is not escaped, the comment may contain text which will prematurely close the comment
8072
- * opening up the application for XSS attack. (In SSR we programmatically create comment nodes which
8073
- * may contain such text and expect them to be safe.)
8076
+ * Defines a schema that allows any property on any element.
8074
8077
  *
8075
- * This function escapes the comment text by looking for comment delimiters (`<` and `>`) and
8076
- * surrounding them with `_>_` where the `_` is a zero width space `\u200B`. The result is that if a
8077
- * comment contains any of the comment start/end delimiters (such as `<!--`, `-->` or `--!>`) the
8078
- * text it will render normally but it will not cause the HTML parser to close/open the comment.
8078
+ * This schema allows you to ignore the errors related to any unknown elements or properties in a
8079
+ * template. The usage of this schema is generally discouraged because it prevents useful validation
8080
+ * and may hide real errors in your template. Consider using the `CUSTOM_ELEMENTS_SCHEMA` instead.
8079
8081
  *
8080
- * @param value text to make safe for comment node by escaping the comment open/close character
8081
- * sequence.
8082
+ * @publicApi
8082
8083
  */
8083
- function escapeCommentText(value) {
8084
- return value.replace(COMMENT_DISALLOWED, (text) => text.replace(COMMENT_DELIMITER, COMMENT_DELIMITER_ESCAPED));
8085
- }
8084
+ const NO_ERRORS_SCHEMA = {
8085
+ name: 'no-errors-schema'
8086
+ };
8086
8087
 
8087
8088
  /**
8088
8089
  * @license
@@ -8091,403 +8092,411 @@ function escapeCommentText(value) {
8091
8092
  * Use of this source code is governed by an MIT-style license that can be
8092
8093
  * found in the LICENSE file at https://angular.io/license
8093
8094
  */
8094
- function normalizeDebugBindingName(name) {
8095
- // Attribute names with `$` (eg `x-y$`) are valid per spec, but unsupported by some browsers
8096
- name = camelCaseToDashCase(name.replace(/[$@]/g, '_'));
8097
- return `ng-reflect-${name}`;
8098
- }
8099
- const CAMEL_CASE_REGEXP = /([A-Z])/g;
8100
- function camelCaseToDashCase(input) {
8101
- return input.replace(CAMEL_CASE_REGEXP, (...m) => '-' + m[1].toLowerCase());
8102
- }
8103
- function normalizeDebugBindingValue(value) {
8104
- try {
8105
- // Limit the size of the value as otherwise the DOM just gets polluted.
8106
- return value != null ? value.toString().slice(0, 30) : value;
8107
- }
8108
- catch (e) {
8109
- return '[ERROR] Exception while trying to serialize the value';
8110
- }
8111
- }
8112
-
8095
+ let shouldThrowErrorOnUnknownElement = false;
8113
8096
  /**
8114
- * @license
8115
- * Copyright Google LLC All Rights Reserved.
8116
- *
8117
- * Use of this source code is governed by an MIT-style license that can be
8118
- * found in the LICENSE file at https://angular.io/license
8097
+ * Sets a strict mode for JIT-compiled components to throw an error on unknown elements,
8098
+ * instead of just logging the error.
8099
+ * (for AOT-compiled ones this check happens at build time).
8119
8100
  */
8120
- // Keeps track of the currently-active LViews.
8121
- const TRACKED_LVIEWS = new Map();
8122
- // Used for generating unique IDs for LViews.
8123
- let uniqueIdCounter = 0;
8124
- /** Gets a unique ID that can be assigned to an LView. */
8125
- function getUniqueLViewId() {
8126
- return uniqueIdCounter++;
8127
- }
8128
- /** Starts tracking an LView. */
8129
- function registerLView(lView) {
8130
- ngDevMode && assertNumber(lView[ID], 'LView must have an ID in order to be registered');
8131
- TRACKED_LVIEWS.set(lView[ID], lView);
8132
- }
8133
- /** Gets an LView by its unique ID. */
8134
- function getLViewById(id) {
8135
- ngDevMode && assertNumber(id, 'ID used for LView lookup must be a number');
8136
- return TRACKED_LVIEWS.get(id) || null;
8137
- }
8138
- /** Stops tracking an LView. */
8139
- function unregisterLView(lView) {
8140
- ngDevMode && assertNumber(lView[ID], 'Cannot stop tracking an LView that does not have an ID');
8141
- TRACKED_LVIEWS.delete(lView[ID]);
8101
+ function ɵsetUnknownElementStrictMode(shouldThrow) {
8102
+ shouldThrowErrorOnUnknownElement = shouldThrow;
8142
8103
  }
8143
-
8144
8104
  /**
8145
- * @license
8146
- * Copyright Google LLC All Rights Reserved.
8147
- *
8148
- * Use of this source code is governed by an MIT-style license that can be
8149
- * found in the LICENSE file at https://angular.io/license
8105
+ * Gets the current value of the strict mode.
8150
8106
  */
8107
+ function ɵgetUnknownElementStrictMode() {
8108
+ return shouldThrowErrorOnUnknownElement;
8109
+ }
8110
+ let shouldThrowErrorOnUnknownProperty = false;
8151
8111
  /**
8152
- * The internal view context which is specific to a given DOM element, directive or
8153
- * component instance. Each value in here (besides the LView and element node details)
8154
- * can be present, null or undefined. If undefined then it implies the value has not been
8155
- * looked up yet, otherwise, if null, then a lookup was executed and nothing was found.
8156
- *
8157
- * Each value will get filled when the respective value is examined within the getContext
8158
- * function. The component, element and each directive instance will share the same instance
8159
- * of the context.
8112
+ * Sets a strict mode for JIT-compiled components to throw an error on unknown properties,
8113
+ * instead of just logging the error.
8114
+ * (for AOT-compiled ones this check happens at build time).
8160
8115
  */
8161
- class LContext {
8162
- constructor(
8163
- /**
8164
- * ID of the component's parent view data.
8165
- */
8166
- lViewId,
8167
- /**
8168
- * The index instance of the node.
8169
- */
8170
- nodeIndex,
8171
- /**
8172
- * The instance of the DOM node that is attached to the lNode.
8173
- */
8174
- native) {
8175
- this.lViewId = lViewId;
8176
- this.nodeIndex = nodeIndex;
8177
- this.native = native;
8178
- }
8179
- /** Component's parent view data. */
8180
- get lView() {
8181
- return getLViewById(this.lViewId);
8182
- }
8116
+ function ɵsetUnknownPropertyStrictMode(shouldThrow) {
8117
+ shouldThrowErrorOnUnknownProperty = shouldThrow;
8183
8118
  }
8184
-
8185
8119
  /**
8186
- * @license
8187
- * Copyright Google LLC All Rights Reserved.
8188
- *
8189
- * Use of this source code is governed by an MIT-style license that can be
8190
- * found in the LICENSE file at https://angular.io/license
8120
+ * Gets the current value of the strict mode.
8191
8121
  */
8122
+ function ɵgetUnknownPropertyStrictMode() {
8123
+ return shouldThrowErrorOnUnknownProperty;
8124
+ }
8192
8125
  /**
8193
- * Returns the matching `LContext` data for a given DOM node, directive or component instance.
8126
+ * Validates that the element is known at runtime and produces
8127
+ * an error if it's not the case.
8128
+ * This check is relevant for JIT-compiled components (for AOT-compiled
8129
+ * ones this check happens at build time).
8194
8130
  *
8195
- * This function will examine the provided DOM element, component, or directive instance\'s
8196
- * monkey-patched property to derive the `LContext` data. Once called then the monkey-patched
8197
- * value will be that of the newly created `LContext`.
8131
+ * The element is considered known if either:
8132
+ * - it's a known HTML element
8133
+ * - it's a known custom element
8134
+ * - the element matches any directive
8135
+ * - the element is allowed by one of the schemas
8198
8136
  *
8199
- * If the monkey-patched value is the `LView` instance then the context value for that
8200
- * target will be created and the monkey-patch reference will be updated. Therefore when this
8201
- * function is called it may mutate the provided element\'s, component\'s or any of the associated
8202
- * directive\'s monkey-patch values.
8203
- *
8204
- * If the monkey-patch value is not detected then the code will walk up the DOM until an element
8205
- * is found which contains a monkey-patch reference. When that occurs then the provided element
8206
- * will be updated with a new context (which is then returned). If the monkey-patch value is not
8207
- * detected for a component/directive instance then it will throw an error (all components and
8208
- * directives should be automatically monkey-patched by ivy).
8209
- *
8210
- * @param target Component, Directive or DOM Node.
8137
+ * @param element Element to validate
8138
+ * @param lView An `LView` that represents a current component that is being rendered
8139
+ * @param tagName Name of the tag to check
8140
+ * @param schemas Array of schemas
8141
+ * @param hasDirectives Boolean indicating that the element matches any directive
8211
8142
  */
8212
- function getLContext(target) {
8213
- let mpValue = readPatchedData(target);
8214
- if (mpValue) {
8215
- // only when it's an array is it considered an LView instance
8216
- // ... otherwise it's an already constructed LContext instance
8217
- if (isLView(mpValue)) {
8218
- const lView = mpValue;
8219
- let nodeIndex;
8220
- let component = undefined;
8221
- let directives = undefined;
8222
- if (isComponentInstance(target)) {
8223
- nodeIndex = findViaComponent(lView, target);
8224
- if (nodeIndex == -1) {
8225
- throw new Error('The provided component was not found in the application');
8226
- }
8227
- component = target;
8228
- }
8229
- else if (isDirectiveInstance(target)) {
8230
- nodeIndex = findViaDirective(lView, target);
8231
- if (nodeIndex == -1) {
8232
- throw new Error('The provided directive was not found in the application');
8233
- }
8234
- directives = getDirectivesAtNodeIndex(nodeIndex, lView, false);
8143
+ function validateElementIsKnown(element, lView, tagName, schemas, hasDirectives) {
8144
+ // If `schemas` is set to `null`, that's an indication that this Component was compiled in AOT
8145
+ // mode where this check happens at compile time. In JIT mode, `schemas` is always present and
8146
+ // defined as an array (as an empty array in case `schemas` field is not defined) and we should
8147
+ // execute the check below.
8148
+ if (schemas === null)
8149
+ return;
8150
+ // If the element matches any directive, it's considered as valid.
8151
+ if (!hasDirectives && tagName !== null) {
8152
+ // The element is unknown if it's an instance of HTMLUnknownElement, or it isn't registered
8153
+ // as a custom element. Note that unknown elements with a dash in their name won't be instances
8154
+ // of HTMLUnknownElement in browsers that support web components.
8155
+ const isUnknown =
8156
+ // Note that we can't check for `typeof HTMLUnknownElement === 'function'`,
8157
+ // because while most browsers return 'function', IE returns 'object'.
8158
+ (typeof HTMLUnknownElement !== 'undefined' && HTMLUnknownElement &&
8159
+ element instanceof HTMLUnknownElement) ||
8160
+ (typeof customElements !== 'undefined' && tagName.indexOf('-') > -1 &&
8161
+ !customElements.get(tagName));
8162
+ if (isUnknown && !matchingSchemas(schemas, tagName)) {
8163
+ const isHostStandalone = isHostComponentStandalone(lView);
8164
+ const templateLocation = getTemplateLocationDetails(lView);
8165
+ const schemas = `'${isHostStandalone ? '@Component' : '@NgModule'}.schemas'`;
8166
+ let message = `'${tagName}' is not a known element${templateLocation}:\n`;
8167
+ message += `1. If '${tagName}' is an Angular component, then verify that it is ${isHostStandalone ? 'included in the \'@Component.imports\' of this component' :
8168
+ 'a part of an @NgModule where this component is declared'}.\n`;
8169
+ if (tagName && tagName.indexOf('-') > -1) {
8170
+ message +=
8171
+ `2. If '${tagName}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the ${schemas} of this component to suppress this message.`;
8235
8172
  }
8236
8173
  else {
8237
- nodeIndex = findViaNativeElement(lView, target);
8238
- if (nodeIndex == -1) {
8239
- return null;
8240
- }
8241
- }
8242
- // the goal is not to fill the entire context full of data because the lookups
8243
- // are expensive. Instead, only the target data (the element, component, container, ICU
8244
- // expression or directive details) are filled into the context. If called multiple times
8245
- // with different target values then the missing target data will be filled in.
8246
- const native = unwrapRNode(lView[nodeIndex]);
8247
- const existingCtx = readPatchedData(native);
8248
- const context = (existingCtx && !Array.isArray(existingCtx)) ?
8249
- existingCtx :
8250
- createLContext(lView, nodeIndex, native);
8251
- // only when the component has been discovered then update the monkey-patch
8252
- if (component && context.component === undefined) {
8253
- context.component = component;
8254
- attachPatchData(context.component, context);
8174
+ message +=
8175
+ `2. To allow any element add 'NO_ERRORS_SCHEMA' to the ${schemas} of this component.`;
8255
8176
  }
8256
- // only when the directives have been discovered then update the monkey-patch
8257
- if (directives && context.directives === undefined) {
8258
- context.directives = directives;
8259
- for (let i = 0; i < directives.length; i++) {
8260
- attachPatchData(directives[i], context);
8261
- }
8177
+ if (shouldThrowErrorOnUnknownElement) {
8178
+ throw new RuntimeError(304 /* RuntimeErrorCode.UNKNOWN_ELEMENT */, message);
8262
8179
  }
8263
- attachPatchData(context.native, context);
8264
- mpValue = context;
8265
- }
8266
- }
8267
- else {
8268
- const rElement = target;
8269
- ngDevMode && assertDomNode(rElement);
8270
- // if the context is not found then we need to traverse upwards up the DOM
8271
- // to find the nearest element that has already been monkey patched with data
8272
- let parent = rElement;
8273
- while (parent = parent.parentNode) {
8274
- const parentContext = readPatchedData(parent);
8275
- if (parentContext) {
8276
- const lView = Array.isArray(parentContext) ? parentContext : parentContext.lView;
8277
- // the edge of the app was also reached here through another means
8278
- // (maybe because the DOM was changed manually).
8279
- if (!lView) {
8280
- return null;
8281
- }
8282
- const index = findViaNativeElement(lView, rElement);
8283
- if (index >= 0) {
8284
- const native = unwrapRNode(lView[index]);
8285
- const context = createLContext(lView, index, native);
8286
- attachPatchData(native, context);
8287
- mpValue = context;
8288
- break;
8289
- }
8180
+ else {
8181
+ console.error(formatRuntimeError(304 /* RuntimeErrorCode.UNKNOWN_ELEMENT */, message));
8290
8182
  }
8291
8183
  }
8292
8184
  }
8293
- return mpValue || null;
8294
8185
  }
8295
8186
  /**
8296
- * Creates an empty instance of a `LContext` context
8187
+ * Validates that the property of the element is known at runtime and returns
8188
+ * false if it's not the case.
8189
+ * This check is relevant for JIT-compiled components (for AOT-compiled
8190
+ * ones this check happens at build time).
8191
+ *
8192
+ * The property is considered known if either:
8193
+ * - it's a known property of the element
8194
+ * - the element is allowed by one of the schemas
8195
+ * - the property is used for animations
8196
+ *
8197
+ * @param element Element to validate
8198
+ * @param propName Name of the property to check
8199
+ * @param tagName Name of the tag hosting the property
8200
+ * @param schemas Array of schemas
8297
8201
  */
8298
- function createLContext(lView, nodeIndex, native) {
8299
- return new LContext(lView[ID], nodeIndex, native);
8202
+ function isPropertyValid(element, propName, tagName, schemas) {
8203
+ // If `schemas` is set to `null`, that's an indication that this Component was compiled in AOT
8204
+ // mode where this check happens at compile time. In JIT mode, `schemas` is always present and
8205
+ // defined as an array (as an empty array in case `schemas` field is not defined) and we should
8206
+ // execute the check below.
8207
+ if (schemas === null)
8208
+ return true;
8209
+ // The property is considered valid if the element matches the schema, it exists on the element,
8210
+ // or it is synthetic, and we are in a browser context (web worker nodes should be skipped).
8211
+ if (matchingSchemas(schemas, tagName) || propName in element || isAnimationProp(propName)) {
8212
+ return true;
8213
+ }
8214
+ // Note: `typeof Node` returns 'function' in most browsers, but on IE it is 'object' so we
8215
+ // need to account for both here, while being careful with `typeof null` also returning 'object'.
8216
+ return typeof Node === 'undefined' || Node === null || !(element instanceof Node);
8300
8217
  }
8301
8218
  /**
8302
- * Takes a component instance and returns the view for that component.
8219
+ * Logs or throws an error that a property is not supported on an element.
8303
8220
  *
8304
- * @param componentInstance
8305
- * @returns The component's view
8221
+ * @param propName Name of the invalid property
8222
+ * @param tagName Name of the tag hosting the property
8223
+ * @param nodeType Type of the node hosting the property
8224
+ * @param lView An `LView` that represents a current component
8306
8225
  */
8307
- function getComponentViewByInstance(componentInstance) {
8308
- let patchedData = readPatchedData(componentInstance);
8309
- let lView;
8310
- if (isLView(patchedData)) {
8311
- const contextLView = patchedData;
8312
- const nodeIndex = findViaComponent(contextLView, componentInstance);
8313
- lView = getComponentLViewByIndex(nodeIndex, contextLView);
8314
- const context = createLContext(contextLView, nodeIndex, lView[HOST]);
8315
- context.component = componentInstance;
8316
- attachPatchData(componentInstance, context);
8317
- attachPatchData(context.native, context);
8226
+ function handleUnknownPropertyError(propName, tagName, nodeType, lView) {
8227
+ // Special-case a situation when a structural directive is applied to
8228
+ // an `<ng-template>` element, for example: `<ng-template *ngIf="true">`.
8229
+ // In this case the compiler generates the `ɵɵtemplate` instruction with
8230
+ // the `null` as the tagName. The directive matching logic at runtime relies
8231
+ // on this effect (see `isInlineTemplate`), thus using the 'ng-template' as
8232
+ // a default value of the `tNode.value` is not feasible at this moment.
8233
+ if (!tagName && nodeType === 4 /* TNodeType.Container */) {
8234
+ tagName = 'ng-template';
8235
+ }
8236
+ const isHostStandalone = isHostComponentStandalone(lView);
8237
+ const templateLocation = getTemplateLocationDetails(lView);
8238
+ let message = `Can't bind to '${propName}' since it isn't a known property of '${tagName}'${templateLocation}.`;
8239
+ const schemas = `'${isHostStandalone ? '@Component' : '@NgModule'}.schemas'`;
8240
+ const importLocation = isHostStandalone ?
8241
+ 'included in the \'@Component.imports\' of this component' :
8242
+ 'a part of an @NgModule where this component is declared';
8243
+ if (KNOWN_CONTROL_FLOW_DIRECTIVES.has(propName)) {
8244
+ // Most likely this is a control flow directive (such as `*ngIf`) used in
8245
+ // a template, but the directive or the `CommonModule` is not imported.
8246
+ const correspondingImport = KNOWN_CONTROL_FLOW_DIRECTIVES.get(propName);
8247
+ message += `\nIf the '${propName}' is an Angular control flow directive, ` +
8248
+ `please make sure that either the '${correspondingImport}' directive or the 'CommonModule' is ${importLocation}.`;
8318
8249
  }
8319
8250
  else {
8320
- const context = patchedData;
8321
- const contextLView = context.lView;
8322
- ngDevMode && assertLView(contextLView);
8323
- lView = getComponentLViewByIndex(context.nodeIndex, contextLView);
8251
+ // May be an Angular component, which is not imported/declared?
8252
+ message += `\n1. If '${tagName}' is an Angular component and it has the ` +
8253
+ `'${propName}' input, then verify that it is ${importLocation}.`;
8254
+ // May be a Web Component?
8255
+ if (tagName && tagName.indexOf('-') > -1) {
8256
+ message += `\n2. If '${tagName}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' ` +
8257
+ `to the ${schemas} of this component to suppress this message.`;
8258
+ message += `\n3. To allow any property add 'NO_ERRORS_SCHEMA' to ` +
8259
+ `the ${schemas} of this component.`;
8260
+ }
8261
+ else {
8262
+ // If it's expected, the error can be suppressed by the `NO_ERRORS_SCHEMA` schema.
8263
+ message += `\n2. To allow any property add 'NO_ERRORS_SCHEMA' to ` +
8264
+ `the ${schemas} of this component.`;
8265
+ }
8324
8266
  }
8325
- return lView;
8267
+ reportUnknownPropertyError(message);
8326
8268
  }
8327
- /**
8328
- * This property will be monkey-patched on elements, components and directives.
8329
- */
8330
- const MONKEY_PATCH_KEY_NAME = '__ngContext__';
8331
- /**
8332
- * Assigns the given data to the given target (which could be a component,
8333
- * directive or DOM node instance) using monkey-patching.
8334
- */
8335
- function attachPatchData(target, data) {
8336
- ngDevMode && assertDefined(target, 'Target expected');
8337
- // Only attach the ID of the view in order to avoid memory leaks (see #41047). We only do this
8338
- // for `LView`, because we have control over when an `LView` is created and destroyed, whereas
8339
- // we can't know when to remove an `LContext`.
8340
- if (isLView(data)) {
8341
- target[MONKEY_PATCH_KEY_NAME] = data[ID];
8342
- registerLView(data);
8269
+ function reportUnknownPropertyError(message) {
8270
+ if (shouldThrowErrorOnUnknownProperty) {
8271
+ throw new RuntimeError(303 /* RuntimeErrorCode.UNKNOWN_BINDING */, message);
8343
8272
  }
8344
8273
  else {
8345
- target[MONKEY_PATCH_KEY_NAME] = data;
8274
+ console.error(formatRuntimeError(303 /* RuntimeErrorCode.UNKNOWN_BINDING */, message));
8346
8275
  }
8347
8276
  }
8348
8277
  /**
8349
- * Returns the monkey-patch value data present on the target (which could be
8350
- * a component, directive or a DOM node).
8278
+ * WARNING: this is a **dev-mode only** function (thus should always be guarded by the `ngDevMode`)
8279
+ * and must **not** be used in production bundles. The function makes megamorphic reads, which might
8280
+ * be too slow for production mode and also it relies on the constructor function being available.
8281
+ *
8282
+ * Gets a reference to the host component def (where a current component is declared).
8283
+ *
8284
+ * @param lView An `LView` that represents a current component that is being rendered.
8351
8285
  */
8352
- function readPatchedData(target) {
8353
- ngDevMode && assertDefined(target, 'Target expected');
8354
- const data = target[MONKEY_PATCH_KEY_NAME];
8355
- return (typeof data === 'number') ? getLViewById(data) : data || null;
8356
- }
8357
- function readPatchedLView(target) {
8358
- const value = readPatchedData(target);
8359
- if (value) {
8360
- return (isLView(value) ? value : value.lView);
8361
- }
8362
- return null;
8286
+ function getDeclarationComponentDef(lView) {
8287
+ !ngDevMode && throwError('Must never be called in production mode');
8288
+ const declarationLView = lView[DECLARATION_COMPONENT_VIEW];
8289
+ const context = declarationLView[CONTEXT];
8290
+ // Unable to obtain a context.
8291
+ if (!context)
8292
+ return null;
8293
+ return context.constructor ? getComponentDef$1(context.constructor) : null;
8363
8294
  }
8364
- function isComponentInstance(instance) {
8365
- return instance && instance.constructor && instance.constructor.ɵcmp;
8295
+ /**
8296
+ * WARNING: this is a **dev-mode only** function (thus should always be guarded by the `ngDevMode`)
8297
+ * and must **not** be used in production bundles. The function makes megamorphic reads, which might
8298
+ * be too slow for production mode.
8299
+ *
8300
+ * Checks if the current component is declared inside of a standalone component template.
8301
+ *
8302
+ * @param lView An `LView` that represents a current component that is being rendered.
8303
+ */
8304
+ function isHostComponentStandalone(lView) {
8305
+ !ngDevMode && throwError('Must never be called in production mode');
8306
+ const componentDef = getDeclarationComponentDef(lView);
8307
+ // Treat host component as non-standalone if we can't obtain the def.
8308
+ return !!componentDef?.standalone;
8366
8309
  }
8367
- function isDirectiveInstance(instance) {
8368
- return instance && instance.constructor && instance.constructor.ɵdir;
8310
+ /**
8311
+ * WARNING: this is a **dev-mode only** function (thus should always be guarded by the `ngDevMode`)
8312
+ * and must **not** be used in production bundles. The function makes megamorphic reads, which might
8313
+ * be too slow for production mode.
8314
+ *
8315
+ * Constructs a string describing the location of the host component template. The function is used
8316
+ * in dev mode to produce error messages.
8317
+ *
8318
+ * @param lView An `LView` that represents a current component that is being rendered.
8319
+ */
8320
+ function getTemplateLocationDetails(lView) {
8321
+ !ngDevMode && throwError('Must never be called in production mode');
8322
+ const hostComponentDef = getDeclarationComponentDef(lView);
8323
+ const componentClassName = hostComponentDef?.type?.name;
8324
+ return componentClassName ? ` (used in the '${componentClassName}' component template)` : '';
8369
8325
  }
8370
8326
  /**
8371
- * Locates the element within the given LView and returns the matching index
8327
+ * The set of known control flow directives and their corresponding imports.
8328
+ * We use this set to produce a more precises error message with a note
8329
+ * that the `CommonModule` should also be included.
8372
8330
  */
8373
- function findViaNativeElement(lView, target) {
8374
- const tView = lView[TVIEW];
8375
- for (let i = HEADER_OFFSET; i < tView.bindingStartIndex; i++) {
8376
- if (unwrapRNode(lView[i]) === target) {
8377
- return i;
8331
+ const KNOWN_CONTROL_FLOW_DIRECTIVES = new Map([
8332
+ ['ngIf', 'NgIf'], ['ngFor', 'NgFor'], ['ngSwitchCase', 'NgSwitchCase'],
8333
+ ['ngSwitchDefault', 'NgSwitchDefault']
8334
+ ]);
8335
+ /**
8336
+ * Returns true if the tag name is allowed by specified schemas.
8337
+ * @param schemas Array of schemas
8338
+ * @param tagName Name of the tag
8339
+ */
8340
+ function matchingSchemas(schemas, tagName) {
8341
+ if (schemas !== null) {
8342
+ for (let i = 0; i < schemas.length; i++) {
8343
+ const schema = schemas[i];
8344
+ if (schema === NO_ERRORS_SCHEMA ||
8345
+ schema === CUSTOM_ELEMENTS_SCHEMA && tagName && tagName.indexOf('-') > -1) {
8346
+ return true;
8347
+ }
8378
8348
  }
8379
8349
  }
8380
- return -1;
8350
+ return false;
8381
8351
  }
8352
+
8382
8353
  /**
8383
- * Locates the next tNode (child, sibling or parent).
8354
+ * @license
8355
+ * Copyright Google LLC All Rights Reserved.
8356
+ *
8357
+ * Use of this source code is governed by an MIT-style license that can be
8358
+ * found in the LICENSE file at https://angular.io/license
8384
8359
  */
8385
- function traverseNextElement(tNode) {
8386
- if (tNode.child) {
8387
- return tNode.child;
8388
- }
8389
- else if (tNode.next) {
8390
- return tNode.next;
8391
- }
8392
- else {
8393
- // Let's take the following template: <div><span>text</span></div><component/>
8394
- // After checking the text node, we need to find the next parent that has a "next" TNode,
8395
- // in this case the parent `div`, so that we can find the component.
8396
- while (tNode.parent && !tNode.parent.next) {
8397
- tNode = tNode.parent;
8398
- }
8399
- return tNode.parent && tNode.parent.next;
8400
- }
8360
+ const ERROR_ORIGINAL_ERROR = 'ngOriginalError';
8361
+ function wrappedError(message, originalError) {
8362
+ const msg = `${message} caused by: ${originalError instanceof Error ? originalError.message : originalError}`;
8363
+ const error = Error(msg);
8364
+ error[ERROR_ORIGINAL_ERROR] = originalError;
8365
+ return error;
8401
8366
  }
8367
+ function getOriginalError(error) {
8368
+ return error[ERROR_ORIGINAL_ERROR];
8369
+ }
8370
+
8402
8371
  /**
8403
- * Locates the component within the given LView and returns the matching index
8372
+ * @license
8373
+ * Copyright Google LLC All Rights Reserved.
8374
+ *
8375
+ * Use of this source code is governed by an MIT-style license that can be
8376
+ * found in the LICENSE file at https://angular.io/license
8404
8377
  */
8405
- function findViaComponent(lView, componentInstance) {
8406
- const componentIndices = lView[TVIEW].components;
8407
- if (componentIndices) {
8408
- for (let i = 0; i < componentIndices.length; i++) {
8409
- const elementComponentIndex = componentIndices[i];
8410
- const componentView = getComponentLViewByIndex(elementComponentIndex, lView);
8411
- if (componentView[CONTEXT] === componentInstance) {
8412
- return elementComponentIndex;
8413
- }
8378
+ /**
8379
+ * Provides a hook for centralized exception handling.
8380
+ *
8381
+ * The default implementation of `ErrorHandler` prints error messages to the `console`. To
8382
+ * intercept error handling, write a custom exception handler that replaces this default as
8383
+ * appropriate for your app.
8384
+ *
8385
+ * @usageNotes
8386
+ * ### Example
8387
+ *
8388
+ * ```
8389
+ * class MyErrorHandler implements ErrorHandler {
8390
+ * handleError(error) {
8391
+ * // do something with the exception
8392
+ * }
8393
+ * }
8394
+ *
8395
+ * @NgModule({
8396
+ * providers: [{provide: ErrorHandler, useClass: MyErrorHandler}]
8397
+ * })
8398
+ * class MyModule {}
8399
+ * ```
8400
+ *
8401
+ * @publicApi
8402
+ */
8403
+ class ErrorHandler {
8404
+ constructor() {
8405
+ /**
8406
+ * @internal
8407
+ */
8408
+ this._console = console;
8409
+ }
8410
+ handleError(error) {
8411
+ const originalError = this._findOriginalError(error);
8412
+ this._console.error('ERROR', error);
8413
+ if (originalError) {
8414
+ this._console.error('ORIGINAL ERROR', originalError);
8414
8415
  }
8415
8416
  }
8416
- else {
8417
- const rootComponentView = getComponentLViewByIndex(HEADER_OFFSET, lView);
8418
- const rootComponent = rootComponentView[CONTEXT];
8419
- if (rootComponent === componentInstance) {
8420
- // we are dealing with the root element here therefore we know that the
8421
- // element is the very first element after the HEADER data in the lView
8422
- return HEADER_OFFSET;
8417
+ /** @internal */
8418
+ _findOriginalError(error) {
8419
+ let e = error && getOriginalError(error);
8420
+ while (e && getOriginalError(e)) {
8421
+ e = getOriginalError(e);
8423
8422
  }
8423
+ return e || null;
8424
8424
  }
8425
- return -1;
8426
8425
  }
8426
+
8427
8427
  /**
8428
- * Locates the directive within the given LView and returns the matching index
8428
+ * @license
8429
+ * Copyright Google LLC All Rights Reserved.
8430
+ *
8431
+ * Use of this source code is governed by an MIT-style license that can be
8432
+ * found in the LICENSE file at https://angular.io/license
8429
8433
  */
8430
- function findViaDirective(lView, directiveInstance) {
8431
- // if a directive is monkey patched then it will (by default)
8432
- // have a reference to the LView of the current view. The
8433
- // element bound to the directive being search lives somewhere
8434
- // in the view data. We loop through the nodes and check their
8435
- // list of directives for the instance.
8436
- let tNode = lView[TVIEW].firstChild;
8437
- while (tNode) {
8438
- const directiveIndexStart = tNode.directiveStart;
8439
- const directiveIndexEnd = tNode.directiveEnd;
8440
- for (let i = directiveIndexStart; i < directiveIndexEnd; i++) {
8441
- if (lView[i] === directiveInstance) {
8442
- return tNode.index;
8443
- }
8444
- }
8445
- tNode = traverseNextElement(tNode);
8446
- }
8447
- return -1;
8448
- }
8449
8434
  /**
8450
- * Returns a list of directives extracted from the given view based on the
8451
- * provided list of directive index values.
8435
+ * Disallowed strings in the comment.
8452
8436
  *
8453
- * @param nodeIndex The node index
8454
- * @param lView The target view data
8455
- * @param includeComponents Whether or not to include components in returned directives
8437
+ * see: https://html.spec.whatwg.org/multipage/syntax.html#comments
8456
8438
  */
8457
- function getDirectivesAtNodeIndex(nodeIndex, lView, includeComponents) {
8458
- const tNode = lView[TVIEW].data[nodeIndex];
8459
- if (tNode.directiveStart === 0)
8460
- return EMPTY_ARRAY;
8461
- const results = [];
8462
- for (let i = tNode.directiveStart; i < tNode.directiveEnd; i++) {
8463
- const directiveInstance = lView[i];
8464
- if (!isComponentInstance(directiveInstance) || includeComponents) {
8465
- results.push(directiveInstance);
8466
- }
8467
- }
8468
- return results;
8469
- }
8470
- function getComponentAtNodeIndex(nodeIndex, lView) {
8471
- const tNode = lView[TVIEW].data[nodeIndex];
8472
- const { directiveStart, componentOffset } = tNode;
8473
- return componentOffset > -1 ? lView[directiveStart + componentOffset] : null;
8439
+ const COMMENT_DISALLOWED = /^>|^->|<!--|-->|--!>|<!-$/g;
8440
+ /**
8441
+ * Delimiter in the disallowed strings which needs to be wrapped with zero with character.
8442
+ */
8443
+ const COMMENT_DELIMITER = /(<|>)/;
8444
+ const COMMENT_DELIMITER_ESCAPED = '\u200B$1\u200B';
8445
+ /**
8446
+ * Escape the content of comment strings so that it can be safely inserted into a comment node.
8447
+ *
8448
+ * The issue is that HTML does not specify any way to escape comment end text inside the comment.
8449
+ * Consider: `<!-- The way you close a comment is with ">", and "->" at the beginning or by "-->" or
8450
+ * "--!>" at the end. -->`. Above the `"-->"` is meant to be text not an end to the comment. This
8451
+ * can be created programmatically through DOM APIs. (`<!--` are also disallowed.)
8452
+ *
8453
+ * see: https://html.spec.whatwg.org/multipage/syntax.html#comments
8454
+ *
8455
+ * ```
8456
+ * div.innerHTML = div.innerHTML
8457
+ * ```
8458
+ *
8459
+ * One would expect that the above code would be safe to do, but it turns out that because comment
8460
+ * text is not escaped, the comment may contain text which will prematurely close the comment
8461
+ * opening up the application for XSS attack. (In SSR we programmatically create comment nodes which
8462
+ * may contain such text and expect them to be safe.)
8463
+ *
8464
+ * This function escapes the comment text by looking for comment delimiters (`<` and `>`) and
8465
+ * surrounding them with `_>_` where the `_` is a zero width space `\u200B`. The result is that if a
8466
+ * comment contains any of the comment start/end delimiters (such as `<!--`, `-->` or `--!>`) the
8467
+ * text it will render normally but it will not cause the HTML parser to close/open the comment.
8468
+ *
8469
+ * @param value text to make safe for comment node by escaping the comment open/close character
8470
+ * sequence.
8471
+ */
8472
+ function escapeCommentText(value) {
8473
+ return value.replace(COMMENT_DISALLOWED, (text) => text.replace(COMMENT_DELIMITER, COMMENT_DELIMITER_ESCAPED));
8474
8474
  }
8475
+
8475
8476
  /**
8476
- * Returns a map of local references (local reference name => element or directive instance) that
8477
- * exist on a given element.
8477
+ * @license
8478
+ * Copyright Google LLC All Rights Reserved.
8479
+ *
8480
+ * Use of this source code is governed by an MIT-style license that can be
8481
+ * found in the LICENSE file at https://angular.io/license
8478
8482
  */
8479
- function discoverLocalRefs(lView, nodeIndex) {
8480
- const tNode = lView[TVIEW].data[nodeIndex];
8481
- if (tNode && tNode.localNames) {
8482
- const result = {};
8483
- let localIndex = tNode.index + 1;
8484
- for (let i = 0; i < tNode.localNames.length; i += 2) {
8485
- result[tNode.localNames[i]] = lView[localIndex];
8486
- localIndex++;
8487
- }
8488
- return result;
8483
+ function normalizeDebugBindingName(name) {
8484
+ // Attribute names with `$` (eg `x-y$`) are valid per spec, but unsupported by some browsers
8485
+ name = camelCaseToDashCase(name.replace(/[$@]/g, '_'));
8486
+ return `ng-reflect-${name}`;
8487
+ }
8488
+ const CAMEL_CASE_REGEXP = /([A-Z])/g;
8489
+ function camelCaseToDashCase(input) {
8490
+ return input.replace(CAMEL_CASE_REGEXP, (...m) => '-' + m[1].toLowerCase());
8491
+ }
8492
+ function normalizeDebugBindingValue(value) {
8493
+ try {
8494
+ // Limit the size of the value as otherwise the DOM just gets polluted.
8495
+ return value != null ? value.toString().slice(0, 30) : value;
8496
+ }
8497
+ catch (e) {
8498
+ return '[ERROR] Exception while trying to serialize the value';
8489
8499
  }
8490
- return null;
8491
8500
  }
8492
8501
 
8493
8502
  /**
@@ -9651,6 +9660,19 @@ function writeDirectClass(renderer, element, newValue) {
9651
9660
  }
9652
9661
  ngDevMode && ngDevMode.rendererSetClassName++;
9653
9662
  }
9663
+ /** Sets up the static DOM attributes on an `RNode`. */
9664
+ function setupStaticAttributes(renderer, element, tNode) {
9665
+ const { mergedAttrs, classes, styles } = tNode;
9666
+ if (mergedAttrs !== null) {
9667
+ setUpAttributes(renderer, element, mergedAttrs);
9668
+ }
9669
+ if (classes !== null) {
9670
+ writeDirectClass(renderer, element, classes);
9671
+ }
9672
+ if (styles !== null) {
9673
+ writeDirectStyle(renderer, element, styles);
9674
+ }
9675
+ }
9654
9676
 
9655
9677
  /**
9656
9678
  * @license
@@ -12592,9 +12614,6 @@ function createViewBlueprint(bindingStartIndex, initialViewLength) {
12592
12614
  }
12593
12615
  return blueprint;
12594
12616
  }
12595
- function createError(text, token) {
12596
- return new Error(`Renderer: ${text} [${stringifyForError(token)}]`);
12597
- }
12598
12617
  /**
12599
12618
  * Locates the host native element, used for bootstrapping existing nodes into rendering pipeline.
12600
12619
  *
@@ -12718,26 +12737,49 @@ function createTNode(tView, tParent, type, index, value, attrs) {
12718
12737
  }
12719
12738
  return tNode;
12720
12739
  }
12721
- function generatePropertyAliases(inputAliasMap, directiveDefIdx, propStore) {
12722
- for (let publicName in inputAliasMap) {
12723
- if (inputAliasMap.hasOwnProperty(publicName)) {
12724
- propStore = propStore === null ? {} : propStore;
12725
- const internalName = inputAliasMap[publicName];
12726
- if (propStore.hasOwnProperty(publicName)) {
12727
- propStore[publicName].push(directiveDefIdx, internalName);
12740
+ /**
12741
+ * Generates the `PropertyAliases` data structure from the provided input/output mapping.
12742
+ * @param aliasMap Input/output mapping from the directive definition.
12743
+ * @param directiveIndex Index of the directive.
12744
+ * @param propertyAliases Object in which to store the results.
12745
+ * @param hostDirectiveAliasMap Object used to alias or filter out properties for host directives.
12746
+ * If the mapping is provided, it'll act as an allowlist, as well as a mapping of what public
12747
+ * name inputs/outputs should be exposed under.
12748
+ */
12749
+ function generatePropertyAliases(aliasMap, directiveIndex, propertyAliases, hostDirectiveAliasMap) {
12750
+ for (let publicName in aliasMap) {
12751
+ if (aliasMap.hasOwnProperty(publicName)) {
12752
+ propertyAliases = propertyAliases === null ? {} : propertyAliases;
12753
+ const internalName = aliasMap[publicName];
12754
+ // If there are no host directive mappings, we want to remap using the alias map from the
12755
+ // definition itself. If there is an alias map, it has two functions:
12756
+ // 1. It serves as an allowlist of bindings that are exposed by the host directives. Only the
12757
+ // ones inside the host directive map will be exposed on the host.
12758
+ // 2. The public name of the property is aliased using the host directive alias map, rather
12759
+ // than the alias map from the definition.
12760
+ if (hostDirectiveAliasMap === null) {
12761
+ addPropertyAlias(propertyAliases, directiveIndex, publicName, internalName);
12728
12762
  }
12729
- else {
12730
- (propStore[publicName] = [directiveDefIdx, internalName]);
12763
+ else if (hostDirectiveAliasMap.hasOwnProperty(publicName)) {
12764
+ addPropertyAlias(propertyAliases, directiveIndex, hostDirectiveAliasMap[publicName], internalName);
12731
12765
  }
12732
12766
  }
12733
12767
  }
12734
- return propStore;
12768
+ return propertyAliases;
12769
+ }
12770
+ function addPropertyAlias(propertyAliases, directiveIndex, publicName, internalName) {
12771
+ if (propertyAliases.hasOwnProperty(publicName)) {
12772
+ propertyAliases[publicName].push(directiveIndex, internalName);
12773
+ }
12774
+ else {
12775
+ propertyAliases[publicName] = [directiveIndex, internalName];
12776
+ }
12735
12777
  }
12736
12778
  /**
12737
12779
  * Initializes data structures required to work with directive inputs and outputs.
12738
12780
  * Initialization is done for all directives matched on a given TNode.
12739
12781
  */
12740
- function initializeInputAndOutputAliases(tView, tNode) {
12782
+ function initializeInputAndOutputAliases(tView, tNode, hostDirectiveDefinitionMap) {
12741
12783
  ngDevMode && assertFirstCreatePass(tView);
12742
12784
  const start = tNode.directiveStart;
12743
12785
  const end = tNode.directiveEnd;
@@ -12746,16 +12788,21 @@ function initializeInputAndOutputAliases(tView, tNode) {
12746
12788
  const inputsFromAttrs = ngDevMode ? new TNodeInitialInputs() : [];
12747
12789
  let inputsStore = null;
12748
12790
  let outputsStore = null;
12749
- for (let i = start; i < end; i++) {
12750
- const directiveDef = tViewData[i];
12751
- inputsStore = generatePropertyAliases(directiveDef.inputs, i, inputsStore);
12752
- outputsStore = generatePropertyAliases(directiveDef.outputs, i, outputsStore);
12791
+ for (let directiveIndex = start; directiveIndex < end; directiveIndex++) {
12792
+ const directiveDef = tViewData[directiveIndex];
12793
+ const aliasData = hostDirectiveDefinitionMap ? hostDirectiveDefinitionMap.get(directiveDef) : null;
12794
+ const aliasedInputs = aliasData ? aliasData.inputs : null;
12795
+ const aliasedOutputs = aliasData ? aliasData.outputs : null;
12796
+ inputsStore =
12797
+ generatePropertyAliases(directiveDef.inputs, directiveIndex, inputsStore, aliasedInputs);
12798
+ outputsStore =
12799
+ generatePropertyAliases(directiveDef.outputs, directiveIndex, outputsStore, aliasedOutputs);
12753
12800
  // Do not use unbound attributes as inputs to structural directives, since structural
12754
12801
  // directive inputs can only be set using microsyntax (e.g. `<div *dir="exp">`).
12755
12802
  // TODO(FW-1930): microsyntax expressions may also contain unbound/static attributes, which
12756
12803
  // should be set for inline templates.
12757
12804
  const initialInputs = (inputsStore !== null && tNodeAttrs !== null && !isInlineTemplate(tNode)) ?
12758
- generateInitialInputs(inputsStore, i, tNodeAttrs) :
12805
+ generateInitialInputs(inputsStore, directiveIndex, tNodeAttrs) :
12759
12806
  null;
12760
12807
  inputsFromAttrs.push(initialInputs);
12761
12808
  }
@@ -12871,28 +12918,6 @@ function setNgReflectProperties(lView, element, type, dataValue, value) {
12871
12918
  }
12872
12919
  }
12873
12920
  }
12874
- /**
12875
- * Instantiate a root component.
12876
- */
12877
- function instantiateRootComponent(tView, lView, def) {
12878
- const rootTNode = getCurrentTNode();
12879
- if (tView.firstCreatePass) {
12880
- if (def.providersResolver)
12881
- def.providersResolver(def);
12882
- const directiveIndex = allocExpando(tView, lView, 1, null);
12883
- ngDevMode &&
12884
- assertEqual(directiveIndex, rootTNode.directiveStart, 'Because this is a root component the allocated expando should match the TNode component.');
12885
- configureViewWithDirective(tView, rootTNode, lView, directiveIndex, def);
12886
- initializeInputAndOutputAliases(tView, rootTNode);
12887
- }
12888
- const directive = getNodeInjectable(lView, tView, rootTNode.directiveStart + rootTNode.componentOffset, rootTNode);
12889
- attachPatchData(directive, lView);
12890
- const native = getNativeByTNode(rootTNode, lView);
12891
- if (native) {
12892
- attachPatchData(native, lView);
12893
- }
12894
- return directive;
12895
- }
12896
12921
  /**
12897
12922
  * Resolve the matched directives on a node.
12898
12923
  */
@@ -12902,59 +12927,19 @@ function resolveDirectives(tView, lView, tNode, localRefs) {
12902
12927
  ngDevMode && assertFirstCreatePass(tView);
12903
12928
  let hasDirectives = false;
12904
12929
  if (getBindingsEnabled()) {
12905
- const directiveDefsMatchedBySelectors = findDirectiveDefMatches(tView, lView, tNode);
12906
- const directiveDefs = directiveDefsMatchedBySelectors ?
12907
- findHostDirectiveDefs$1(directiveDefsMatchedBySelectors, tView, lView, tNode) :
12908
- null;
12909
12930
  const exportsMap = localRefs === null ? null : { '': -1 };
12931
+ const matchResult = findDirectiveDefMatches(tView, tNode);
12932
+ let directiveDefs;
12933
+ let hostDirectiveDefs;
12934
+ if (matchResult === null) {
12935
+ directiveDefs = hostDirectiveDefs = null;
12936
+ }
12937
+ else {
12938
+ [directiveDefs, hostDirectiveDefs] = matchResult;
12939
+ }
12910
12940
  if (directiveDefs !== null) {
12911
12941
  hasDirectives = true;
12912
- initTNodeFlags(tNode, tView.data.length, directiveDefs.length);
12913
- // When the same token is provided by several directives on the same node, some rules apply in
12914
- // the viewEngine:
12915
- // - viewProviders have priority over providers
12916
- // - the last directive in NgModule.declarations has priority over the previous one
12917
- // So to match these rules, the order in which providers are added in the arrays is very
12918
- // important.
12919
- for (let i = 0; i < directiveDefs.length; i++) {
12920
- const def = directiveDefs[i];
12921
- if (def.providersResolver)
12922
- def.providersResolver(def);
12923
- }
12924
- let preOrderHooksFound = false;
12925
- let preOrderCheckHooksFound = false;
12926
- let directiveIdx = allocExpando(tView, lView, directiveDefs.length, null);
12927
- ngDevMode &&
12928
- assertSame(directiveIdx, tNode.directiveStart, 'TNode.directiveStart should point to just allocated space');
12929
- for (let i = 0; i < directiveDefs.length; i++) {
12930
- const def = directiveDefs[i];
12931
- // Merge the attrs in the order of matches. This assumes that the first directive is the
12932
- // component itself, so that the component has the least priority.
12933
- tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, def.hostAttrs);
12934
- configureViewWithDirective(tView, tNode, lView, directiveIdx, def);
12935
- saveNameToExportMap(directiveIdx, def, exportsMap);
12936
- if (def.contentQueries !== null)
12937
- tNode.flags |= 4 /* TNodeFlags.hasContentQuery */;
12938
- if (def.hostBindings !== null || def.hostAttrs !== null || def.hostVars !== 0)
12939
- tNode.flags |= 64 /* TNodeFlags.hasHostBindings */;
12940
- const lifeCycleHooks = def.type.prototype;
12941
- // Only push a node index into the preOrderHooks array if this is the first
12942
- // pre-order hook found on this node.
12943
- if (!preOrderHooksFound &&
12944
- (lifeCycleHooks.ngOnChanges || lifeCycleHooks.ngOnInit || lifeCycleHooks.ngDoCheck)) {
12945
- // We will push the actual hook function into this array later during dir instantiation.
12946
- // We cannot do it now because we must ensure hooks are registered in the same
12947
- // order that directives are created (i.e. injection order).
12948
- (tView.preOrderHooks || (tView.preOrderHooks = [])).push(tNode.index);
12949
- preOrderHooksFound = true;
12950
- }
12951
- if (!preOrderCheckHooksFound && (lifeCycleHooks.ngOnChanges || lifeCycleHooks.ngDoCheck)) {
12952
- (tView.preOrderCheckHooks || (tView.preOrderCheckHooks = [])).push(tNode.index);
12953
- preOrderCheckHooksFound = true;
12954
- }
12955
- directiveIdx++;
12956
- }
12957
- initializeInputAndOutputAliases(tView, tNode);
12942
+ initializeDirectives(tView, lView, tNode, directiveDefs, exportsMap, hostDirectiveDefs);
12958
12943
  }
12959
12944
  if (exportsMap)
12960
12945
  cacheMatchingLocalNames(tNode, localRefs, exportsMap);
@@ -12963,17 +12948,71 @@ function resolveDirectives(tView, lView, tNode, localRefs) {
12963
12948
  tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, tNode.attrs);
12964
12949
  return hasDirectives;
12965
12950
  }
12951
+ /** Initializes the data structures necessary for a list of directives to be instantiated. */
12952
+ function initializeDirectives(tView, lView, tNode, directives, exportsMap, hostDirectiveDefs) {
12953
+ ngDevMode && assertFirstCreatePass(tView);
12954
+ // Publishes the directive types to DI so they can be injected. Needs to
12955
+ // happen in a separate pass before the TNode flags have been initialized.
12956
+ for (let i = 0; i < directives.length; i++) {
12957
+ diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, lView), tView, directives[i].type);
12958
+ }
12959
+ initTNodeFlags(tNode, tView.data.length, directives.length);
12960
+ // When the same token is provided by several directives on the same node, some rules apply in
12961
+ // the viewEngine:
12962
+ // - viewProviders have priority over providers
12963
+ // - the last directive in NgModule.declarations has priority over the previous one
12964
+ // So to match these rules, the order in which providers are added in the arrays is very
12965
+ // important.
12966
+ for (let i = 0; i < directives.length; i++) {
12967
+ const def = directives[i];
12968
+ if (def.providersResolver)
12969
+ def.providersResolver(def);
12970
+ }
12971
+ let preOrderHooksFound = false;
12972
+ let preOrderCheckHooksFound = false;
12973
+ let directiveIdx = allocExpando(tView, lView, directives.length, null);
12974
+ ngDevMode &&
12975
+ assertSame(directiveIdx, tNode.directiveStart, 'TNode.directiveStart should point to just allocated space');
12976
+ for (let i = 0; i < directives.length; i++) {
12977
+ const def = directives[i];
12978
+ // Merge the attrs in the order of matches. This assumes that the first directive is the
12979
+ // component itself, so that the component has the least priority.
12980
+ tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, def.hostAttrs);
12981
+ configureViewWithDirective(tView, tNode, lView, directiveIdx, def);
12982
+ saveNameToExportMap(directiveIdx, def, exportsMap);
12983
+ if (def.contentQueries !== null)
12984
+ tNode.flags |= 4 /* TNodeFlags.hasContentQuery */;
12985
+ if (def.hostBindings !== null || def.hostAttrs !== null || def.hostVars !== 0)
12986
+ tNode.flags |= 64 /* TNodeFlags.hasHostBindings */;
12987
+ const lifeCycleHooks = def.type.prototype;
12988
+ // Only push a node index into the preOrderHooks array if this is the first
12989
+ // pre-order hook found on this node.
12990
+ if (!preOrderHooksFound &&
12991
+ (lifeCycleHooks.ngOnChanges || lifeCycleHooks.ngOnInit || lifeCycleHooks.ngDoCheck)) {
12992
+ // We will push the actual hook function into this array later during dir instantiation.
12993
+ // We cannot do it now because we must ensure hooks are registered in the same
12994
+ // order that directives are created (i.e. injection order).
12995
+ (tView.preOrderHooks || (tView.preOrderHooks = [])).push(tNode.index);
12996
+ preOrderHooksFound = true;
12997
+ }
12998
+ if (!preOrderCheckHooksFound && (lifeCycleHooks.ngOnChanges || lifeCycleHooks.ngDoCheck)) {
12999
+ (tView.preOrderCheckHooks || (tView.preOrderCheckHooks = [])).push(tNode.index);
13000
+ preOrderCheckHooksFound = true;
13001
+ }
13002
+ directiveIdx++;
13003
+ }
13004
+ initializeInputAndOutputAliases(tView, tNode, hostDirectiveDefs);
13005
+ }
12966
13006
  /**
12967
13007
  * Add `hostBindings` to the `TView.hostBindingOpCodes`.
12968
13008
  *
12969
13009
  * @param tView `TView` to which the `hostBindings` should be added.
12970
13010
  * @param tNode `TNode` the element which contains the directive
12971
- * @param lView `LView` current `LView`
12972
13011
  * @param directiveIdx Directive index in view.
12973
13012
  * @param directiveVarsIdx Where will the directive's vars be stored
12974
13013
  * @param def `ComponentDef`/`DirectiveDef`, which contains the `hostVars`/`hostBindings` to add.
12975
13014
  */
12976
- function registerHostBindingOpCodes(tView, tNode, lView, directiveIdx, directiveVarsIdx, def) {
13015
+ function registerHostBindingOpCodes(tView, tNode, directiveIdx, directiveVarsIdx, def) {
12977
13016
  ngDevMode && assertFirstCreatePass(tView);
12978
13017
  const hostBindings = def.hostBindings;
12979
13018
  if (hostBindings) {
@@ -13074,38 +13113,64 @@ function invokeHostBindingsInCreationMode(def, directive) {
13074
13113
  * Matches the current node against all available selectors.
13075
13114
  * If a component is matched (at most one), it is returned in first position in the array.
13076
13115
  */
13077
- function findDirectiveDefMatches(tView, viewData, tNode) {
13116
+ function findDirectiveDefMatches(tView, tNode) {
13078
13117
  ngDevMode && assertFirstCreatePass(tView);
13079
13118
  ngDevMode && assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */);
13080
13119
  const registry = tView.directiveRegistry;
13081
13120
  let matches = null;
13121
+ let hostDirectiveDefs = null;
13082
13122
  if (registry) {
13083
13123
  for (let i = 0; i < registry.length; i++) {
13084
13124
  const def = registry[i];
13085
13125
  if (isNodeMatchingSelectorList(tNode, def.selectors, /* isProjectionMode */ false)) {
13086
13126
  matches || (matches = ngDevMode ? new MatchesArray() : []);
13087
- diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, viewData), tView, def.type);
13088
13127
  if (isComponentDef(def)) {
13089
13128
  if (ngDevMode) {
13090
13129
  assertTNodeType(tNode, 2 /* TNodeType.Element */, `"${tNode.value}" tags cannot be used as component hosts. ` +
13091
13130
  `Please use a different tag to activate the ${stringify(def.type)} component.`);
13092
13131
  if (isComponentHost(tNode)) {
13093
- // If another component has been matched previously, it's the first element in the
13094
- // `matches` array, see how we store components/directives in `matches` below.
13095
- throwMultipleComponentError(tNode, matches[0].type, def.type);
13132
+ throwMultipleComponentError(tNode, matches.find(isComponentDef).type, def.type);
13096
13133
  }
13097
13134
  }
13098
- markAsComponentHost(tView, tNode, 0);
13099
- // The component is always stored first with directives after.
13100
- matches.unshift(def);
13135
+ // Components are inserted at the front of the matches array so that their lifecycle
13136
+ // hooks run before any directive lifecycle hooks. This appears to be for ViewEngine
13137
+ // compatibility. This logic doesn't make sense with host directives, because it
13138
+ // would allow the host directives to undo any overrides the host may have made.
13139
+ // To handle this case, the host directives of components are inserted at the beginning
13140
+ // of the array, followed by the component. As such, the insertion order is as follows:
13141
+ // 1. Host directives belonging to the selector-matched component.
13142
+ // 2. Selector-matched component.
13143
+ // 3. Host directives belonging to selector-matched directives.
13144
+ // 4. Selector-matched directives.
13145
+ if (def.findHostDirectiveDefs !== null) {
13146
+ const hostDirectiveMatches = [];
13147
+ hostDirectiveDefs = hostDirectiveDefs || new Map();
13148
+ def.findHostDirectiveDefs(def, hostDirectiveMatches, hostDirectiveDefs);
13149
+ // Add all host directives declared on this component, followed by the component itself.
13150
+ // Host directives should execute first so the host has a chance to override changes
13151
+ // to the DOM made by them.
13152
+ matches.unshift(...hostDirectiveMatches, def);
13153
+ // Component is offset starting from the beginning of the host directives array.
13154
+ const componentOffset = hostDirectiveMatches.length;
13155
+ markAsComponentHost(tView, tNode, componentOffset);
13156
+ }
13157
+ else {
13158
+ // No host directives on this component, just add the
13159
+ // component def to the beginning of the matches.
13160
+ matches.unshift(def);
13161
+ markAsComponentHost(tView, tNode, 0);
13162
+ }
13101
13163
  }
13102
13164
  else {
13165
+ // Append any host directives to the matches first.
13166
+ hostDirectiveDefs = hostDirectiveDefs || new Map();
13167
+ def.findHostDirectiveDefs?.(def, matches, hostDirectiveDefs);
13103
13168
  matches.push(def);
13104
13169
  }
13105
13170
  }
13106
13171
  }
13107
13172
  }
13108
- return matches;
13173
+ return matches === null ? null : [matches, hostDirectiveDefs];
13109
13174
  }
13110
13175
  /**
13111
13176
  * Marks a given TNode as a component's host. This consists of:
@@ -13119,26 +13184,6 @@ function markAsComponentHost(tView, hostTNode, componentOffset) {
13119
13184
  (tView.components || (tView.components = ngDevMode ? new TViewComponents() : []))
13120
13185
  .push(hostTNode.index);
13121
13186
  }
13122
- /**
13123
- * Given an array of directives that were matched by their selectors, this function
13124
- * produces a new array that also includes any host directives that have to be applied.
13125
- * @param selectorMatches Directives matched in a template based on their selectors.
13126
- * @param tView Current TView.
13127
- * @param lView Current LView.
13128
- * @param tNode Current TNode that is being matched.
13129
- */
13130
- function findHostDirectiveDefs$1(selectorMatches, tView, lView, tNode) {
13131
- const matches = [];
13132
- for (const def of selectorMatches) {
13133
- if (def.findHostDirectiveDefs === null) {
13134
- matches.push(def);
13135
- }
13136
- else {
13137
- def.findHostDirectiveDefs(matches, def, tView, lView, tNode);
13138
- }
13139
- }
13140
- return matches;
13141
- }
13142
13187
  /** Caches local names and their matching directive indices for query and template lookups. */
13143
13188
  function cacheMatchingLocalNames(tNode, localRefs, exportsMap) {
13144
13189
  if (localRefs) {
@@ -13206,7 +13251,7 @@ function configureViewWithDirective(tView, tNode, lView, directiveIndex, def) {
13206
13251
  const nodeInjectorFactory = new NodeInjectorFactory(directiveFactory, isComponentDef(def), ɵɵdirectiveInject);
13207
13252
  tView.blueprint[directiveIndex] = nodeInjectorFactory;
13208
13253
  lView[directiveIndex] = nodeInjectorFactory;
13209
- registerHostBindingOpCodes(tView, tNode, lView, directiveIndex, allocExpando(tView, lView, def.hostVars, NO_CHANGE), def);
13254
+ registerHostBindingOpCodes(tView, tNode, directiveIndex, allocExpando(tView, lView, def.hostVars, NO_CHANGE), def);
13210
13255
  }
13211
13256
  function addComponentLogic(lView, hostTNode, def) {
13212
13257
  const native = getNativeByTNode(hostTNode, lView);
@@ -14129,6 +14174,7 @@ class ChainedInjector {
14129
14174
  this.parentInjector = parentInjector;
14130
14175
  }
14131
14176
  get(token, notFoundValue, flags) {
14177
+ flags = convertToBitFlags(flags);
14132
14178
  const value = this.injector.get(token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR, flags);
14133
14179
  if (value !== NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR ||
14134
14180
  notFoundValue === NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) {
@@ -14205,42 +14251,34 @@ class ComponentFactory extends ComponentFactory$1 {
14205
14251
  let component;
14206
14252
  let tElementNode;
14207
14253
  try {
14208
- const componentView = createRootComponentView(hostRNode, this.componentDef, rootLView, rendererFactory, hostRenderer);
14209
- if (hostRNode) {
14210
- if (rootSelectorOrNode) {
14211
- setUpAttributes(hostRenderer, hostRNode, ['ng-version', VERSION.full]);
14212
- }
14213
- else {
14214
- // If host element is created as a part of this function call (i.e. `rootSelectorOrNode`
14215
- // is not defined), also apply attributes and classes extracted from component selector.
14216
- // Extract attributes and classes from the first selector only to match VE behavior.
14217
- const { attrs, classes } = extractAttrsAndClassesFromSelector(this.componentDef.selectors[0]);
14218
- if (attrs) {
14219
- setUpAttributes(hostRenderer, hostRNode, attrs);
14220
- }
14221
- if (classes && classes.length > 0) {
14222
- writeDirectClass(hostRenderer, hostRNode, classes.join(' '));
14223
- }
14224
- }
14254
+ const rootComponentDef = this.componentDef;
14255
+ let rootDirectives;
14256
+ let hostDirectiveDefs = null;
14257
+ if (rootComponentDef.findHostDirectiveDefs) {
14258
+ rootDirectives = [];
14259
+ hostDirectiveDefs = new Map();
14260
+ rootComponentDef.findHostDirectiveDefs(rootComponentDef, rootDirectives, hostDirectiveDefs);
14261
+ rootDirectives.push(rootComponentDef);
14262
+ }
14263
+ else {
14264
+ rootDirectives = [rootComponentDef];
14225
14265
  }
14266
+ const hostTNode = createRootComponentTNode(rootLView, hostRNode);
14267
+ const componentView = createRootComponentView(hostTNode, hostRNode, rootComponentDef, rootDirectives, rootLView, rendererFactory, hostRenderer);
14226
14268
  tElementNode = getTNode(rootTView, HEADER_OFFSET);
14269
+ // TODO(crisbeto): in practice `hostRNode` should always be defined, but there are some tests
14270
+ // where the renderer is mocked out and `undefined` is returned. We should update the tests so
14271
+ // that this check can be removed.
14272
+ if (hostRNode) {
14273
+ setRootNodeAttributes(hostRenderer, rootComponentDef, hostRNode, rootSelectorOrNode);
14274
+ }
14227
14275
  if (projectableNodes !== undefined) {
14228
- const projection = tElementNode.projection = [];
14229
- for (let i = 0; i < this.ngContentSelectors.length; i++) {
14230
- const nodesforSlot = projectableNodes[i];
14231
- // Projectable nodes can be passed as array of arrays or an array of iterables (ngUpgrade
14232
- // case). Here we do normalize passed data structure to be an array of arrays to avoid
14233
- // complex checks down the line.
14234
- // We also normalize the length of the passed in projectable nodes (to match the number of
14235
- // <ng-container> slots defined by a component).
14236
- projection.push(nodesforSlot != null ? Array.from(nodesforSlot) : null);
14237
- }
14276
+ projectNodes(tElementNode, this.ngContentSelectors, projectableNodes);
14238
14277
  }
14239
14278
  // TODO: should LifecycleHooksFeature and other host features be generated by the compiler and
14240
14279
  // executed here?
14241
14280
  // Angular 5 reference: https://stackblitz.com/edit/lifecycle-hooks-vcref
14242
- component =
14243
- createRootComponent(componentView, this.componentDef, rootLView, [LifecycleHooksFeature]);
14281
+ component = createRootComponent(componentView, rootComponentDef, rootDirectives, hostDirectiveDefs, rootLView, [LifecycleHooksFeature]);
14244
14282
  renderView(rootTView, rootLView, null);
14245
14283
  }
14246
14284
  finally {
@@ -14311,11 +14349,22 @@ const NULL_INJECTOR = {
14311
14349
  throwProviderNotFoundError(token, 'NullInjector');
14312
14350
  }
14313
14351
  };
14352
+ /** Creates a TNode that can be used to instantiate a root component. */
14353
+ function createRootComponentTNode(lView, rNode) {
14354
+ const tView = lView[TVIEW];
14355
+ const index = HEADER_OFFSET;
14356
+ ngDevMode && assertIndexInRange(lView, index);
14357
+ lView[index] = rNode;
14358
+ // '#host' is added here as we don't know the real host DOM name (we don't want to read it) and at
14359
+ // the same time we want to communicate the debug `TNode` that this is a special `TNode`
14360
+ // representing a host element.
14361
+ return getOrCreateTNode(tView, index, 2 /* TNodeType.Element */, '#host', null);
14362
+ }
14314
14363
  /**
14315
14364
  * Creates the root component view and the root component node.
14316
14365
  *
14317
14366
  * @param rNode Render host element.
14318
- * @param def ComponentDef
14367
+ * @param rootComponentDef ComponentDef
14319
14368
  * @param rootView The parent view where the host node is stored
14320
14369
  * @param rendererFactory Factory to be used for creating child renderers.
14321
14370
  * @param hostRenderer The current renderer
@@ -14323,72 +14372,95 @@ const NULL_INJECTOR = {
14323
14372
  *
14324
14373
  * @returns Component view created
14325
14374
  */
14326
- function createRootComponentView(rNode, def, rootView, rendererFactory, hostRenderer, sanitizer) {
14375
+ function createRootComponentView(tNode, rNode, rootComponentDef, rootDirectives, rootView, rendererFactory, hostRenderer, sanitizer) {
14327
14376
  const tView = rootView[TVIEW];
14328
- const index = HEADER_OFFSET;
14329
- ngDevMode && assertIndexInRange(rootView, index);
14330
- rootView[index] = rNode;
14331
- // '#host' is added here as we don't know the real host DOM name (we don't want to read it) and at
14332
- // the same time we want to communicate the debug `TNode` that this is a special `TNode`
14333
- // representing a host element.
14334
- const tNode = getOrCreateTNode(tView, index, 2 /* TNodeType.Element */, '#host', null);
14335
- const mergedAttrs = tNode.mergedAttrs = def.hostAttrs;
14336
- if (mergedAttrs !== null) {
14337
- computeStaticStyling(tNode, mergedAttrs, true);
14338
- if (rNode !== null) {
14339
- setUpAttributes(hostRenderer, rNode, mergedAttrs);
14340
- if (tNode.classes !== null) {
14341
- writeDirectClass(hostRenderer, rNode, tNode.classes);
14342
- }
14343
- if (tNode.styles !== null) {
14344
- writeDirectStyle(hostRenderer, rNode, tNode.styles);
14345
- }
14346
- }
14347
- }
14348
- const viewRenderer = rendererFactory.createRenderer(rNode, def);
14349
- const componentView = createLView(rootView, getOrCreateComponentTView(def), null, def.onPush ? 32 /* LViewFlags.Dirty */ : 16 /* LViewFlags.CheckAlways */, rootView[index], tNode, rendererFactory, viewRenderer, sanitizer || null, null, null);
14377
+ applyRootComponentStyling(rootDirectives, tNode, rNode, hostRenderer);
14378
+ const viewRenderer = rendererFactory.createRenderer(rNode, rootComponentDef);
14379
+ 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);
14350
14380
  if (tView.firstCreatePass) {
14351
- diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, rootView), tView, def.type);
14352
- markAsComponentHost(tView, tNode, 0);
14353
- initTNodeFlags(tNode, rootView.length, 1);
14381
+ markAsComponentHost(tView, tNode, rootDirectives.length - 1);
14354
14382
  }
14355
14383
  addToViewTree(rootView, componentView);
14356
14384
  // Store component view at node index, with node as the HOST
14357
- return rootView[index] = componentView;
14385
+ return rootView[tNode.index] = componentView;
14386
+ }
14387
+ /** Sets up the styling information on a root component. */
14388
+ function applyRootComponentStyling(rootDirectives, tNode, rNode, hostRenderer) {
14389
+ for (const def of rootDirectives) {
14390
+ tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, def.hostAttrs);
14391
+ }
14392
+ if (tNode.mergedAttrs !== null) {
14393
+ computeStaticStyling(tNode, tNode.mergedAttrs, true);
14394
+ if (rNode !== null) {
14395
+ setupStaticAttributes(hostRenderer, rNode, tNode);
14396
+ }
14397
+ }
14358
14398
  }
14359
14399
  /**
14360
14400
  * Creates a root component and sets it up with features and host bindings.Shared by
14361
14401
  * renderComponent() and ViewContainerRef.createComponent().
14362
14402
  */
14363
- function createRootComponent(componentView, componentDef, rootLView, hostFeatures) {
14403
+ function createRootComponent(componentView, rootComponentDef, rootDirectives, hostDirectiveDefs, rootLView, hostFeatures) {
14404
+ const rootTNode = getCurrentTNode();
14405
+ ngDevMode && assertDefined(rootTNode, 'tNode should have been already created');
14364
14406
  const tView = rootLView[TVIEW];
14365
- // Create directive instance with factory() and store at next index in viewData
14366
- const component = instantiateRootComponent(tView, rootLView, componentDef);
14367
- // Root view only contains an instance of this component,
14368
- // so we use a reference to that component instance as a context.
14407
+ const native = getNativeByTNode(rootTNode, rootLView);
14408
+ initializeDirectives(tView, rootLView, rootTNode, rootDirectives, null, hostDirectiveDefs);
14409
+ for (let i = 0; i < rootDirectives.length; i++) {
14410
+ const directiveIndex = rootTNode.directiveStart + i;
14411
+ const directiveInstance = getNodeInjectable(rootLView, tView, directiveIndex, rootTNode);
14412
+ attachPatchData(directiveInstance, rootLView);
14413
+ }
14414
+ invokeDirectivesHostBindings(tView, rootLView, rootTNode);
14415
+ if (native) {
14416
+ attachPatchData(native, rootLView);
14417
+ }
14418
+ // We're guaranteed for the `componentOffset` to be positive here
14419
+ // since a root component always matches a component def.
14420
+ ngDevMode &&
14421
+ assertGreaterThan(rootTNode.componentOffset, -1, 'componentOffset must be great than -1');
14422
+ const component = getNodeInjectable(rootLView, tView, rootTNode.directiveStart + rootTNode.componentOffset, rootTNode);
14369
14423
  componentView[CONTEXT] = rootLView[CONTEXT] = component;
14370
14424
  if (hostFeatures !== null) {
14371
14425
  for (const feature of hostFeatures) {
14372
- feature(component, componentDef);
14426
+ feature(component, rootComponentDef);
14373
14427
  }
14374
14428
  }
14375
14429
  // We want to generate an empty QueryList for root content queries for backwards
14376
14430
  // compatibility with ViewEngine.
14377
- if (componentDef.contentQueries) {
14378
- const tNode = getCurrentTNode();
14379
- ngDevMode && assertDefined(tNode, 'TNode expected');
14380
- componentDef.contentQueries(1 /* RenderFlags.Create */, component, tNode.directiveStart);
14431
+ executeContentQueries(tView, rootTNode, componentView);
14432
+ return component;
14433
+ }
14434
+ /** Sets the static attributes on a root component. */
14435
+ function setRootNodeAttributes(hostRenderer, componentDef, hostRNode, rootSelectorOrNode) {
14436
+ if (rootSelectorOrNode) {
14437
+ setUpAttributes(hostRenderer, hostRNode, ['ng-version', VERSION.full]);
14381
14438
  }
14382
- const rootTNode = getCurrentTNode();
14383
- ngDevMode && assertDefined(rootTNode, 'tNode should have been already created');
14384
- if (tView.firstCreatePass &&
14385
- (componentDef.hostBindings !== null || componentDef.hostAttrs !== null)) {
14386
- setSelectedIndex(rootTNode.index);
14387
- const rootTView = rootLView[TVIEW];
14388
- registerHostBindingOpCodes(rootTView, rootTNode, rootLView, rootTNode.directiveStart, rootTNode.directiveEnd, componentDef);
14389
- invokeHostBindingsInCreationMode(componentDef, component);
14439
+ else {
14440
+ // If host element is created as a part of this function call (i.e. `rootSelectorOrNode`
14441
+ // is not defined), also apply attributes and classes extracted from component selector.
14442
+ // Extract attributes and classes from the first selector only to match VE behavior.
14443
+ const { attrs, classes } = extractAttrsAndClassesFromSelector(componentDef.selectors[0]);
14444
+ if (attrs) {
14445
+ setUpAttributes(hostRenderer, hostRNode, attrs);
14446
+ }
14447
+ if (classes && classes.length > 0) {
14448
+ writeDirectClass(hostRenderer, hostRNode, classes.join(' '));
14449
+ }
14450
+ }
14451
+ }
14452
+ /** Projects the `projectableNodes` that were specified when creating a root component. */
14453
+ function projectNodes(tNode, ngContentSelectors, projectableNodes) {
14454
+ const projection = tNode.projection = [];
14455
+ for (let i = 0; i < ngContentSelectors.length; i++) {
14456
+ const nodesforSlot = projectableNodes[i];
14457
+ // Projectable nodes can be passed as array of arrays or an array of iterables (ngUpgrade
14458
+ // case). Here we do normalize passed data structure to be an array of arrays to avoid
14459
+ // complex checks down the line.
14460
+ // We also normalize the length of the passed in projectable nodes (to match the number of
14461
+ // <ng-container> slots defined by a component).
14462
+ projection.push(nodesforSlot != null ? Array.from(nodesforSlot) : null);
14390
14463
  }
14391
- return component;
14392
14464
  }
14393
14465
  /**
14394
14466
  * Used to enable lifecycle hooks on the root component.
@@ -14654,7 +14726,7 @@ function ɵɵCopyDefinitionFeature(definition) {
14654
14726
  * found in the LICENSE file at https://angular.io/license
14655
14727
  */
14656
14728
  /**
14657
- * This feature add the host directives behavior to a directive definition by patching a
14729
+ * This feature adds the host directives behavior to a directive definition by patching a
14658
14730
  * function onto it. The expectation is that the runtime will invoke the function during
14659
14731
  * directive matching.
14660
14732
  *
@@ -14688,17 +14760,22 @@ function ɵɵHostDirectivesFeature(rawHostDirectives) {
14688
14760
  });
14689
14761
  };
14690
14762
  }
14691
- function findHostDirectiveDefs(matches, def, tView, lView, tNode) {
14692
- if (def.hostDirectives !== null) {
14693
- for (const hostDirectiveConfig of def.hostDirectives) {
14763
+ function findHostDirectiveDefs(currentDef, matchedDefs, hostDirectiveDefs) {
14764
+ if (currentDef.hostDirectives !== null) {
14765
+ for (const hostDirectiveConfig of currentDef.hostDirectives) {
14694
14766
  const hostDirectiveDef = getDirectiveDef(hostDirectiveConfig.directive);
14695
- // TODO(crisbeto): assert that the def exists.
14767
+ if (typeof ngDevMode === 'undefined' || ngDevMode) {
14768
+ validateHostDirective(hostDirectiveConfig, hostDirectiveDef, matchedDefs);
14769
+ }
14770
+ // We need to patch the `declaredInputs` so that
14771
+ // `ngOnChanges` can map the properties correctly.
14772
+ patchDeclaredInputs(hostDirectiveDef.declaredInputs, hostDirectiveConfig.inputs);
14696
14773
  // Host directives execute before the host so that its host bindings can be overwritten.
14697
- findHostDirectiveDefs(matches, hostDirectiveDef, tView, lView, tNode);
14774
+ findHostDirectiveDefs(hostDirectiveDef, matchedDefs, hostDirectiveDefs);
14775
+ hostDirectiveDefs.set(hostDirectiveDef, hostDirectiveConfig);
14776
+ matchedDefs.push(hostDirectiveDef);
14698
14777
  }
14699
14778
  }
14700
- // Push the def itself at the end since it needs to execute after the host directives.
14701
- matches.push(def);
14702
14779
  }
14703
14780
  /**
14704
14781
  * Converts an array in the form of `['publicName', 'alias', 'otherPublicName', 'otherAlias']` into
@@ -14714,6 +14791,90 @@ function bindingArrayToMap(bindings) {
14714
14791
  }
14715
14792
  return result;
14716
14793
  }
14794
+ /**
14795
+ * `ngOnChanges` has some leftover legacy ViewEngine behavior where the keys inside the
14796
+ * `SimpleChanges` event refer to the *declared* name of the input, not its public name or its
14797
+ * minified name. E.g. in `@Input('alias') foo: string`, the name in the `SimpleChanges` object
14798
+ * will always be `foo`, and not `alias` or the minified name of `foo` in apps using property
14799
+ * minification.
14800
+ *
14801
+ * This is achieved through the `DirectiveDef.declaredInputs` map that is constructed when the
14802
+ * definition is declared. When a property is written to the directive instance, the
14803
+ * `NgOnChangesFeature` will try to remap the property name being written to using the
14804
+ * `declaredInputs`.
14805
+ *
14806
+ * Since the host directive input remapping happens during directive matching, `declaredInputs`
14807
+ * won't contain the new alias that the input is available under. This function addresses the
14808
+ * issue by patching the host directive aliases to the `declaredInputs`. There is *not* a risk of
14809
+ * this patching accidentally introducing new inputs to the host directive, because `declaredInputs`
14810
+ * is used *only* by the `NgOnChangesFeature` when determining what name is used in the
14811
+ * `SimpleChanges` object which won't be reached if an input doesn't exist.
14812
+ */
14813
+ function patchDeclaredInputs(declaredInputs, exposedInputs) {
14814
+ for (const publicName in exposedInputs) {
14815
+ if (exposedInputs.hasOwnProperty(publicName)) {
14816
+ const remappedPublicName = exposedInputs[publicName];
14817
+ const privateName = declaredInputs[publicName];
14818
+ // We *technically* shouldn't be able to hit this case because we can't have multiple
14819
+ // inputs on the same property and we have validations against conflicting aliases in
14820
+ // `validateMappings`. If we somehow did, it would lead to `ngOnChanges` being invoked
14821
+ // with the wrong name so we have a non-user-friendly assertion here just in case.
14822
+ if ((typeof ngDevMode === 'undefined' || ngDevMode) &&
14823
+ declaredInputs.hasOwnProperty(remappedPublicName)) {
14824
+ assertEqual(declaredInputs[remappedPublicName], declaredInputs[publicName], `Conflicting host directive input alias ${publicName}.`);
14825
+ }
14826
+ declaredInputs[remappedPublicName] = privateName;
14827
+ }
14828
+ }
14829
+ }
14830
+ /**
14831
+ * Verifies that the host directive has been configured correctly.
14832
+ * @param hostDirectiveConfig Host directive configuration object.
14833
+ * @param directiveDef Directive definition of the host directive.
14834
+ * @param matchedDefs Directives that have been matched so far.
14835
+ */
14836
+ function validateHostDirective(hostDirectiveConfig, directiveDef, matchedDefs) {
14837
+ // TODO(crisbeto): implement more of these checks in the compiler.
14838
+ const type = hostDirectiveConfig.directive;
14839
+ if (directiveDef === null) {
14840
+ if (getComponentDef$1(type) !== null) {
14841
+ throw new RuntimeError(310 /* RuntimeErrorCode.HOST_DIRECTIVE_COMPONENT */, `Host directive ${type.name} cannot be a component.`);
14842
+ }
14843
+ throw new RuntimeError(307 /* RuntimeErrorCode.HOST_DIRECTIVE_UNRESOLVABLE */, `Could not resolve metadata for host directive ${type.name}. ` +
14844
+ `Make sure that the ${type.name} class is annotated with an @Directive decorator.`);
14845
+ }
14846
+ if (!directiveDef.standalone) {
14847
+ throw new RuntimeError(308 /* RuntimeErrorCode.HOST_DIRECTIVE_NOT_STANDALONE */, `Host directive ${directiveDef.type.name} must be standalone.`);
14848
+ }
14849
+ if (matchedDefs.indexOf(directiveDef) > -1) {
14850
+ throw new RuntimeError(309 /* RuntimeErrorCode.DUPLICATE_DIRECTITVE */, `Directive ${directiveDef.type.name} matches multiple times on the same element. ` +
14851
+ `Directives can only match an element once.`);
14852
+ }
14853
+ validateMappings('input', directiveDef, hostDirectiveConfig.inputs);
14854
+ validateMappings('output', directiveDef, hostDirectiveConfig.outputs);
14855
+ }
14856
+ /**
14857
+ * Checks that the host directive inputs/outputs configuration is valid.
14858
+ * @param bindingType Kind of binding that is being validated. Used in the error message.
14859
+ * @param def Definition of the host directive that is being validated against.
14860
+ * @param hostDirectiveDefs Host directive mapping object that shold be validated.
14861
+ */
14862
+ function validateMappings(bindingType, def, hostDirectiveDefs) {
14863
+ const className = def.type.name;
14864
+ const bindings = bindingType === 'input' ? def.inputs : def.outputs;
14865
+ for (const publicName in hostDirectiveDefs) {
14866
+ if (hostDirectiveDefs.hasOwnProperty(publicName)) {
14867
+ if (!bindings.hasOwnProperty(publicName)) {
14868
+ throw new RuntimeError(311 /* RuntimeErrorCode.HOST_DIRECTIVE_UNDEFINED_BINDING */, `Directive ${className} does not have an ${bindingType} with a public name of ${publicName}.`);
14869
+ }
14870
+ const remappedPublicName = hostDirectiveDefs[publicName];
14871
+ if (bindings.hasOwnProperty(remappedPublicName) &&
14872
+ bindings[remappedPublicName] !== publicName) {
14873
+ throw new RuntimeError(312 /* RuntimeErrorCode.HOST_DIRECTIVE_CONFLICTING_ALIAS */, `Cannot alias ${bindingType} ${publicName} of host directive ${className} to ${remappedPublicName}, because it already has a different ${bindingType} with the same public name.`);
14874
+ }
14875
+ }
14876
+ }
14877
+ }
14717
14878
 
14718
14879
  /**
14719
14880
  * @license
@@ -15657,18 +15818,7 @@ function ɵɵelementStart(index, name, attrsIndex, localRefsIndex) {
15657
15818
  elementStartFirstCreatePass(adjustedIndex, tView, lView, native, name, attrsIndex, localRefsIndex) :
15658
15819
  tView.data[adjustedIndex];
15659
15820
  setCurrentTNode(tNode, true);
15660
- const mergedAttrs = tNode.mergedAttrs;
15661
- if (mergedAttrs !== null) {
15662
- setUpAttributes(renderer, native, mergedAttrs);
15663
- }
15664
- const classes = tNode.classes;
15665
- if (classes !== null) {
15666
- writeDirectClass(renderer, native, classes);
15667
- }
15668
- const styles = tNode.styles;
15669
- if (styles !== null) {
15670
- writeDirectStyle(renderer, native, styles);
15671
- }
15821
+ setupStaticAttributes(renderer, native, tNode);
15672
15822
  if ((tNode.flags & 32 /* TNodeFlags.isDetached */) !== 32 /* TNodeFlags.isDetached */) {
15673
15823
  // In the i18n case, the translation may have removed this element, so only add it if it is not
15674
15824
  // detached. See `TNodeType.Placeholder` and `LFrame.inI18n` for more context.
@@ -22358,7 +22508,7 @@ function getDirectives(node) {
22358
22508
  return [];
22359
22509
  }
22360
22510
  if (context.directives === undefined) {
22361
- context.directives = getDirectivesAtNodeIndex(nodeIndex, lView, false);
22511
+ context.directives = getDirectivesAtNodeIndex(nodeIndex, lView);
22362
22512
  }
22363
22513
  // The `directives` in this case are a named array called `LComponentView`. Clone the
22364
22514
  // result so we don't expose an internal data structure in the user's console.
@@ -24627,7 +24777,7 @@ function generateStandaloneInDeclarationsError(type, location) {
24627
24777
  function verifySemanticsOfNgModuleDef(moduleType, allowDuplicateDeclarationsInRoot, importingModule) {
24628
24778
  if (verifiedNgModule.get(moduleType))
24629
24779
  return;
24630
- // skip verifications of standalone components, directives and pipes
24780
+ // skip verifications of standalone components, directives, and pipes
24631
24781
  if (isStandalone(moduleType))
24632
24782
  return;
24633
24783
  verifiedNgModule.set(moduleType, true);
@@ -26135,7 +26285,7 @@ class TestBedImpl {
26135
26285
  return TestBedImpl.INSTANCE.overrideProvider(token, provider);
26136
26286
  }
26137
26287
  static inject(token, notFoundValue, flags) {
26138
- return TestBedImpl.INSTANCE.inject(token, notFoundValue, flags);
26288
+ return TestBedImpl.INSTANCE.inject(token, notFoundValue, ɵconvertToBitFlags(flags));
26139
26289
  }
26140
26290
  /** @deprecated from v9.0.0 use TestBed.inject */
26141
26291
  static get(token, notFoundValue = Injector$1.THROW_IF_NOT_FOUND, flags = InjectFlags$1.Default) {
@@ -26268,7 +26418,7 @@ class TestBedImpl {
26268
26418
  return this;
26269
26419
  }
26270
26420
  const UNDEFINED = {};
26271
- const result = this.testModuleRef.injector.get(token, UNDEFINED, flags);
26421
+ const result = this.testModuleRef.injector.get(token, UNDEFINED, ɵconvertToBitFlags(flags));
26272
26422
  return result === UNDEFINED ? this.compiler.injector.get(token, notFoundValue, flags) :
26273
26423
  result;
26274
26424
  }