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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) 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/injector.mjs +1 -1
  4. package/esm2020/src/di/injector_compatibility.mjs +15 -11
  5. package/esm2020/src/di/interface/injector.mjs +1 -1
  6. package/esm2020/src/di/r3_injector.mjs +3 -2
  7. package/esm2020/src/render3/component_ref.mjs +102 -83
  8. package/esm2020/src/render3/context_discovery.mjs +7 -7
  9. package/esm2020/src/render3/di.mjs +3 -2
  10. package/esm2020/src/render3/features/host_directives_feature.mjs +2 -3
  11. package/esm2020/src/render3/instructions/element.mjs +3 -15
  12. package/esm2020/src/render3/instructions/shared.mjs +93 -106
  13. package/esm2020/src/render3/ng_module_ref.mjs +1 -1
  14. package/esm2020/src/render3/node_manipulation.mjs +15 -1
  15. package/esm2020/src/render3/util/discovery_utils.mjs +2 -2
  16. package/esm2020/src/util/is_dev_mode.mjs +11 -19
  17. package/esm2020/src/version.mjs +1 -1
  18. package/esm2020/src/zone/async-stack-tagging.mjs +28 -0
  19. package/esm2020/src/zone/ng_zone.mjs +8 -3
  20. package/esm2020/testing/src/logger.mjs +3 -3
  21. package/esm2020/testing/src/ng_zone_mock.mjs +3 -3
  22. package/esm2020/testing/src/test_bed.mjs +4 -4
  23. package/fesm2015/core.mjs +1506 -1470
  24. package/fesm2015/core.mjs.map +1 -1
  25. package/fesm2015/testing.mjs +888 -877
  26. package/fesm2015/testing.mjs.map +1 -1
  27. package/fesm2020/core.mjs +1509 -1475
  28. package/fesm2020/core.mjs.map +1 -1
  29. package/fesm2020/testing.mjs +886 -876
  30. package/fesm2020/testing.mjs.map +1 -1
  31. package/index.d.ts +60 -5
  32. package/package.json +1 -1
  33. 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.4
3
3
  * (c) 2010-2022 Google LLC. https://angular.io/
4
4
  * License: MIT
5
5
  */
6
6
 
7
- import { getDebugNode as getDebugNode$1, RendererFactory2 as RendererFactory2$1, InjectionToken as InjectionToken$1, ɵstringify, ɵReflectionCapabilities, Directive, Component, Pipe, NgModule, ɵgetInjectableDef, resolveForwardRef as resolveForwardRef$1, ɵNG_COMP_DEF, ɵRender3NgModuleRef, ApplicationInitStatus, LOCALE_ID as LOCALE_ID$1, ɵDEFAULT_LOCALE_ID, ɵsetLocaleId, ɵRender3ComponentFactory, ɵcompileComponent, ɵNG_DIR_DEF, ɵcompileDirective, ɵNG_PIPE_DEF, ɵcompilePipe, ɵNG_MOD_DEF, ɵtransitiveScopesFor, ɵpatchComponentDefWithScope, ɵNG_INJ_DEF, ɵcompileNgModuleDefs, NgZone, Compiler, COMPILER_OPTIONS, ɵNgModuleFactory, ModuleWithComponentFactories, Injector as Injector$1, InjectFlags as InjectFlags$1, ɵsetAllowDuplicateNgModuleIdsForTest, ɵresetCompiledComponents, ɵsetUnknownElementStrictMode as ɵsetUnknownElementStrictMode$1, ɵsetUnknownPropertyStrictMode as ɵsetUnknownPropertyStrictMode$1, ɵgetUnknownElementStrictMode as ɵgetUnknownElementStrictMode$1, ɵgetUnknownPropertyStrictMode as ɵgetUnknownPropertyStrictMode$1, ɵflushModuleScopingQueueAsMuchAsPossible } from '@angular/core';
7
+ import { getDebugNode as getDebugNode$1, RendererFactory2 as RendererFactory2$1, InjectionToken as InjectionToken$1, ɵstringify, ɵReflectionCapabilities, Directive, Component, Pipe, NgModule, ɵgetInjectableDef, resolveForwardRef as resolveForwardRef$1, ɵNG_COMP_DEF, ɵRender3NgModuleRef, ApplicationInitStatus, LOCALE_ID as LOCALE_ID$1, ɵDEFAULT_LOCALE_ID, ɵsetLocaleId, ɵRender3ComponentFactory, ɵcompileComponent, ɵNG_DIR_DEF, ɵcompileDirective, ɵNG_PIPE_DEF, ɵcompilePipe, ɵNG_MOD_DEF, ɵtransitiveScopesFor, ɵpatchComponentDefWithScope, ɵNG_INJ_DEF, ɵcompileNgModuleDefs, NgZone, Compiler, COMPILER_OPTIONS, ɵNgModuleFactory, ModuleWithComponentFactories, ɵconvertToBitFlags, Injector as Injector$1, InjectFlags as InjectFlags$1, ɵsetAllowDuplicateNgModuleIdsForTest, ɵresetCompiledComponents, ɵsetUnknownElementStrictMode as ɵsetUnknownElementStrictMode$1, ɵsetUnknownPropertyStrictMode as ɵsetUnknownPropertyStrictMode$1, ɵgetUnknownElementStrictMode as ɵgetUnknownElementStrictMode$1, ɵgetUnknownPropertyStrictMode as ɵgetUnknownPropertyStrictMode$1, ɵflushModuleScopingQueueAsMuchAsPossible } from '@angular/core';
8
8
  import { __awaiter } from 'tslib';
9
9
  import { ResourceLoader } from '@angular/compiler';
10
10
  import { Subject, Subscription } from 'rxjs';
@@ -2175,17 +2175,21 @@ Please check that 1) the type for the parameter at index ${index} is correct and
2175
2175
  * @publicApi
2176
2176
  */
2177
2177
  function inject$1(token, flags = InjectFlags.Default) {
2178
- if (typeof flags !== 'number') {
2179
- // While TypeScript doesn't accept it without a cast, bitwise OR with false-y values in
2180
- // JavaScript is a no-op. We can use that for a very codesize-efficient conversion from
2181
- // `InjectOptions` to `InjectFlags`.
2182
- flags = (0 /* InternalInjectFlags.Default */ | // comment to force a line break in the formatter
2183
- (flags.optional && 8 /* InternalInjectFlags.Optional */) |
2184
- (flags.host && 1 /* InternalInjectFlags.Host */) |
2185
- (flags.self && 2 /* InternalInjectFlags.Self */) |
2186
- (flags.skipSelf && 4 /* InternalInjectFlags.SkipSelf */));
2187
- }
2188
- return ɵɵinject(token, flags);
2178
+ return ɵɵinject(token, convertToBitFlags(flags));
2179
+ }
2180
+ // Converts object-based DI flags (`InjectOptions`) to bit flags (`InjectFlags`).
2181
+ function convertToBitFlags(flags) {
2182
+ if (typeof flags === 'undefined' || typeof flags === 'number') {
2183
+ return flags;
2184
+ }
2185
+ // While TypeScript doesn't accept it without a cast, bitwise OR with false-y values in
2186
+ // JavaScript is a no-op. We can use that for a very codesize-efficient conversion from
2187
+ // `InjectOptions` to `InjectFlags`.
2188
+ return (0 /* InternalInjectFlags.Default */ | // comment to force a line break in the formatter
2189
+ (flags.optional && 8 /* InternalInjectFlags.Optional */) |
2190
+ (flags.host && 1 /* InternalInjectFlags.Host */) |
2191
+ (flags.self && 2 /* InternalInjectFlags.Self */) |
2192
+ (flags.skipSelf && 4 /* InternalInjectFlags.SkipSelf */));
2189
2193
  }
2190
2194
  function injectArgs(types) {
2191
2195
  const args = [];
@@ -5233,7 +5237,7 @@ class NodeInjector {
5233
5237
  this._lView = _lView;
5234
5238
  }
5235
5239
  get(token, notFoundValue, flags) {
5236
- return getOrCreateInjectable(this._tNode, this._lView, token, flags, notFoundValue);
5240
+ return getOrCreateInjectable(this._tNode, this._lView, token, convertToBitFlags(flags), notFoundValue);
5237
5241
  }
5238
5242
  }
5239
5243
  /** Creates a `NodeInjector` for the current node. */
@@ -7136,6 +7140,7 @@ class R3Injector extends EnvironmentInjector {
7136
7140
  }
7137
7141
  get(token, notFoundValue = THROW_IF_NOT_FOUND, flags = InjectFlags.Default) {
7138
7142
  this.assertNotDestroyed();
7143
+ flags = convertToBitFlags(flags);
7139
7144
  // Set the injection context.
7140
7145
  const previousInjector = setCurrentInjector(this);
7141
7146
  const previousInjectImplementation = setInjectImplementation(undefined);
@@ -7637,7 +7642,7 @@ class Version {
7637
7642
  /**
7638
7643
  * @publicApi
7639
7644
  */
7640
- const VERSION = new Version('15.0.0-next.3');
7645
+ const VERSION = new Version('15.0.0-next.4');
7641
7646
 
7642
7647
  /**
7643
7648
  * @license
@@ -7672,29 +7677,70 @@ const NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR = {};
7672
7677
  * Use of this source code is governed by an MIT-style license that can be
7673
7678
  * found in the LICENSE file at https://angular.io/license
7674
7679
  */
7680
+ // Keeps track of the currently-active LViews.
7681
+ const TRACKED_LVIEWS = new Map();
7682
+ // Used for generating unique IDs for LViews.
7683
+ let uniqueIdCounter = 0;
7684
+ /** Gets a unique ID that can be assigned to an LView. */
7685
+ function getUniqueLViewId() {
7686
+ return uniqueIdCounter++;
7687
+ }
7688
+ /** Starts tracking an LView. */
7689
+ function registerLView(lView) {
7690
+ ngDevMode && assertNumber(lView[ID], 'LView must have an ID in order to be registered');
7691
+ TRACKED_LVIEWS.set(lView[ID], lView);
7692
+ }
7693
+ /** Gets an LView by its unique ID. */
7694
+ function getLViewById(id) {
7695
+ ngDevMode && assertNumber(id, 'ID used for LView lookup must be a number');
7696
+ return TRACKED_LVIEWS.get(id) || null;
7697
+ }
7698
+ /** Stops tracking an LView. */
7699
+ function unregisterLView(lView) {
7700
+ ngDevMode && assertNumber(lView[ID], 'Cannot stop tracking an LView that does not have an ID');
7701
+ TRACKED_LVIEWS.delete(lView[ID]);
7702
+ }
7703
+
7675
7704
  /**
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.
7705
+ * @license
7706
+ * Copyright Google LLC All Rights Reserved.
7680
7707
  *
7681
- * @publicApi
7708
+ * Use of this source code is governed by an MIT-style license that can be
7709
+ * found in the LICENSE file at https://angular.io/license
7682
7710
  */
7683
- const CUSTOM_ELEMENTS_SCHEMA = {
7684
- name: 'custom-elements'
7685
- };
7686
7711
  /**
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.
7712
+ * The internal view context which is specific to a given DOM element, directive or
7713
+ * component instance. Each value in here (besides the LView and element node details)
7714
+ * can be present, null or undefined. If undefined then it implies the value has not been
7715
+ * looked up yet, otherwise, if null, then a lookup was executed and nothing was found.
7692
7716
  *
7693
- * @publicApi
7717
+ * Each value will get filled when the respective value is examined within the getContext
7718
+ * function. The component, element and each directive instance will share the same instance
7719
+ * of the context.
7694
7720
  */
7695
- const NO_ERRORS_SCHEMA = {
7696
- name: 'no-errors-schema'
7697
- };
7721
+ class LContext {
7722
+ constructor(
7723
+ /**
7724
+ * ID of the component's parent view data.
7725
+ */
7726
+ lViewId,
7727
+ /**
7728
+ * The index instance of the node.
7729
+ */
7730
+ nodeIndex,
7731
+ /**
7732
+ * The instance of the DOM node that is attached to the lNode.
7733
+ */
7734
+ native) {
7735
+ this.lViewId = lViewId;
7736
+ this.nodeIndex = nodeIndex;
7737
+ this.native = native;
7738
+ }
7739
+ /** Component's parent view data. */
7740
+ get lView() {
7741
+ return getLViewById(this.lViewId);
7742
+ }
7743
+ }
7698
7744
 
7699
7745
  /**
7700
7746
  * @license
@@ -7703,337 +7749,305 @@ const NO_ERRORS_SCHEMA = {
7703
7749
  * Use of this source code is governed by an MIT-style license that can be
7704
7750
  * found in the LICENSE file at https://angular.io/license
7705
7751
  */
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
7752
  /**
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).
7753
+ * Returns the matching `LContext` data for a given DOM node, directive or component instance.
7741
7754
  *
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
7755
+ * This function will examine the provided DOM element, component, or directive instance\'s
7756
+ * monkey-patched property to derive the `LContext` data. Once called then the monkey-patched
7757
+ * value will be that of the newly created `LContext`.
7747
7758
  *
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
7759
+ * If the monkey-patched value is the `LView` instance then the context value for that
7760
+ * target will be created and the monkey-patch reference will be updated. Therefore when this
7761
+ * function is called it may mutate the provided element\'s, component\'s or any of the associated
7762
+ * directive\'s monkey-patch values.
7763
+ *
7764
+ * If the monkey-patch value is not detected then the code will walk up the DOM until an element
7765
+ * is found which contains a monkey-patch reference. When that occurs then the provided element
7766
+ * will be updated with a new context (which is then returned). If the monkey-patch value is not
7767
+ * detected for a component/directive instance then it will throw an error (all components and
7768
+ * directives should be automatically monkey-patched by ivy).
7769
+ *
7770
+ * @param target Component, Directive or DOM Node.
7753
7771
  */
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.`;
7772
+ function getLContext(target) {
7773
+ let mpValue = readPatchedData(target);
7774
+ if (mpValue) {
7775
+ // only when it's an array is it considered an LView instance
7776
+ // ... otherwise it's an already constructed LContext instance
7777
+ if (isLView(mpValue)) {
7778
+ const lView = mpValue;
7779
+ let nodeIndex;
7780
+ let component = undefined;
7781
+ let directives = undefined;
7782
+ if (isComponentInstance(target)) {
7783
+ nodeIndex = findViaComponent(lView, target);
7784
+ if (nodeIndex == -1) {
7785
+ throw new Error('The provided component was not found in the application');
7786
+ }
7787
+ component = target;
7788
+ }
7789
+ else if (isDirectiveInstance(target)) {
7790
+ nodeIndex = findViaDirective(lView, target);
7791
+ if (nodeIndex == -1) {
7792
+ throw new Error('The provided directive was not found in the application');
7793
+ }
7794
+ directives = getDirectivesAtNodeIndex(nodeIndex, lView);
7783
7795
  }
7784
7796
  else {
7785
- message +=
7786
- `2. To allow any element add 'NO_ERRORS_SCHEMA' to the ${schemas} of this component.`;
7797
+ nodeIndex = findViaNativeElement(lView, target);
7798
+ if (nodeIndex == -1) {
7799
+ return null;
7800
+ }
7787
7801
  }
7788
- if (shouldThrowErrorOnUnknownElement) {
7789
- throw new RuntimeError(304 /* RuntimeErrorCode.UNKNOWN_ELEMENT */, message);
7802
+ // the goal is not to fill the entire context full of data because the lookups
7803
+ // are expensive. Instead, only the target data (the element, component, container, ICU
7804
+ // expression or directive details) are filled into the context. If called multiple times
7805
+ // with different target values then the missing target data will be filled in.
7806
+ const native = unwrapRNode(lView[nodeIndex]);
7807
+ const existingCtx = readPatchedData(native);
7808
+ const context = (existingCtx && !Array.isArray(existingCtx)) ?
7809
+ existingCtx :
7810
+ createLContext(lView, nodeIndex, native);
7811
+ // only when the component has been discovered then update the monkey-patch
7812
+ if (component && context.component === undefined) {
7813
+ context.component = component;
7814
+ attachPatchData(context.component, context);
7790
7815
  }
7791
- else {
7792
- console.error(formatRuntimeError(304 /* RuntimeErrorCode.UNKNOWN_ELEMENT */, message));
7816
+ // only when the directives have been discovered then update the monkey-patch
7817
+ if (directives && context.directives === undefined) {
7818
+ context.directives = directives;
7819
+ for (let i = 0; i < directives.length; i++) {
7820
+ attachPatchData(directives[i], context);
7821
+ }
7822
+ }
7823
+ attachPatchData(context.native, context);
7824
+ mpValue = context;
7825
+ }
7826
+ }
7827
+ else {
7828
+ const rElement = target;
7829
+ ngDevMode && assertDomNode(rElement);
7830
+ // if the context is not found then we need to traverse upwards up the DOM
7831
+ // to find the nearest element that has already been monkey patched with data
7832
+ let parent = rElement;
7833
+ while (parent = parent.parentNode) {
7834
+ const parentContext = readPatchedData(parent);
7835
+ if (parentContext) {
7836
+ const lView = Array.isArray(parentContext) ? parentContext : parentContext.lView;
7837
+ // the edge of the app was also reached here through another means
7838
+ // (maybe because the DOM was changed manually).
7839
+ if (!lView) {
7840
+ return null;
7841
+ }
7842
+ const index = findViaNativeElement(lView, rElement);
7843
+ if (index >= 0) {
7844
+ const native = unwrapRNode(lView[index]);
7845
+ const context = createLContext(lView, index, native);
7846
+ attachPatchData(native, context);
7847
+ mpValue = context;
7848
+ break;
7849
+ }
7793
7850
  }
7794
7851
  }
7795
7852
  }
7853
+ return mpValue || null;
7796
7854
  }
7797
7855
  /**
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).
7856
+ * Creates an empty instance of a `LContext` context
7857
+ */
7858
+ function createLContext(lView, nodeIndex, native) {
7859
+ return new LContext(lView[ID], nodeIndex, native);
7860
+ }
7861
+ /**
7862
+ * Takes a component instance and returns the view for that component.
7802
7863
  *
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
7812
- */
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);
7828
- }
7829
- /**
7830
- * Logs or throws an error that a property is not supported on an element.
7831
- *
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
7864
+ * @param componentInstance
7865
+ * @returns The component's view
7836
7866
  */
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}.`;
7867
+ function getComponentViewByInstance(componentInstance) {
7868
+ let patchedData = readPatchedData(componentInstance);
7869
+ let lView;
7870
+ if (isLView(patchedData)) {
7871
+ const contextLView = patchedData;
7872
+ const nodeIndex = findViaComponent(contextLView, componentInstance);
7873
+ lView = getComponentLViewByIndex(nodeIndex, contextLView);
7874
+ const context = createLContext(contextLView, nodeIndex, lView[HOST]);
7875
+ context.component = componentInstance;
7876
+ attachPatchData(componentInstance, context);
7877
+ attachPatchData(context.native, context);
7860
7878
  }
7861
7879
  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
- }
7880
+ const context = patchedData;
7881
+ const contextLView = context.lView;
7882
+ ngDevMode && assertLView(contextLView);
7883
+ lView = getComponentLViewByIndex(context.nodeIndex, contextLView);
7877
7884
  }
7878
- reportUnknownPropertyError(message);
7885
+ return lView;
7879
7886
  }
7880
- function reportUnknownPropertyError(message) {
7881
- if (shouldThrowErrorOnUnknownProperty) {
7882
- throw new RuntimeError(303 /* RuntimeErrorCode.UNKNOWN_BINDING */, message);
7887
+ /**
7888
+ * This property will be monkey-patched on elements, components and directives.
7889
+ */
7890
+ const MONKEY_PATCH_KEY_NAME = '__ngContext__';
7891
+ /**
7892
+ * Assigns the given data to the given target (which could be a component,
7893
+ * directive or DOM node instance) using monkey-patching.
7894
+ */
7895
+ function attachPatchData(target, data) {
7896
+ ngDevMode && assertDefined(target, 'Target expected');
7897
+ // Only attach the ID of the view in order to avoid memory leaks (see #41047). We only do this
7898
+ // for `LView`, because we have control over when an `LView` is created and destroyed, whereas
7899
+ // we can't know when to remove an `LContext`.
7900
+ if (isLView(data)) {
7901
+ target[MONKEY_PATCH_KEY_NAME] = data[ID];
7902
+ registerLView(data);
7883
7903
  }
7884
7904
  else {
7885
- console.error(formatRuntimeError(303 /* RuntimeErrorCode.UNKNOWN_BINDING */, message));
7905
+ target[MONKEY_PATCH_KEY_NAME] = data;
7886
7906
  }
7887
7907
  }
7888
7908
  /**
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.
7909
+ * Returns the monkey-patch value data present on the target (which could be
7910
+ * a component, directive or a DOM node).
7896
7911
  */
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;
7912
+ function readPatchedData(target) {
7913
+ ngDevMode && assertDefined(target, 'Target expected');
7914
+ const data = target[MONKEY_PATCH_KEY_NAME];
7915
+ return (typeof data === 'number') ? getLViewById(data) : data || null;
7916
+ }
7917
+ function readPatchedLView(target) {
7918
+ const value = readPatchedData(target);
7919
+ if (value) {
7920
+ return (isLView(value) ? value : value.lView);
7921
+ }
7922
+ return null;
7923
+ }
7924
+ function isComponentInstance(instance) {
7925
+ return instance && instance.constructor && instance.constructor.ɵcmp;
7926
+ }
7927
+ function isDirectiveInstance(instance) {
7928
+ return instance && instance.constructor && instance.constructor.ɵdir;
7905
7929
  }
7906
7930
  /**
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.
7931
+ * Locates the element within the given LView and returns the matching index
7914
7932
  */
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 === null || componentDef === void 0 ? void 0 : componentDef.standalone);
7933
+ function findViaNativeElement(lView, target) {
7934
+ const tView = lView[TVIEW];
7935
+ for (let i = HEADER_OFFSET; i < tView.bindingStartIndex; i++) {
7936
+ if (unwrapRNode(lView[i]) === target) {
7937
+ return i;
7938
+ }
7939
+ }
7940
+ return -1;
7920
7941
  }
7921
7942
  /**
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.
7943
+ * Locates the next tNode (child, sibling or parent).
7930
7944
  */
7931
- function getTemplateLocationDetails(lView) {
7932
- var _a;
7933
- !ngDevMode && throwError('Must never be called in production mode');
7934
- const hostComponentDef = getDeclarationComponentDef(lView);
7935
- const componentClassName = (_a = hostComponentDef === null || hostComponentDef === void 0 ? void 0 : hostComponentDef.type) === null || _a === void 0 ? void 0 : _a.name;
7936
- return componentClassName ? ` (used in the '${componentClassName}' component template)` : '';
7945
+ function traverseNextElement(tNode) {
7946
+ if (tNode.child) {
7947
+ return tNode.child;
7948
+ }
7949
+ else if (tNode.next) {
7950
+ return tNode.next;
7951
+ }
7952
+ else {
7953
+ // Let's take the following template: <div><span>text</span></div><component/>
7954
+ // After checking the text node, we need to find the next parent that has a "next" TNode,
7955
+ // in this case the parent `div`, so that we can find the component.
7956
+ while (tNode.parent && !tNode.parent.next) {
7957
+ tNode = tNode.parent;
7958
+ }
7959
+ return tNode.parent && tNode.parent.next;
7960
+ }
7937
7961
  }
7938
7962
  /**
7939
- * The set of known control flow directives and their corresponding imports.
7940
- * We use this set to produce a more precises error message with a note
7941
- * that the `CommonModule` should also be included.
7963
+ * Locates the component within the given LView and returns the matching index
7942
7964
  */
7943
- const KNOWN_CONTROL_FLOW_DIRECTIVES = new Map([
7944
- ['ngIf', 'NgIf'], ['ngFor', 'NgFor'], ['ngSwitchCase', 'NgSwitchCase'],
7945
- ['ngSwitchDefault', 'NgSwitchDefault']
7946
- ]);
7965
+ function findViaComponent(lView, componentInstance) {
7966
+ const componentIndices = lView[TVIEW].components;
7967
+ if (componentIndices) {
7968
+ for (let i = 0; i < componentIndices.length; i++) {
7969
+ const elementComponentIndex = componentIndices[i];
7970
+ const componentView = getComponentLViewByIndex(elementComponentIndex, lView);
7971
+ if (componentView[CONTEXT] === componentInstance) {
7972
+ return elementComponentIndex;
7973
+ }
7974
+ }
7975
+ }
7976
+ else {
7977
+ const rootComponentView = getComponentLViewByIndex(HEADER_OFFSET, lView);
7978
+ const rootComponent = rootComponentView[CONTEXT];
7979
+ if (rootComponent === componentInstance) {
7980
+ // we are dealing with the root element here therefore we know that the
7981
+ // element is the very first element after the HEADER data in the lView
7982
+ return HEADER_OFFSET;
7983
+ }
7984
+ }
7985
+ return -1;
7986
+ }
7947
7987
  /**
7948
- * Returns true if the tag name is allowed by specified schemas.
7949
- * @param schemas Array of schemas
7950
- * @param tagName Name of the tag
7988
+ * Locates the directive within the given LView and returns the matching index
7951
7989
  */
7952
- function matchingSchemas(schemas, tagName) {
7953
- if (schemas !== null) {
7954
- for (let i = 0; i < schemas.length; i++) {
7955
- const schema = schemas[i];
7956
- if (schema === NO_ERRORS_SCHEMA ||
7957
- schema === CUSTOM_ELEMENTS_SCHEMA && tagName && tagName.indexOf('-') > -1) {
7958
- return true;
7990
+ function findViaDirective(lView, directiveInstance) {
7991
+ // if a directive is monkey patched then it will (by default)
7992
+ // have a reference to the LView of the current view. The
7993
+ // element bound to the directive being search lives somewhere
7994
+ // in the view data. We loop through the nodes and check their
7995
+ // list of directives for the instance.
7996
+ let tNode = lView[TVIEW].firstChild;
7997
+ while (tNode) {
7998
+ const directiveIndexStart = tNode.directiveStart;
7999
+ const directiveIndexEnd = tNode.directiveEnd;
8000
+ for (let i = directiveIndexStart; i < directiveIndexEnd; i++) {
8001
+ if (lView[i] === directiveInstance) {
8002
+ return tNode.index;
7959
8003
  }
7960
8004
  }
8005
+ tNode = traverseNextElement(tNode);
7961
8006
  }
7962
- return false;
8007
+ return -1;
7963
8008
  }
7964
-
7965
8009
  /**
7966
- * @license
7967
- * Copyright Google LLC All Rights Reserved.
8010
+ * Returns a list of directives applied to a node at a specific index. The list includes
8011
+ * directives matched by selector and any host directives, but it excludes components.
8012
+ * Use `getComponentAtNodeIndex` to find the component applied to a node.
7968
8013
  *
7969
- * Use of this source code is governed by an MIT-style license that can be
7970
- * found in the LICENSE file at https://angular.io/license
8014
+ * @param nodeIndex The node index
8015
+ * @param lView The target view data
7971
8016
  */
7972
- const ERROR_ORIGINAL_ERROR = 'ngOriginalError';
7973
- function wrappedError(message, originalError) {
7974
- const msg = `${message} caused by: ${originalError instanceof Error ? originalError.message : originalError}`;
7975
- const error = Error(msg);
7976
- error[ERROR_ORIGINAL_ERROR] = originalError;
7977
- return error;
8017
+ function getDirectivesAtNodeIndex(nodeIndex, lView) {
8018
+ const tNode = lView[TVIEW].data[nodeIndex];
8019
+ if (tNode.directiveStart === 0)
8020
+ return EMPTY_ARRAY;
8021
+ const results = [];
8022
+ for (let i = tNode.directiveStart; i < tNode.directiveEnd; i++) {
8023
+ const directiveInstance = lView[i];
8024
+ if (!isComponentInstance(directiveInstance)) {
8025
+ results.push(directiveInstance);
8026
+ }
8027
+ }
8028
+ return results;
7978
8029
  }
7979
- function getOriginalError(error) {
7980
- return error[ERROR_ORIGINAL_ERROR];
8030
+ function getComponentAtNodeIndex(nodeIndex, lView) {
8031
+ const tNode = lView[TVIEW].data[nodeIndex];
8032
+ const { directiveStart, componentOffset } = tNode;
8033
+ return componentOffset > -1 ? lView[directiveStart + componentOffset] : null;
7981
8034
  }
7982
-
7983
- /**
7984
- * @license
7985
- * Copyright Google LLC All Rights Reserved.
7986
- *
7987
- * Use of this source code is governed by an MIT-style license that can be
7988
- * found in the LICENSE file at https://angular.io/license
7989
- */
7990
8035
  /**
7991
- * Provides a hook for centralized exception handling.
7992
- *
7993
- * The default implementation of `ErrorHandler` prints error messages to the `console`. To
7994
- * intercept error handling, write a custom exception handler that replaces this default as
7995
- * appropriate for your app.
7996
- *
7997
- * @usageNotes
7998
- * ### Example
7999
- *
8000
- * ```
8001
- * class MyErrorHandler implements ErrorHandler {
8002
- * handleError(error) {
8003
- * // do something with the exception
8004
- * }
8005
- * }
8006
- *
8007
- * @NgModule({
8008
- * providers: [{provide: ErrorHandler, useClass: MyErrorHandler}]
8009
- * })
8010
- * class MyModule {}
8011
- * ```
8012
- *
8013
- * @publicApi
8036
+ * Returns a map of local references (local reference name => element or directive instance) that
8037
+ * exist on a given element.
8014
8038
  */
8015
- class ErrorHandler {
8016
- constructor() {
8017
- /**
8018
- * @internal
8019
- */
8020
- this._console = console;
8021
- }
8022
- handleError(error) {
8023
- const originalError = this._findOriginalError(error);
8024
- this._console.error('ERROR', error);
8025
- if (originalError) {
8026
- this._console.error('ORIGINAL ERROR', originalError);
8027
- }
8028
- }
8029
- /** @internal */
8030
- _findOriginalError(error) {
8031
- let e = error && getOriginalError(error);
8032
- while (e && getOriginalError(e)) {
8033
- e = getOriginalError(e);
8039
+ function discoverLocalRefs(lView, nodeIndex) {
8040
+ const tNode = lView[TVIEW].data[nodeIndex];
8041
+ if (tNode && tNode.localNames) {
8042
+ const result = {};
8043
+ let localIndex = tNode.index + 1;
8044
+ for (let i = 0; i < tNode.localNames.length; i += 2) {
8045
+ result[tNode.localNames[i]] = lView[localIndex];
8046
+ localIndex++;
8034
8047
  }
8035
- return e || null;
8048
+ return result;
8036
8049
  }
8050
+ return null;
8037
8051
  }
8038
8052
 
8039
8053
  /**
@@ -8044,46 +8058,28 @@ class ErrorHandler {
8044
8058
  * found in the LICENSE file at https://angular.io/license
8045
8059
  */
8046
8060
  /**
8047
- * Disallowed strings in the comment.
8061
+ * Defines a schema that allows an NgModule to contain the following:
8062
+ * - Non-Angular elements named with dash case (`-`).
8063
+ * - Element properties named with dash case (`-`).
8064
+ * Dash case is the naming convention for custom elements.
8048
8065
  *
8049
- * see: https://html.spec.whatwg.org/multipage/syntax.html#comments
8050
- */
8051
- const COMMENT_DISALLOWED = /^>|^->|<!--|-->|--!>|<!-$/g;
8052
- /**
8053
- * Delimiter in the disallowed strings which needs to be wrapped with zero with character.
8066
+ * @publicApi
8054
8067
  */
8055
- const COMMENT_DELIMITER = /(<|>)/;
8056
- const COMMENT_DELIMITER_ESCAPED = '\u200B$1\u200B';
8068
+ const CUSTOM_ELEMENTS_SCHEMA = {
8069
+ name: 'custom-elements'
8070
+ };
8057
8071
  /**
8058
- * Escape the content of comment strings so that it can be safely inserted into a comment node.
8059
- *
8060
- * The issue is that HTML does not specify any way to escape comment end text inside the comment.
8061
- * Consider: `<!-- The way you close a comment is with ">", and "->" at the beginning or by "-->" or
8062
- * "--!>" at the end. -->`. Above the `"-->"` is meant to be text not an end to the comment. This
8063
- * can be created programmatically through DOM APIs. (`<!--` are also disallowed.)
8064
- *
8065
- * see: https://html.spec.whatwg.org/multipage/syntax.html#comments
8066
- *
8067
- * ```
8068
- * div.innerHTML = div.innerHTML
8069
- * ```
8070
- *
8071
- * One would expect that the above code would be safe to do, but it turns out that because comment
8072
- * text is not escaped, the comment may contain text which will prematurely close the comment
8073
- * opening up the application for XSS attack. (In SSR we programmatically create comment nodes which
8074
- * may contain such text and expect them to be safe.)
8072
+ * Defines a schema that allows any property on any element.
8075
8073
  *
8076
- * This function escapes the comment text by looking for comment delimiters (`<` and `>`) and
8077
- * surrounding them with `_>_` where the `_` is a zero width space `\u200B`. The result is that if a
8078
- * comment contains any of the comment start/end delimiters (such as `<!--`, `-->` or `--!>`) the
8079
- * text it will render normally but it will not cause the HTML parser to close/open the comment.
8074
+ * This schema allows you to ignore the errors related to any unknown elements or properties in a
8075
+ * template. The usage of this schema is generally discouraged because it prevents useful validation
8076
+ * and may hide real errors in your template. Consider using the `CUSTOM_ELEMENTS_SCHEMA` instead.
8080
8077
  *
8081
- * @param value text to make safe for comment node by escaping the comment open/close character
8082
- * sequence.
8078
+ * @publicApi
8083
8079
  */
8084
- function escapeCommentText(value) {
8085
- return value.replace(COMMENT_DISALLOWED, (text) => text.replace(COMMENT_DELIMITER, COMMENT_DELIMITER_ESCAPED));
8086
- }
8080
+ const NO_ERRORS_SCHEMA = {
8081
+ name: 'no-errors-schema'
8082
+ };
8087
8083
 
8088
8084
  /**
8089
8085
  * @license
@@ -8092,403 +8088,412 @@ function escapeCommentText(value) {
8092
8088
  * Use of this source code is governed by an MIT-style license that can be
8093
8089
  * found in the LICENSE file at https://angular.io/license
8094
8090
  */
8095
- function normalizeDebugBindingName(name) {
8096
- // Attribute names with `$` (eg `x-y$`) are valid per spec, but unsupported by some browsers
8097
- name = camelCaseToDashCase(name.replace(/[$@]/g, '_'));
8098
- return `ng-reflect-${name}`;
8099
- }
8100
- const CAMEL_CASE_REGEXP = /([A-Z])/g;
8101
- function camelCaseToDashCase(input) {
8102
- return input.replace(CAMEL_CASE_REGEXP, (...m) => '-' + m[1].toLowerCase());
8103
- }
8104
- function normalizeDebugBindingValue(value) {
8105
- try {
8106
- // Limit the size of the value as otherwise the DOM just gets polluted.
8107
- return value != null ? value.toString().slice(0, 30) : value;
8108
- }
8109
- catch (e) {
8110
- return '[ERROR] Exception while trying to serialize the value';
8111
- }
8112
- }
8113
-
8091
+ let shouldThrowErrorOnUnknownElement = false;
8114
8092
  /**
8115
- * @license
8116
- * Copyright Google LLC All Rights Reserved.
8117
- *
8118
- * Use of this source code is governed by an MIT-style license that can be
8119
- * found in the LICENSE file at https://angular.io/license
8093
+ * Sets a strict mode for JIT-compiled components to throw an error on unknown elements,
8094
+ * instead of just logging the error.
8095
+ * (for AOT-compiled ones this check happens at build time).
8120
8096
  */
8121
- // Keeps track of the currently-active LViews.
8122
- const TRACKED_LVIEWS = new Map();
8123
- // Used for generating unique IDs for LViews.
8124
- let uniqueIdCounter = 0;
8125
- /** Gets a unique ID that can be assigned to an LView. */
8126
- function getUniqueLViewId() {
8127
- return uniqueIdCounter++;
8128
- }
8129
- /** Starts tracking an LView. */
8130
- function registerLView(lView) {
8131
- ngDevMode && assertNumber(lView[ID], 'LView must have an ID in order to be registered');
8132
- TRACKED_LVIEWS.set(lView[ID], lView);
8133
- }
8134
- /** Gets an LView by its unique ID. */
8135
- function getLViewById(id) {
8136
- ngDevMode && assertNumber(id, 'ID used for LView lookup must be a number');
8137
- return TRACKED_LVIEWS.get(id) || null;
8138
- }
8139
- /** Stops tracking an LView. */
8140
- function unregisterLView(lView) {
8141
- ngDevMode && assertNumber(lView[ID], 'Cannot stop tracking an LView that does not have an ID');
8142
- TRACKED_LVIEWS.delete(lView[ID]);
8097
+ function ɵsetUnknownElementStrictMode(shouldThrow) {
8098
+ shouldThrowErrorOnUnknownElement = shouldThrow;
8143
8099
  }
8144
-
8145
8100
  /**
8146
- * @license
8147
- * Copyright Google LLC All Rights Reserved.
8148
- *
8149
- * Use of this source code is governed by an MIT-style license that can be
8150
- * found in the LICENSE file at https://angular.io/license
8101
+ * Gets the current value of the strict mode.
8151
8102
  */
8103
+ function ɵgetUnknownElementStrictMode() {
8104
+ return shouldThrowErrorOnUnknownElement;
8105
+ }
8106
+ let shouldThrowErrorOnUnknownProperty = false;
8152
8107
  /**
8153
- * The internal view context which is specific to a given DOM element, directive or
8154
- * component instance. Each value in here (besides the LView and element node details)
8155
- * can be present, null or undefined. If undefined then it implies the value has not been
8156
- * looked up yet, otherwise, if null, then a lookup was executed and nothing was found.
8157
- *
8158
- * Each value will get filled when the respective value is examined within the getContext
8159
- * function. The component, element and each directive instance will share the same instance
8160
- * of the context.
8108
+ * Sets a strict mode for JIT-compiled components to throw an error on unknown properties,
8109
+ * instead of just logging the error.
8110
+ * (for AOT-compiled ones this check happens at build time).
8161
8111
  */
8162
- class LContext {
8163
- constructor(
8164
- /**
8165
- * ID of the component's parent view data.
8166
- */
8167
- lViewId,
8168
- /**
8169
- * The index instance of the node.
8170
- */
8171
- nodeIndex,
8172
- /**
8173
- * The instance of the DOM node that is attached to the lNode.
8174
- */
8175
- native) {
8176
- this.lViewId = lViewId;
8177
- this.nodeIndex = nodeIndex;
8178
- this.native = native;
8179
- }
8180
- /** Component's parent view data. */
8181
- get lView() {
8182
- return getLViewById(this.lViewId);
8183
- }
8112
+ function ɵsetUnknownPropertyStrictMode(shouldThrow) {
8113
+ shouldThrowErrorOnUnknownProperty = shouldThrow;
8184
8114
  }
8185
-
8186
8115
  /**
8187
- * @license
8188
- * Copyright Google LLC All Rights Reserved.
8189
- *
8190
- * Use of this source code is governed by an MIT-style license that can be
8191
- * found in the LICENSE file at https://angular.io/license
8116
+ * Gets the current value of the strict mode.
8192
8117
  */
8118
+ function ɵgetUnknownPropertyStrictMode() {
8119
+ return shouldThrowErrorOnUnknownProperty;
8120
+ }
8193
8121
  /**
8194
- * Returns the matching `LContext` data for a given DOM node, directive or component instance.
8195
- *
8196
- * This function will examine the provided DOM element, component, or directive instance\'s
8197
- * monkey-patched property to derive the `LContext` data. Once called then the monkey-patched
8198
- * value will be that of the newly created `LContext`.
8122
+ * Validates that the element is known at runtime and produces
8123
+ * an error if it's not the case.
8124
+ * This check is relevant for JIT-compiled components (for AOT-compiled
8125
+ * ones this check happens at build time).
8199
8126
  *
8200
- * If the monkey-patched value is the `LView` instance then the context value for that
8201
- * target will be created and the monkey-patch reference will be updated. Therefore when this
8202
- * function is called it may mutate the provided element\'s, component\'s or any of the associated
8203
- * directive\'s monkey-patch values.
8204
- *
8205
- * If the monkey-patch value is not detected then the code will walk up the DOM until an element
8206
- * is found which contains a monkey-patch reference. When that occurs then the provided element
8207
- * will be updated with a new context (which is then returned). If the monkey-patch value is not
8208
- * detected for a component/directive instance then it will throw an error (all components and
8209
- * directives should be automatically monkey-patched by ivy).
8127
+ * The element is considered known if either:
8128
+ * - it's a known HTML element
8129
+ * - it's a known custom element
8130
+ * - the element matches any directive
8131
+ * - the element is allowed by one of the schemas
8210
8132
  *
8211
- * @param target Component, Directive or DOM Node.
8133
+ * @param element Element to validate
8134
+ * @param lView An `LView` that represents a current component that is being rendered
8135
+ * @param tagName Name of the tag to check
8136
+ * @param schemas Array of schemas
8137
+ * @param hasDirectives Boolean indicating that the element matches any directive
8212
8138
  */
8213
- function getLContext(target) {
8214
- let mpValue = readPatchedData(target);
8215
- if (mpValue) {
8216
- // only when it's an array is it considered an LView instance
8217
- // ... otherwise it's an already constructed LContext instance
8218
- if (isLView(mpValue)) {
8219
- const lView = mpValue;
8220
- let nodeIndex;
8221
- let component = undefined;
8222
- let directives = undefined;
8223
- if (isComponentInstance(target)) {
8224
- nodeIndex = findViaComponent(lView, target);
8225
- if (nodeIndex == -1) {
8226
- throw new Error('The provided component was not found in the application');
8227
- }
8228
- component = target;
8229
- }
8230
- else if (isDirectiveInstance(target)) {
8231
- nodeIndex = findViaDirective(lView, target);
8232
- if (nodeIndex == -1) {
8233
- throw new Error('The provided directive was not found in the application');
8234
- }
8235
- directives = getDirectivesAtNodeIndex(nodeIndex, lView, false);
8139
+ function validateElementIsKnown(element, lView, tagName, schemas, hasDirectives) {
8140
+ // If `schemas` is set to `null`, that's an indication that this Component was compiled in AOT
8141
+ // mode where this check happens at compile time. In JIT mode, `schemas` is always present and
8142
+ // defined as an array (as an empty array in case `schemas` field is not defined) and we should
8143
+ // execute the check below.
8144
+ if (schemas === null)
8145
+ return;
8146
+ // If the element matches any directive, it's considered as valid.
8147
+ if (!hasDirectives && tagName !== null) {
8148
+ // The element is unknown if it's an instance of HTMLUnknownElement, or it isn't registered
8149
+ // as a custom element. Note that unknown elements with a dash in their name won't be instances
8150
+ // of HTMLUnknownElement in browsers that support web components.
8151
+ const isUnknown =
8152
+ // Note that we can't check for `typeof HTMLUnknownElement === 'function'`,
8153
+ // because while most browsers return 'function', IE returns 'object'.
8154
+ (typeof HTMLUnknownElement !== 'undefined' && HTMLUnknownElement &&
8155
+ element instanceof HTMLUnknownElement) ||
8156
+ (typeof customElements !== 'undefined' && tagName.indexOf('-') > -1 &&
8157
+ !customElements.get(tagName));
8158
+ if (isUnknown && !matchingSchemas(schemas, tagName)) {
8159
+ const isHostStandalone = isHostComponentStandalone(lView);
8160
+ const templateLocation = getTemplateLocationDetails(lView);
8161
+ const schemas = `'${isHostStandalone ? '@Component' : '@NgModule'}.schemas'`;
8162
+ let message = `'${tagName}' is not a known element${templateLocation}:\n`;
8163
+ message += `1. If '${tagName}' is an Angular component, then verify that it is ${isHostStandalone ? 'included in the \'@Component.imports\' of this component' :
8164
+ 'a part of an @NgModule where this component is declared'}.\n`;
8165
+ if (tagName && tagName.indexOf('-') > -1) {
8166
+ message +=
8167
+ `2. If '${tagName}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the ${schemas} of this component to suppress this message.`;
8236
8168
  }
8237
8169
  else {
8238
- nodeIndex = findViaNativeElement(lView, target);
8239
- if (nodeIndex == -1) {
8240
- return null;
8241
- }
8242
- }
8243
- // the goal is not to fill the entire context full of data because the lookups
8244
- // are expensive. Instead, only the target data (the element, component, container, ICU
8245
- // expression or directive details) are filled into the context. If called multiple times
8246
- // with different target values then the missing target data will be filled in.
8247
- const native = unwrapRNode(lView[nodeIndex]);
8248
- const existingCtx = readPatchedData(native);
8249
- const context = (existingCtx && !Array.isArray(existingCtx)) ?
8250
- existingCtx :
8251
- createLContext(lView, nodeIndex, native);
8252
- // only when the component has been discovered then update the monkey-patch
8253
- if (component && context.component === undefined) {
8254
- context.component = component;
8255
- attachPatchData(context.component, context);
8170
+ message +=
8171
+ `2. To allow any element add 'NO_ERRORS_SCHEMA' to the ${schemas} of this component.`;
8256
8172
  }
8257
- // only when the directives have been discovered then update the monkey-patch
8258
- if (directives && context.directives === undefined) {
8259
- context.directives = directives;
8260
- for (let i = 0; i < directives.length; i++) {
8261
- attachPatchData(directives[i], context);
8262
- }
8173
+ if (shouldThrowErrorOnUnknownElement) {
8174
+ throw new RuntimeError(304 /* RuntimeErrorCode.UNKNOWN_ELEMENT */, message);
8263
8175
  }
8264
- attachPatchData(context.native, context);
8265
- mpValue = context;
8266
- }
8267
- }
8268
- else {
8269
- const rElement = target;
8270
- ngDevMode && assertDomNode(rElement);
8271
- // if the context is not found then we need to traverse upwards up the DOM
8272
- // to find the nearest element that has already been monkey patched with data
8273
- let parent = rElement;
8274
- while (parent = parent.parentNode) {
8275
- const parentContext = readPatchedData(parent);
8276
- if (parentContext) {
8277
- const lView = Array.isArray(parentContext) ? parentContext : parentContext.lView;
8278
- // the edge of the app was also reached here through another means
8279
- // (maybe because the DOM was changed manually).
8280
- if (!lView) {
8281
- return null;
8282
- }
8283
- const index = findViaNativeElement(lView, rElement);
8284
- if (index >= 0) {
8285
- const native = unwrapRNode(lView[index]);
8286
- const context = createLContext(lView, index, native);
8287
- attachPatchData(native, context);
8288
- mpValue = context;
8289
- break;
8290
- }
8176
+ else {
8177
+ console.error(formatRuntimeError(304 /* RuntimeErrorCode.UNKNOWN_ELEMENT */, message));
8291
8178
  }
8292
8179
  }
8293
8180
  }
8294
- return mpValue || null;
8295
8181
  }
8296
8182
  /**
8297
- * Creates an empty instance of a `LContext` context
8183
+ * Validates that the property of the element is known at runtime and returns
8184
+ * false if it's not the case.
8185
+ * This check is relevant for JIT-compiled components (for AOT-compiled
8186
+ * ones this check happens at build time).
8187
+ *
8188
+ * The property is considered known if either:
8189
+ * - it's a known property of the element
8190
+ * - the element is allowed by one of the schemas
8191
+ * - the property is used for animations
8192
+ *
8193
+ * @param element Element to validate
8194
+ * @param propName Name of the property to check
8195
+ * @param tagName Name of the tag hosting the property
8196
+ * @param schemas Array of schemas
8298
8197
  */
8299
- function createLContext(lView, nodeIndex, native) {
8300
- return new LContext(lView[ID], nodeIndex, native);
8198
+ function isPropertyValid(element, propName, tagName, schemas) {
8199
+ // If `schemas` is set to `null`, that's an indication that this Component was compiled in AOT
8200
+ // mode where this check happens at compile time. In JIT mode, `schemas` is always present and
8201
+ // defined as an array (as an empty array in case `schemas` field is not defined) and we should
8202
+ // execute the check below.
8203
+ if (schemas === null)
8204
+ return true;
8205
+ // The property is considered valid if the element matches the schema, it exists on the element,
8206
+ // or it is synthetic, and we are in a browser context (web worker nodes should be skipped).
8207
+ if (matchingSchemas(schemas, tagName) || propName in element || isAnimationProp(propName)) {
8208
+ return true;
8209
+ }
8210
+ // Note: `typeof Node` returns 'function' in most browsers, but on IE it is 'object' so we
8211
+ // need to account for both here, while being careful with `typeof null` also returning 'object'.
8212
+ return typeof Node === 'undefined' || Node === null || !(element instanceof Node);
8301
8213
  }
8302
8214
  /**
8303
- * Takes a component instance and returns the view for that component.
8215
+ * Logs or throws an error that a property is not supported on an element.
8304
8216
  *
8305
- * @param componentInstance
8306
- * @returns The component's view
8217
+ * @param propName Name of the invalid property
8218
+ * @param tagName Name of the tag hosting the property
8219
+ * @param nodeType Type of the node hosting the property
8220
+ * @param lView An `LView` that represents a current component
8307
8221
  */
8308
- function getComponentViewByInstance(componentInstance) {
8309
- let patchedData = readPatchedData(componentInstance);
8310
- let lView;
8311
- if (isLView(patchedData)) {
8312
- const contextLView = patchedData;
8313
- const nodeIndex = findViaComponent(contextLView, componentInstance);
8314
- lView = getComponentLViewByIndex(nodeIndex, contextLView);
8315
- const context = createLContext(contextLView, nodeIndex, lView[HOST]);
8316
- context.component = componentInstance;
8317
- attachPatchData(componentInstance, context);
8318
- attachPatchData(context.native, context);
8222
+ function handleUnknownPropertyError(propName, tagName, nodeType, lView) {
8223
+ // Special-case a situation when a structural directive is applied to
8224
+ // an `<ng-template>` element, for example: `<ng-template *ngIf="true">`.
8225
+ // In this case the compiler generates the `ɵɵtemplate` instruction with
8226
+ // the `null` as the tagName. The directive matching logic at runtime relies
8227
+ // on this effect (see `isInlineTemplate`), thus using the 'ng-template' as
8228
+ // a default value of the `tNode.value` is not feasible at this moment.
8229
+ if (!tagName && nodeType === 4 /* TNodeType.Container */) {
8230
+ tagName = 'ng-template';
8231
+ }
8232
+ const isHostStandalone = isHostComponentStandalone(lView);
8233
+ const templateLocation = getTemplateLocationDetails(lView);
8234
+ let message = `Can't bind to '${propName}' since it isn't a known property of '${tagName}'${templateLocation}.`;
8235
+ const schemas = `'${isHostStandalone ? '@Component' : '@NgModule'}.schemas'`;
8236
+ const importLocation = isHostStandalone ?
8237
+ 'included in the \'@Component.imports\' of this component' :
8238
+ 'a part of an @NgModule where this component is declared';
8239
+ if (KNOWN_CONTROL_FLOW_DIRECTIVES.has(propName)) {
8240
+ // Most likely this is a control flow directive (such as `*ngIf`) used in
8241
+ // a template, but the directive or the `CommonModule` is not imported.
8242
+ const correspondingImport = KNOWN_CONTROL_FLOW_DIRECTIVES.get(propName);
8243
+ message += `\nIf the '${propName}' is an Angular control flow directive, ` +
8244
+ `please make sure that either the '${correspondingImport}' directive or the 'CommonModule' is ${importLocation}.`;
8319
8245
  }
8320
8246
  else {
8321
- const context = patchedData;
8322
- const contextLView = context.lView;
8323
- ngDevMode && assertLView(contextLView);
8324
- lView = getComponentLViewByIndex(context.nodeIndex, contextLView);
8247
+ // May be an Angular component, which is not imported/declared?
8248
+ message += `\n1. If '${tagName}' is an Angular component and it has the ` +
8249
+ `'${propName}' input, then verify that it is ${importLocation}.`;
8250
+ // May be a Web Component?
8251
+ if (tagName && tagName.indexOf('-') > -1) {
8252
+ message += `\n2. If '${tagName}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' ` +
8253
+ `to the ${schemas} of this component to suppress this message.`;
8254
+ message += `\n3. To allow any property add 'NO_ERRORS_SCHEMA' to ` +
8255
+ `the ${schemas} of this component.`;
8256
+ }
8257
+ else {
8258
+ // If it's expected, the error can be suppressed by the `NO_ERRORS_SCHEMA` schema.
8259
+ message += `\n2. To allow any property add 'NO_ERRORS_SCHEMA' to ` +
8260
+ `the ${schemas} of this component.`;
8261
+ }
8262
+ }
8263
+ reportUnknownPropertyError(message);
8264
+ }
8265
+ function reportUnknownPropertyError(message) {
8266
+ if (shouldThrowErrorOnUnknownProperty) {
8267
+ throw new RuntimeError(303 /* RuntimeErrorCode.UNKNOWN_BINDING */, message);
8268
+ }
8269
+ else {
8270
+ console.error(formatRuntimeError(303 /* RuntimeErrorCode.UNKNOWN_BINDING */, message));
8325
8271
  }
8326
- return lView;
8327
8272
  }
8328
8273
  /**
8329
- * This property will be monkey-patched on elements, components and directives.
8274
+ * WARNING: this is a **dev-mode only** function (thus should always be guarded by the `ngDevMode`)
8275
+ * and must **not** be used in production bundles. The function makes megamorphic reads, which might
8276
+ * be too slow for production mode and also it relies on the constructor function being available.
8277
+ *
8278
+ * Gets a reference to the host component def (where a current component is declared).
8279
+ *
8280
+ * @param lView An `LView` that represents a current component that is being rendered.
8330
8281
  */
8331
- const MONKEY_PATCH_KEY_NAME = '__ngContext__';
8282
+ function getDeclarationComponentDef(lView) {
8283
+ !ngDevMode && throwError('Must never be called in production mode');
8284
+ const declarationLView = lView[DECLARATION_COMPONENT_VIEW];
8285
+ const context = declarationLView[CONTEXT];
8286
+ // Unable to obtain a context.
8287
+ if (!context)
8288
+ return null;
8289
+ return context.constructor ? getComponentDef$1(context.constructor) : null;
8290
+ }
8332
8291
  /**
8333
- * Assigns the given data to the given target (which could be a component,
8334
- * directive or DOM node instance) using monkey-patching.
8292
+ * WARNING: this is a **dev-mode only** function (thus should always be guarded by the `ngDevMode`)
8293
+ * and must **not** be used in production bundles. The function makes megamorphic reads, which might
8294
+ * be too slow for production mode.
8295
+ *
8296
+ * Checks if the current component is declared inside of a standalone component template.
8297
+ *
8298
+ * @param lView An `LView` that represents a current component that is being rendered.
8335
8299
  */
8336
- function attachPatchData(target, data) {
8337
- ngDevMode && assertDefined(target, 'Target expected');
8338
- // Only attach the ID of the view in order to avoid memory leaks (see #41047). We only do this
8339
- // for `LView`, because we have control over when an `LView` is created and destroyed, whereas
8340
- // we can't know when to remove an `LContext`.
8341
- if (isLView(data)) {
8342
- target[MONKEY_PATCH_KEY_NAME] = data[ID];
8343
- registerLView(data);
8344
- }
8345
- else {
8346
- target[MONKEY_PATCH_KEY_NAME] = data;
8347
- }
8300
+ function isHostComponentStandalone(lView) {
8301
+ !ngDevMode && throwError('Must never be called in production mode');
8302
+ const componentDef = getDeclarationComponentDef(lView);
8303
+ // Treat host component as non-standalone if we can't obtain the def.
8304
+ return !!(componentDef === null || componentDef === void 0 ? void 0 : componentDef.standalone);
8348
8305
  }
8349
8306
  /**
8350
- * Returns the monkey-patch value data present on the target (which could be
8351
- * a component, directive or a DOM node).
8307
+ * WARNING: this is a **dev-mode only** function (thus should always be guarded by the `ngDevMode`)
8308
+ * and must **not** be used in production bundles. The function makes megamorphic reads, which might
8309
+ * be too slow for production mode.
8310
+ *
8311
+ * Constructs a string describing the location of the host component template. The function is used
8312
+ * in dev mode to produce error messages.
8313
+ *
8314
+ * @param lView An `LView` that represents a current component that is being rendered.
8352
8315
  */
8353
- function readPatchedData(target) {
8354
- ngDevMode && assertDefined(target, 'Target expected');
8355
- const data = target[MONKEY_PATCH_KEY_NAME];
8356
- return (typeof data === 'number') ? getLViewById(data) : data || null;
8316
+ function getTemplateLocationDetails(lView) {
8317
+ var _a;
8318
+ !ngDevMode && throwError('Must never be called in production mode');
8319
+ const hostComponentDef = getDeclarationComponentDef(lView);
8320
+ const componentClassName = (_a = hostComponentDef === null || hostComponentDef === void 0 ? void 0 : hostComponentDef.type) === null || _a === void 0 ? void 0 : _a.name;
8321
+ return componentClassName ? ` (used in the '${componentClassName}' component template)` : '';
8357
8322
  }
8358
- function readPatchedLView(target) {
8359
- const value = readPatchedData(target);
8360
- if (value) {
8361
- return (isLView(value) ? value : value.lView);
8323
+ /**
8324
+ * The set of known control flow directives and their corresponding imports.
8325
+ * We use this set to produce a more precises error message with a note
8326
+ * that the `CommonModule` should also be included.
8327
+ */
8328
+ const KNOWN_CONTROL_FLOW_DIRECTIVES = new Map([
8329
+ ['ngIf', 'NgIf'], ['ngFor', 'NgFor'], ['ngSwitchCase', 'NgSwitchCase'],
8330
+ ['ngSwitchDefault', 'NgSwitchDefault']
8331
+ ]);
8332
+ /**
8333
+ * Returns true if the tag name is allowed by specified schemas.
8334
+ * @param schemas Array of schemas
8335
+ * @param tagName Name of the tag
8336
+ */
8337
+ function matchingSchemas(schemas, tagName) {
8338
+ if (schemas !== null) {
8339
+ for (let i = 0; i < schemas.length; i++) {
8340
+ const schema = schemas[i];
8341
+ if (schema === NO_ERRORS_SCHEMA ||
8342
+ schema === CUSTOM_ELEMENTS_SCHEMA && tagName && tagName.indexOf('-') > -1) {
8343
+ return true;
8344
+ }
8345
+ }
8362
8346
  }
8363
- return null;
8347
+ return false;
8364
8348
  }
8365
- function isComponentInstance(instance) {
8366
- return instance && instance.constructor && instance.constructor.ɵcmp;
8349
+
8350
+ /**
8351
+ * @license
8352
+ * Copyright Google LLC All Rights Reserved.
8353
+ *
8354
+ * Use of this source code is governed by an MIT-style license that can be
8355
+ * found in the LICENSE file at https://angular.io/license
8356
+ */
8357
+ const ERROR_ORIGINAL_ERROR = 'ngOriginalError';
8358
+ function wrappedError(message, originalError) {
8359
+ const msg = `${message} caused by: ${originalError instanceof Error ? originalError.message : originalError}`;
8360
+ const error = Error(msg);
8361
+ error[ERROR_ORIGINAL_ERROR] = originalError;
8362
+ return error;
8367
8363
  }
8368
- function isDirectiveInstance(instance) {
8369
- return instance && instance.constructor && instance.constructor.ɵdir;
8364
+ function getOriginalError(error) {
8365
+ return error[ERROR_ORIGINAL_ERROR];
8370
8366
  }
8367
+
8371
8368
  /**
8372
- * Locates the element within the given LView and returns the matching index
8369
+ * @license
8370
+ * Copyright Google LLC All Rights Reserved.
8371
+ *
8372
+ * Use of this source code is governed by an MIT-style license that can be
8373
+ * found in the LICENSE file at https://angular.io/license
8373
8374
  */
8374
- function findViaNativeElement(lView, target) {
8375
- const tView = lView[TVIEW];
8376
- for (let i = HEADER_OFFSET; i < tView.bindingStartIndex; i++) {
8377
- if (unwrapRNode(lView[i]) === target) {
8378
- return i;
8375
+ /**
8376
+ * Provides a hook for centralized exception handling.
8377
+ *
8378
+ * The default implementation of `ErrorHandler` prints error messages to the `console`. To
8379
+ * intercept error handling, write a custom exception handler that replaces this default as
8380
+ * appropriate for your app.
8381
+ *
8382
+ * @usageNotes
8383
+ * ### Example
8384
+ *
8385
+ * ```
8386
+ * class MyErrorHandler implements ErrorHandler {
8387
+ * handleError(error) {
8388
+ * // do something with the exception
8389
+ * }
8390
+ * }
8391
+ *
8392
+ * @NgModule({
8393
+ * providers: [{provide: ErrorHandler, useClass: MyErrorHandler}]
8394
+ * })
8395
+ * class MyModule {}
8396
+ * ```
8397
+ *
8398
+ * @publicApi
8399
+ */
8400
+ class ErrorHandler {
8401
+ constructor() {
8402
+ /**
8403
+ * @internal
8404
+ */
8405
+ this._console = console;
8406
+ }
8407
+ handleError(error) {
8408
+ const originalError = this._findOriginalError(error);
8409
+ this._console.error('ERROR', error);
8410
+ if (originalError) {
8411
+ this._console.error('ORIGINAL ERROR', originalError);
8412
+ }
8413
+ }
8414
+ /** @internal */
8415
+ _findOriginalError(error) {
8416
+ let e = error && getOriginalError(error);
8417
+ while (e && getOriginalError(e)) {
8418
+ e = getOriginalError(e);
8379
8419
  }
8420
+ return e || null;
8380
8421
  }
8381
- return -1;
8382
8422
  }
8423
+
8383
8424
  /**
8384
- * Locates the next tNode (child, sibling or parent).
8425
+ * @license
8426
+ * Copyright Google LLC All Rights Reserved.
8427
+ *
8428
+ * Use of this source code is governed by an MIT-style license that can be
8429
+ * found in the LICENSE file at https://angular.io/license
8385
8430
  */
8386
- function traverseNextElement(tNode) {
8387
- if (tNode.child) {
8388
- return tNode.child;
8389
- }
8390
- else if (tNode.next) {
8391
- return tNode.next;
8392
- }
8393
- else {
8394
- // Let's take the following template: <div><span>text</span></div><component/>
8395
- // After checking the text node, we need to find the next parent that has a "next" TNode,
8396
- // in this case the parent `div`, so that we can find the component.
8397
- while (tNode.parent && !tNode.parent.next) {
8398
- tNode = tNode.parent;
8399
- }
8400
- return tNode.parent && tNode.parent.next;
8401
- }
8402
- }
8403
8431
  /**
8404
- * Locates the component within the given LView and returns the matching index
8432
+ * Disallowed strings in the comment.
8433
+ *
8434
+ * see: https://html.spec.whatwg.org/multipage/syntax.html#comments
8405
8435
  */
8406
- function findViaComponent(lView, componentInstance) {
8407
- const componentIndices = lView[TVIEW].components;
8408
- if (componentIndices) {
8409
- for (let i = 0; i < componentIndices.length; i++) {
8410
- const elementComponentIndex = componentIndices[i];
8411
- const componentView = getComponentLViewByIndex(elementComponentIndex, lView);
8412
- if (componentView[CONTEXT] === componentInstance) {
8413
- return elementComponentIndex;
8414
- }
8415
- }
8416
- }
8417
- else {
8418
- const rootComponentView = getComponentLViewByIndex(HEADER_OFFSET, lView);
8419
- const rootComponent = rootComponentView[CONTEXT];
8420
- if (rootComponent === componentInstance) {
8421
- // we are dealing with the root element here therefore we know that the
8422
- // element is the very first element after the HEADER data in the lView
8423
- return HEADER_OFFSET;
8424
- }
8425
- }
8426
- return -1;
8427
- }
8436
+ const COMMENT_DISALLOWED = /^>|^->|<!--|-->|--!>|<!-$/g;
8428
8437
  /**
8429
- * Locates the directive within the given LView and returns the matching index
8438
+ * Delimiter in the disallowed strings which needs to be wrapped with zero with character.
8430
8439
  */
8431
- function findViaDirective(lView, directiveInstance) {
8432
- // if a directive is monkey patched then it will (by default)
8433
- // have a reference to the LView of the current view. The
8434
- // element bound to the directive being search lives somewhere
8435
- // in the view data. We loop through the nodes and check their
8436
- // list of directives for the instance.
8437
- let tNode = lView[TVIEW].firstChild;
8438
- while (tNode) {
8439
- const directiveIndexStart = tNode.directiveStart;
8440
- const directiveIndexEnd = tNode.directiveEnd;
8441
- for (let i = directiveIndexStart; i < directiveIndexEnd; i++) {
8442
- if (lView[i] === directiveInstance) {
8443
- return tNode.index;
8444
- }
8445
- }
8446
- tNode = traverseNextElement(tNode);
8447
- }
8448
- return -1;
8449
- }
8440
+ const COMMENT_DELIMITER = /(<|>)/;
8441
+ const COMMENT_DELIMITER_ESCAPED = '\u200B$1\u200B';
8450
8442
  /**
8451
- * Returns a list of directives extracted from the given view based on the
8452
- * provided list of directive index values.
8443
+ * Escape the content of comment strings so that it can be safely inserted into a comment node.
8453
8444
  *
8454
- * @param nodeIndex The node index
8455
- * @param lView The target view data
8456
- * @param includeComponents Whether or not to include components in returned directives
8445
+ * The issue is that HTML does not specify any way to escape comment end text inside the comment.
8446
+ * Consider: `<!-- The way you close a comment is with ">", and "->" at the beginning or by "-->" or
8447
+ * "--!>" at the end. -->`. Above the `"-->"` is meant to be text not an end to the comment. This
8448
+ * can be created programmatically through DOM APIs. (`<!--` are also disallowed.)
8449
+ *
8450
+ * see: https://html.spec.whatwg.org/multipage/syntax.html#comments
8451
+ *
8452
+ * ```
8453
+ * div.innerHTML = div.innerHTML
8454
+ * ```
8455
+ *
8456
+ * One would expect that the above code would be safe to do, but it turns out that because comment
8457
+ * text is not escaped, the comment may contain text which will prematurely close the comment
8458
+ * opening up the application for XSS attack. (In SSR we programmatically create comment nodes which
8459
+ * may contain such text and expect them to be safe.)
8460
+ *
8461
+ * This function escapes the comment text by looking for comment delimiters (`<` and `>`) and
8462
+ * surrounding them with `_>_` where the `_` is a zero width space `\u200B`. The result is that if a
8463
+ * comment contains any of the comment start/end delimiters (such as `<!--`, `-->` or `--!>`) the
8464
+ * text it will render normally but it will not cause the HTML parser to close/open the comment.
8465
+ *
8466
+ * @param value text to make safe for comment node by escaping the comment open/close character
8467
+ * sequence.
8457
8468
  */
8458
- function getDirectivesAtNodeIndex(nodeIndex, lView, includeComponents) {
8459
- const tNode = lView[TVIEW].data[nodeIndex];
8460
- if (tNode.directiveStart === 0)
8461
- return EMPTY_ARRAY;
8462
- const results = [];
8463
- for (let i = tNode.directiveStart; i < tNode.directiveEnd; i++) {
8464
- const directiveInstance = lView[i];
8465
- if (!isComponentInstance(directiveInstance) || includeComponents) {
8466
- results.push(directiveInstance);
8467
- }
8468
- }
8469
- return results;
8470
- }
8471
- function getComponentAtNodeIndex(nodeIndex, lView) {
8472
- const tNode = lView[TVIEW].data[nodeIndex];
8473
- const { directiveStart, componentOffset } = tNode;
8474
- return componentOffset > -1 ? lView[directiveStart + componentOffset] : null;
8469
+ function escapeCommentText(value) {
8470
+ return value.replace(COMMENT_DISALLOWED, (text) => text.replace(COMMENT_DELIMITER, COMMENT_DELIMITER_ESCAPED));
8475
8471
  }
8472
+
8476
8473
  /**
8477
- * Returns a map of local references (local reference name => element or directive instance) that
8478
- * exist on a given element.
8474
+ * @license
8475
+ * Copyright Google LLC All Rights Reserved.
8476
+ *
8477
+ * Use of this source code is governed by an MIT-style license that can be
8478
+ * found in the LICENSE file at https://angular.io/license
8479
8479
  */
8480
- function discoverLocalRefs(lView, nodeIndex) {
8481
- const tNode = lView[TVIEW].data[nodeIndex];
8482
- if (tNode && tNode.localNames) {
8483
- const result = {};
8484
- let localIndex = tNode.index + 1;
8485
- for (let i = 0; i < tNode.localNames.length; i += 2) {
8486
- result[tNode.localNames[i]] = lView[localIndex];
8487
- localIndex++;
8488
- }
8489
- return result;
8480
+ function normalizeDebugBindingName(name) {
8481
+ // Attribute names with `$` (eg `x-y$`) are valid per spec, but unsupported by some browsers
8482
+ name = camelCaseToDashCase(name.replace(/[$@]/g, '_'));
8483
+ return `ng-reflect-${name}`;
8484
+ }
8485
+ const CAMEL_CASE_REGEXP = /([A-Z])/g;
8486
+ function camelCaseToDashCase(input) {
8487
+ return input.replace(CAMEL_CASE_REGEXP, (...m) => '-' + m[1].toLowerCase());
8488
+ }
8489
+ function normalizeDebugBindingValue(value) {
8490
+ try {
8491
+ // Limit the size of the value as otherwise the DOM just gets polluted.
8492
+ return value != null ? value.toString().slice(0, 30) : value;
8493
+ }
8494
+ catch (e) {
8495
+ return '[ERROR] Exception while trying to serialize the value';
8490
8496
  }
8491
- return null;
8492
8497
  }
8493
8498
 
8494
8499
  /**
@@ -9652,6 +9657,19 @@ function writeDirectClass(renderer, element, newValue) {
9652
9657
  }
9653
9658
  ngDevMode && ngDevMode.rendererSetClassName++;
9654
9659
  }
9660
+ /** Sets up the static DOM attributes on an `RNode`. */
9661
+ function setupStaticAttributes(renderer, element, tNode) {
9662
+ const { mergedAttrs, classes, styles } = tNode;
9663
+ if (mergedAttrs !== null) {
9664
+ setUpAttributes(renderer, element, mergedAttrs);
9665
+ }
9666
+ if (classes !== null) {
9667
+ writeDirectClass(renderer, element, classes);
9668
+ }
9669
+ if (styles !== null) {
9670
+ writeDirectStyle(renderer, element, styles);
9671
+ }
9672
+ }
9655
9673
 
9656
9674
  /**
9657
9675
  * @license
@@ -12873,28 +12891,6 @@ function setNgReflectProperties(lView, element, type, dataValue, value) {
12873
12891
  }
12874
12892
  }
12875
12893
  }
12876
- /**
12877
- * Instantiate a root component.
12878
- */
12879
- function instantiateRootComponent(tView, lView, def) {
12880
- const rootTNode = getCurrentTNode();
12881
- if (tView.firstCreatePass) {
12882
- if (def.providersResolver)
12883
- def.providersResolver(def);
12884
- const directiveIndex = allocExpando(tView, lView, 1, null);
12885
- ngDevMode &&
12886
- assertEqual(directiveIndex, rootTNode.directiveStart, 'Because this is a root component the allocated expando should match the TNode component.');
12887
- configureViewWithDirective(tView, rootTNode, lView, directiveIndex, def);
12888
- initializeInputAndOutputAliases(tView, rootTNode);
12889
- }
12890
- const directive = getNodeInjectable(lView, tView, rootTNode.directiveStart + rootTNode.componentOffset, rootTNode);
12891
- attachPatchData(directive, lView);
12892
- const native = getNativeByTNode(rootTNode, lView);
12893
- if (native) {
12894
- attachPatchData(native, lView);
12895
- }
12896
- return directive;
12897
- }
12898
12894
  /**
12899
12895
  * Resolve the matched directives on a node.
12900
12896
  */
@@ -12904,59 +12900,16 @@ function resolveDirectives(tView, lView, tNode, localRefs) {
12904
12900
  ngDevMode && assertFirstCreatePass(tView);
12905
12901
  let hasDirectives = false;
12906
12902
  if (getBindingsEnabled()) {
12907
- const directiveDefsMatchedBySelectors = findDirectiveDefMatches(tView, lView, tNode);
12908
- const directiveDefs = directiveDefsMatchedBySelectors ?
12909
- findHostDirectiveDefs$1(directiveDefsMatchedBySelectors, tView, lView, tNode) :
12910
- null;
12903
+ const directiveDefs = findDirectiveDefMatches(tView, lView, tNode);
12911
12904
  const exportsMap = localRefs === null ? null : { '': -1 };
12912
12905
  if (directiveDefs !== null) {
12913
- hasDirectives = true;
12914
- initTNodeFlags(tNode, tView.data.length, directiveDefs.length);
12915
- // When the same token is provided by several directives on the same node, some rules apply in
12916
- // the viewEngine:
12917
- // - viewProviders have priority over providers
12918
- // - the last directive in NgModule.declarations has priority over the previous one
12919
- // So to match these rules, the order in which providers are added in the arrays is very
12920
- // important.
12921
- for (let i = 0; i < directiveDefs.length; i++) {
12922
- const def = directiveDefs[i];
12923
- if (def.providersResolver)
12924
- def.providersResolver(def);
12925
- }
12926
- let preOrderHooksFound = false;
12927
- let preOrderCheckHooksFound = false;
12928
- let directiveIdx = allocExpando(tView, lView, directiveDefs.length, null);
12929
- ngDevMode &&
12930
- assertSame(directiveIdx, tNode.directiveStart, 'TNode.directiveStart should point to just allocated space');
12906
+ // Publishes the directive types to DI so they can be injected. Needs to
12907
+ // happen in a separate pass before the TNode flags have been initialized.
12931
12908
  for (let i = 0; i < directiveDefs.length; i++) {
12932
- const def = directiveDefs[i];
12933
- // Merge the attrs in the order of matches. This assumes that the first directive is the
12934
- // component itself, so that the component has the least priority.
12935
- tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, def.hostAttrs);
12936
- configureViewWithDirective(tView, tNode, lView, directiveIdx, def);
12937
- saveNameToExportMap(directiveIdx, def, exportsMap);
12938
- if (def.contentQueries !== null)
12939
- tNode.flags |= 4 /* TNodeFlags.hasContentQuery */;
12940
- if (def.hostBindings !== null || def.hostAttrs !== null || def.hostVars !== 0)
12941
- tNode.flags |= 64 /* TNodeFlags.hasHostBindings */;
12942
- const lifeCycleHooks = def.type.prototype;
12943
- // Only push a node index into the preOrderHooks array if this is the first
12944
- // pre-order hook found on this node.
12945
- if (!preOrderHooksFound &&
12946
- (lifeCycleHooks.ngOnChanges || lifeCycleHooks.ngOnInit || lifeCycleHooks.ngDoCheck)) {
12947
- // We will push the actual hook function into this array later during dir instantiation.
12948
- // We cannot do it now because we must ensure hooks are registered in the same
12949
- // order that directives are created (i.e. injection order).
12950
- (tView.preOrderHooks || (tView.preOrderHooks = [])).push(tNode.index);
12951
- preOrderHooksFound = true;
12952
- }
12953
- if (!preOrderCheckHooksFound && (lifeCycleHooks.ngOnChanges || lifeCycleHooks.ngDoCheck)) {
12954
- (tView.preOrderCheckHooks || (tView.preOrderCheckHooks = [])).push(tNode.index);
12955
- preOrderCheckHooksFound = true;
12956
- }
12957
- directiveIdx++;
12909
+ diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, lView), tView, directiveDefs[i].type);
12958
12910
  }
12959
- initializeInputAndOutputAliases(tView, tNode);
12911
+ hasDirectives = true;
12912
+ initializeDirectives(tView, lView, tNode, directiveDefs, exportsMap);
12960
12913
  }
12961
12914
  if (exportsMap)
12962
12915
  cacheMatchingLocalNames(tNode, localRefs, exportsMap);
@@ -12965,17 +12918,66 @@ function resolveDirectives(tView, lView, tNode, localRefs) {
12965
12918
  tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, tNode.attrs);
12966
12919
  return hasDirectives;
12967
12920
  }
12921
+ /** Initializes the data structures necessary for a list of directives to be instantiated. */
12922
+ function initializeDirectives(tView, lView, tNode, directives, exportsMap) {
12923
+ ngDevMode && assertFirstCreatePass(tView);
12924
+ initTNodeFlags(tNode, tView.data.length, directives.length);
12925
+ // When the same token is provided by several directives on the same node, some rules apply in
12926
+ // the viewEngine:
12927
+ // - viewProviders have priority over providers
12928
+ // - the last directive in NgModule.declarations has priority over the previous one
12929
+ // So to match these rules, the order in which providers are added in the arrays is very
12930
+ // important.
12931
+ for (let i = 0; i < directives.length; i++) {
12932
+ const def = directives[i];
12933
+ if (def.providersResolver)
12934
+ def.providersResolver(def);
12935
+ }
12936
+ let preOrderHooksFound = false;
12937
+ let preOrderCheckHooksFound = false;
12938
+ let directiveIdx = allocExpando(tView, lView, directives.length, null);
12939
+ ngDevMode &&
12940
+ assertSame(directiveIdx, tNode.directiveStart, 'TNode.directiveStart should point to just allocated space');
12941
+ for (let i = 0; i < directives.length; i++) {
12942
+ const def = directives[i];
12943
+ // Merge the attrs in the order of matches. This assumes that the first directive is the
12944
+ // component itself, so that the component has the least priority.
12945
+ tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, def.hostAttrs);
12946
+ configureViewWithDirective(tView, tNode, lView, directiveIdx, def);
12947
+ saveNameToExportMap(directiveIdx, def, exportsMap);
12948
+ if (def.contentQueries !== null)
12949
+ tNode.flags |= 4 /* TNodeFlags.hasContentQuery */;
12950
+ if (def.hostBindings !== null || def.hostAttrs !== null || def.hostVars !== 0)
12951
+ tNode.flags |= 64 /* TNodeFlags.hasHostBindings */;
12952
+ const lifeCycleHooks = def.type.prototype;
12953
+ // Only push a node index into the preOrderHooks array if this is the first
12954
+ // pre-order hook found on this node.
12955
+ if (!preOrderHooksFound &&
12956
+ (lifeCycleHooks.ngOnChanges || lifeCycleHooks.ngOnInit || lifeCycleHooks.ngDoCheck)) {
12957
+ // We will push the actual hook function into this array later during dir instantiation.
12958
+ // We cannot do it now because we must ensure hooks are registered in the same
12959
+ // order that directives are created (i.e. injection order).
12960
+ (tView.preOrderHooks || (tView.preOrderHooks = [])).push(tNode.index);
12961
+ preOrderHooksFound = true;
12962
+ }
12963
+ if (!preOrderCheckHooksFound && (lifeCycleHooks.ngOnChanges || lifeCycleHooks.ngDoCheck)) {
12964
+ (tView.preOrderCheckHooks || (tView.preOrderCheckHooks = [])).push(tNode.index);
12965
+ preOrderCheckHooksFound = true;
12966
+ }
12967
+ directiveIdx++;
12968
+ }
12969
+ initializeInputAndOutputAliases(tView, tNode);
12970
+ }
12968
12971
  /**
12969
12972
  * Add `hostBindings` to the `TView.hostBindingOpCodes`.
12970
12973
  *
12971
12974
  * @param tView `TView` to which the `hostBindings` should be added.
12972
12975
  * @param tNode `TNode` the element which contains the directive
12973
- * @param lView `LView` current `LView`
12974
12976
  * @param directiveIdx Directive index in view.
12975
12977
  * @param directiveVarsIdx Where will the directive's vars be stored
12976
12978
  * @param def `ComponentDef`/`DirectiveDef`, which contains the `hostVars`/`hostBindings` to add.
12977
12979
  */
12978
- function registerHostBindingOpCodes(tView, tNode, lView, directiveIdx, directiveVarsIdx, def) {
12980
+ function registerHostBindingOpCodes(tView, tNode, directiveIdx, directiveVarsIdx, def) {
12979
12981
  ngDevMode && assertFirstCreatePass(tView);
12980
12982
  const hostBindings = def.hostBindings;
12981
12983
  if (hostBindings) {
@@ -13076,7 +13078,8 @@ function invokeHostBindingsInCreationMode(def, directive) {
13076
13078
  * Matches the current node against all available selectors.
13077
13079
  * If a component is matched (at most one), it is returned in first position in the array.
13078
13080
  */
13079
- function findDirectiveDefMatches(tView, viewData, tNode) {
13081
+ function findDirectiveDefMatches(tView, lView, tNode) {
13082
+ var _a;
13080
13083
  ngDevMode && assertFirstCreatePass(tView);
13081
13084
  ngDevMode && assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */);
13082
13085
  const registry = tView.directiveRegistry;
@@ -13086,22 +13089,45 @@ function findDirectiveDefMatches(tView, viewData, tNode) {
13086
13089
  const def = registry[i];
13087
13090
  if (isNodeMatchingSelectorList(tNode, def.selectors, /* isProjectionMode */ false)) {
13088
13091
  matches || (matches = ngDevMode ? new MatchesArray() : []);
13089
- diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, viewData), tView, def.type);
13090
13092
  if (isComponentDef(def)) {
13091
13093
  if (ngDevMode) {
13092
13094
  assertTNodeType(tNode, 2 /* TNodeType.Element */, `"${tNode.value}" tags cannot be used as component hosts. ` +
13093
13095
  `Please use a different tag to activate the ${stringify(def.type)} component.`);
13094
13096
  if (isComponentHost(tNode)) {
13095
- // If another component has been matched previously, it's the first element in the
13096
- // `matches` array, see how we store components/directives in `matches` below.
13097
- throwMultipleComponentError(tNode, matches[0].type, def.type);
13097
+ throwMultipleComponentError(tNode, matches.find(isComponentDef).type, def.type);
13098
13098
  }
13099
13099
  }
13100
- markAsComponentHost(tView, tNode, 0);
13101
- // The component is always stored first with directives after.
13102
- matches.unshift(def);
13100
+ // Components are inserted at the front of the matches array so that their lifecycle
13101
+ // hooks run before any directive lifecycle hooks. This appears to be for ViewEngine
13102
+ // compatibility. This logic doesn't make sense with host directives, because it
13103
+ // would allow the host directives to undo any overrides the host may have made.
13104
+ // To handle this case, the host directives of components are inserted at the beginning
13105
+ // of the array, followed by the component. As such, the insertion order is as follows:
13106
+ // 1. Host directives belonging to the selector-matched component.
13107
+ // 2. Selector-matched component.
13108
+ // 3. Host directives belonging to selector-matched directives.
13109
+ // 4. Selector-matched directives.
13110
+ if (def.findHostDirectiveDefs !== null) {
13111
+ const hostDirectiveMatches = [];
13112
+ def.findHostDirectiveDefs(hostDirectiveMatches, def, tView, lView, tNode);
13113
+ // Add all host directives declared on this component, followed by the component itself.
13114
+ // Host directives should execute first so the host has a chance to override changes
13115
+ // to the DOM made by them.
13116
+ matches.unshift(...hostDirectiveMatches, def);
13117
+ // Component is offset starting from the beginning of the host directives array.
13118
+ const componentOffset = hostDirectiveMatches.length;
13119
+ markAsComponentHost(tView, tNode, componentOffset);
13120
+ }
13121
+ else {
13122
+ // No host directives on this component, just add the
13123
+ // component def to the beginning of the matches.
13124
+ matches.unshift(def);
13125
+ markAsComponentHost(tView, tNode, 0);
13126
+ }
13103
13127
  }
13104
13128
  else {
13129
+ // Append any host directives to the matches first.
13130
+ (_a = def.findHostDirectiveDefs) === null || _a === void 0 ? void 0 : _a.call(def, matches, def, tView, lView, tNode);
13105
13131
  matches.push(def);
13106
13132
  }
13107
13133
  }
@@ -13121,26 +13147,6 @@ function markAsComponentHost(tView, hostTNode, componentOffset) {
13121
13147
  (tView.components || (tView.components = ngDevMode ? new TViewComponents() : []))
13122
13148
  .push(hostTNode.index);
13123
13149
  }
13124
- /**
13125
- * Given an array of directives that were matched by their selectors, this function
13126
- * produces a new array that also includes any host directives that have to be applied.
13127
- * @param selectorMatches Directives matched in a template based on their selectors.
13128
- * @param tView Current TView.
13129
- * @param lView Current LView.
13130
- * @param tNode Current TNode that is being matched.
13131
- */
13132
- function findHostDirectiveDefs$1(selectorMatches, tView, lView, tNode) {
13133
- const matches = [];
13134
- for (const def of selectorMatches) {
13135
- if (def.findHostDirectiveDefs === null) {
13136
- matches.push(def);
13137
- }
13138
- else {
13139
- def.findHostDirectiveDefs(matches, def, tView, lView, tNode);
13140
- }
13141
- }
13142
- return matches;
13143
- }
13144
13150
  /** Caches local names and their matching directive indices for query and template lookups. */
13145
13151
  function cacheMatchingLocalNames(tNode, localRefs, exportsMap) {
13146
13152
  if (localRefs) {
@@ -13208,7 +13214,7 @@ function configureViewWithDirective(tView, tNode, lView, directiveIndex, def) {
13208
13214
  const nodeInjectorFactory = new NodeInjectorFactory(directiveFactory, isComponentDef(def), ɵɵdirectiveInject);
13209
13215
  tView.blueprint[directiveIndex] = nodeInjectorFactory;
13210
13216
  lView[directiveIndex] = nodeInjectorFactory;
13211
- registerHostBindingOpCodes(tView, tNode, lView, directiveIndex, allocExpando(tView, lView, def.hostVars, NO_CHANGE), def);
13217
+ registerHostBindingOpCodes(tView, tNode, directiveIndex, allocExpando(tView, lView, def.hostVars, NO_CHANGE), def);
13212
13218
  }
13213
13219
  function addComponentLogic(lView, hostTNode, def) {
13214
13220
  const native = getNativeByTNode(hostTNode, lView);
@@ -14131,6 +14137,7 @@ class ChainedInjector {
14131
14137
  this.parentInjector = parentInjector;
14132
14138
  }
14133
14139
  get(token, notFoundValue, flags) {
14140
+ flags = convertToBitFlags(flags);
14134
14141
  const value = this.injector.get(token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR, flags);
14135
14142
  if (value !== NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR ||
14136
14143
  notFoundValue === NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) {
@@ -14207,42 +14214,23 @@ class ComponentFactory extends ComponentFactory$1 {
14207
14214
  let component;
14208
14215
  let tElementNode;
14209
14216
  try {
14210
- const componentView = createRootComponentView(hostRNode, this.componentDef, rootLView, rendererFactory, hostRenderer);
14217
+ const rootDirectives = [this.componentDef];
14218
+ const hostTNode = createRootComponentTNode(rootLView, hostRNode);
14219
+ const componentView = createRootComponentView(hostTNode, hostRNode, this.componentDef, rootDirectives, rootLView, rendererFactory, hostRenderer);
14220
+ tElementNode = getTNode(rootTView, HEADER_OFFSET);
14221
+ // TODO(crisbeto): in practice `hostRNode` should always be defined, but there are some tests
14222
+ // where the renderer is mocked out and `undefined` is returned. We should update the tests so
14223
+ // that this check can be removed.
14211
14224
  if (hostRNode) {
14212
- if (rootSelectorOrNode) {
14213
- setUpAttributes(hostRenderer, hostRNode, ['ng-version', VERSION.full]);
14214
- }
14215
- else {
14216
- // If host element is created as a part of this function call (i.e. `rootSelectorOrNode`
14217
- // is not defined), also apply attributes and classes extracted from component selector.
14218
- // Extract attributes and classes from the first selector only to match VE behavior.
14219
- const { attrs, classes } = extractAttrsAndClassesFromSelector(this.componentDef.selectors[0]);
14220
- if (attrs) {
14221
- setUpAttributes(hostRenderer, hostRNode, attrs);
14222
- }
14223
- if (classes && classes.length > 0) {
14224
- writeDirectClass(hostRenderer, hostRNode, classes.join(' '));
14225
- }
14226
- }
14225
+ setRootNodeAttributes(hostRenderer, this.componentDef, hostRNode, rootSelectorOrNode);
14227
14226
  }
14228
- tElementNode = getTNode(rootTView, HEADER_OFFSET);
14229
14227
  if (projectableNodes !== undefined) {
14230
- const projection = tElementNode.projection = [];
14231
- for (let i = 0; i < this.ngContentSelectors.length; i++) {
14232
- const nodesforSlot = projectableNodes[i];
14233
- // Projectable nodes can be passed as array of arrays or an array of iterables (ngUpgrade
14234
- // case). Here we do normalize passed data structure to be an array of arrays to avoid
14235
- // complex checks down the line.
14236
- // We also normalize the length of the passed in projectable nodes (to match the number of
14237
- // <ng-container> slots defined by a component).
14238
- projection.push(nodesforSlot != null ? Array.from(nodesforSlot) : null);
14239
- }
14228
+ projectNodes(tElementNode, this.ngContentSelectors, projectableNodes);
14240
14229
  }
14241
14230
  // TODO: should LifecycleHooksFeature and other host features be generated by the compiler and
14242
14231
  // executed here?
14243
14232
  // Angular 5 reference: https://stackblitz.com/edit/lifecycle-hooks-vcref
14244
- component =
14245
- createRootComponent(componentView, this.componentDef, rootLView, [LifecycleHooksFeature]);
14233
+ component = createRootComponent(componentView, this.componentDef, rootDirectives, rootLView, [LifecycleHooksFeature]);
14246
14234
  renderView(rootTView, rootLView, null);
14247
14235
  }
14248
14236
  finally {
@@ -14313,11 +14301,22 @@ const NULL_INJECTOR = {
14313
14301
  throwProviderNotFoundError(token, 'NullInjector');
14314
14302
  }
14315
14303
  };
14304
+ /** Creates a TNode that can be used to instantiate a root component. */
14305
+ function createRootComponentTNode(lView, rNode) {
14306
+ const tView = lView[TVIEW];
14307
+ const index = HEADER_OFFSET;
14308
+ ngDevMode && assertIndexInRange(lView, index);
14309
+ lView[index] = rNode;
14310
+ // '#host' is added here as we don't know the real host DOM name (we don't want to read it) and at
14311
+ // the same time we want to communicate the debug `TNode` that this is a special `TNode`
14312
+ // representing a host element.
14313
+ return getOrCreateTNode(tView, index, 2 /* TNodeType.Element */, '#host', null);
14314
+ }
14316
14315
  /**
14317
14316
  * Creates the root component view and the root component node.
14318
14317
  *
14319
14318
  * @param rNode Render host element.
14320
- * @param def ComponentDef
14319
+ * @param rootComponentDef ComponentDef
14321
14320
  * @param rootView The parent view where the host node is stored
14322
14321
  * @param rendererFactory Factory to be used for creating child renderers.
14323
14322
  * @param hostRenderer The current renderer
@@ -14325,72 +14324,96 @@ const NULL_INJECTOR = {
14325
14324
  *
14326
14325
  * @returns Component view created
14327
14326
  */
14328
- function createRootComponentView(rNode, def, rootView, rendererFactory, hostRenderer, sanitizer) {
14327
+ function createRootComponentView(tNode, rNode, rootComponentDef, rootDirectives, rootView, rendererFactory, hostRenderer, sanitizer) {
14329
14328
  const tView = rootView[TVIEW];
14330
- const index = HEADER_OFFSET;
14331
- ngDevMode && assertIndexInRange(rootView, index);
14332
- rootView[index] = rNode;
14333
- // '#host' is added here as we don't know the real host DOM name (we don't want to read it) and at
14334
- // the same time we want to communicate the debug `TNode` that this is a special `TNode`
14335
- // representing a host element.
14336
- const tNode = getOrCreateTNode(tView, index, 2 /* TNodeType.Element */, '#host', null);
14337
- const mergedAttrs = tNode.mergedAttrs = def.hostAttrs;
14338
- if (mergedAttrs !== null) {
14339
- computeStaticStyling(tNode, mergedAttrs, true);
14340
- if (rNode !== null) {
14341
- setUpAttributes(hostRenderer, rNode, mergedAttrs);
14342
- if (tNode.classes !== null) {
14343
- writeDirectClass(hostRenderer, rNode, tNode.classes);
14344
- }
14345
- if (tNode.styles !== null) {
14346
- writeDirectStyle(hostRenderer, rNode, tNode.styles);
14347
- }
14348
- }
14349
- }
14350
- const viewRenderer = rendererFactory.createRenderer(rNode, def);
14351
- const componentView = createLView(rootView, getOrCreateComponentTView(def), null, def.onPush ? 32 /* LViewFlags.Dirty */ : 16 /* LViewFlags.CheckAlways */, rootView[index], tNode, rendererFactory, viewRenderer, sanitizer || null, null, null);
14329
+ applyRootComponentStyling(rootDirectives, tNode, rNode, hostRenderer);
14330
+ const viewRenderer = rendererFactory.createRenderer(rNode, rootComponentDef);
14331
+ 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);
14352
14332
  if (tView.firstCreatePass) {
14353
- diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, rootView), tView, def.type);
14333
+ diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, rootView), tView, rootComponentDef.type);
14354
14334
  markAsComponentHost(tView, tNode, 0);
14355
- initTNodeFlags(tNode, rootView.length, 1);
14356
14335
  }
14357
14336
  addToViewTree(rootView, componentView);
14358
14337
  // Store component view at node index, with node as the HOST
14359
- return rootView[index] = componentView;
14338
+ return rootView[tNode.index] = componentView;
14339
+ }
14340
+ /** Sets up the styling information on a root component. */
14341
+ function applyRootComponentStyling(rootDirectives, tNode, rNode, hostRenderer) {
14342
+ for (const def of rootDirectives) {
14343
+ tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, def.hostAttrs);
14344
+ }
14345
+ if (tNode.mergedAttrs !== null) {
14346
+ computeStaticStyling(tNode, tNode.mergedAttrs, true);
14347
+ if (rNode !== null) {
14348
+ setupStaticAttributes(hostRenderer, rNode, tNode);
14349
+ }
14350
+ }
14360
14351
  }
14361
14352
  /**
14362
14353
  * Creates a root component and sets it up with features and host bindings.Shared by
14363
14354
  * renderComponent() and ViewContainerRef.createComponent().
14364
14355
  */
14365
- function createRootComponent(componentView, componentDef, rootLView, hostFeatures) {
14356
+ function createRootComponent(componentView, rootComponentDef, rootDirectives, rootLView, hostFeatures) {
14357
+ const rootTNode = getCurrentTNode();
14358
+ ngDevMode && assertDefined(rootTNode, 'tNode should have been already created');
14366
14359
  const tView = rootLView[TVIEW];
14367
- // Create directive instance with factory() and store at next index in viewData
14368
- const component = instantiateRootComponent(tView, rootLView, componentDef);
14369
- // Root view only contains an instance of this component,
14370
- // so we use a reference to that component instance as a context.
14360
+ const native = getNativeByTNode(rootTNode, rootLView);
14361
+ initializeDirectives(tView, rootLView, rootTNode, rootDirectives, null);
14362
+ for (let i = 0; i < rootDirectives.length; i++) {
14363
+ const directiveIndex = rootTNode.directiveStart + i;
14364
+ const directiveInstance = getNodeInjectable(rootLView, tView, directiveIndex, rootTNode);
14365
+ attachPatchData(directiveInstance, rootLView);
14366
+ }
14367
+ invokeDirectivesHostBindings(tView, rootLView, rootTNode);
14368
+ if (native) {
14369
+ attachPatchData(native, rootLView);
14370
+ }
14371
+ // We're guaranteed for the `componentOffset` to be positive here
14372
+ // since a root component always matches a component def.
14373
+ ngDevMode &&
14374
+ assertGreaterThan(rootTNode.componentOffset, -1, 'componentOffset must be great than -1');
14375
+ const component = getNodeInjectable(rootLView, tView, rootTNode.directiveStart + rootTNode.componentOffset, rootTNode);
14371
14376
  componentView[CONTEXT] = rootLView[CONTEXT] = component;
14372
14377
  if (hostFeatures !== null) {
14373
14378
  for (const feature of hostFeatures) {
14374
- feature(component, componentDef);
14379
+ feature(component, rootComponentDef);
14375
14380
  }
14376
14381
  }
14377
14382
  // We want to generate an empty QueryList for root content queries for backwards
14378
14383
  // compatibility with ViewEngine.
14379
- if (componentDef.contentQueries) {
14380
- const tNode = getCurrentTNode();
14381
- ngDevMode && assertDefined(tNode, 'TNode expected');
14382
- componentDef.contentQueries(1 /* RenderFlags.Create */, component, tNode.directiveStart);
14384
+ executeContentQueries(tView, rootTNode, componentView);
14385
+ return component;
14386
+ }
14387
+ /** Sets the static attributes on a root component. */
14388
+ function setRootNodeAttributes(hostRenderer, componentDef, hostRNode, rootSelectorOrNode) {
14389
+ if (rootSelectorOrNode) {
14390
+ setUpAttributes(hostRenderer, hostRNode, ['ng-version', VERSION.full]);
14383
14391
  }
14384
- const rootTNode = getCurrentTNode();
14385
- ngDevMode && assertDefined(rootTNode, 'tNode should have been already created');
14386
- if (tView.firstCreatePass &&
14387
- (componentDef.hostBindings !== null || componentDef.hostAttrs !== null)) {
14388
- setSelectedIndex(rootTNode.index);
14389
- const rootTView = rootLView[TVIEW];
14390
- registerHostBindingOpCodes(rootTView, rootTNode, rootLView, rootTNode.directiveStart, rootTNode.directiveEnd, componentDef);
14391
- invokeHostBindingsInCreationMode(componentDef, component);
14392
+ else {
14393
+ // If host element is created as a part of this function call (i.e. `rootSelectorOrNode`
14394
+ // is not defined), also apply attributes and classes extracted from component selector.
14395
+ // Extract attributes and classes from the first selector only to match VE behavior.
14396
+ const { attrs, classes } = extractAttrsAndClassesFromSelector(componentDef.selectors[0]);
14397
+ if (attrs) {
14398
+ setUpAttributes(hostRenderer, hostRNode, attrs);
14399
+ }
14400
+ if (classes && classes.length > 0) {
14401
+ writeDirectClass(hostRenderer, hostRNode, classes.join(' '));
14402
+ }
14403
+ }
14404
+ }
14405
+ /** Projects the `projectableNodes` that were specified when creating a root component. */
14406
+ function projectNodes(tNode, ngContentSelectors, projectableNodes) {
14407
+ const projection = tNode.projection = [];
14408
+ for (let i = 0; i < ngContentSelectors.length; i++) {
14409
+ const nodesforSlot = projectableNodes[i];
14410
+ // Projectable nodes can be passed as array of arrays or an array of iterables (ngUpgrade
14411
+ // case). Here we do normalize passed data structure to be an array of arrays to avoid
14412
+ // complex checks down the line.
14413
+ // We also normalize the length of the passed in projectable nodes (to match the number of
14414
+ // <ng-container> slots defined by a component).
14415
+ projection.push(nodesforSlot != null ? Array.from(nodesforSlot) : null);
14392
14416
  }
14393
- return component;
14394
14417
  }
14395
14418
  /**
14396
14419
  * Used to enable lifecycle hooks on the root component.
@@ -14697,10 +14720,9 @@ function findHostDirectiveDefs(matches, def, tView, lView, tNode) {
14697
14720
  // TODO(crisbeto): assert that the def exists.
14698
14721
  // Host directives execute before the host so that its host bindings can be overwritten.
14699
14722
  findHostDirectiveDefs(matches, hostDirectiveDef, tView, lView, tNode);
14723
+ matches.push(hostDirectiveDef);
14700
14724
  }
14701
14725
  }
14702
- // Push the def itself at the end since it needs to execute after the host directives.
14703
- matches.push(def);
14704
14726
  }
14705
14727
  /**
14706
14728
  * Converts an array in the form of `['publicName', 'alias', 'otherPublicName', 'otherAlias']` into
@@ -15659,18 +15681,7 @@ function ɵɵelementStart(index, name, attrsIndex, localRefsIndex) {
15659
15681
  elementStartFirstCreatePass(adjustedIndex, tView, lView, native, name, attrsIndex, localRefsIndex) :
15660
15682
  tView.data[adjustedIndex];
15661
15683
  setCurrentTNode(tNode, true);
15662
- const mergedAttrs = tNode.mergedAttrs;
15663
- if (mergedAttrs !== null) {
15664
- setUpAttributes(renderer, native, mergedAttrs);
15665
- }
15666
- const classes = tNode.classes;
15667
- if (classes !== null) {
15668
- writeDirectClass(renderer, native, classes);
15669
- }
15670
- const styles = tNode.styles;
15671
- if (styles !== null) {
15672
- writeDirectStyle(renderer, native, styles);
15673
- }
15684
+ setupStaticAttributes(renderer, native, tNode);
15674
15685
  if ((tNode.flags & 32 /* TNodeFlags.isDetached */) !== 32 /* TNodeFlags.isDetached */) {
15675
15686
  // In the i18n case, the translation may have removed this element, so only add it if it is not
15676
15687
  // detached. See `TNodeType.Placeholder` and `LFrame.inI18n` for more context.
@@ -22360,7 +22371,7 @@ function getDirectives(node) {
22360
22371
  return [];
22361
22372
  }
22362
22373
  if (context.directives === undefined) {
22363
- context.directives = getDirectivesAtNodeIndex(nodeIndex, lView, false);
22374
+ context.directives = getDirectivesAtNodeIndex(nodeIndex, lView);
22364
22375
  }
22365
22376
  // The `directives` in this case are a named array called `LComponentView`. Clone the
22366
22377
  // result so we don't expose an internal data structure in the user's console.
@@ -26143,7 +26154,7 @@ class TestBedImpl {
26143
26154
  return TestBedImpl.INSTANCE.overrideProvider(token, provider);
26144
26155
  }
26145
26156
  static inject(token, notFoundValue, flags) {
26146
- return TestBedImpl.INSTANCE.inject(token, notFoundValue, flags);
26157
+ return TestBedImpl.INSTANCE.inject(token, notFoundValue, ɵconvertToBitFlags(flags));
26147
26158
  }
26148
26159
  /** @deprecated from v9.0.0 use TestBed.inject */
26149
26160
  static get(token, notFoundValue = Injector$1.THROW_IF_NOT_FOUND, flags = InjectFlags$1.Default) {
@@ -26277,7 +26288,7 @@ class TestBedImpl {
26277
26288
  return this;
26278
26289
  }
26279
26290
  const UNDEFINED = {};
26280
- const result = this.testModuleRef.injector.get(token, UNDEFINED, flags);
26291
+ const result = this.testModuleRef.injector.get(token, UNDEFINED, ɵconvertToBitFlags(flags));
26281
26292
  return result === UNDEFINED ? this.compiler.injector.get(token, notFoundValue, flags) :
26282
26293
  result;
26283
26294
  }