@angular/core 16.0.0-next.2 → 16.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 (73) hide show
  1. package/esm2020/src/application_ref.mjs +95 -87
  2. package/esm2020/src/application_tokens.mjs +59 -21
  3. package/esm2020/src/change_detection/change_detector_ref.mjs +4 -4
  4. package/esm2020/src/compiler/compiler_facade_interface.mjs +1 -1
  5. package/esm2020/src/core.mjs +2 -2
  6. package/esm2020/src/core_private_export.mjs +3 -3
  7. package/esm2020/src/core_reactivity_export_internal.mjs +1 -1
  8. package/esm2020/src/core_render3_private_export.mjs +1 -2
  9. package/esm2020/src/di/contextual.mjs +37 -0
  10. package/esm2020/src/di/index.mjs +2 -1
  11. package/esm2020/src/di/r3_injector.mjs +8 -1
  12. package/esm2020/src/errors.mjs +1 -1
  13. package/esm2020/src/hydration/annotate.mjs +250 -6
  14. package/esm2020/src/hydration/api.mjs +22 -1
  15. package/esm2020/src/hydration/cleanup.mjs +101 -0
  16. package/esm2020/src/hydration/compression.mjs +69 -0
  17. package/esm2020/src/hydration/error_handling.mjs +362 -12
  18. package/esm2020/src/hydration/interfaces.mjs +25 -2
  19. package/esm2020/src/hydration/node_lookup_utils.mjs +217 -17
  20. package/esm2020/src/hydration/skip_hydration.mjs +16 -1
  21. package/esm2020/src/hydration/utils.mjs +95 -7
  22. package/esm2020/src/hydration/views.mjs +84 -0
  23. package/esm2020/src/linker/destroy_ref.mjs +5 -2
  24. package/esm2020/src/linker/template_ref.mjs +17 -2
  25. package/esm2020/src/linker/view_container_ref.mjs +111 -35
  26. package/esm2020/src/metadata/directives.mjs +8 -3
  27. package/esm2020/src/render3/component_ref.mjs +2 -2
  28. package/esm2020/src/render3/definition.mjs +114 -45
  29. package/esm2020/src/render3/instructions/element.mjs +31 -14
  30. package/esm2020/src/render3/instructions/element_container.mjs +9 -13
  31. package/esm2020/src/render3/instructions/element_validation.mjs +2 -2
  32. package/esm2020/src/render3/instructions/projection.mjs +7 -4
  33. package/esm2020/src/render3/instructions/shared.mjs +53 -16
  34. package/esm2020/src/render3/instructions/template.mjs +54 -6
  35. package/esm2020/src/render3/instructions/text.mjs +6 -6
  36. package/esm2020/src/render3/interfaces/container.mjs +3 -2
  37. package/esm2020/src/render3/interfaces/public_definitions.mjs +1 -1
  38. package/esm2020/src/render3/interfaces/type_checks.mjs +5 -2
  39. package/esm2020/src/render3/interfaces/view.mjs +1 -1
  40. package/esm2020/src/render3/jit/module.mjs +3 -2
  41. package/esm2020/src/render3/ng_module_ref.mjs +9 -5
  42. package/esm2020/src/render3/node_manipulation.mjs +2 -2
  43. package/esm2020/src/render3/node_selector_matcher.mjs +17 -5
  44. package/esm2020/src/render3/util/discovery_utils.mjs +3 -2
  45. package/esm2020/src/render3/util/view_utils.mjs +12 -1
  46. package/esm2020/src/render3/view_ref.mjs +1 -1
  47. package/esm2020/src/signals/index.mjs +2 -1
  48. package/esm2020/src/signals/src/computed.mjs +3 -3
  49. package/esm2020/src/signals/src/effect.mjs +1 -3
  50. package/esm2020/src/signals/src/signal.mjs +3 -3
  51. package/esm2020/src/signals/src/watch.mjs +3 -3
  52. package/esm2020/src/signals/src/weak_ref.mjs +18 -2
  53. package/esm2020/src/util/ng_dev_mode.mjs +3 -1
  54. package/esm2020/src/version.mjs +1 -1
  55. package/esm2020/src/zone/ng_zone.mjs +62 -1
  56. package/esm2020/testing/src/logger.mjs +3 -3
  57. package/esm2020/testing/src/ng_zone_mock.mjs +3 -3
  58. package/esm2020/testing/src/test_bed_compiler.mjs +15 -10
  59. package/fesm2015/core.mjs +3339 -1754
  60. package/fesm2015/core.mjs.map +1 -1
  61. package/fesm2015/testing.mjs +2619 -1376
  62. package/fesm2015/testing.mjs.map +1 -1
  63. package/fesm2020/core.mjs +3348 -1760
  64. package/fesm2020/core.mjs.map +1 -1
  65. package/fesm2020/testing.mjs +2569 -1322
  66. package/fesm2020/testing.mjs.map +1 -1
  67. package/index.d.ts +564 -682
  68. package/package.json +1 -1
  69. package/schematics/migrations/relative-link-resolution/bundle.js +7 -7
  70. package/schematics/migrations/router-link-with-href/bundle.js +10 -10
  71. package/schematics/ng-generate/standalone-migration/bundle.js +644 -448
  72. package/schematics/ng-generate/standalone-migration/bundle.js.map +3 -3
  73. package/testing/index.d.ts +1 -1
@@ -1,13 +1,14 @@
1
1
  /**
2
- * @license Angular v16.0.0-next.2
2
+ * @license Angular v16.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, 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, ɵisEnvironmentProviders, 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, EnvironmentInjector as EnvironmentInjector$1, ɵflushModuleScopingQueueAsMuchAsPossible } from '@angular/core';
7
+ import { getDebugNode, 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, ɵprovideNgZoneChangeDetection, Compiler, COMPILER_OPTIONS, ɵNgModuleFactory, ɵisEnvironmentProviders, 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, EnvironmentInjector as EnvironmentInjector$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';
11
+ import { first } from 'rxjs/operators';
11
12
 
12
13
  /**
13
14
  * Wraps a test function in an asynchronous test zone. The test will automatically
@@ -1638,6 +1639,8 @@ function ngDevModeResetPerfCounters() {
1638
1639
  rendererCreateComment: 0,
1639
1640
  hydratedNodes: 0,
1640
1641
  hydratedComponents: 0,
1642
+ dehydratedViewsRemoved: 0,
1643
+ dehydratedViewsCleanupRuns: 0,
1641
1644
  };
1642
1645
  // Make sure to refer to ngDevMode as ['ngDevMode'] for closure.
1643
1646
  const allowNgDevModeTrue = locationString.indexOf('ngDevMode=false') === -1;
@@ -2198,244 +2201,846 @@ const NG_ELEMENT_ID = getClosureSafeProperty({ __NG_ELEMENT_ID__: getClosureSafe
2198
2201
  */
2199
2202
  const NG_ENV_ID = getClosureSafeProperty({ __NG_ENV_ID__: getClosureSafeProperty });
2200
2203
 
2201
- /** Counter used to generate unique IDs for component definitions. */
2202
- let componentDefCount = 0;
2203
2204
  /**
2204
- * Create a component definition object.
2205
- *
2206
- *
2207
- * # Example
2208
- * ```
2209
- * class MyDirective {
2210
- * // Generated by Angular Template Compiler
2211
- * // [Symbol] syntax will not be supported by TypeScript until v2.7
2212
- * static ɵcmp = defineComponent({
2213
- * ...
2214
- * });
2215
- * }
2216
- * ```
2217
- * @codeGenApi
2218
- */
2219
- function ɵɵdefineComponent(componentDefinition) {
2220
- return noSideEffects(() => {
2221
- // Initialize ngDevMode. This must be the first statement in ɵɵdefineComponent.
2222
- // See the `initNgDevMode` docstring for more information.
2223
- (typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode();
2224
- const type = componentDefinition.type;
2225
- const standalone = componentDefinition.standalone === true;
2226
- const declaredInputs = {};
2227
- const def = {
2228
- type: type,
2229
- providersResolver: null,
2230
- decls: componentDefinition.decls,
2231
- vars: componentDefinition.vars,
2232
- factory: null,
2233
- template: componentDefinition.template || null,
2234
- consts: componentDefinition.consts || null,
2235
- ngContentSelectors: componentDefinition.ngContentSelectors,
2236
- hostBindings: componentDefinition.hostBindings || null,
2237
- hostVars: componentDefinition.hostVars || 0,
2238
- hostAttrs: componentDefinition.hostAttrs || null,
2239
- contentQueries: componentDefinition.contentQueries || null,
2240
- declaredInputs: declaredInputs,
2241
- inputs: null,
2242
- outputs: null,
2243
- exportAs: componentDefinition.exportAs || null,
2244
- onPush: componentDefinition.changeDetection === ChangeDetectionStrategy.OnPush,
2245
- directiveDefs: null,
2246
- pipeDefs: null,
2247
- standalone,
2248
- dependencies: standalone && componentDefinition.dependencies || null,
2249
- getStandaloneInjector: null,
2250
- selectors: componentDefinition.selectors || EMPTY_ARRAY,
2251
- viewQuery: componentDefinition.viewQuery || null,
2252
- features: componentDefinition.features || null,
2253
- data: componentDefinition.data || {},
2254
- encapsulation: componentDefinition.encapsulation || ViewEncapsulation.Emulated,
2255
- id: `c${componentDefCount++}`,
2256
- styles: componentDefinition.styles || EMPTY_ARRAY,
2257
- _: null,
2258
- setInput: null,
2259
- schemas: componentDefinition.schemas || null,
2260
- tView: null,
2261
- findHostDirectiveDefs: null,
2262
- hostDirectives: null,
2263
- };
2264
- const dependencies = componentDefinition.dependencies;
2265
- const feature = componentDefinition.features;
2266
- def.inputs = invertObject(componentDefinition.inputs, declaredInputs),
2267
- def.outputs = invertObject(componentDefinition.outputs),
2268
- feature && feature.forEach((fn) => fn(def));
2269
- def.directiveDefs = dependencies ?
2270
- (() => (typeof dependencies === 'function' ? dependencies() : dependencies)
2271
- .map(extractDirectiveDef)
2272
- .filter(nonNull)) :
2273
- null;
2274
- def.pipeDefs = dependencies ?
2275
- (() => (typeof dependencies === 'function' ? dependencies() : dependencies)
2276
- .map(getPipeDef$1)
2277
- .filter(nonNull)) :
2278
- null;
2279
- return def;
2280
- });
2281
- }
2282
- /**
2283
- * Generated next to NgModules to monkey-patch directive and pipe references onto a component's
2284
- * definition, when generating a direct reference in the component file would otherwise create an
2285
- * import cycle.
2286
- *
2287
- * See [this explanation](https://hackmd.io/Odw80D0pR6yfsOjg_7XCJg?view) for more details.
2288
- *
2289
- * @codeGenApi
2290
- */
2291
- function ɵɵsetComponentScope(type, directives, pipes) {
2292
- const def = type.ɵcmp;
2293
- def.directiveDefs = () => (typeof directives === 'function' ? directives() : directives).map(extractDirectiveDef);
2294
- def.pipeDefs = () => (typeof pipes === 'function' ? pipes() : pipes).map(getPipeDef$1);
2295
- }
2296
- function extractDirectiveDef(type) {
2297
- return getComponentDef$1(type) || getDirectiveDef(type);
2298
- }
2299
- function nonNull(value) {
2300
- return value !== null;
2301
- }
2302
- /**
2303
- * @codeGenApi
2304
- */
2305
- function ɵɵdefineNgModule(def) {
2306
- return noSideEffects(() => {
2307
- const res = {
2308
- type: def.type,
2309
- bootstrap: def.bootstrap || EMPTY_ARRAY,
2310
- declarations: def.declarations || EMPTY_ARRAY,
2311
- imports: def.imports || EMPTY_ARRAY,
2312
- exports: def.exports || EMPTY_ARRAY,
2313
- transitiveCompileScopes: null,
2314
- schemas: def.schemas || null,
2315
- id: def.id || null,
2316
- };
2317
- return res;
2318
- });
2319
- }
2320
- /**
2321
- * Adds the module metadata that is necessary to compute the module's transitive scope to an
2322
- * existing module definition.
2205
+ * Returns an index of `classToSearch` in `className` taking token boundaries into account.
2323
2206
  *
2324
- * Scope metadata of modules is not used in production builds, so calls to this function can be
2325
- * marked pure to tree-shake it from the bundle, allowing for all referenced declarations
2326
- * to become eligible for tree-shaking as well.
2207
+ * `classIndexOf('AB A', 'A', 0)` will be 3 (not 0 since `AB!==A`)
2327
2208
  *
2328
- * @codeGenApi
2209
+ * @param className A string containing classes (whitespace separated)
2210
+ * @param classToSearch A class name to locate
2211
+ * @param startingIndex Starting location of search
2212
+ * @returns an index of the located class (or -1 if not found)
2329
2213
  */
2330
- function ɵɵsetNgModuleScope(type, scope) {
2331
- return noSideEffects(() => {
2332
- const ngModuleDef = getNgModuleDef(type, true);
2333
- ngModuleDef.declarations = scope.declarations || EMPTY_ARRAY;
2334
- ngModuleDef.imports = scope.imports || EMPTY_ARRAY;
2335
- ngModuleDef.exports = scope.exports || EMPTY_ARRAY;
2336
- });
2214
+ function classIndexOf(className, classToSearch, startingIndex) {
2215
+ ngDevMode && assertNotEqual(classToSearch, '', 'can not look for "" string.');
2216
+ let end = className.length;
2217
+ while (true) {
2218
+ const foundIndex = className.indexOf(classToSearch, startingIndex);
2219
+ if (foundIndex === -1)
2220
+ return foundIndex;
2221
+ if (foundIndex === 0 || className.charCodeAt(foundIndex - 1) <= 32 /* CharCode.SPACE */) {
2222
+ // Ensure that it has leading whitespace
2223
+ const length = classToSearch.length;
2224
+ if (foundIndex + length === end ||
2225
+ className.charCodeAt(foundIndex + length) <= 32 /* CharCode.SPACE */) {
2226
+ // Ensure that it has trailing whitespace
2227
+ return foundIndex;
2228
+ }
2229
+ }
2230
+ // False positive, keep searching from where we left off.
2231
+ startingIndex = foundIndex + 1;
2232
+ }
2337
2233
  }
2234
+
2338
2235
  /**
2339
- * Inverts an inputs or outputs lookup such that the keys, which were the
2340
- * minified keys, are part of the values, and the values are parsed so that
2341
- * the publicName of the property is the new key
2342
- *
2343
- * e.g. for
2344
- *
2345
- * ```
2346
- * class Comp {
2347
- * @Input()
2348
- * propName1: string;
2349
- *
2350
- * @Input('publicName2')
2351
- * declaredPropName2: number;
2352
- * }
2353
- * ```
2354
- *
2355
- * will be serialized as
2356
- *
2357
- * ```
2358
- * {
2359
- * propName1: 'propName1',
2360
- * declaredPropName2: ['publicName2', 'declaredPropName2'],
2361
- * }
2362
- * ```
2363
- *
2364
- * which is than translated by the minifier as:
2236
+ * Assigns all attribute values to the provided element via the inferred renderer.
2365
2237
  *
2366
- * ```
2367
- * {
2368
- * minifiedPropName1: 'propName1',
2369
- * minifiedPropName2: ['publicName2', 'declaredPropName2'],
2370
- * }
2371
- * ```
2238
+ * This function accepts two forms of attribute entries:
2372
2239
  *
2373
- * becomes: (public name => minifiedName)
2240
+ * default: (key, value):
2241
+ * attrs = [key1, value1, key2, value2]
2374
2242
  *
2375
- * ```
2376
- * {
2377
- * 'propName1': 'minifiedPropName1',
2378
- * 'publicName2': 'minifiedPropName2',
2379
- * }
2380
- * ```
2243
+ * namespaced: (NAMESPACE_MARKER, uri, name, value)
2244
+ * attrs = [NAMESPACE_MARKER, uri, name, value, NAMESPACE_MARKER, uri, name, value]
2381
2245
  *
2382
- * Optionally the function can take `secondary` which will result in: (public name => declared name)
2246
+ * The `attrs` array can contain a mix of both the default and namespaced entries.
2247
+ * The "default" values are set without a marker, but if the function comes across
2248
+ * a marker value then it will attempt to set a namespaced value. If the marker is
2249
+ * not of a namespaced value then the function will quit and return the index value
2250
+ * where it stopped during the iteration of the attrs array.
2383
2251
  *
2384
- * ```
2385
- * {
2386
- * 'propName1': 'propName1',
2387
- * 'publicName2': 'declaredPropName2',
2388
- * }
2389
- * ```
2252
+ * See [AttributeMarker] to understand what the namespace marker value is.
2390
2253
  *
2391
-
2254
+ * Note that this instruction does not support assigning style and class values to
2255
+ * an element. See `elementStart` and `elementHostAttrs` to learn how styling values
2256
+ * are applied to an element.
2257
+ * @param renderer The renderer to be used
2258
+ * @param native The element that the attributes will be assigned to
2259
+ * @param attrs The attribute array of values that will be assigned to the element
2260
+ * @returns the index value that was last accessed in the attributes array
2392
2261
  */
2393
- function invertObject(obj, secondary) {
2394
- if (obj == null)
2395
- return EMPTY_OBJ;
2396
- const newLookup = {};
2397
- for (const minifiedKey in obj) {
2398
- if (obj.hasOwnProperty(minifiedKey)) {
2399
- let publicName = obj[minifiedKey];
2400
- let declaredName = publicName;
2401
- if (Array.isArray(publicName)) {
2402
- declaredName = publicName[1];
2403
- publicName = publicName[0];
2262
+ function setUpAttributes(renderer, native, attrs) {
2263
+ let i = 0;
2264
+ while (i < attrs.length) {
2265
+ const value = attrs[i];
2266
+ if (typeof value === 'number') {
2267
+ // only namespaces are supported. Other value types (such as style/class
2268
+ // entries) are not supported in this function.
2269
+ if (value !== 0 /* AttributeMarker.NamespaceURI */) {
2270
+ break;
2404
2271
  }
2405
- newLookup[publicName] = minifiedKey;
2406
- if (secondary) {
2407
- (secondary[publicName] = declaredName);
2272
+ // we just landed on the marker value ... therefore
2273
+ // we should skip to the next entry
2274
+ i++;
2275
+ const namespaceURI = attrs[i++];
2276
+ const attrName = attrs[i++];
2277
+ const attrVal = attrs[i++];
2278
+ ngDevMode && ngDevMode.rendererSetAttribute++;
2279
+ renderer.setAttribute(native, attrName, attrVal, namespaceURI);
2280
+ }
2281
+ else {
2282
+ // attrName is string;
2283
+ const attrName = value;
2284
+ const attrVal = attrs[++i];
2285
+ // Standard attributes
2286
+ ngDevMode && ngDevMode.rendererSetAttribute++;
2287
+ if (isAnimationProp(attrName)) {
2288
+ renderer.setProperty(native, attrName, attrVal);
2289
+ }
2290
+ else {
2291
+ renderer.setAttribute(native, attrName, attrVal);
2408
2292
  }
2293
+ i++;
2409
2294
  }
2410
2295
  }
2411
- return newLookup;
2296
+ // another piece of code may iterate over the same attributes array. Therefore
2297
+ // it may be helpful to return the exact spot where the attributes array exited
2298
+ // whether by running into an unsupported marker or if all the static values were
2299
+ // iterated over.
2300
+ return i;
2412
2301
  }
2413
2302
  /**
2414
- * Create a directive definition object.
2415
- *
2416
- * # Example
2417
- * ```ts
2418
- * class MyDirective {
2419
- * // Generated by Angular Template Compiler
2420
- * // [Symbol] syntax will not be supported by TypeScript until v2.7
2421
- * static ɵdir = ɵɵdefineDirective({
2422
- * ...
2423
- * });
2424
- * }
2425
- * ```
2426
- *
2427
- * @codeGenApi
2303
+ * Test whether the given value is a marker that indicates that the following
2304
+ * attribute values in a `TAttributes` array are only the names of attributes,
2305
+ * and not name-value pairs.
2306
+ * @param marker The attribute marker to test.
2307
+ * @returns true if the marker is a "name-only" marker (e.g. `Bindings`, `Template` or `I18n`).
2428
2308
  */
2429
- const ɵɵdefineDirective = ɵɵdefineComponent;
2309
+ function isNameOnlyAttributeMarker(marker) {
2310
+ return marker === 3 /* AttributeMarker.Bindings */ || marker === 4 /* AttributeMarker.Template */ ||
2311
+ marker === 6 /* AttributeMarker.I18n */;
2312
+ }
2313
+ function isAnimationProp(name) {
2314
+ // Perf note: accessing charCodeAt to check for the first character of a string is faster as
2315
+ // compared to accessing a character at index 0 (ex. name[0]). The main reason for this is that
2316
+ // charCodeAt doesn't allocate memory to return a substring.
2317
+ return name.charCodeAt(0) === 64 /* CharCode.AT_SIGN */;
2318
+ }
2430
2319
  /**
2431
- * Create a pipe definition object.
2320
+ * Merges `src` `TAttributes` into `dst` `TAttributes` removing any duplicates in the process.
2432
2321
  *
2433
- * # Example
2434
- * ```
2435
- * class MyPipe implements PipeTransform {
2436
- * // Generated by Angular Template Compiler
2437
- * static ɵpipe = definePipe({
2438
- * ...
2322
+ * This merge function keeps the order of attrs same.
2323
+ *
2324
+ * @param dst Location of where the merged `TAttributes` should end up.
2325
+ * @param src `TAttributes` which should be appended to `dst`
2326
+ */
2327
+ function mergeHostAttrs(dst, src) {
2328
+ if (src === null || src.length === 0) {
2329
+ // do nothing
2330
+ }
2331
+ else if (dst === null || dst.length === 0) {
2332
+ // We have source, but dst is empty, just make a copy.
2333
+ dst = src.slice();
2334
+ }
2335
+ else {
2336
+ let srcMarker = -1 /* AttributeMarker.ImplicitAttributes */;
2337
+ for (let i = 0; i < src.length; i++) {
2338
+ const item = src[i];
2339
+ if (typeof item === 'number') {
2340
+ srcMarker = item;
2341
+ }
2342
+ else {
2343
+ if (srcMarker === 0 /* AttributeMarker.NamespaceURI */) {
2344
+ // Case where we need to consume `key1`, `key2`, `value` items.
2345
+ }
2346
+ else if (srcMarker === -1 /* AttributeMarker.ImplicitAttributes */ ||
2347
+ srcMarker === 2 /* AttributeMarker.Styles */) {
2348
+ // Case where we have to consume `key1` and `value` only.
2349
+ mergeHostAttribute(dst, srcMarker, item, null, src[++i]);
2350
+ }
2351
+ else {
2352
+ // Case where we have to consume `key1` only.
2353
+ mergeHostAttribute(dst, srcMarker, item, null, null);
2354
+ }
2355
+ }
2356
+ }
2357
+ }
2358
+ return dst;
2359
+ }
2360
+ /**
2361
+ * Append `key`/`value` to existing `TAttributes` taking region marker and duplicates into account.
2362
+ *
2363
+ * @param dst `TAttributes` to append to.
2364
+ * @param marker Region where the `key`/`value` should be added.
2365
+ * @param key1 Key to add to `TAttributes`
2366
+ * @param key2 Key to add to `TAttributes` (in case of `AttributeMarker.NamespaceURI`)
2367
+ * @param value Value to add or to overwrite to `TAttributes` Only used if `marker` is not Class.
2368
+ */
2369
+ function mergeHostAttribute(dst, marker, key1, key2, value) {
2370
+ let i = 0;
2371
+ // Assume that new markers will be inserted at the end.
2372
+ let markerInsertPosition = dst.length;
2373
+ // scan until correct type.
2374
+ if (marker === -1 /* AttributeMarker.ImplicitAttributes */) {
2375
+ markerInsertPosition = -1;
2376
+ }
2377
+ else {
2378
+ while (i < dst.length) {
2379
+ const dstValue = dst[i++];
2380
+ if (typeof dstValue === 'number') {
2381
+ if (dstValue === marker) {
2382
+ markerInsertPosition = -1;
2383
+ break;
2384
+ }
2385
+ else if (dstValue > marker) {
2386
+ // We need to save this as we want the markers to be inserted in specific order.
2387
+ markerInsertPosition = i - 1;
2388
+ break;
2389
+ }
2390
+ }
2391
+ }
2392
+ }
2393
+ // search until you find place of insertion
2394
+ while (i < dst.length) {
2395
+ const item = dst[i];
2396
+ if (typeof item === 'number') {
2397
+ // since `i` started as the index after the marker, we did not find it if we are at the next
2398
+ // marker
2399
+ break;
2400
+ }
2401
+ else if (item === key1) {
2402
+ // We already have same token
2403
+ if (key2 === null) {
2404
+ if (value !== null) {
2405
+ dst[i + 1] = value;
2406
+ }
2407
+ return;
2408
+ }
2409
+ else if (key2 === dst[i + 1]) {
2410
+ dst[i + 2] = value;
2411
+ return;
2412
+ }
2413
+ }
2414
+ // Increment counter.
2415
+ i++;
2416
+ if (key2 !== null)
2417
+ i++;
2418
+ if (value !== null)
2419
+ i++;
2420
+ }
2421
+ // insert at location.
2422
+ if (markerInsertPosition !== -1) {
2423
+ dst.splice(markerInsertPosition, 0, marker);
2424
+ i = markerInsertPosition + 1;
2425
+ }
2426
+ dst.splice(i++, 0, key1);
2427
+ if (key2 !== null) {
2428
+ dst.splice(i++, 0, key2);
2429
+ }
2430
+ if (value !== null) {
2431
+ dst.splice(i++, 0, value);
2432
+ }
2433
+ }
2434
+
2435
+ const NG_TEMPLATE_SELECTOR = 'ng-template';
2436
+ /**
2437
+ * Search the `TAttributes` to see if it contains `cssClassToMatch` (case insensitive)
2438
+ *
2439
+ * @param attrs `TAttributes` to search through.
2440
+ * @param cssClassToMatch class to match (lowercase)
2441
+ * @param isProjectionMode Whether or not class matching should look into the attribute `class` in
2442
+ * addition to the `AttributeMarker.Classes`.
2443
+ */
2444
+ function isCssClassMatching(attrs, cssClassToMatch, isProjectionMode) {
2445
+ // TODO(misko): The fact that this function needs to know about `isProjectionMode` seems suspect.
2446
+ // It is strange to me that sometimes the class information comes in form of `class` attribute
2447
+ // and sometimes in form of `AttributeMarker.Classes`. Some investigation is needed to determine
2448
+ // if that is the right behavior.
2449
+ ngDevMode &&
2450
+ assertEqual(cssClassToMatch, cssClassToMatch.toLowerCase(), 'Class name expected to be lowercase.');
2451
+ let i = 0;
2452
+ // Indicates whether we are processing value from the implicit
2453
+ // attribute section (i.e. before the first marker in the array).
2454
+ let isImplicitAttrsSection = true;
2455
+ while (i < attrs.length) {
2456
+ let item = attrs[i++];
2457
+ if (typeof item === 'string' && isImplicitAttrsSection) {
2458
+ const value = attrs[i++];
2459
+ if (isProjectionMode && item === 'class') {
2460
+ // We found a `class` attribute in the implicit attribute section,
2461
+ // check if it matches the value of the `cssClassToMatch` argument.
2462
+ if (classIndexOf(value.toLowerCase(), cssClassToMatch, 0) !== -1) {
2463
+ return true;
2464
+ }
2465
+ }
2466
+ }
2467
+ else if (item === 1 /* AttributeMarker.Classes */) {
2468
+ // We found the classes section. Start searching for the class.
2469
+ while (i < attrs.length && typeof (item = attrs[i++]) == 'string') {
2470
+ // while we have strings
2471
+ if (item.toLowerCase() === cssClassToMatch)
2472
+ return true;
2473
+ }
2474
+ return false;
2475
+ }
2476
+ else if (typeof item === 'number') {
2477
+ // We've came across a first marker, which indicates
2478
+ // that the implicit attribute section is over.
2479
+ isImplicitAttrsSection = false;
2480
+ }
2481
+ }
2482
+ return false;
2483
+ }
2484
+ /**
2485
+ * Checks whether the `tNode` represents an inline template (e.g. `*ngFor`).
2486
+ *
2487
+ * @param tNode current TNode
2488
+ */
2489
+ function isInlineTemplate(tNode) {
2490
+ return tNode.type === 4 /* TNodeType.Container */ && tNode.value !== NG_TEMPLATE_SELECTOR;
2491
+ }
2492
+ /**
2493
+ * Function that checks whether a given tNode matches tag-based selector and has a valid type.
2494
+ *
2495
+ * Matching can be performed in 2 modes: projection mode (when we project nodes) and regular
2496
+ * directive matching mode:
2497
+ * - in the "directive matching" mode we do _not_ take TContainer's tagName into account if it is
2498
+ * different from NG_TEMPLATE_SELECTOR (value different from NG_TEMPLATE_SELECTOR indicates that a
2499
+ * tag name was extracted from * syntax so we would match the same directive twice);
2500
+ * - in the "projection" mode, we use a tag name potentially extracted from the * syntax processing
2501
+ * (applicable to TNodeType.Container only).
2502
+ */
2503
+ function hasTagAndTypeMatch(tNode, currentSelector, isProjectionMode) {
2504
+ const tagNameToCompare = tNode.type === 4 /* TNodeType.Container */ && !isProjectionMode ? NG_TEMPLATE_SELECTOR : tNode.value;
2505
+ return currentSelector === tagNameToCompare;
2506
+ }
2507
+ /**
2508
+ * A utility function to match an Ivy node static data against a simple CSS selector
2509
+ *
2510
+ * @param node static data of the node to match
2511
+ * @param selector The selector to try matching against the node.
2512
+ * @param isProjectionMode if `true` we are matching for content projection, otherwise we are doing
2513
+ * directive matching.
2514
+ * @returns true if node matches the selector.
2515
+ */
2516
+ function isNodeMatchingSelector(tNode, selector, isProjectionMode) {
2517
+ ngDevMode && assertDefined(selector[0], 'Selector should have a tag name');
2518
+ let mode = 4 /* SelectorFlags.ELEMENT */;
2519
+ const nodeAttrs = tNode.attrs || [];
2520
+ // Find the index of first attribute that has no value, only a name.
2521
+ const nameOnlyMarkerIdx = getNameOnlyMarkerIndex(nodeAttrs);
2522
+ // When processing ":not" selectors, we skip to the next ":not" if the
2523
+ // current one doesn't match
2524
+ let skipToNextSelector = false;
2525
+ for (let i = 0; i < selector.length; i++) {
2526
+ const current = selector[i];
2527
+ if (typeof current === 'number') {
2528
+ // If we finish processing a :not selector and it hasn't failed, return false
2529
+ if (!skipToNextSelector && !isPositive(mode) && !isPositive(current)) {
2530
+ return false;
2531
+ }
2532
+ // If we are skipping to the next :not() and this mode flag is positive,
2533
+ // it's a part of the current :not() selector, and we should keep skipping
2534
+ if (skipToNextSelector && isPositive(current))
2535
+ continue;
2536
+ skipToNextSelector = false;
2537
+ mode = current | (mode & 1 /* SelectorFlags.NOT */);
2538
+ continue;
2539
+ }
2540
+ if (skipToNextSelector)
2541
+ continue;
2542
+ if (mode & 4 /* SelectorFlags.ELEMENT */) {
2543
+ mode = 2 /* SelectorFlags.ATTRIBUTE */ | mode & 1 /* SelectorFlags.NOT */;
2544
+ if (current !== '' && !hasTagAndTypeMatch(tNode, current, isProjectionMode) ||
2545
+ current === '' && selector.length === 1) {
2546
+ if (isPositive(mode))
2547
+ return false;
2548
+ skipToNextSelector = true;
2549
+ }
2550
+ }
2551
+ else {
2552
+ const selectorAttrValue = mode & 8 /* SelectorFlags.CLASS */ ? current : selector[++i];
2553
+ // special case for matching against classes when a tNode has been instantiated with
2554
+ // class and style values as separate attribute values (e.g. ['title', CLASS, 'foo'])
2555
+ if ((mode & 8 /* SelectorFlags.CLASS */) && tNode.attrs !== null) {
2556
+ if (!isCssClassMatching(tNode.attrs, selectorAttrValue, isProjectionMode)) {
2557
+ if (isPositive(mode))
2558
+ return false;
2559
+ skipToNextSelector = true;
2560
+ }
2561
+ continue;
2562
+ }
2563
+ const attrName = (mode & 8 /* SelectorFlags.CLASS */) ? 'class' : current;
2564
+ const attrIndexInNode = findAttrIndexInNode(attrName, nodeAttrs, isInlineTemplate(tNode), isProjectionMode);
2565
+ if (attrIndexInNode === -1) {
2566
+ if (isPositive(mode))
2567
+ return false;
2568
+ skipToNextSelector = true;
2569
+ continue;
2570
+ }
2571
+ if (selectorAttrValue !== '') {
2572
+ let nodeAttrValue;
2573
+ if (attrIndexInNode > nameOnlyMarkerIdx) {
2574
+ nodeAttrValue = '';
2575
+ }
2576
+ else {
2577
+ ngDevMode &&
2578
+ assertNotEqual(nodeAttrs[attrIndexInNode], 0 /* AttributeMarker.NamespaceURI */, 'We do not match directives on namespaced attributes');
2579
+ // we lowercase the attribute value to be able to match
2580
+ // selectors without case-sensitivity
2581
+ // (selectors are already in lowercase when generated)
2582
+ nodeAttrValue = nodeAttrs[attrIndexInNode + 1].toLowerCase();
2583
+ }
2584
+ const compareAgainstClassName = mode & 8 /* SelectorFlags.CLASS */ ? nodeAttrValue : null;
2585
+ if (compareAgainstClassName &&
2586
+ classIndexOf(compareAgainstClassName, selectorAttrValue, 0) !== -1 ||
2587
+ mode & 2 /* SelectorFlags.ATTRIBUTE */ && selectorAttrValue !== nodeAttrValue) {
2588
+ if (isPositive(mode))
2589
+ return false;
2590
+ skipToNextSelector = true;
2591
+ }
2592
+ }
2593
+ }
2594
+ }
2595
+ return isPositive(mode) || skipToNextSelector;
2596
+ }
2597
+ function isPositive(mode) {
2598
+ return (mode & 1 /* SelectorFlags.NOT */) === 0;
2599
+ }
2600
+ /**
2601
+ * Examines the attribute's definition array for a node to find the index of the
2602
+ * attribute that matches the given `name`.
2603
+ *
2604
+ * NOTE: This will not match namespaced attributes.
2605
+ *
2606
+ * Attribute matching depends upon `isInlineTemplate` and `isProjectionMode`.
2607
+ * The following table summarizes which types of attributes we attempt to match:
2608
+ *
2609
+ * ===========================================================================================================
2610
+ * Modes | Normal Attributes | Bindings Attributes | Template Attributes | I18n
2611
+ * Attributes
2612
+ * ===========================================================================================================
2613
+ * Inline + Projection | YES | YES | NO | YES
2614
+ * -----------------------------------------------------------------------------------------------------------
2615
+ * Inline + Directive | NO | NO | YES | NO
2616
+ * -----------------------------------------------------------------------------------------------------------
2617
+ * Non-inline + Projection | YES | YES | NO | YES
2618
+ * -----------------------------------------------------------------------------------------------------------
2619
+ * Non-inline + Directive | YES | YES | NO | YES
2620
+ * ===========================================================================================================
2621
+ *
2622
+ * @param name the name of the attribute to find
2623
+ * @param attrs the attribute array to examine
2624
+ * @param isInlineTemplate true if the node being matched is an inline template (e.g. `*ngFor`)
2625
+ * rather than a manually expanded template node (e.g `<ng-template>`).
2626
+ * @param isProjectionMode true if we are matching against content projection otherwise we are
2627
+ * matching against directives.
2628
+ */
2629
+ function findAttrIndexInNode(name, attrs, isInlineTemplate, isProjectionMode) {
2630
+ if (attrs === null)
2631
+ return -1;
2632
+ let i = 0;
2633
+ if (isProjectionMode || !isInlineTemplate) {
2634
+ let bindingsMode = false;
2635
+ while (i < attrs.length) {
2636
+ const maybeAttrName = attrs[i];
2637
+ if (maybeAttrName === name) {
2638
+ return i;
2639
+ }
2640
+ else if (maybeAttrName === 3 /* AttributeMarker.Bindings */ || maybeAttrName === 6 /* AttributeMarker.I18n */) {
2641
+ bindingsMode = true;
2642
+ }
2643
+ else if (maybeAttrName === 1 /* AttributeMarker.Classes */ || maybeAttrName === 2 /* AttributeMarker.Styles */) {
2644
+ let value = attrs[++i];
2645
+ // We should skip classes here because we have a separate mechanism for
2646
+ // matching classes in projection mode.
2647
+ while (typeof value === 'string') {
2648
+ value = attrs[++i];
2649
+ }
2650
+ continue;
2651
+ }
2652
+ else if (maybeAttrName === 4 /* AttributeMarker.Template */) {
2653
+ // We do not care about Template attributes in this scenario.
2654
+ break;
2655
+ }
2656
+ else if (maybeAttrName === 0 /* AttributeMarker.NamespaceURI */) {
2657
+ // Skip the whole namespaced attribute and value. This is by design.
2658
+ i += 4;
2659
+ continue;
2660
+ }
2661
+ // In binding mode there are only names, rather than name-value pairs.
2662
+ i += bindingsMode ? 1 : 2;
2663
+ }
2664
+ // We did not match the attribute
2665
+ return -1;
2666
+ }
2667
+ else {
2668
+ return matchTemplateAttribute(attrs, name);
2669
+ }
2670
+ }
2671
+ function isNodeMatchingSelectorList(tNode, selector, isProjectionMode = false) {
2672
+ for (let i = 0; i < selector.length; i++) {
2673
+ if (isNodeMatchingSelector(tNode, selector[i], isProjectionMode)) {
2674
+ return true;
2675
+ }
2676
+ }
2677
+ return false;
2678
+ }
2679
+ function getProjectAsAttrValue(tNode) {
2680
+ const nodeAttrs = tNode.attrs;
2681
+ if (nodeAttrs != null) {
2682
+ const ngProjectAsAttrIdx = nodeAttrs.indexOf(5 /* AttributeMarker.ProjectAs */);
2683
+ // only check for ngProjectAs in attribute names, don't accidentally match attribute's value
2684
+ // (attribute names are stored at even indexes)
2685
+ if ((ngProjectAsAttrIdx & 1) === 0) {
2686
+ return nodeAttrs[ngProjectAsAttrIdx + 1];
2687
+ }
2688
+ }
2689
+ return null;
2690
+ }
2691
+ function getNameOnlyMarkerIndex(nodeAttrs) {
2692
+ for (let i = 0; i < nodeAttrs.length; i++) {
2693
+ const nodeAttr = nodeAttrs[i];
2694
+ if (isNameOnlyAttributeMarker(nodeAttr)) {
2695
+ return i;
2696
+ }
2697
+ }
2698
+ return nodeAttrs.length;
2699
+ }
2700
+ function matchTemplateAttribute(attrs, name) {
2701
+ let i = attrs.indexOf(4 /* AttributeMarker.Template */);
2702
+ if (i > -1) {
2703
+ i++;
2704
+ while (i < attrs.length) {
2705
+ const attr = attrs[i];
2706
+ // Return in case we checked all template attrs and are switching to the next section in the
2707
+ // attrs array (that starts with a number that represents an attribute marker).
2708
+ if (typeof attr === 'number')
2709
+ return -1;
2710
+ if (attr === name)
2711
+ return i;
2712
+ i++;
2713
+ }
2714
+ }
2715
+ return -1;
2716
+ }
2717
+ /**
2718
+ * Checks whether a selector is inside a CssSelectorList
2719
+ * @param selector Selector to be checked.
2720
+ * @param list List in which to look for the selector.
2721
+ */
2722
+ function isSelectorInSelectorList(selector, list) {
2723
+ selectorListLoop: for (let i = 0; i < list.length; i++) {
2724
+ const currentSelectorInList = list[i];
2725
+ if (selector.length !== currentSelectorInList.length) {
2726
+ continue;
2727
+ }
2728
+ for (let j = 0; j < selector.length; j++) {
2729
+ if (selector[j] !== currentSelectorInList[j]) {
2730
+ continue selectorListLoop;
2731
+ }
2732
+ }
2733
+ return true;
2734
+ }
2735
+ return false;
2736
+ }
2737
+ function maybeWrapInNotSelector(isNegativeMode, chunk) {
2738
+ return isNegativeMode ? ':not(' + chunk.trim() + ')' : chunk;
2739
+ }
2740
+ function stringifyCSSSelector(selector) {
2741
+ let result = selector[0];
2742
+ let i = 1;
2743
+ let mode = 2 /* SelectorFlags.ATTRIBUTE */;
2744
+ let currentChunk = '';
2745
+ let isNegativeMode = false;
2746
+ while (i < selector.length) {
2747
+ let valueOrMarker = selector[i];
2748
+ if (typeof valueOrMarker === 'string') {
2749
+ if (mode & 2 /* SelectorFlags.ATTRIBUTE */) {
2750
+ const attrValue = selector[++i];
2751
+ currentChunk +=
2752
+ '[' + valueOrMarker + (attrValue.length > 0 ? '="' + attrValue + '"' : '') + ']';
2753
+ }
2754
+ else if (mode & 8 /* SelectorFlags.CLASS */) {
2755
+ currentChunk += '.' + valueOrMarker;
2756
+ }
2757
+ else if (mode & 4 /* SelectorFlags.ELEMENT */) {
2758
+ currentChunk += ' ' + valueOrMarker;
2759
+ }
2760
+ }
2761
+ else {
2762
+ //
2763
+ // Append current chunk to the final result in case we come across SelectorFlag, which
2764
+ // indicates that the previous section of a selector is over. We need to accumulate content
2765
+ // between flags to make sure we wrap the chunk later in :not() selector if needed, e.g.
2766
+ // ```
2767
+ // ['', Flags.CLASS, '.classA', Flags.CLASS | Flags.NOT, '.classB', '.classC']
2768
+ // ```
2769
+ // should be transformed to `.classA :not(.classB .classC)`.
2770
+ //
2771
+ // Note: for negative selector part, we accumulate content between flags until we find the
2772
+ // next negative flag. This is needed to support a case where `:not()` rule contains more than
2773
+ // one chunk, e.g. the following selector:
2774
+ // ```
2775
+ // ['', Flags.ELEMENT | Flags.NOT, 'p', Flags.CLASS, 'foo', Flags.CLASS | Flags.NOT, 'bar']
2776
+ // ```
2777
+ // should be stringified to `:not(p.foo) :not(.bar)`
2778
+ //
2779
+ if (currentChunk !== '' && !isPositive(valueOrMarker)) {
2780
+ result += maybeWrapInNotSelector(isNegativeMode, currentChunk);
2781
+ currentChunk = '';
2782
+ }
2783
+ mode = valueOrMarker;
2784
+ // According to CssSelector spec, once we come across `SelectorFlags.NOT` flag, the negative
2785
+ // mode is maintained for remaining chunks of a selector.
2786
+ isNegativeMode = isNegativeMode || !isPositive(mode);
2787
+ }
2788
+ i++;
2789
+ }
2790
+ if (currentChunk !== '') {
2791
+ result += maybeWrapInNotSelector(isNegativeMode, currentChunk);
2792
+ }
2793
+ return result;
2794
+ }
2795
+ /**
2796
+ * Generates string representation of CSS selector in parsed form.
2797
+ *
2798
+ * ComponentDef and DirectiveDef are generated with the selector in parsed form to avoid doing
2799
+ * additional parsing at runtime (for example, for directive matching). However in some cases (for
2800
+ * example, while bootstrapping a component), a string version of the selector is required to query
2801
+ * for the host element on the page. This function takes the parsed form of a selector and returns
2802
+ * its string representation.
2803
+ *
2804
+ * @param selectorList selector in parsed form
2805
+ * @returns string representation of a given selector
2806
+ */
2807
+ function stringifyCSSSelectorList(selectorList) {
2808
+ return selectorList.map(stringifyCSSSelector).join(',');
2809
+ }
2810
+ /**
2811
+ * Extracts attributes and classes information from a given CSS selector.
2812
+ *
2813
+ * This function is used while creating a component dynamically. In this case, the host element
2814
+ * (that is created dynamically) should contain attributes and classes specified in component's CSS
2815
+ * selector.
2816
+ *
2817
+ * @param selector CSS selector in parsed form (in a form of array)
2818
+ * @returns object with `attrs` and `classes` fields that contain extracted information
2819
+ */
2820
+ function extractAttrsAndClassesFromSelector(selector) {
2821
+ const attrs = [];
2822
+ const classes = [];
2823
+ let i = 1;
2824
+ let mode = 2 /* SelectorFlags.ATTRIBUTE */;
2825
+ while (i < selector.length) {
2826
+ let valueOrMarker = selector[i];
2827
+ if (typeof valueOrMarker === 'string') {
2828
+ if (mode === 2 /* SelectorFlags.ATTRIBUTE */) {
2829
+ if (valueOrMarker !== '') {
2830
+ attrs.push(valueOrMarker, selector[++i]);
2831
+ }
2832
+ }
2833
+ else if (mode === 8 /* SelectorFlags.CLASS */) {
2834
+ classes.push(valueOrMarker);
2835
+ }
2836
+ }
2837
+ else {
2838
+ // According to CssSelector spec, once we come across `SelectorFlags.NOT` flag, the negative
2839
+ // mode is maintained for remaining chunks of a selector. Since attributes and classes are
2840
+ // extracted only for "positive" part of the selector, we can stop here.
2841
+ if (!isPositive(mode))
2842
+ break;
2843
+ mode = valueOrMarker;
2844
+ }
2845
+ i++;
2846
+ }
2847
+ return { attrs, classes };
2848
+ }
2849
+
2850
+ /**
2851
+ * Create a component definition object.
2852
+ *
2853
+ *
2854
+ * # Example
2855
+ * ```
2856
+ * class MyComponent {
2857
+ * // Generated by Angular Template Compiler
2858
+ * // [Symbol] syntax will not be supported by TypeScript until v2.7
2859
+ * static ɵcmp = defineComponent({
2860
+ * ...
2861
+ * });
2862
+ * }
2863
+ * ```
2864
+ * @codeGenApi
2865
+ */
2866
+ function ɵɵdefineComponent(componentDefinition) {
2867
+ return noSideEffects(() => {
2868
+ // Initialize ngDevMode. This must be the first statement in ɵɵdefineComponent.
2869
+ // See the `initNgDevMode` docstring for more information.
2870
+ (typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode();
2871
+ const baseDef = getNgDirectiveDef(componentDefinition);
2872
+ const def = Object.assign(Object.assign({}, baseDef), { decls: componentDefinition.decls, vars: componentDefinition.vars, template: componentDefinition.template, consts: componentDefinition.consts || null, ngContentSelectors: componentDefinition.ngContentSelectors, onPush: componentDefinition.changeDetection === ChangeDetectionStrategy.OnPush, directiveDefs: null, pipeDefs: null, dependencies: baseDef.standalone && componentDefinition.dependencies || null, getStandaloneInjector: null, data: componentDefinition.data || {}, encapsulation: componentDefinition.encapsulation || ViewEncapsulation.Emulated, styles: componentDefinition.styles || EMPTY_ARRAY, _: null, schemas: componentDefinition.schemas || null, tView: null, id: '' });
2873
+ initFeatures(def);
2874
+ const dependencies = componentDefinition.dependencies;
2875
+ def.directiveDefs = extractDefListOrFactory(dependencies, /* pipeDef */ false);
2876
+ def.pipeDefs = extractDefListOrFactory(dependencies, /* pipeDef */ true);
2877
+ def.id = getComponentId(def);
2878
+ return def;
2879
+ });
2880
+ }
2881
+ /**
2882
+ * Generated next to NgModules to monkey-patch directive and pipe references onto a component's
2883
+ * definition, when generating a direct reference in the component file would otherwise create an
2884
+ * import cycle.
2885
+ *
2886
+ * See [this explanation](https://hackmd.io/Odw80D0pR6yfsOjg_7XCJg?view) for more details.
2887
+ *
2888
+ * @codeGenApi
2889
+ */
2890
+ function ɵɵsetComponentScope(type, directives, pipes) {
2891
+ const def = type.ɵcmp;
2892
+ def.directiveDefs = extractDefListOrFactory(directives, /* pipeDef */ false);
2893
+ def.pipeDefs = extractDefListOrFactory(pipes, /* pipeDef */ true);
2894
+ }
2895
+ function extractDirectiveDef(type) {
2896
+ return getComponentDef$1(type) || getDirectiveDef(type);
2897
+ }
2898
+ function nonNull(value) {
2899
+ return value !== null;
2900
+ }
2901
+ /**
2902
+ * @codeGenApi
2903
+ */
2904
+ function ɵɵdefineNgModule(def) {
2905
+ return noSideEffects(() => {
2906
+ const res = {
2907
+ type: def.type,
2908
+ bootstrap: def.bootstrap || EMPTY_ARRAY,
2909
+ declarations: def.declarations || EMPTY_ARRAY,
2910
+ imports: def.imports || EMPTY_ARRAY,
2911
+ exports: def.exports || EMPTY_ARRAY,
2912
+ transitiveCompileScopes: null,
2913
+ schemas: def.schemas || null,
2914
+ id: def.id || null,
2915
+ };
2916
+ return res;
2917
+ });
2918
+ }
2919
+ /**
2920
+ * Adds the module metadata that is necessary to compute the module's transitive scope to an
2921
+ * existing module definition.
2922
+ *
2923
+ * Scope metadata of modules is not used in production builds, so calls to this function can be
2924
+ * marked pure to tree-shake it from the bundle, allowing for all referenced declarations
2925
+ * to become eligible for tree-shaking as well.
2926
+ *
2927
+ * @codeGenApi
2928
+ */
2929
+ function ɵɵsetNgModuleScope(type, scope) {
2930
+ return noSideEffects(() => {
2931
+ const ngModuleDef = getNgModuleDef(type, true);
2932
+ ngModuleDef.declarations = scope.declarations || EMPTY_ARRAY;
2933
+ ngModuleDef.imports = scope.imports || EMPTY_ARRAY;
2934
+ ngModuleDef.exports = scope.exports || EMPTY_ARRAY;
2935
+ });
2936
+ }
2937
+ /**
2938
+ * Inverts an inputs or outputs lookup such that the keys, which were the
2939
+ * minified keys, are part of the values, and the values are parsed so that
2940
+ * the publicName of the property is the new key
2941
+ *
2942
+ * e.g. for
2943
+ *
2944
+ * ```
2945
+ * class Comp {
2946
+ * @Input()
2947
+ * propName1: string;
2948
+ *
2949
+ * @Input('publicName2')
2950
+ * declaredPropName2: number;
2951
+ * }
2952
+ * ```
2953
+ *
2954
+ * will be serialized as
2955
+ *
2956
+ * ```
2957
+ * {
2958
+ * propName1: 'propName1',
2959
+ * declaredPropName2: ['publicName2', 'declaredPropName2'],
2960
+ * }
2961
+ * ```
2962
+ *
2963
+ * which is than translated by the minifier as:
2964
+ *
2965
+ * ```
2966
+ * {
2967
+ * minifiedPropName1: 'propName1',
2968
+ * minifiedPropName2: ['publicName2', 'declaredPropName2'],
2969
+ * }
2970
+ * ```
2971
+ *
2972
+ * becomes: (public name => minifiedName)
2973
+ *
2974
+ * ```
2975
+ * {
2976
+ * 'propName1': 'minifiedPropName1',
2977
+ * 'publicName2': 'minifiedPropName2',
2978
+ * }
2979
+ * ```
2980
+ *
2981
+ * Optionally the function can take `secondary` which will result in: (public name => declared name)
2982
+ *
2983
+ * ```
2984
+ * {
2985
+ * 'propName1': 'propName1',
2986
+ * 'publicName2': 'declaredPropName2',
2987
+ * }
2988
+ * ```
2989
+ *
2990
+
2991
+ */
2992
+ function invertObject(obj, secondary) {
2993
+ if (obj == null)
2994
+ return EMPTY_OBJ;
2995
+ const newLookup = {};
2996
+ for (const minifiedKey in obj) {
2997
+ if (obj.hasOwnProperty(minifiedKey)) {
2998
+ let publicName = obj[minifiedKey];
2999
+ let declaredName = publicName;
3000
+ if (Array.isArray(publicName)) {
3001
+ declaredName = publicName[1];
3002
+ publicName = publicName[0];
3003
+ }
3004
+ newLookup[publicName] = minifiedKey;
3005
+ if (secondary) {
3006
+ (secondary[publicName] = declaredName);
3007
+ }
3008
+ }
3009
+ }
3010
+ return newLookup;
3011
+ }
3012
+ /**
3013
+ * Create a directive definition object.
3014
+ *
3015
+ * # Example
3016
+ * ```ts
3017
+ * class MyDirective {
3018
+ * // Generated by Angular Template Compiler
3019
+ * // [Symbol] syntax will not be supported by TypeScript until v2.7
3020
+ * static ɵdir = ɵɵdefineDirective({
3021
+ * ...
3022
+ * });
3023
+ * }
3024
+ * ```
3025
+ *
3026
+ * @codeGenApi
3027
+ */
3028
+ function ɵɵdefineDirective(directiveDefinition) {
3029
+ return noSideEffects(() => {
3030
+ const def = getNgDirectiveDef(directiveDefinition);
3031
+ initFeatures(def);
3032
+ return def;
3033
+ });
3034
+ }
3035
+ /**
3036
+ * Create a pipe definition object.
3037
+ *
3038
+ * # Example
3039
+ * ```
3040
+ * class MyPipe implements PipeTransform {
3041
+ * // Generated by Angular Template Compiler
3042
+ * static ɵpipe = definePipe({
3043
+ * ...
2439
3044
  * });
2440
3045
  * }
2441
3046
  * ```
@@ -2484,7 +3089,101 @@ function getNgModuleDef(type, throwNotFound) {
2484
3089
  if (!ngModuleDef && throwNotFound === true) {
2485
3090
  throw new Error(`Type ${stringify(type)} does not have 'ɵmod' property.`);
2486
3091
  }
2487
- return ngModuleDef;
3092
+ return ngModuleDef;
3093
+ }
3094
+ function getNgDirectiveDef(directiveDefinition) {
3095
+ const declaredInputs = {};
3096
+ return {
3097
+ type: directiveDefinition.type,
3098
+ providersResolver: null,
3099
+ factory: null,
3100
+ hostBindings: directiveDefinition.hostBindings || null,
3101
+ hostVars: directiveDefinition.hostVars || 0,
3102
+ hostAttrs: directiveDefinition.hostAttrs || null,
3103
+ contentQueries: directiveDefinition.contentQueries || null,
3104
+ declaredInputs,
3105
+ exportAs: directiveDefinition.exportAs || null,
3106
+ standalone: directiveDefinition.standalone === true,
3107
+ selectors: directiveDefinition.selectors || EMPTY_ARRAY,
3108
+ viewQuery: directiveDefinition.viewQuery || null,
3109
+ features: directiveDefinition.features || null,
3110
+ setInput: null,
3111
+ findHostDirectiveDefs: null,
3112
+ hostDirectives: null,
3113
+ inputs: invertObject(directiveDefinition.inputs, declaredInputs),
3114
+ outputs: invertObject(directiveDefinition.outputs),
3115
+ };
3116
+ }
3117
+ function initFeatures(definition) {
3118
+ var _a;
3119
+ (_a = definition.features) === null || _a === void 0 ? void 0 : _a.forEach((fn) => fn(definition));
3120
+ }
3121
+ function extractDefListOrFactory(dependencies, pipeDef) {
3122
+ if (!dependencies) {
3123
+ return null;
3124
+ }
3125
+ const defExtractor = pipeDef ? getPipeDef$1 : extractDirectiveDef;
3126
+ return () => (typeof dependencies === 'function' ? dependencies() : dependencies)
3127
+ .map(dep => defExtractor(dep))
3128
+ .filter(nonNull);
3129
+ }
3130
+ /**
3131
+ * A map that contains the generated component IDs and type.
3132
+ */
3133
+ const GENERATED_COMP_IDS = new Map();
3134
+ /**
3135
+ * A method can returns a component ID from the component definition using a variant of DJB2 hash
3136
+ * algorithm.
3137
+ */
3138
+ function getComponentId(componentDef) {
3139
+ let hash = 0;
3140
+ // We cannot rely solely on the component selector as the same selector can be used in different
3141
+ // modules.
3142
+ //
3143
+ // `componentDef.style` is not used, due to it causing inconsistencies. Ex: when server
3144
+ // component styles has no sourcemaps and browsers do.
3145
+ //
3146
+ // Example:
3147
+ // https://github.com/angular/components/blob/d9f82c8f95309e77a6d82fd574c65871e91354c2/src/material/core/option/option.ts#L248
3148
+ // https://github.com/angular/components/blob/285f46dc2b4c5b127d356cb7c4714b221f03ce50/src/material/legacy-core/option/option.ts#L32
3149
+ const hashSelectors = [
3150
+ componentDef.selectors,
3151
+ componentDef.ngContentSelectors,
3152
+ componentDef.hostVars,
3153
+ componentDef.hostAttrs,
3154
+ componentDef.consts,
3155
+ componentDef.vars,
3156
+ componentDef.decls,
3157
+ componentDef.encapsulation,
3158
+ componentDef.standalone,
3159
+ // We cannot use 'componentDef.type.name' as the name of the symbol will change and will not
3160
+ // match in the server and browser bundles.
3161
+ Object.getOwnPropertyNames(componentDef.type.prototype),
3162
+ !!componentDef.contentQueries,
3163
+ !!componentDef.viewQuery,
3164
+ ].join('|');
3165
+ for (const char of hashSelectors) {
3166
+ hash = Math.imul(31, hash) + char.charCodeAt(0) << 0;
3167
+ }
3168
+ // Force positive number hash.
3169
+ // 2147483647 = equivalent of Integer.MAX_VALUE.
3170
+ hash += 2147483647 + 1;
3171
+ const compId = 'c' + hash;
3172
+ if (typeof ngDevMode === 'undefined' || ngDevMode) {
3173
+ if (GENERATED_COMP_IDS.has(compId)) {
3174
+ const previousCompDefType = GENERATED_COMP_IDS.get(compId);
3175
+ if (previousCompDefType !== componentDef.type) {
3176
+ // TODO: use `formatRuntimeError` to have an error code and we can later on create an error
3177
+ // guide to explain this further.
3178
+ console.warn(`Component ID generation collision detected. Components '${previousCompDefType.name}' and '${componentDef.type.name}' with selector '${stringifyCSSSelectorList(componentDef
3179
+ .selectors)}' generated the same component ID. To fix this, you can change the selector of one of those components or add an extra host attribute to force a different ID.`);
3180
+ }
3181
+ }
3182
+ else {
3183
+ GENERATED_COMP_IDS.set(compId, componentDef.type);
3184
+ }
3185
+ }
3186
+ return compId;
2488
3187
  }
2489
3188
 
2490
3189
  /**
@@ -2514,13 +3213,14 @@ const HAS_TRANSPLANTED_VIEWS = 2;
2514
3213
  const NATIVE = 7;
2515
3214
  const VIEW_REFS = 8;
2516
3215
  const MOVED_VIEWS = 9;
3216
+ const DEHYDRATED_VIEWS = 10;
2517
3217
  /**
2518
3218
  * Size of LContainer's header. Represents the index after which all views in the
2519
3219
  * container will be inserted. We need to keep a record of current views so we know
2520
3220
  * which views are already in the DOM (and don't need to be re-added) and so we can
2521
3221
  * remove views from the DOM when they are no longer required.
2522
3222
  */
2523
- const CONTAINER_HEADER_OFFSET = 10;
3223
+ const CONTAINER_HEADER_OFFSET = 11;
2524
3224
  // Note: This hack is necessary so we don't erroneously get a circular dependency
2525
3225
  // failure based on types.
2526
3226
  const unusedValueExportToPlacateAjd$4 = 1;
@@ -2589,11 +3289,14 @@ function isDirectiveHost(tNode) {
2589
3289
  return (tNode.flags & 1 /* TNodeFlags.isDirectiveHost */) === 1 /* TNodeFlags.isDirectiveHost */;
2590
3290
  }
2591
3291
  function isComponentDef(def) {
2592
- return def.template !== null;
3292
+ return !!def.template;
2593
3293
  }
2594
3294
  function isRootView(target) {
2595
3295
  return (target[FLAGS] & 256 /* LViewFlags.IsRoot */) !== 0;
2596
3296
  }
3297
+ function isProjectionTNode(tNode) {
3298
+ return (tNode.type & 16 /* TNodeType.Projection */) === 16 /* TNodeType.Projection */;
3299
+ }
2597
3300
 
2598
3301
  // [Assert functions do not constraint type when they are guarded by a truthy
2599
3302
  // expression.](https://github.com/microsoft/TypeScript/issues/37295)
@@ -3007,6 +3710,17 @@ function storeLViewOnDestroy(lView, onDestroyCallback) {
3007
3710
  }
3008
3711
  lView[ON_DESTROY_HOOKS].push(onDestroyCallback);
3009
3712
  }
3713
+ /**
3714
+ * Removes previously registered LView-specific destroy callback.
3715
+ */
3716
+ function removeLViewOnDestroy(lView, onDestroyCallback) {
3717
+ if (lView[ON_DESTROY_HOOKS] === null)
3718
+ return;
3719
+ const destroyCBIdx = lView[ON_DESTROY_HOOKS].indexOf(onDestroyCallback);
3720
+ if (destroyCBIdx !== -1) {
3721
+ lView[ON_DESTROY_HOOKS].splice(destroyCBIdx, 1);
3722
+ }
3723
+ }
3010
3724
 
3011
3725
  const instructionState = {
3012
3726
  lFrame: createLFrame(null),
@@ -3046,7 +3760,7 @@ function getBindingsEnabled() {
3046
3760
  * Returns true if currently inside a skip hydration block.
3047
3761
  * @returns boolean
3048
3762
  */
3049
- function isInSkipHydrationBlock() {
3763
+ function isInSkipHydrationBlock$1() {
3050
3764
  return instructionState.skipHydrationRootTNode !== null;
3051
3765
  }
3052
3766
  /**
@@ -3991,206 +4705,6 @@ function assertPureTNodeType(type) {
3991
4705
  }
3992
4706
  }
3993
4707
 
3994
- /**
3995
- * Assigns all attribute values to the provided element via the inferred renderer.
3996
- *
3997
- * This function accepts two forms of attribute entries:
3998
- *
3999
- * default: (key, value):
4000
- * attrs = [key1, value1, key2, value2]
4001
- *
4002
- * namespaced: (NAMESPACE_MARKER, uri, name, value)
4003
- * attrs = [NAMESPACE_MARKER, uri, name, value, NAMESPACE_MARKER, uri, name, value]
4004
- *
4005
- * The `attrs` array can contain a mix of both the default and namespaced entries.
4006
- * The "default" values are set without a marker, but if the function comes across
4007
- * a marker value then it will attempt to set a namespaced value. If the marker is
4008
- * not of a namespaced value then the function will quit and return the index value
4009
- * where it stopped during the iteration of the attrs array.
4010
- *
4011
- * See [AttributeMarker] to understand what the namespace marker value is.
4012
- *
4013
- * Note that this instruction does not support assigning style and class values to
4014
- * an element. See `elementStart` and `elementHostAttrs` to learn how styling values
4015
- * are applied to an element.
4016
- * @param renderer The renderer to be used
4017
- * @param native The element that the attributes will be assigned to
4018
- * @param attrs The attribute array of values that will be assigned to the element
4019
- * @returns the index value that was last accessed in the attributes array
4020
- */
4021
- function setUpAttributes(renderer, native, attrs) {
4022
- let i = 0;
4023
- while (i < attrs.length) {
4024
- const value = attrs[i];
4025
- if (typeof value === 'number') {
4026
- // only namespaces are supported. Other value types (such as style/class
4027
- // entries) are not supported in this function.
4028
- if (value !== 0 /* AttributeMarker.NamespaceURI */) {
4029
- break;
4030
- }
4031
- // we just landed on the marker value ... therefore
4032
- // we should skip to the next entry
4033
- i++;
4034
- const namespaceURI = attrs[i++];
4035
- const attrName = attrs[i++];
4036
- const attrVal = attrs[i++];
4037
- ngDevMode && ngDevMode.rendererSetAttribute++;
4038
- renderer.setAttribute(native, attrName, attrVal, namespaceURI);
4039
- }
4040
- else {
4041
- // attrName is string;
4042
- const attrName = value;
4043
- const attrVal = attrs[++i];
4044
- // Standard attributes
4045
- ngDevMode && ngDevMode.rendererSetAttribute++;
4046
- if (isAnimationProp(attrName)) {
4047
- renderer.setProperty(native, attrName, attrVal);
4048
- }
4049
- else {
4050
- renderer.setAttribute(native, attrName, attrVal);
4051
- }
4052
- i++;
4053
- }
4054
- }
4055
- // another piece of code may iterate over the same attributes array. Therefore
4056
- // it may be helpful to return the exact spot where the attributes array exited
4057
- // whether by running into an unsupported marker or if all the static values were
4058
- // iterated over.
4059
- return i;
4060
- }
4061
- /**
4062
- * Test whether the given value is a marker that indicates that the following
4063
- * attribute values in a `TAttributes` array are only the names of attributes,
4064
- * and not name-value pairs.
4065
- * @param marker The attribute marker to test.
4066
- * @returns true if the marker is a "name-only" marker (e.g. `Bindings`, `Template` or `I18n`).
4067
- */
4068
- function isNameOnlyAttributeMarker(marker) {
4069
- return marker === 3 /* AttributeMarker.Bindings */ || marker === 4 /* AttributeMarker.Template */ ||
4070
- marker === 6 /* AttributeMarker.I18n */;
4071
- }
4072
- function isAnimationProp(name) {
4073
- // Perf note: accessing charCodeAt to check for the first character of a string is faster as
4074
- // compared to accessing a character at index 0 (ex. name[0]). The main reason for this is that
4075
- // charCodeAt doesn't allocate memory to return a substring.
4076
- return name.charCodeAt(0) === 64 /* CharCode.AT_SIGN */;
4077
- }
4078
- /**
4079
- * Merges `src` `TAttributes` into `dst` `TAttributes` removing any duplicates in the process.
4080
- *
4081
- * This merge function keeps the order of attrs same.
4082
- *
4083
- * @param dst Location of where the merged `TAttributes` should end up.
4084
- * @param src `TAttributes` which should be appended to `dst`
4085
- */
4086
- function mergeHostAttrs(dst, src) {
4087
- if (src === null || src.length === 0) {
4088
- // do nothing
4089
- }
4090
- else if (dst === null || dst.length === 0) {
4091
- // We have source, but dst is empty, just make a copy.
4092
- dst = src.slice();
4093
- }
4094
- else {
4095
- let srcMarker = -1 /* AttributeMarker.ImplicitAttributes */;
4096
- for (let i = 0; i < src.length; i++) {
4097
- const item = src[i];
4098
- if (typeof item === 'number') {
4099
- srcMarker = item;
4100
- }
4101
- else {
4102
- if (srcMarker === 0 /* AttributeMarker.NamespaceURI */) {
4103
- // Case where we need to consume `key1`, `key2`, `value` items.
4104
- }
4105
- else if (srcMarker === -1 /* AttributeMarker.ImplicitAttributes */ ||
4106
- srcMarker === 2 /* AttributeMarker.Styles */) {
4107
- // Case where we have to consume `key1` and `value` only.
4108
- mergeHostAttribute(dst, srcMarker, item, null, src[++i]);
4109
- }
4110
- else {
4111
- // Case where we have to consume `key1` only.
4112
- mergeHostAttribute(dst, srcMarker, item, null, null);
4113
- }
4114
- }
4115
- }
4116
- }
4117
- return dst;
4118
- }
4119
- /**
4120
- * Append `key`/`value` to existing `TAttributes` taking region marker and duplicates into account.
4121
- *
4122
- * @param dst `TAttributes` to append to.
4123
- * @param marker Region where the `key`/`value` should be added.
4124
- * @param key1 Key to add to `TAttributes`
4125
- * @param key2 Key to add to `TAttributes` (in case of `AttributeMarker.NamespaceURI`)
4126
- * @param value Value to add or to overwrite to `TAttributes` Only used if `marker` is not Class.
4127
- */
4128
- function mergeHostAttribute(dst, marker, key1, key2, value) {
4129
- let i = 0;
4130
- // Assume that new markers will be inserted at the end.
4131
- let markerInsertPosition = dst.length;
4132
- // scan until correct type.
4133
- if (marker === -1 /* AttributeMarker.ImplicitAttributes */) {
4134
- markerInsertPosition = -1;
4135
- }
4136
- else {
4137
- while (i < dst.length) {
4138
- const dstValue = dst[i++];
4139
- if (typeof dstValue === 'number') {
4140
- if (dstValue === marker) {
4141
- markerInsertPosition = -1;
4142
- break;
4143
- }
4144
- else if (dstValue > marker) {
4145
- // We need to save this as we want the markers to be inserted in specific order.
4146
- markerInsertPosition = i - 1;
4147
- break;
4148
- }
4149
- }
4150
- }
4151
- }
4152
- // search until you find place of insertion
4153
- while (i < dst.length) {
4154
- const item = dst[i];
4155
- if (typeof item === 'number') {
4156
- // since `i` started as the index after the marker, we did not find it if we are at the next
4157
- // marker
4158
- break;
4159
- }
4160
- else if (item === key1) {
4161
- // We already have same token
4162
- if (key2 === null) {
4163
- if (value !== null) {
4164
- dst[i + 1] = value;
4165
- }
4166
- return;
4167
- }
4168
- else if (key2 === dst[i + 1]) {
4169
- dst[i + 2] = value;
4170
- return;
4171
- }
4172
- }
4173
- // Increment counter.
4174
- i++;
4175
- if (key2 !== null)
4176
- i++;
4177
- if (value !== null)
4178
- i++;
4179
- }
4180
- // insert at location.
4181
- if (markerInsertPosition !== -1) {
4182
- dst.splice(markerInsertPosition, 0, marker);
4183
- i = markerInsertPosition + 1;
4184
- }
4185
- dst.splice(i++, 0, key1);
4186
- if (key2 !== null) {
4187
- dst.splice(i++, 0, key2);
4188
- }
4189
- if (value !== null) {
4190
- dst.splice(i++, 0, value);
4191
- }
4192
- }
4193
-
4194
4708
  /// Parent Injector Utils ///////////////////////////////////////////////////////////////
4195
4709
  function hasParentInjector(parentLocation) {
4196
4710
  return parentLocation !== NO_PARENT_INJECTOR;
@@ -8309,6 +8823,7 @@ class R3Injector extends EnvironmentInjector {
8309
8823
  }
8310
8824
  onDestroy(callback) {
8311
8825
  this._onDestroyHooks.push(callback);
8826
+ return () => this.removeOnDestroy(callback);
8312
8827
  }
8313
8828
  runInContext(fn) {
8314
8829
  this.assertNotDestroyed();
@@ -8483,6 +8998,12 @@ class R3Injector extends EnvironmentInjector {
8483
8998
  return this.injectorDefTypes.has(providedIn);
8484
8999
  }
8485
9000
  }
9001
+ removeOnDestroy(callback) {
9002
+ const destroyCBIdx = this._onDestroyHooks.indexOf(callback);
9003
+ if (destroyCBIdx !== -1) {
9004
+ this._onDestroyHooks.splice(destroyCBIdx, 1);
9005
+ }
9006
+ }
8486
9007
  }
8487
9008
  function injectableDefOrInjectorDefFactory(token) {
8488
9009
  // Most tokens will have an injectable def directly on them, which specifies a factory directly.
@@ -8605,35 +9126,41 @@ function forEachSingleProvider(providers, fn) {
8605
9126
  }
8606
9127
 
8607
9128
  /**
8608
- * A [DI token](guide/glossary#di-token "DI token definition") representing a unique string ID, used
9129
+ * A [DI token](guide/glossary#di-token "DI token definition") representing a string ID, used
8609
9130
  * primarily for prefixing application attributes and CSS styles when
8610
9131
  * {@link ViewEncapsulation#Emulated ViewEncapsulation.Emulated} is being used.
8611
9132
  *
8612
- * BY default, the value is randomly generated and assigned to the application by Angular.
8613
- * To provide a custom ID value, use a DI provider <!-- TODO: provider --> to configure
8614
- * the root {@link Injector} that uses this token.
9133
+ * The token is needed in cases when multiple applications are bootstrapped on a page
9134
+ * (for example, using `bootstrapApplication` calls). In this case, ensure that those applications
9135
+ * have different `APP_ID` value setup. For example:
9136
+ *
9137
+ * ```
9138
+ * bootstrapApplication(ComponentA, {
9139
+ * providers: [
9140
+ * { provide: APP_ID, useValue: 'app-a' },
9141
+ * // ... other providers ...
9142
+ * ]
9143
+ * });
9144
+ *
9145
+ * bootstrapApplication(ComponentB, {
9146
+ * providers: [
9147
+ * { provide: APP_ID, useValue: 'app-b' },
9148
+ * // ... other providers ...
9149
+ * ]
9150
+ * });
9151
+ * ```
9152
+ *
9153
+ * By default, when there is only one application bootstrapped, you don't need to provide the
9154
+ * `APP_ID` token (the `ng` will be used as an app ID).
8615
9155
  *
8616
9156
  * @publicApi
8617
9157
  */
8618
9158
  const APP_ID = new InjectionToken('AppId', {
8619
9159
  providedIn: 'root',
8620
- factory: _appIdRandomProviderFactory,
9160
+ factory: () => DEFAULT_APP_ID,
8621
9161
  });
8622
- function _appIdRandomProviderFactory() {
8623
- return `${_randomChar()}${_randomChar()}${_randomChar()}`;
8624
- }
8625
- /**
8626
- * Providers that generate a random `APP_ID_TOKEN`.
8627
- * @publicApi
8628
- */
8629
- const APP_ID_RANDOM_PROVIDER = {
8630
- provide: APP_ID,
8631
- useFactory: _appIdRandomProviderFactory,
8632
- deps: [],
8633
- };
8634
- function _randomChar() {
8635
- return String.fromCharCode(97 + Math.floor(Math.random() * 25));
8636
- }
9162
+ /** Default value of the `APP_ID` token. */
9163
+ const DEFAULT_APP_ID = 'ng';
8637
9164
  /**
8638
9165
  * A function that is executed when a platform is initialized.
8639
9166
  * @publicApi
@@ -8662,6 +9189,38 @@ const PACKAGE_ROOT_URL = new InjectionToken('Application Packages Root URL');
8662
9189
  * @publicApi
8663
9190
  */
8664
9191
  const ANIMATION_MODULE_TYPE = new InjectionToken('AnimationModuleType');
9192
+ // TODO(crisbeto): link to CSP guide here.
9193
+ /**
9194
+ * Token used to configure the [Content Security Policy](https://web.dev/strict-csp/) nonce that
9195
+ * Angular will apply when inserting inline styles. If not provided, Angular will look up its value
9196
+ * from the `ngCspNonce` attribute of the application root node.
9197
+ *
9198
+ * @publicApi
9199
+ */
9200
+ const CSP_NONCE = new InjectionToken('CSP nonce', {
9201
+ providedIn: 'root',
9202
+ factory: () => {
9203
+ var _a;
9204
+ // Ideally we wouldn't have to use `querySelector` here since we know that the nonce will be on
9205
+ // the root node, but because the token value is used in renderers, it has to be available
9206
+ // *very* early in the bootstrapping process. This should be a fairly shallow search, because
9207
+ // the app won't have been added to the DOM yet. Some approaches that were considered:
9208
+ // 1. Find the root node through `ApplicationRef.components[i].location` - normally this would
9209
+ // be enough for our purposes, but the token is injected very early so the `components` array
9210
+ // isn't populated yet.
9211
+ // 2. Find the root `LView` through the current `LView` - renderers are a prerequisite to
9212
+ // creating the `LView`. This means that no `LView` will have been entered when this factory is
9213
+ // invoked for the root component.
9214
+ // 3. Have the token factory return `() => string` which is invoked when a nonce is requested -
9215
+ // the slightly later execution does allow us to get an `LView` reference, but the fact that
9216
+ // it is a function means that it could be executed at *any* time (including immediately) which
9217
+ // may lead to weird bugs.
9218
+ // 4. Have the `ComponentFactory` read the attribute and provide it to the injector under the
9219
+ // hood - has the same problem as #1 and #2 in that the renderer is used to query for the root
9220
+ // node and the nonce value needs to be available when the renderer is created.
9221
+ return ((_a = getDocument().body.querySelector('[ngCspNonce]')) === null || _a === void 0 ? void 0 : _a.getAttribute('ngCspNonce')) || null;
9222
+ },
9223
+ });
8665
9224
 
8666
9225
  function escapeTransferStateContent(text) {
8667
9226
  const escapedText = {
@@ -8804,8 +9363,31 @@ function retrieveTransferredState(doc, appId) {
8804
9363
  return initialState;
8805
9364
  }
8806
9365
 
8807
- /* Represents a key in NghDom that holds information about <ng-container>s. */
9366
+ /** Encodes that the node lookup should start from the host node of this component. */
9367
+ const REFERENCE_NODE_HOST = 'h';
9368
+ /** Encodes that the node lookup should start from the document body node. */
9369
+ const REFERENCE_NODE_BODY = 'b';
9370
+ /**
9371
+ * Describes navigation steps that the runtime logic need to perform,
9372
+ * starting from a given (known) element.
9373
+ */
9374
+ var NodeNavigationStep;
9375
+ (function (NodeNavigationStep) {
9376
+ NodeNavigationStep["FirstChild"] = "f";
9377
+ NodeNavigationStep["NextSibling"] = "n";
9378
+ })(NodeNavigationStep || (NodeNavigationStep = {}));
9379
+ /**
9380
+ * Keys within serialized view data structure to represent various
9381
+ * parts. See the `SerializedView` interface below for additional information.
9382
+ */
8808
9383
  const ELEMENT_CONTAINERS = 'e';
9384
+ const TEMPLATES = 't';
9385
+ const CONTAINERS = 'c';
9386
+ const MULTIPLIER = 'x';
9387
+ const NUM_ROOT_NODES = 'r';
9388
+ const TEMPLATE_ID = 'i'; // as it's also an "id"
9389
+ const NODES = 'n';
9390
+ const DISCONNECTED_NODES = 'd';
8809
9391
 
8810
9392
  /**
8811
9393
  * The name of the key used in the TransferState collection,
@@ -8868,7 +9450,7 @@ function retrieveHydrationInfoImpl(rNode, injector) {
8868
9450
  return dehydratedView;
8869
9451
  }
8870
9452
  /**
8871
- * Sets the implementation for the `retrieveNghInfo` function.
9453
+ * Sets the implementation for the `retrieveHydrationInfo` function.
8872
9454
  */
8873
9455
  function enableRetrieveHydrationInfoImpl() {
8874
9456
  _retrieveHydrationInfoImpl = retrieveHydrationInfoImpl;
@@ -8898,7 +9480,48 @@ function getComponentLViewForHydration(viewRef) {
8898
9480
  if (isRootView(lView)) {
8899
9481
  lView = lView[HEADER_OFFSET];
8900
9482
  }
8901
- return lView;
9483
+ return lView;
9484
+ }
9485
+ function getTextNodeContent(node) {
9486
+ var _a;
9487
+ return (_a = node.textContent) === null || _a === void 0 ? void 0 : _a.replace(/\s/gm, '');
9488
+ }
9489
+ /**
9490
+ * Restores text nodes and separators into the DOM that were lost during SSR
9491
+ * serialization. The hydration process replaces empty text nodes and text
9492
+ * nodes that are immediately adjacent to other text nodes with comment nodes
9493
+ * that this method filters on to restore those missing nodes that the
9494
+ * hydration process is expecting to be present.
9495
+ *
9496
+ * @param node The app's root HTML Element
9497
+ */
9498
+ function processTextNodeMarkersBeforeHydration(node) {
9499
+ const doc = getDocument();
9500
+ const commentNodesIterator = doc.createNodeIterator(node, NodeFilter.SHOW_COMMENT, {
9501
+ acceptNode(node) {
9502
+ const content = getTextNodeContent(node);
9503
+ const isTextNodeMarker = content === "ngetn" /* TextNodeMarker.EmptyNode */ || content === "ngtns" /* TextNodeMarker.Separator */;
9504
+ return isTextNodeMarker ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
9505
+ }
9506
+ });
9507
+ let currentNode;
9508
+ // We cannot modify the DOM while using the commentIterator,
9509
+ // because it throws off the iterator state.
9510
+ // So we collect all marker nodes first and then follow up with
9511
+ // applying the changes to the DOM: either inserting an empty node
9512
+ // or just removing the marker if it was used as a separator.
9513
+ const nodes = [];
9514
+ while (currentNode = commentNodesIterator.nextNode()) {
9515
+ nodes.push(currentNode);
9516
+ }
9517
+ for (const node of nodes) {
9518
+ if (node.textContent === "ngetn" /* TextNodeMarker.EmptyNode */) {
9519
+ node.replaceWith(doc.createTextNode(''));
9520
+ }
9521
+ else {
9522
+ node.remove();
9523
+ }
9524
+ }
8902
9525
  }
8903
9526
  /**
8904
9527
  * Marks a node as "claimed" by hydration process.
@@ -8919,830 +9542,447 @@ function markRNodeAsClaimedByHydration(node, checkIfAlreadyClaimed = true) {
8919
9542
  function isRNodeClaimedForHydration(node) {
8920
9543
  return !!node.__claimed;
8921
9544
  }
8922
- function storeNgContainerInfo(hydrationInfo, index, firstChild) {
9545
+ function setSegmentHead(hydrationInfo, index, node) {
8923
9546
  var _a;
8924
- (_a = hydrationInfo.ngContainers) !== null && _a !== void 0 ? _a : (hydrationInfo.ngContainers = {});
8925
- hydrationInfo.ngContainers[index] = { firstChild };
9547
+ (_a = hydrationInfo.segmentHeads) !== null && _a !== void 0 ? _a : (hydrationInfo.segmentHeads = {});
9548
+ hydrationInfo.segmentHeads[index] = node;
8926
9549
  }
8927
- function getNgContainerSize(hydrationInfo, index) {
9550
+ function getSegmentHead(hydrationInfo, index) {
8928
9551
  var _a, _b;
8929
- return (_b = (_a = hydrationInfo.data[ELEMENT_CONTAINERS]) === null || _a === void 0 ? void 0 : _a[index]) !== null && _b !== void 0 ? _b : null;
8930
- }
8931
-
8932
- /**
8933
- * Represents a component created by a `ComponentFactory`.
8934
- * Provides access to the component instance and related objects,
8935
- * and provides the means of destroying the instance.
8936
- *
8937
- * @publicApi
8938
- */
8939
- class ComponentRef$1 {
8940
- }
8941
- /**
8942
- * Base class for a factory that can create a component dynamically.
8943
- * Instantiate a factory for a given type of component with `resolveComponentFactory()`.
8944
- * Use the resulting `ComponentFactory.create()` method to create a component of that type.
8945
- *
8946
- * @see [Dynamic Components](guide/dynamic-component-loader)
8947
- *
8948
- * @publicApi
8949
- *
8950
- * @deprecated Angular no longer requires Component factories. Please use other APIs where
8951
- * Component class can be used directly.
8952
- */
8953
- class ComponentFactory$1 {
8954
- }
8955
-
8956
- function noComponentFactoryError(component) {
8957
- const error = Error(`No component factory found for ${stringify(component)}. Did you add it to @NgModule.entryComponents?`);
8958
- error[ERROR_COMPONENT] = component;
8959
- return error;
8960
- }
8961
- const ERROR_COMPONENT = 'ngComponent';
8962
- function getComponent$1(error) {
8963
- return error[ERROR_COMPONENT];
8964
- }
8965
- class _NullComponentFactoryResolver {
8966
- resolveComponentFactory(component) {
8967
- throw noComponentFactoryError(component);
8968
- }
8969
- }
8970
- /**
8971
- * A simple registry that maps `Components` to generated `ComponentFactory` classes
8972
- * that can be used to create instances of components.
8973
- * Use to obtain the factory for a given component type,
8974
- * then use the factory's `create()` method to create a component of that type.
8975
- *
8976
- * Note: since v13, dynamic component creation via
8977
- * [`ViewContainerRef.createComponent`](api/core/ViewContainerRef#createComponent)
8978
- * does **not** require resolving component factory: component class can be used directly.
8979
- *
8980
- * @publicApi
8981
- *
8982
- * @deprecated Angular no longer requires Component factories. Please use other APIs where
8983
- * Component class can be used directly.
8984
- */
8985
- class ComponentFactoryResolver$1 {
8986
- }
8987
- ComponentFactoryResolver$1.NULL = ( /* @__PURE__ */new _NullComponentFactoryResolver());
8988
-
8989
- /**
8990
- * Creates an ElementRef from the most recent node.
8991
- *
8992
- * @returns The ElementRef instance to use
8993
- */
8994
- function injectElementRef() {
8995
- return createElementRef(getCurrentTNode(), getLView());
8996
- }
8997
- /**
8998
- * Creates an ElementRef given a node.
8999
- *
9000
- * @param tNode The node for which you'd like an ElementRef
9001
- * @param lView The view to which the node belongs
9002
- * @returns The ElementRef instance to use
9003
- */
9004
- function createElementRef(tNode, lView) {
9005
- return new ElementRef(getNativeByTNode(tNode, lView));
9006
- }
9007
- /**
9008
- * A wrapper around a native element inside of a View.
9009
- *
9010
- * An `ElementRef` is backed by a render-specific element. In the browser, this is usually a DOM
9011
- * element.
9012
- *
9013
- * @security Permitting direct access to the DOM can make your application more vulnerable to
9014
- * XSS attacks. Carefully review any use of `ElementRef` in your code. For more detail, see the
9015
- * [Security Guide](https://g.co/ng/security).
9016
- *
9017
- * @publicApi
9018
- */
9019
- // Note: We don't expose things like `Injector`, `ViewContainer`, ... here,
9020
- // i.e. users have to ask for what they need. With that, we can build better analysis tools
9021
- // and could do better codegen in the future.
9022
- class ElementRef {
9023
- constructor(nativeElement) {
9024
- this.nativeElement = nativeElement;
9025
- }
9026
- }
9027
- /**
9028
- * @internal
9029
- * @nocollapse
9030
- */
9031
- ElementRef.__NG_ELEMENT_ID__ = injectElementRef;
9032
- /**
9033
- * Unwraps `ElementRef` and return the `nativeElement`.
9034
- *
9035
- * @param value value to unwrap
9036
- * @returns `nativeElement` if `ElementRef` otherwise returns value as is.
9037
- */
9038
- function unwrapElementRef(value) {
9039
- return value instanceof ElementRef ? value.nativeElement : value;
9040
- }
9041
-
9042
- /**
9043
- * Creates and initializes a custom renderer that implements the `Renderer2` base class.
9044
- *
9045
- * @publicApi
9046
- */
9047
- class RendererFactory2 {
9048
- }
9049
- /**
9050
- * Extend this base class to implement custom rendering. By default, Angular
9051
- * renders a template into DOM. You can use custom rendering to intercept
9052
- * rendering calls, or to render to something other than DOM.
9053
- *
9054
- * Create your custom renderer using `RendererFactory2`.
9055
- *
9056
- * Use a custom renderer to bypass Angular's templating and
9057
- * make custom UI changes that can't be expressed declaratively.
9058
- * For example if you need to set a property or an attribute whose name is
9059
- * not statically known, use the `setProperty()` or
9060
- * `setAttribute()` method.
9061
- *
9062
- * @publicApi
9063
- */
9064
- class Renderer2 {
9065
- }
9066
- /**
9067
- * @internal
9068
- * @nocollapse
9069
- */
9070
- Renderer2.__NG_ELEMENT_ID__ = () => injectRenderer2();
9071
- /** Injects a Renderer2 for the current component. */
9072
- function injectRenderer2() {
9073
- // We need the Renderer to be based on the component that it's being injected into, however since
9074
- // DI happens before we've entered its view, `getLView` will return the parent view instead.
9075
- const lView = getLView();
9076
- const tNode = getCurrentTNode();
9077
- const nodeAtIndex = getComponentLViewByIndex(tNode.index, lView);
9078
- return (isLView(nodeAtIndex) ? nodeAtIndex : lView)[RENDERER];
9552
+ return (_b = (_a = hydrationInfo.segmentHeads) === null || _a === void 0 ? void 0 : _a[index]) !== null && _b !== void 0 ? _b : null;
9079
9553
  }
9080
-
9081
9554
  /**
9082
- * Sanitizer is used by the views to sanitize potentially dangerous values.
9083
- *
9084
- * @publicApi
9555
+ * Returns the size of an <ng-container>, using either the information
9556
+ * serialized in `ELEMENT_CONTAINERS` (element container size) or by
9557
+ * computing the sum of root nodes in all dehydrated views in a given
9558
+ * container (in case this `<ng-container>` was also used as a view
9559
+ * container host node, e.g. <ng-container *ngIf>).
9085
9560
  */
9086
- class Sanitizer {
9561
+ function getNgContainerSize(hydrationInfo, index) {
9562
+ var _a, _b, _c;
9563
+ const data = hydrationInfo.data;
9564
+ let size = (_b = (_a = data[ELEMENT_CONTAINERS]) === null || _a === void 0 ? void 0 : _a[index]) !== null && _b !== void 0 ? _b : null;
9565
+ // If there is no serialized information available in the `ELEMENT_CONTAINERS` slot,
9566
+ // check if we have info about view containers at this location (e.g.
9567
+ // `<ng-container *ngIf>`) and use container size as a number of root nodes in this
9568
+ // element container.
9569
+ if (size === null && ((_c = data[CONTAINERS]) === null || _c === void 0 ? void 0 : _c[index])) {
9570
+ size = calcSerializedContainerSize(hydrationInfo, index);
9571
+ }
9572
+ return size;
9573
+ }
9574
+ function getSerializedContainerViews(hydrationInfo, index) {
9575
+ var _a, _b;
9576
+ return (_b = (_a = hydrationInfo.data[CONTAINERS]) === null || _a === void 0 ? void 0 : _a[index]) !== null && _b !== void 0 ? _b : null;
9087
9577
  }
9088
- /** @nocollapse */
9089
- Sanitizer.ɵprov = ɵɵdefineInjectable({
9090
- token: Sanitizer,
9091
- providedIn: 'root',
9092
- factory: () => null,
9093
- });
9094
-
9095
9578
  /**
9096
- * @description Represents the version of Angular
9097
- *
9098
- * @publicApi
9579
+ * Computes the size of a serialized container (the number of root nodes)
9580
+ * by calculating the sum of root nodes in all dehydrated views in this container.
9099
9581
  */
9100
- class Version {
9101
- constructor(full) {
9102
- this.full = full;
9103
- this.major = full.split('.')[0];
9104
- this.minor = full.split('.')[1];
9105
- this.patch = full.split('.').slice(2).join('.');
9582
+ function calcSerializedContainerSize(hydrationInfo, index) {
9583
+ var _a, _b;
9584
+ const views = (_a = getSerializedContainerViews(hydrationInfo, index)) !== null && _a !== void 0 ? _a : [];
9585
+ let numNodes = 0;
9586
+ for (let view of views) {
9587
+ numNodes += view[NUM_ROOT_NODES] * ((_b = view[MULTIPLIER]) !== null && _b !== void 0 ? _b : 1);
9106
9588
  }
9589
+ return numNodes;
9107
9590
  }
9108
9591
  /**
9109
- * @publicApi
9110
- */
9111
- const VERSION = new Version('16.0.0-next.2');
9112
-
9113
- // This default value is when checking the hierarchy for a token.
9114
- //
9115
- // It means both:
9116
- // - the token is not provided by the current injector,
9117
- // - only the element injectors should be checked (ie do not check module injectors
9118
- //
9119
- // mod1
9120
- // /
9121
- // el1 mod2
9122
- // \ /
9123
- // el2
9124
- //
9125
- // When requesting el2.injector.get(token), we should check in the following order and return the
9126
- // first found value:
9127
- // - el2.injector.get(token, default)
9128
- // - el1.injector.get(token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) -> do not check the module
9129
- // - mod2.injector.get(token, default)
9130
- const NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR = {};
9131
-
9132
- const ERROR_ORIGINAL_ERROR = 'ngOriginalError';
9133
- function wrappedError(message, originalError) {
9134
- const msg = `${message} caused by: ${originalError instanceof Error ? originalError.message : originalError}`;
9135
- const error = Error(msg);
9136
- error[ERROR_ORIGINAL_ERROR] = originalError;
9137
- return error;
9138
- }
9139
- function getOriginalError(error) {
9140
- return error[ERROR_ORIGINAL_ERROR];
9141
- }
9142
-
9143
- /**
9144
- * Provides a hook for centralized exception handling.
9145
- *
9146
- * The default implementation of `ErrorHandler` prints error messages to the `console`. To
9147
- * intercept error handling, write a custom exception handler that replaces this default as
9148
- * appropriate for your app.
9149
- *
9150
- * @usageNotes
9151
- * ### Example
9152
- *
9153
- * ```
9154
- * class MyErrorHandler implements ErrorHandler {
9155
- * handleError(error) {
9156
- * // do something with the exception
9157
- * }
9158
- * }
9159
- *
9160
- * @NgModule({
9161
- * providers: [{provide: ErrorHandler, useClass: MyErrorHandler}]
9162
- * })
9163
- * class MyModule {}
9164
- * ```
9165
- *
9166
- * @publicApi
9592
+ * Checks whether a node is annotated as "disconnected", i.e. not present
9593
+ * in the DOM at serialization time. We should not attempt hydration for
9594
+ * such nodes and instead, use a regular "creation mode".
9167
9595
  */
9168
- class ErrorHandler {
9169
- constructor() {
9170
- /**
9171
- * @internal
9172
- */
9173
- this._console = console;
9174
- }
9175
- handleError(error) {
9176
- const originalError = this._findOriginalError(error);
9177
- this._console.error('ERROR', error);
9178
- if (originalError) {
9179
- this._console.error('ORIGINAL ERROR', originalError);
9180
- }
9181
- }
9182
- /** @internal */
9183
- _findOriginalError(error) {
9184
- let e = error && getOriginalError(error);
9185
- while (e && getOriginalError(e)) {
9186
- e = getOriginalError(e);
9187
- }
9188
- return e || null;
9596
+ function isDisconnectedNode(hydrationInfo, index) {
9597
+ var _a;
9598
+ // Check if we are processing disconnected info for the first time.
9599
+ if (typeof hydrationInfo.disconnectedNodes === 'undefined') {
9600
+ const nodeIds = hydrationInfo.data[DISCONNECTED_NODES];
9601
+ hydrationInfo.disconnectedNodes = nodeIds ? (new Set(nodeIds)) : null;
9189
9602
  }
9603
+ return !!((_a = hydrationInfo.disconnectedNodes) === null || _a === void 0 ? void 0 : _a.has(index));
9190
9604
  }
9191
9605
 
9192
- const NG_DEV_MODE = typeof ngDevMode === 'undefined' || !!ngDevMode;
9193
9606
  /**
9194
- * Internal token that specifies whether hydration is enabled.
9607
+ * Represents a component created by a `ComponentFactory`.
9608
+ * Provides access to the component instance and related objects,
9609
+ * and provides the means of destroying the instance.
9610
+ *
9611
+ * @publicApi
9195
9612
  */
9196
- const IS_HYDRATION_FEATURE_ENABLED = new InjectionToken(NG_DEV_MODE ? 'IS_HYDRATION_FEATURE_ENABLED' : '');
9197
- // By default (in client rendering mode), we remove all the contents
9198
- // of the host element and render an application after that.
9199
- const PRESERVE_HOST_CONTENT_DEFAULT = false;
9613
+ class ComponentRef$1 {
9614
+ }
9200
9615
  /**
9201
- * Internal token that indicates whether host element content should be
9202
- * retained during the bootstrap.
9616
+ * Base class for a factory that can create a component dynamically.
9617
+ * Instantiate a factory for a given type of component with `resolveComponentFactory()`.
9618
+ * Use the resulting `ComponentFactory.create()` method to create a component of that type.
9619
+ *
9620
+ * @see [Dynamic Components](guide/dynamic-component-loader)
9621
+ *
9622
+ * @publicApi
9623
+ *
9624
+ * @deprecated Angular no longer requires Component factories. Please use other APIs where
9625
+ * Component class can be used directly.
9203
9626
  */
9204
- const PRESERVE_HOST_CONTENT = new InjectionToken(NG_DEV_MODE ? 'PRESERVE_HOST_CONTENT' : '', {
9205
- providedIn: 'root',
9206
- factory: () => PRESERVE_HOST_CONTENT_DEFAULT,
9207
- });
9208
-
9209
- function normalizeDebugBindingName(name) {
9210
- // Attribute names with `$` (eg `x-y$`) are valid per spec, but unsupported by some browsers
9211
- name = camelCaseToDashCase(name.replace(/[$@]/g, '_'));
9212
- return `ng-reflect-${name}`;
9213
- }
9214
- const CAMEL_CASE_REGEXP = /([A-Z])/g;
9215
- function camelCaseToDashCase(input) {
9216
- return input.replace(CAMEL_CASE_REGEXP, (...m) => '-' + m[1].toLowerCase());
9217
- }
9218
- function normalizeDebugBindingValue(value) {
9219
- try {
9220
- // Limit the size of the value as otherwise the DOM just gets polluted.
9221
- return value != null ? value.toString().slice(0, 30) : value;
9222
- }
9223
- catch (e) {
9224
- return '[ERROR] Exception while trying to serialize the value';
9225
- }
9627
+ class ComponentFactory$1 {
9226
9628
  }
9227
9629
 
9228
- /** Verifies that a given type is a Standalone Component. */
9229
- function assertStandaloneComponentType(type) {
9230
- assertComponentDef(type);
9231
- const componentDef = getComponentDef$1(type);
9232
- if (!componentDef.standalone) {
9233
- throw new RuntimeError(907 /* RuntimeErrorCode.TYPE_IS_NOT_STANDALONE */, `The ${stringifyForError(type)} component is not marked as standalone, ` +
9234
- `but Angular expects to have a standalone component here. ` +
9235
- `Please make sure the ${stringifyForError(type)} component has ` +
9236
- `the \`standalone: true\` flag in the decorator.`);
9237
- }
9238
- }
9239
- /** Verifies whether a given type is a component */
9240
- function assertComponentDef(type) {
9241
- if (!getComponentDef$1(type)) {
9242
- throw new RuntimeError(906 /* RuntimeErrorCode.MISSING_GENERATED_DEF */, `The ${stringifyForError(type)} is not an Angular component, ` +
9243
- `make sure it has the \`@Component\` decorator.`);
9244
- }
9245
- }
9246
- /** Called when there are multiple component selectors that match a given node */
9247
- function throwMultipleComponentError(tNode, first, second) {
9248
- throw new RuntimeError(-300 /* RuntimeErrorCode.MULTIPLE_COMPONENTS_MATCH */, `Multiple components match node with tagname ${tNode.value}: ` +
9249
- `${stringifyForError(first)} and ` +
9250
- `${stringifyForError(second)}`);
9630
+ function noComponentFactoryError(component) {
9631
+ const error = Error(`No component factory found for ${stringify(component)}. Did you add it to @NgModule.entryComponents?`);
9632
+ error[ERROR_COMPONENT] = component;
9633
+ return error;
9251
9634
  }
9252
- /** Throws an ExpressionChangedAfterChecked error if checkNoChanges mode is on. */
9253
- function throwErrorIfNoChangesMode(creationMode, oldValue, currValue, propName) {
9254
- const field = propName ? ` for '${propName}'` : '';
9255
- let msg = `ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value${field}: '${oldValue}'. Current value: '${currValue}'.`;
9256
- if (creationMode) {
9257
- msg +=
9258
- ` It seems like the view has been created after its parent and its children have been dirty checked.` +
9259
- ` Has it been created in a change detection hook?`;
9260
- }
9261
- throw new RuntimeError(-100 /* RuntimeErrorCode.EXPRESSION_CHANGED_AFTER_CHECKED */, msg);
9635
+ const ERROR_COMPONENT = 'ngComponent';
9636
+ function getComponent$1(error) {
9637
+ return error[ERROR_COMPONENT];
9262
9638
  }
9263
- function constructDetailsForInterpolation(lView, rootIndex, expressionIndex, meta, changedValue) {
9264
- const [propName, prefix, ...chunks] = meta.split(INTERPOLATION_DELIMITER);
9265
- let oldValue = prefix, newValue = prefix;
9266
- for (let i = 0; i < chunks.length; i++) {
9267
- const slotIdx = rootIndex + i;
9268
- oldValue += `${lView[slotIdx]}${chunks[i]}`;
9269
- newValue += `${slotIdx === expressionIndex ? changedValue : lView[slotIdx]}${chunks[i]}`;
9639
+ class _NullComponentFactoryResolver {
9640
+ resolveComponentFactory(component) {
9641
+ throw noComponentFactoryError(component);
9270
9642
  }
9271
- return { propName, oldValue, newValue };
9272
9643
  }
9273
9644
  /**
9274
- * Constructs an object that contains details for the ExpressionChangedAfterItHasBeenCheckedError:
9275
- * - property name (for property bindings or interpolations)
9276
- * - old and new values, enriched using information from metadata
9645
+ * A simple registry that maps `Components` to generated `ComponentFactory` classes
9646
+ * that can be used to create instances of components.
9647
+ * Use to obtain the factory for a given component type,
9648
+ * then use the factory's `create()` method to create a component of that type.
9277
9649
  *
9278
- * More information on the metadata storage format can be found in `storePropertyBindingMetadata`
9279
- * function description.
9650
+ * Note: since v13, dynamic component creation via
9651
+ * [`ViewContainerRef.createComponent`](api/core/ViewContainerRef#createComponent)
9652
+ * does **not** require resolving component factory: component class can be used directly.
9653
+ *
9654
+ * @publicApi
9655
+ *
9656
+ * @deprecated Angular no longer requires Component factories. Please use other APIs where
9657
+ * Component class can be used directly.
9280
9658
  */
9281
- function getExpressionChangedErrorDetails(lView, bindingIndex, oldValue, newValue) {
9282
- const tData = lView[TVIEW].data;
9283
- const metadata = tData[bindingIndex];
9284
- if (typeof metadata === 'string') {
9285
- // metadata for property interpolation
9286
- if (metadata.indexOf(INTERPOLATION_DELIMITER) > -1) {
9287
- return constructDetailsForInterpolation(lView, bindingIndex, bindingIndex, metadata, newValue);
9288
- }
9289
- // metadata for property binding
9290
- return { propName: metadata, oldValue, newValue };
9291
- }
9292
- // metadata is not available for this expression, check if this expression is a part of the
9293
- // property interpolation by going from the current binding index left and look for a string that
9294
- // contains INTERPOLATION_DELIMITER, the layout in tView.data for this case will look like this:
9295
- // [..., 'id�Prefix � and � suffix', null, null, null, ...]
9296
- if (metadata === null) {
9297
- let idx = bindingIndex - 1;
9298
- while (typeof tData[idx] !== 'string' && tData[idx + 1] === null) {
9299
- idx--;
9300
- }
9301
- const meta = tData[idx];
9302
- if (typeof meta === 'string') {
9303
- const matches = meta.match(new RegExp(INTERPOLATION_DELIMITER, 'g'));
9304
- // first interpolation delimiter separates property name from interpolation parts (in case of
9305
- // property interpolations), so we subtract one from total number of found delimiters
9306
- if (matches && (matches.length - 1) > bindingIndex - idx) {
9307
- return constructDetailsForInterpolation(lView, idx, bindingIndex, meta, newValue);
9308
- }
9309
- }
9310
- }
9311
- return { propName: undefined, oldValue, newValue };
9659
+ class ComponentFactoryResolver$1 {
9312
9660
  }
9661
+ ComponentFactoryResolver$1.NULL = ( /* @__PURE__ */new _NullComponentFactoryResolver());
9313
9662
 
9314
9663
  /**
9315
- * Returns an index of `classToSearch` in `className` taking token boundaries into account.
9664
+ * Creates an ElementRef from the most recent node.
9316
9665
  *
9317
- * `classIndexOf('AB A', 'A', 0)` will be 3 (not 0 since `AB!==A`)
9666
+ * @returns The ElementRef instance to use
9667
+ */
9668
+ function injectElementRef() {
9669
+ return createElementRef(getCurrentTNode(), getLView());
9670
+ }
9671
+ /**
9672
+ * Creates an ElementRef given a node.
9318
9673
  *
9319
- * @param className A string containing classes (whitespace separated)
9320
- * @param classToSearch A class name to locate
9321
- * @param startingIndex Starting location of search
9322
- * @returns an index of the located class (or -1 if not found)
9674
+ * @param tNode The node for which you'd like an ElementRef
9675
+ * @param lView The view to which the node belongs
9676
+ * @returns The ElementRef instance to use
9323
9677
  */
9324
- function classIndexOf(className, classToSearch, startingIndex) {
9325
- ngDevMode && assertNotEqual(classToSearch, '', 'can not look for "" string.');
9326
- let end = className.length;
9327
- while (true) {
9328
- const foundIndex = className.indexOf(classToSearch, startingIndex);
9329
- if (foundIndex === -1)
9330
- return foundIndex;
9331
- if (foundIndex === 0 || className.charCodeAt(foundIndex - 1) <= 32 /* CharCode.SPACE */) {
9332
- // Ensure that it has leading whitespace
9333
- const length = classToSearch.length;
9334
- if (foundIndex + length === end ||
9335
- className.charCodeAt(foundIndex + length) <= 32 /* CharCode.SPACE */) {
9336
- // Ensure that it has trailing whitespace
9337
- return foundIndex;
9338
- }
9339
- }
9340
- // False positive, keep searching from where we left off.
9341
- startingIndex = foundIndex + 1;
9678
+ function createElementRef(tNode, lView) {
9679
+ return new ElementRef(getNativeByTNode(tNode, lView));
9680
+ }
9681
+ /**
9682
+ * A wrapper around a native element inside of a View.
9683
+ *
9684
+ * An `ElementRef` is backed by a render-specific element. In the browser, this is usually a DOM
9685
+ * element.
9686
+ *
9687
+ * @security Permitting direct access to the DOM can make your application more vulnerable to
9688
+ * XSS attacks. Carefully review any use of `ElementRef` in your code. For more detail, see the
9689
+ * [Security Guide](https://g.co/ng/security).
9690
+ *
9691
+ * @publicApi
9692
+ */
9693
+ // Note: We don't expose things like `Injector`, `ViewContainer`, ... here,
9694
+ // i.e. users have to ask for what they need. With that, we can build better analysis tools
9695
+ // and could do better codegen in the future.
9696
+ class ElementRef {
9697
+ constructor(nativeElement) {
9698
+ this.nativeElement = nativeElement;
9342
9699
  }
9343
9700
  }
9701
+ /**
9702
+ * @internal
9703
+ * @nocollapse
9704
+ */
9705
+ ElementRef.__NG_ELEMENT_ID__ = injectElementRef;
9706
+ /**
9707
+ * Unwraps `ElementRef` and return the `nativeElement`.
9708
+ *
9709
+ * @param value value to unwrap
9710
+ * @returns `nativeElement` if `ElementRef` otherwise returns value as is.
9711
+ */
9712
+ function unwrapElementRef(value) {
9713
+ return value instanceof ElementRef ? value.nativeElement : value;
9714
+ }
9344
9715
 
9345
- const NG_TEMPLATE_SELECTOR = 'ng-template';
9346
9716
  /**
9347
- * Search the `TAttributes` to see if it contains `cssClassToMatch` (case insensitive)
9717
+ * Creates and initializes a custom renderer that implements the `Renderer2` base class.
9348
9718
  *
9349
- * @param attrs `TAttributes` to search through.
9350
- * @param cssClassToMatch class to match (lowercase)
9351
- * @param isProjectionMode Whether or not class matching should look into the attribute `class` in
9352
- * addition to the `AttributeMarker.Classes`.
9719
+ * @publicApi
9353
9720
  */
9354
- function isCssClassMatching(attrs, cssClassToMatch, isProjectionMode) {
9355
- // TODO(misko): The fact that this function needs to know about `isProjectionMode` seems suspect.
9356
- // It is strange to me that sometimes the class information comes in form of `class` attribute
9357
- // and sometimes in form of `AttributeMarker.Classes`. Some investigation is needed to determine
9358
- // if that is the right behavior.
9359
- ngDevMode &&
9360
- assertEqual(cssClassToMatch, cssClassToMatch.toLowerCase(), 'Class name expected to be lowercase.');
9361
- let i = 0;
9362
- while (i < attrs.length) {
9363
- let item = attrs[i++];
9364
- if (isProjectionMode && item === 'class') {
9365
- item = attrs[i];
9366
- if (classIndexOf(item.toLowerCase(), cssClassToMatch, 0) !== -1) {
9367
- return true;
9368
- }
9369
- }
9370
- else if (item === 1 /* AttributeMarker.Classes */) {
9371
- // We found the classes section. Start searching for the class.
9372
- while (i < attrs.length && typeof (item = attrs[i++]) == 'string') {
9373
- // while we have strings
9374
- if (item.toLowerCase() === cssClassToMatch)
9375
- return true;
9376
- }
9377
- return false;
9378
- }
9379
- }
9380
- return false;
9721
+ class RendererFactory2 {
9722
+ }
9723
+ /**
9724
+ * Extend this base class to implement custom rendering. By default, Angular
9725
+ * renders a template into DOM. You can use custom rendering to intercept
9726
+ * rendering calls, or to render to something other than DOM.
9727
+ *
9728
+ * Create your custom renderer using `RendererFactory2`.
9729
+ *
9730
+ * Use a custom renderer to bypass Angular's templating and
9731
+ * make custom UI changes that can't be expressed declaratively.
9732
+ * For example if you need to set a property or an attribute whose name is
9733
+ * not statically known, use the `setProperty()` or
9734
+ * `setAttribute()` method.
9735
+ *
9736
+ * @publicApi
9737
+ */
9738
+ class Renderer2 {
9381
9739
  }
9382
9740
  /**
9383
- * Checks whether the `tNode` represents an inline template (e.g. `*ngFor`).
9384
- *
9385
- * @param tNode current TNode
9741
+ * @internal
9742
+ * @nocollapse
9386
9743
  */
9387
- function isInlineTemplate(tNode) {
9388
- return tNode.type === 4 /* TNodeType.Container */ && tNode.value !== NG_TEMPLATE_SELECTOR;
9744
+ Renderer2.__NG_ELEMENT_ID__ = () => injectRenderer2();
9745
+ /** Injects a Renderer2 for the current component. */
9746
+ function injectRenderer2() {
9747
+ // We need the Renderer to be based on the component that it's being injected into, however since
9748
+ // DI happens before we've entered its view, `getLView` will return the parent view instead.
9749
+ const lView = getLView();
9750
+ const tNode = getCurrentTNode();
9751
+ const nodeAtIndex = getComponentLViewByIndex(tNode.index, lView);
9752
+ return (isLView(nodeAtIndex) ? nodeAtIndex : lView)[RENDERER];
9389
9753
  }
9754
+
9390
9755
  /**
9391
- * Function that checks whether a given tNode matches tag-based selector and has a valid type.
9756
+ * Sanitizer is used by the views to sanitize potentially dangerous values.
9392
9757
  *
9393
- * Matching can be performed in 2 modes: projection mode (when we project nodes) and regular
9394
- * directive matching mode:
9395
- * - in the "directive matching" mode we do _not_ take TContainer's tagName into account if it is
9396
- * different from NG_TEMPLATE_SELECTOR (value different from NG_TEMPLATE_SELECTOR indicates that a
9397
- * tag name was extracted from * syntax so we would match the same directive twice);
9398
- * - in the "projection" mode, we use a tag name potentially extracted from the * syntax processing
9399
- * (applicable to TNodeType.Container only).
9758
+ * @publicApi
9400
9759
  */
9401
- function hasTagAndTypeMatch(tNode, currentSelector, isProjectionMode) {
9402
- const tagNameToCompare = tNode.type === 4 /* TNodeType.Container */ && !isProjectionMode ? NG_TEMPLATE_SELECTOR : tNode.value;
9403
- return currentSelector === tagNameToCompare;
9760
+ class Sanitizer {
9404
9761
  }
9762
+ /** @nocollapse */
9763
+ Sanitizer.ɵprov = ɵɵdefineInjectable({
9764
+ token: Sanitizer,
9765
+ providedIn: 'root',
9766
+ factory: () => null,
9767
+ });
9768
+
9405
9769
  /**
9406
- * A utility function to match an Ivy node static data against a simple CSS selector
9770
+ * @description Represents the version of Angular
9407
9771
  *
9408
- * @param node static data of the node to match
9409
- * @param selector The selector to try matching against the node.
9410
- * @param isProjectionMode if `true` we are matching for content projection, otherwise we are doing
9411
- * directive matching.
9412
- * @returns true if node matches the selector.
9772
+ * @publicApi
9413
9773
  */
9414
- function isNodeMatchingSelector(tNode, selector, isProjectionMode) {
9415
- ngDevMode && assertDefined(selector[0], 'Selector should have a tag name');
9416
- let mode = 4 /* SelectorFlags.ELEMENT */;
9417
- const nodeAttrs = tNode.attrs || [];
9418
- // Find the index of first attribute that has no value, only a name.
9419
- const nameOnlyMarkerIdx = getNameOnlyMarkerIndex(nodeAttrs);
9420
- // When processing ":not" selectors, we skip to the next ":not" if the
9421
- // current one doesn't match
9422
- let skipToNextSelector = false;
9423
- for (let i = 0; i < selector.length; i++) {
9424
- const current = selector[i];
9425
- if (typeof current === 'number') {
9426
- // If we finish processing a :not selector and it hasn't failed, return false
9427
- if (!skipToNextSelector && !isPositive(mode) && !isPositive(current)) {
9428
- return false;
9429
- }
9430
- // If we are skipping to the next :not() and this mode flag is positive,
9431
- // it's a part of the current :not() selector, and we should keep skipping
9432
- if (skipToNextSelector && isPositive(current))
9433
- continue;
9434
- skipToNextSelector = false;
9435
- mode = current | (mode & 1 /* SelectorFlags.NOT */);
9436
- continue;
9437
- }
9438
- if (skipToNextSelector)
9439
- continue;
9440
- if (mode & 4 /* SelectorFlags.ELEMENT */) {
9441
- mode = 2 /* SelectorFlags.ATTRIBUTE */ | mode & 1 /* SelectorFlags.NOT */;
9442
- if (current !== '' && !hasTagAndTypeMatch(tNode, current, isProjectionMode) ||
9443
- current === '' && selector.length === 1) {
9444
- if (isPositive(mode))
9445
- return false;
9446
- skipToNextSelector = true;
9447
- }
9448
- }
9449
- else {
9450
- const selectorAttrValue = mode & 8 /* SelectorFlags.CLASS */ ? current : selector[++i];
9451
- // special case for matching against classes when a tNode has been instantiated with
9452
- // class and style values as separate attribute values (e.g. ['title', CLASS, 'foo'])
9453
- if ((mode & 8 /* SelectorFlags.CLASS */) && tNode.attrs !== null) {
9454
- if (!isCssClassMatching(tNode.attrs, selectorAttrValue, isProjectionMode)) {
9455
- if (isPositive(mode))
9456
- return false;
9457
- skipToNextSelector = true;
9458
- }
9459
- continue;
9460
- }
9461
- const attrName = (mode & 8 /* SelectorFlags.CLASS */) ? 'class' : current;
9462
- const attrIndexInNode = findAttrIndexInNode(attrName, nodeAttrs, isInlineTemplate(tNode), isProjectionMode);
9463
- if (attrIndexInNode === -1) {
9464
- if (isPositive(mode))
9465
- return false;
9466
- skipToNextSelector = true;
9467
- continue;
9468
- }
9469
- if (selectorAttrValue !== '') {
9470
- let nodeAttrValue;
9471
- if (attrIndexInNode > nameOnlyMarkerIdx) {
9472
- nodeAttrValue = '';
9473
- }
9474
- else {
9475
- ngDevMode &&
9476
- assertNotEqual(nodeAttrs[attrIndexInNode], 0 /* AttributeMarker.NamespaceURI */, 'We do not match directives on namespaced attributes');
9477
- // we lowercase the attribute value to be able to match
9478
- // selectors without case-sensitivity
9479
- // (selectors are already in lowercase when generated)
9480
- nodeAttrValue = nodeAttrs[attrIndexInNode + 1].toLowerCase();
9481
- }
9482
- const compareAgainstClassName = mode & 8 /* SelectorFlags.CLASS */ ? nodeAttrValue : null;
9483
- if (compareAgainstClassName &&
9484
- classIndexOf(compareAgainstClassName, selectorAttrValue, 0) !== -1 ||
9485
- mode & 2 /* SelectorFlags.ATTRIBUTE */ && selectorAttrValue !== nodeAttrValue) {
9486
- if (isPositive(mode))
9487
- return false;
9488
- skipToNextSelector = true;
9489
- }
9490
- }
9491
- }
9774
+ class Version {
9775
+ constructor(full) {
9776
+ this.full = full;
9777
+ this.major = full.split('.')[0];
9778
+ this.minor = full.split('.')[1];
9779
+ this.patch = full.split('.').slice(2).join('.');
9492
9780
  }
9493
- return isPositive(mode) || skipToNextSelector;
9494
9781
  }
9495
- function isPositive(mode) {
9496
- return (mode & 1 /* SelectorFlags.NOT */) === 0;
9782
+ /**
9783
+ * @publicApi
9784
+ */
9785
+ const VERSION = new Version('16.0.0-next.4');
9786
+
9787
+ // This default value is when checking the hierarchy for a token.
9788
+ //
9789
+ // It means both:
9790
+ // - the token is not provided by the current injector,
9791
+ // - only the element injectors should be checked (ie do not check module injectors
9792
+ //
9793
+ // mod1
9794
+ // /
9795
+ // el1 mod2
9796
+ // \ /
9797
+ // el2
9798
+ //
9799
+ // When requesting el2.injector.get(token), we should check in the following order and return the
9800
+ // first found value:
9801
+ // - el2.injector.get(token, default)
9802
+ // - el1.injector.get(token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) -> do not check the module
9803
+ // - mod2.injector.get(token, default)
9804
+ const NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR = {};
9805
+
9806
+ const ERROR_ORIGINAL_ERROR = 'ngOriginalError';
9807
+ function wrappedError(message, originalError) {
9808
+ const msg = `${message} caused by: ${originalError instanceof Error ? originalError.message : originalError}`;
9809
+ const error = Error(msg);
9810
+ error[ERROR_ORIGINAL_ERROR] = originalError;
9811
+ return error;
9812
+ }
9813
+ function getOriginalError(error) {
9814
+ return error[ERROR_ORIGINAL_ERROR];
9497
9815
  }
9816
+
9498
9817
  /**
9499
- * Examines the attribute's definition array for a node to find the index of the
9500
- * attribute that matches the given `name`.
9818
+ * Provides a hook for centralized exception handling.
9501
9819
  *
9502
- * NOTE: This will not match namespaced attributes.
9820
+ * The default implementation of `ErrorHandler` prints error messages to the `console`. To
9821
+ * intercept error handling, write a custom exception handler that replaces this default as
9822
+ * appropriate for your app.
9503
9823
  *
9504
- * Attribute matching depends upon `isInlineTemplate` and `isProjectionMode`.
9505
- * The following table summarizes which types of attributes we attempt to match:
9824
+ * @usageNotes
9825
+ * ### Example
9506
9826
  *
9507
- * ===========================================================================================================
9508
- * Modes | Normal Attributes | Bindings Attributes | Template Attributes | I18n
9509
- * Attributes
9510
- * ===========================================================================================================
9511
- * Inline + Projection | YES | YES | NO | YES
9512
- * -----------------------------------------------------------------------------------------------------------
9513
- * Inline + Directive | NO | NO | YES | NO
9514
- * -----------------------------------------------------------------------------------------------------------
9515
- * Non-inline + Projection | YES | YES | NO | YES
9516
- * -----------------------------------------------------------------------------------------------------------
9517
- * Non-inline + Directive | YES | YES | NO | YES
9518
- * ===========================================================================================================
9827
+ * ```
9828
+ * class MyErrorHandler implements ErrorHandler {
9829
+ * handleError(error) {
9830
+ * // do something with the exception
9831
+ * }
9832
+ * }
9519
9833
  *
9520
- * @param name the name of the attribute to find
9521
- * @param attrs the attribute array to examine
9522
- * @param isInlineTemplate true if the node being matched is an inline template (e.g. `*ngFor`)
9523
- * rather than a manually expanded template node (e.g `<ng-template>`).
9524
- * @param isProjectionMode true if we are matching against content projection otherwise we are
9525
- * matching against directives.
9526
- */
9527
- function findAttrIndexInNode(name, attrs, isInlineTemplate, isProjectionMode) {
9528
- if (attrs === null)
9529
- return -1;
9530
- let i = 0;
9531
- if (isProjectionMode || !isInlineTemplate) {
9532
- let bindingsMode = false;
9533
- while (i < attrs.length) {
9534
- const maybeAttrName = attrs[i];
9535
- if (maybeAttrName === name) {
9536
- return i;
9537
- }
9538
- else if (maybeAttrName === 3 /* AttributeMarker.Bindings */ || maybeAttrName === 6 /* AttributeMarker.I18n */) {
9539
- bindingsMode = true;
9540
- }
9541
- else if (maybeAttrName === 1 /* AttributeMarker.Classes */ || maybeAttrName === 2 /* AttributeMarker.Styles */) {
9542
- let value = attrs[++i];
9543
- // We should skip classes here because we have a separate mechanism for
9544
- // matching classes in projection mode.
9545
- while (typeof value === 'string') {
9546
- value = attrs[++i];
9547
- }
9548
- continue;
9549
- }
9550
- else if (maybeAttrName === 4 /* AttributeMarker.Template */) {
9551
- // We do not care about Template attributes in this scenario.
9552
- break;
9553
- }
9554
- else if (maybeAttrName === 0 /* AttributeMarker.NamespaceURI */) {
9555
- // Skip the whole namespaced attribute and value. This is by design.
9556
- i += 4;
9557
- continue;
9558
- }
9559
- // In binding mode there are only names, rather than name-value pairs.
9560
- i += bindingsMode ? 1 : 2;
9561
- }
9562
- // We did not match the attribute
9563
- return -1;
9564
- }
9565
- else {
9566
- return matchTemplateAttribute(attrs, name);
9834
+ * @NgModule({
9835
+ * providers: [{provide: ErrorHandler, useClass: MyErrorHandler}]
9836
+ * })
9837
+ * class MyModule {}
9838
+ * ```
9839
+ *
9840
+ * @publicApi
9841
+ */
9842
+ class ErrorHandler {
9843
+ constructor() {
9844
+ /**
9845
+ * @internal
9846
+ */
9847
+ this._console = console;
9567
9848
  }
9568
- }
9569
- function isNodeMatchingSelectorList(tNode, selector, isProjectionMode = false) {
9570
- for (let i = 0; i < selector.length; i++) {
9571
- if (isNodeMatchingSelector(tNode, selector[i], isProjectionMode)) {
9572
- return true;
9849
+ handleError(error) {
9850
+ const originalError = this._findOriginalError(error);
9851
+ this._console.error('ERROR', error);
9852
+ if (originalError) {
9853
+ this._console.error('ORIGINAL ERROR', originalError);
9573
9854
  }
9574
9855
  }
9575
- return false;
9576
- }
9577
- function getProjectAsAttrValue(tNode) {
9578
- const nodeAttrs = tNode.attrs;
9579
- if (nodeAttrs != null) {
9580
- const ngProjectAsAttrIdx = nodeAttrs.indexOf(5 /* AttributeMarker.ProjectAs */);
9581
- // only check for ngProjectAs in attribute names, don't accidentally match attribute's value
9582
- // (attribute names are stored at even indexes)
9583
- if ((ngProjectAsAttrIdx & 1) === 0) {
9584
- return nodeAttrs[ngProjectAsAttrIdx + 1];
9856
+ /** @internal */
9857
+ _findOriginalError(error) {
9858
+ let e = error && getOriginalError(error);
9859
+ while (e && getOriginalError(e)) {
9860
+ e = getOriginalError(e);
9585
9861
  }
9862
+ return e || null;
9586
9863
  }
9587
- return null;
9588
9864
  }
9589
- function getNameOnlyMarkerIndex(nodeAttrs) {
9590
- for (let i = 0; i < nodeAttrs.length; i++) {
9591
- const nodeAttr = nodeAttrs[i];
9592
- if (isNameOnlyAttributeMarker(nodeAttr)) {
9593
- return i;
9594
- }
9865
+
9866
+ const NG_DEV_MODE = typeof ngDevMode === 'undefined' || !!ngDevMode;
9867
+ /**
9868
+ * Internal token that specifies whether hydration is enabled.
9869
+ */
9870
+ const IS_HYDRATION_FEATURE_ENABLED = new InjectionToken(NG_DEV_MODE ? 'IS_HYDRATION_FEATURE_ENABLED' : '');
9871
+ // By default (in client rendering mode), we remove all the contents
9872
+ // of the host element and render an application after that.
9873
+ const PRESERVE_HOST_CONTENT_DEFAULT = false;
9874
+ /**
9875
+ * Internal token that indicates whether host element content should be
9876
+ * retained during the bootstrap.
9877
+ */
9878
+ const PRESERVE_HOST_CONTENT = new InjectionToken(NG_DEV_MODE ? 'PRESERVE_HOST_CONTENT' : '', {
9879
+ providedIn: 'root',
9880
+ factory: () => PRESERVE_HOST_CONTENT_DEFAULT,
9881
+ });
9882
+
9883
+ function normalizeDebugBindingName(name) {
9884
+ // Attribute names with `$` (eg `x-y$`) are valid per spec, but unsupported by some browsers
9885
+ name = camelCaseToDashCase(name.replace(/[$@]/g, '_'));
9886
+ return `ng-reflect-${name}`;
9887
+ }
9888
+ const CAMEL_CASE_REGEXP = /([A-Z])/g;
9889
+ function camelCaseToDashCase(input) {
9890
+ return input.replace(CAMEL_CASE_REGEXP, (...m) => '-' + m[1].toLowerCase());
9891
+ }
9892
+ function normalizeDebugBindingValue(value) {
9893
+ try {
9894
+ // Limit the size of the value as otherwise the DOM just gets polluted.
9895
+ return value != null ? value.toString().slice(0, 30) : value;
9896
+ }
9897
+ catch (e) {
9898
+ return '[ERROR] Exception while trying to serialize the value';
9595
9899
  }
9596
- return nodeAttrs.length;
9597
9900
  }
9598
- function matchTemplateAttribute(attrs, name) {
9599
- let i = attrs.indexOf(4 /* AttributeMarker.Template */);
9600
- if (i > -1) {
9601
- i++;
9602
- while (i < attrs.length) {
9603
- const attr = attrs[i];
9604
- // Return in case we checked all template attrs and are switching to the next section in the
9605
- // attrs array (that starts with a number that represents an attribute marker).
9606
- if (typeof attr === 'number')
9607
- return -1;
9608
- if (attr === name)
9609
- return i;
9610
- i++;
9611
- }
9901
+
9902
+ /** Verifies that a given type is a Standalone Component. */
9903
+ function assertStandaloneComponentType(type) {
9904
+ assertComponentDef(type);
9905
+ const componentDef = getComponentDef$1(type);
9906
+ if (!componentDef.standalone) {
9907
+ throw new RuntimeError(907 /* RuntimeErrorCode.TYPE_IS_NOT_STANDALONE */, `The ${stringifyForError(type)} component is not marked as standalone, ` +
9908
+ `but Angular expects to have a standalone component here. ` +
9909
+ `Please make sure the ${stringifyForError(type)} component has ` +
9910
+ `the \`standalone: true\` flag in the decorator.`);
9612
9911
  }
9613
- return -1;
9614
9912
  }
9615
- /**
9616
- * Checks whether a selector is inside a CssSelectorList
9617
- * @param selector Selector to be checked.
9618
- * @param list List in which to look for the selector.
9619
- */
9620
- function isSelectorInSelectorList(selector, list) {
9621
- selectorListLoop: for (let i = 0; i < list.length; i++) {
9622
- const currentSelectorInList = list[i];
9623
- if (selector.length !== currentSelectorInList.length) {
9624
- continue;
9625
- }
9626
- for (let j = 0; j < selector.length; j++) {
9627
- if (selector[j] !== currentSelectorInList[j]) {
9628
- continue selectorListLoop;
9629
- }
9630
- }
9631
- return true;
9913
+ /** Verifies whether a given type is a component */
9914
+ function assertComponentDef(type) {
9915
+ if (!getComponentDef$1(type)) {
9916
+ throw new RuntimeError(906 /* RuntimeErrorCode.MISSING_GENERATED_DEF */, `The ${stringifyForError(type)} is not an Angular component, ` +
9917
+ `make sure it has the \`@Component\` decorator.`);
9632
9918
  }
9633
- return false;
9634
9919
  }
9635
- function maybeWrapInNotSelector(isNegativeMode, chunk) {
9636
- return isNegativeMode ? ':not(' + chunk.trim() + ')' : chunk;
9920
+ /** Called when there are multiple component selectors that match a given node */
9921
+ function throwMultipleComponentError(tNode, first, second) {
9922
+ throw new RuntimeError(-300 /* RuntimeErrorCode.MULTIPLE_COMPONENTS_MATCH */, `Multiple components match node with tagname ${tNode.value}: ` +
9923
+ `${stringifyForError(first)} and ` +
9924
+ `${stringifyForError(second)}`);
9637
9925
  }
9638
- function stringifyCSSSelector(selector) {
9639
- let result = selector[0];
9640
- let i = 1;
9641
- let mode = 2 /* SelectorFlags.ATTRIBUTE */;
9642
- let currentChunk = '';
9643
- let isNegativeMode = false;
9644
- while (i < selector.length) {
9645
- let valueOrMarker = selector[i];
9646
- if (typeof valueOrMarker === 'string') {
9647
- if (mode & 2 /* SelectorFlags.ATTRIBUTE */) {
9648
- const attrValue = selector[++i];
9649
- currentChunk +=
9650
- '[' + valueOrMarker + (attrValue.length > 0 ? '="' + attrValue + '"' : '') + ']';
9651
- }
9652
- else if (mode & 8 /* SelectorFlags.CLASS */) {
9653
- currentChunk += '.' + valueOrMarker;
9654
- }
9655
- else if (mode & 4 /* SelectorFlags.ELEMENT */) {
9656
- currentChunk += ' ' + valueOrMarker;
9657
- }
9658
- }
9659
- else {
9660
- //
9661
- // Append current chunk to the final result in case we come across SelectorFlag, which
9662
- // indicates that the previous section of a selector is over. We need to accumulate content
9663
- // between flags to make sure we wrap the chunk later in :not() selector if needed, e.g.
9664
- // ```
9665
- // ['', Flags.CLASS, '.classA', Flags.CLASS | Flags.NOT, '.classB', '.classC']
9666
- // ```
9667
- // should be transformed to `.classA :not(.classB .classC)`.
9668
- //
9669
- // Note: for negative selector part, we accumulate content between flags until we find the
9670
- // next negative flag. This is needed to support a case where `:not()` rule contains more than
9671
- // one chunk, e.g. the following selector:
9672
- // ```
9673
- // ['', Flags.ELEMENT | Flags.NOT, 'p', Flags.CLASS, 'foo', Flags.CLASS | Flags.NOT, 'bar']
9674
- // ```
9675
- // should be stringified to `:not(p.foo) :not(.bar)`
9676
- //
9677
- if (currentChunk !== '' && !isPositive(valueOrMarker)) {
9678
- result += maybeWrapInNotSelector(isNegativeMode, currentChunk);
9679
- currentChunk = '';
9680
- }
9681
- mode = valueOrMarker;
9682
- // According to CssSelector spec, once we come across `SelectorFlags.NOT` flag, the negative
9683
- // mode is maintained for remaining chunks of a selector.
9684
- isNegativeMode = isNegativeMode || !isPositive(mode);
9685
- }
9686
- i++;
9687
- }
9688
- if (currentChunk !== '') {
9689
- result += maybeWrapInNotSelector(isNegativeMode, currentChunk);
9926
+ /** Throws an ExpressionChangedAfterChecked error if checkNoChanges mode is on. */
9927
+ function throwErrorIfNoChangesMode(creationMode, oldValue, currValue, propName) {
9928
+ const field = propName ? ` for '${propName}'` : '';
9929
+ let msg = `ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value${field}: '${oldValue}'. Current value: '${currValue}'.`;
9930
+ if (creationMode) {
9931
+ msg +=
9932
+ ` It seems like the view has been created after its parent and its children have been dirty checked.` +
9933
+ ` Has it been created in a change detection hook?`;
9690
9934
  }
9691
- return result;
9935
+ throw new RuntimeError(-100 /* RuntimeErrorCode.EXPRESSION_CHANGED_AFTER_CHECKED */, msg);
9692
9936
  }
9693
- /**
9694
- * Generates string representation of CSS selector in parsed form.
9695
- *
9696
- * ComponentDef and DirectiveDef are generated with the selector in parsed form to avoid doing
9697
- * additional parsing at runtime (for example, for directive matching). However in some cases (for
9698
- * example, while bootstrapping a component), a string version of the selector is required to query
9699
- * for the host element on the page. This function takes the parsed form of a selector and returns
9700
- * its string representation.
9701
- *
9702
- * @param selectorList selector in parsed form
9703
- * @returns string representation of a given selector
9704
- */
9705
- function stringifyCSSSelectorList(selectorList) {
9706
- return selectorList.map(stringifyCSSSelector).join(',');
9937
+ function constructDetailsForInterpolation(lView, rootIndex, expressionIndex, meta, changedValue) {
9938
+ const [propName, prefix, ...chunks] = meta.split(INTERPOLATION_DELIMITER);
9939
+ let oldValue = prefix, newValue = prefix;
9940
+ for (let i = 0; i < chunks.length; i++) {
9941
+ const slotIdx = rootIndex + i;
9942
+ oldValue += `${lView[slotIdx]}${chunks[i]}`;
9943
+ newValue += `${slotIdx === expressionIndex ? changedValue : lView[slotIdx]}${chunks[i]}`;
9944
+ }
9945
+ return { propName, oldValue, newValue };
9707
9946
  }
9708
9947
  /**
9709
- * Extracts attributes and classes information from a given CSS selector.
9710
- *
9711
- * This function is used while creating a component dynamically. In this case, the host element
9712
- * (that is created dynamically) should contain attributes and classes specified in component's CSS
9713
- * selector.
9948
+ * Constructs an object that contains details for the ExpressionChangedAfterItHasBeenCheckedError:
9949
+ * - property name (for property bindings or interpolations)
9950
+ * - old and new values, enriched using information from metadata
9714
9951
  *
9715
- * @param selector CSS selector in parsed form (in a form of array)
9716
- * @returns object with `attrs` and `classes` fields that contain extracted information
9952
+ * More information on the metadata storage format can be found in `storePropertyBindingMetadata`
9953
+ * function description.
9717
9954
  */
9718
- function extractAttrsAndClassesFromSelector(selector) {
9719
- const attrs = [];
9720
- const classes = [];
9721
- let i = 1;
9722
- let mode = 2 /* SelectorFlags.ATTRIBUTE */;
9723
- while (i < selector.length) {
9724
- let valueOrMarker = selector[i];
9725
- if (typeof valueOrMarker === 'string') {
9726
- if (mode === 2 /* SelectorFlags.ATTRIBUTE */) {
9727
- if (valueOrMarker !== '') {
9728
- attrs.push(valueOrMarker, selector[++i]);
9729
- }
9730
- }
9731
- else if (mode === 8 /* SelectorFlags.CLASS */) {
9732
- classes.push(valueOrMarker);
9733
- }
9955
+ function getExpressionChangedErrorDetails(lView, bindingIndex, oldValue, newValue) {
9956
+ const tData = lView[TVIEW].data;
9957
+ const metadata = tData[bindingIndex];
9958
+ if (typeof metadata === 'string') {
9959
+ // metadata for property interpolation
9960
+ if (metadata.indexOf(INTERPOLATION_DELIMITER) > -1) {
9961
+ return constructDetailsForInterpolation(lView, bindingIndex, bindingIndex, metadata, newValue);
9734
9962
  }
9735
- else {
9736
- // According to CssSelector spec, once we come across `SelectorFlags.NOT` flag, the negative
9737
- // mode is maintained for remaining chunks of a selector. Since attributes and classes are
9738
- // extracted only for "positive" part of the selector, we can stop here.
9739
- if (!isPositive(mode))
9740
- break;
9741
- mode = valueOrMarker;
9963
+ // metadata for property binding
9964
+ return { propName: metadata, oldValue, newValue };
9965
+ }
9966
+ // metadata is not available for this expression, check if this expression is a part of the
9967
+ // property interpolation by going from the current binding index left and look for a string that
9968
+ // contains INTERPOLATION_DELIMITER, the layout in tView.data for this case will look like this:
9969
+ // [..., 'id�Prefix � and � suffix', null, null, null, ...]
9970
+ if (metadata === null) {
9971
+ let idx = bindingIndex - 1;
9972
+ while (typeof tData[idx] !== 'string' && tData[idx + 1] === null) {
9973
+ idx--;
9974
+ }
9975
+ const meta = tData[idx];
9976
+ if (typeof meta === 'string') {
9977
+ const matches = meta.match(new RegExp(INTERPOLATION_DELIMITER, 'g'));
9978
+ // first interpolation delimiter separates property name from interpolation parts (in case of
9979
+ // property interpolations), so we subtract one from total number of found delimiters
9980
+ if (matches && (matches.length - 1) > bindingIndex - idx) {
9981
+ return constructDetailsForInterpolation(lView, idx, bindingIndex, meta, newValue);
9982
+ }
9742
9983
  }
9743
- i++;
9744
9984
  }
9745
- return { attrs, classes };
9985
+ return { propName: undefined, oldValue, newValue };
9746
9986
  }
9747
9987
 
9748
9988
  /** A special value which designates that a value has not changed. */
@@ -9801,6 +10041,33 @@ function selectIndexInternal(tView, lView, index, checkNoChangesMode) {
9801
10041
  setSelectedIndex(index);
9802
10042
  }
9803
10043
 
10044
+ /**
10045
+ * Runs the given function in the context of the given `Injector`.
10046
+ *
10047
+ * Within the function's stack frame, `inject` can be used to inject dependencies from the given
10048
+ * `Injector`. Note that `inject` is only usable synchronously, and cannot be used in any
10049
+ * asynchronous callbacks or after any `await` points.
10050
+ *
10051
+ * @param injector the injector which will satisfy calls to `inject` while `fn` is executing
10052
+ * @param fn the closure to be run in the context of `injector`
10053
+ * @returns the return value of the function, if any
10054
+ * @publicApi
10055
+ */
10056
+ function runInInjectionContext(injector, fn) {
10057
+ if (injector instanceof R3Injector) {
10058
+ injector.assertNotDestroyed();
10059
+ }
10060
+ const prevInjector = setCurrentInjector(injector);
10061
+ const previousInjectImplementation = setInjectImplementation(undefined);
10062
+ try {
10063
+ return fn();
10064
+ }
10065
+ finally {
10066
+ setCurrentInjector(prevInjector);
10067
+ setInjectImplementation(previousInjectImplementation);
10068
+ }
10069
+ }
10070
+
9804
10071
  /**
9805
10072
  * A mapping of the @angular/core API surface used in generated expressions to the actual symbols.
9806
10073
  *
@@ -11240,7 +11507,7 @@ function getOrCreateComponentTView(def) {
11240
11507
  // Declaration node here is null since this function is called when we dynamically create a
11241
11508
  // component and hence there is no declaration.
11242
11509
  const declTNode = null;
11243
- return def.tView = createTView(1 /* TViewType.Component */, declTNode, def.template, def.decls, def.vars, def.directiveDefs, def.pipeDefs, def.viewQuery, def.schemas, def.consts);
11510
+ return def.tView = createTView(1 /* TViewType.Component */, declTNode, def.template, def.decls, def.vars, def.directiveDefs, def.pipeDefs, def.viewQuery, def.schemas, def.consts, def.id);
11244
11511
  }
11245
11512
  return tView;
11246
11513
  }
@@ -11257,7 +11524,7 @@ function getOrCreateComponentTView(def) {
11257
11524
  * @param schemas Schemas for this view
11258
11525
  * @param consts Constants for this view
11259
11526
  */
11260
- function createTView(type, declTNode, templateFn, decls, vars, directives, pipes, viewQuery, schemas, constsOrFactory) {
11527
+ function createTView(type, declTNode, templateFn, decls, vars, directives, pipes, viewQuery, schemas, constsOrFactory, ssrId) {
11261
11528
  ngDevMode && ngDevMode.tView++;
11262
11529
  const bindingStartIndex = HEADER_OFFSET + decls;
11263
11530
  // This length does not yet contain host bindings from child directives because at this point,
@@ -11296,7 +11563,8 @@ function createTView(type, declTNode, templateFn, decls, vars, directives, pipes
11296
11563
  firstChild: null,
11297
11564
  schemas: schemas,
11298
11565
  consts: consts,
11299
- incompleteFirstPass: false
11566
+ incompleteFirstPass: false,
11567
+ ssrId,
11300
11568
  };
11301
11569
  if (ngDevMode) {
11302
11570
  // For performance reasons it is important that the tView retains the same shape during runtime.
@@ -11316,23 +11584,58 @@ function createViewBlueprint(bindingStartIndex, initialViewLength) {
11316
11584
  /**
11317
11585
  * Locates the host native element, used for bootstrapping existing nodes into rendering pipeline.
11318
11586
  *
11319
- * @param rendererFactory Factory function to create renderer instance.
11587
+ * @param renderer the renderer used to locate the element.
11320
11588
  * @param elementOrSelector Render element or CSS selector to locate the element.
11321
11589
  * @param encapsulation View Encapsulation defined for component that requests host element.
11322
11590
  * @param injector Root view injector instance.
11323
11591
  */
11324
11592
  function locateHostElement(renderer, elementOrSelector, encapsulation, injector) {
11325
11593
  // Note: we use default value for the `PRESERVE_HOST_CONTENT` here even though it's a
11326
- // tree-shakable one (providedIn:'root'). This code path can be triggered during dynamic component
11327
- // creation (after calling ViewContainerRef.createComponent) when an injector instance can be
11328
- // provided. The injector instance might be disconnected from the main DI tree, thus the
11329
- // `PRESERVE_HOST_CONTENT` woild not be able to instantiate. In this case, the default value will
11330
- // be used.
11594
+ // tree-shakable one (providedIn:'root'). This code path can be triggered during dynamic
11595
+ // component creation (after calling ViewContainerRef.createComponent) when an injector
11596
+ // instance can be provided. The injector instance might be disconnected from the main DI
11597
+ // tree, thus the `PRESERVE_HOST_CONTENT` woild not be able to instantiate. In this case, the
11598
+ // default value will be used.
11331
11599
  const preserveHostContent = injector.get(PRESERVE_HOST_CONTENT, PRESERVE_HOST_CONTENT_DEFAULT);
11332
11600
  // When using native Shadow DOM, do not clear host element to allow native slot
11333
11601
  // projection.
11334
11602
  const preserveContent = preserveHostContent || encapsulation === ViewEncapsulation.ShadowDom;
11335
- return renderer.selectRootElement(elementOrSelector, preserveContent);
11603
+ const rootElement = renderer.selectRootElement(elementOrSelector, preserveContent);
11604
+ applyRootElementTransform(rootElement);
11605
+ return rootElement;
11606
+ }
11607
+ /**
11608
+ * Applies any root element transformations that are needed. If hydration is enabled,
11609
+ * this will process corrupted text nodes.
11610
+ *
11611
+ * @param rootElement the app root HTML Element
11612
+ */
11613
+ function applyRootElementTransform(rootElement) {
11614
+ _applyRootElementTransformImpl(rootElement);
11615
+ }
11616
+ /**
11617
+ * Reference to a function that applies transformations to the root HTML element
11618
+ * of an app. When hydration is enabled, this processes any corrupt text nodes
11619
+ * so they are properly hydratable on the client.
11620
+ *
11621
+ * @param rootElement the app root HTML Element
11622
+ */
11623
+ let _applyRootElementTransformImpl = (rootElement) => null;
11624
+ /**
11625
+ * Processes text node markers before hydration begins. This replaces any special comment
11626
+ * nodes that were added prior to serialization are swapped out to restore proper text
11627
+ * nodes before hydration.
11628
+ *
11629
+ * @param rootElement the app root HTML Element
11630
+ */
11631
+ function applyRootElementTransformImpl(rootElement) {
11632
+ processTextNodeMarkersBeforeHydration(rootElement);
11633
+ }
11634
+ /**
11635
+ * Sets the implementation for the `applyRootElementTransform` function.
11636
+ */
11637
+ function enableApplyRootElementTransformImpl() {
11638
+ _applyRootElementTransformImpl = applyRootElementTransformImpl;
11336
11639
  }
11337
11640
  /**
11338
11641
  * Saves context for this cleanup function in LView.cleanupInstances.
@@ -11344,9 +11647,9 @@ function locateHostElement(renderer, elementOrSelector, encapsulation, injector)
11344
11647
  function storeCleanupWithContext(tView, lView, context, cleanupFn) {
11345
11648
  const lCleanup = getOrCreateLViewCleanup(lView);
11346
11649
  // Historically the `storeCleanupWithContext` was used to register both framework-level and
11347
- // user-defined cleanup callbacks, but over time those two types of cleanups were separated. This
11348
- // dev mode checks assures that user-level cleanup callbacks are _not_ stored in data structures
11349
- // reserved for framework-specific hooks.
11650
+ // user-defined cleanup callbacks, but over time those two types of cleanups were separated.
11651
+ // This dev mode checks assures that user-level cleanup callbacks are _not_ stored in data
11652
+ // structures reserved for framework-specific hooks.
11350
11653
  ngDevMode &&
11351
11654
  assertDefined(context, 'Cleanup context is mandatory when registering framework-level destroy hooks');
11352
11655
  lCleanup.push(context);
@@ -12065,7 +12368,8 @@ function createLContainer(hostNative, currentView, native, tNode) {
12065
12368
  tNode,
12066
12369
  native,
12067
12370
  null,
12068
- null, // moved views
12371
+ null,
12372
+ null, // dehydrated views
12069
12373
  ];
12070
12374
  ngDevMode &&
12071
12375
  assertEqual(lContainer.length, CONTAINER_HEADER_OFFSET, 'Should allocate correct number of slots for LContainer header.');
@@ -12887,7 +13191,7 @@ class ComponentFactory extends ComponentFactory$1 {
12887
13191
  const rootFlags = this.componentDef.onPush ? 32 /* LViewFlags.Dirty */ | 256 /* LViewFlags.IsRoot */ :
12888
13192
  16 /* LViewFlags.CheckAlways */ | 256 /* LViewFlags.IsRoot */;
12889
13193
  // Create the root view. Uses empty TView and ContentTemplate.
12890
- const rootTView = createTView(0 /* TViewType.Root */, null, null, 1, 0, null, null, null, null, null);
13194
+ const rootTView = createTView(0 /* TViewType.Root */, null, null, 1, 0, null, null, null, null, null, null);
12891
13195
  const rootLView = createLView(null, rootTView, null, rootFlags, null, null, rendererFactory, hostRenderer, sanitizer, rootViewInjector, null, null);
12892
13196
  // rootView is the parent when bootstrapping
12893
13197
  // TODO(misko): it looks like we are entering view here but we don't really need to as
@@ -14154,33 +14458,731 @@ function ɵɵattributeInterpolateV(attrName, values, sanitizer, namespace) {
14154
14458
  for (let i = 2; i < values.length; i += 2) {
14155
14459
  interpolationInBetween.push(values[i]);
14156
14460
  }
14157
- storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - interpolationInBetween.length + 1, ...interpolationInBetween);
14461
+ storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - interpolationInBetween.length + 1, ...interpolationInBetween);
14462
+ }
14463
+ }
14464
+ return ɵɵattributeInterpolateV;
14465
+ }
14466
+
14467
+ /**
14468
+ * Synchronously perform change detection on a component (and possibly its sub-components).
14469
+ *
14470
+ * This function triggers change detection in a synchronous way on a component.
14471
+ *
14472
+ * @param component The component which the change detection should be performed on.
14473
+ */
14474
+ function detectChanges(component) {
14475
+ const view = getComponentViewByInstance(component);
14476
+ detectChangesInternal(view[TVIEW], view, component);
14477
+ }
14478
+
14479
+ const AT_THIS_LOCATION = '<-- AT THIS LOCATION';
14480
+ /**
14481
+ * Retrieves a user friendly string for a given TNodeType for use in
14482
+ * friendly error messages
14483
+ *
14484
+ * @param tNodeType
14485
+ * @returns
14486
+ */
14487
+ function getFriendlyStringFromTNodeType(tNodeType) {
14488
+ switch (tNodeType) {
14489
+ case 4 /* TNodeType.Container */:
14490
+ return 'view container';
14491
+ case 2 /* TNodeType.Element */:
14492
+ return 'element';
14493
+ case 8 /* TNodeType.ElementContainer */:
14494
+ return 'ng-container';
14495
+ case 32 /* TNodeType.Icu */:
14496
+ return 'icu';
14497
+ case 64 /* TNodeType.Placeholder */:
14498
+ return 'i18n';
14499
+ case 16 /* TNodeType.Projection */:
14500
+ return 'projection';
14501
+ case 1 /* TNodeType.Text */:
14502
+ return 'text';
14503
+ default:
14504
+ // This should not happen as we cover all possible TNode types above.
14505
+ return '<unknown>';
14506
+ }
14507
+ }
14508
+ /**
14509
+ * Validates that provided nodes match during the hydration process.
14510
+ */
14511
+ function validateMatchingNode(node, nodeType, tagName, lView, tNode, isViewContainerAnchor = false) {
14512
+ var _a, _b, _c;
14513
+ if (!node ||
14514
+ (node.nodeType !== nodeType ||
14515
+ (node.nodeType === Node.ELEMENT_NODE &&
14516
+ node.tagName.toLowerCase() !== (tagName === null || tagName === void 0 ? void 0 : tagName.toLowerCase())))) {
14517
+ const expectedNode = shortRNodeDescription(nodeType, tagName, null);
14518
+ let header = `During hydration Angular expected ${expectedNode} but `;
14519
+ const hostComponentDef = getDeclarationComponentDef(lView);
14520
+ const componentClassName = (_a = hostComponentDef === null || hostComponentDef === void 0 ? void 0 : hostComponentDef.type) === null || _a === void 0 ? void 0 : _a.name;
14521
+ const expected = `Angular expected this DOM:\n\n${describeExpectedDom(lView, tNode, isViewContainerAnchor)}\n\n`;
14522
+ let actual = '';
14523
+ if (!node) {
14524
+ // No node found during hydration.
14525
+ header += `the node was not found.\n\n`;
14526
+ }
14527
+ else {
14528
+ const actualNode = shortRNodeDescription(node.nodeType, (_b = node.tagName) !== null && _b !== void 0 ? _b : null, (_c = node.textContent) !== null && _c !== void 0 ? _c : null);
14529
+ header += `found ${actualNode}.\n\n`;
14530
+ actual = `Actual DOM is:\n\n${describeDomFromNode(node)}\n\n`;
14531
+ }
14532
+ const footer = getHydrationErrorFooter(componentClassName);
14533
+ const message = header + expected + actual + getHydrationAttributeNote() + footer;
14534
+ throw new RuntimeError(500 /* RuntimeErrorCode.HYDRATION_NODE_MISMATCH */, message);
14535
+ }
14536
+ }
14537
+ /**
14538
+ * Validates that a given node has sibling nodes
14539
+ */
14540
+ function validateSiblingNodeExists(node) {
14541
+ validateNodeExists(node);
14542
+ if (!node.nextSibling) {
14543
+ const header = 'During hydration Angular expected more sibling nodes to be present.\n\n';
14544
+ const actual = `Actual DOM is:\n\n${describeDomFromNode(node)}\n\n`;
14545
+ const footer = getHydrationErrorFooter();
14546
+ const message = header + actual + footer;
14547
+ throw new RuntimeError(501 /* RuntimeErrorCode.HYDRATION_MISSING_SIBLINGS */, message);
14548
+ }
14549
+ }
14550
+ /**
14551
+ * Validates that a node exists or throws
14552
+ */
14553
+ function validateNodeExists(node) {
14554
+ if (!node) {
14555
+ throw new RuntimeError(502 /* RuntimeErrorCode.HYDRATION_MISSING_NODE */, `Hydration expected an element to be present at this location.`);
14556
+ }
14557
+ }
14558
+ /**
14559
+ * Builds the hydration error message when a node is not found
14560
+ *
14561
+ * @param lView the LView where the node exists
14562
+ * @param tNode the TNode
14563
+ */
14564
+ function nodeNotFoundError(lView, tNode) {
14565
+ const header = 'During serialization, Angular was unable to find an element in the DOM:\n\n';
14566
+ const expected = `${describeExpectedDom(lView, tNode, false)}\n\n`;
14567
+ const footer = getHydrationErrorFooter();
14568
+ throw new RuntimeError(502 /* RuntimeErrorCode.HYDRATION_MISSING_NODE */, header + expected + footer);
14569
+ }
14570
+ /**
14571
+ * Builds a hydration error message when a node is not found at a path location
14572
+ *
14573
+ * @param host the Host Node
14574
+ * @param path the path to the node
14575
+ */
14576
+ function nodeNotFoundAtPathError(host, path) {
14577
+ const header = `During hydration Angular was unable to locate a node ` +
14578
+ `using the "${path}" path, starting from the ${describeRNode(host)} node.\n\n`;
14579
+ const footer = getHydrationErrorFooter();
14580
+ throw new RuntimeError(502 /* RuntimeErrorCode.HYDRATION_MISSING_NODE */, header + footer);
14581
+ }
14582
+ /**
14583
+ * Builds the hydration error message in the case that dom nodes are created outside of
14584
+ * the Angular context and are being used as projected nodes
14585
+ *
14586
+ * @param lView the LView
14587
+ * @param tNode the TNode
14588
+ * @returns an error
14589
+ */
14590
+ function unsupportedProjectionOfDomNodes(rNode) {
14591
+ const header = 'During serialization, Angular detected DOM nodes ' +
14592
+ 'that were created outside of Angular context and provided as projectable nodes ' +
14593
+ '(likely via `ViewContainerRef.createComponent` or `createComponent` APIs). ' +
14594
+ 'Hydration is not supported for such cases, consider refactoring the code to avoid ' +
14595
+ 'this pattern or using `ngSkipHydration` on the host element of the component.\n\n';
14596
+ const actual = `${describeDomFromNode(rNode)}\n\n`;
14597
+ const message = header + actual + getHydrationAttributeNote();
14598
+ return new RuntimeError(503 /* RuntimeErrorCode.UNSUPPORTED_PROJECTION_DOM_NODES */, message);
14599
+ }
14600
+ /**
14601
+ * Builds the hydration error message in the case that ngSkipHydration was used on a
14602
+ * node that is not a component host element or host binding
14603
+ *
14604
+ * @param rNode the HTML Element
14605
+ * @returns an error
14606
+ */
14607
+ function invalidSkipHydrationHost(rNode) {
14608
+ const header = 'The `ngSkipHydration` flag is applied on a node ' +
14609
+ 'that doesn\'t act as a component host. Hydration can be ' +
14610
+ 'skipped only on per-component basis.\n\n';
14611
+ const actual = `${describeDomFromNode(rNode)}\n\n`;
14612
+ const footer = 'Please move the `ngSkipHydration` attribute to the component host element.';
14613
+ const message = header + actual + footer;
14614
+ return new RuntimeError(504 /* RuntimeErrorCode.INVALID_SKIP_HYDRATION_HOST */, message);
14615
+ }
14616
+ /**
14617
+ * Builds the hydration error message in the case that a user is attempting to enable
14618
+ * hydration on internationalized nodes, which is not yet supported.
14619
+ *
14620
+ * @param rNode the HTML Element
14621
+ * @returns an error
14622
+ */
14623
+ function notYetSupportedI18nBlockError(rNode) {
14624
+ const header = 'Hydration for nodes marked with `i18n` is not yet supported. ' +
14625
+ 'You can opt-out a component that uses `i18n` in a template using ' +
14626
+ 'the `ngSkipHydration` attribute or fall back to the previous ' +
14627
+ 'hydration logic (which re-creates the application structure).\n\n';
14628
+ const actual = `${describeDomFromNode(rNode)}\n\n`;
14629
+ const message = header + actual;
14630
+ return new RuntimeError(518 /* RuntimeErrorCode.HYDRATION_I18N_NOT_YET_SUPPORTED */, message);
14631
+ }
14632
+ // Stringification methods
14633
+ /**
14634
+ * Stringifies a given TNode's attributes
14635
+ *
14636
+ * @param tNode a provided TNode
14637
+ * @returns string
14638
+ */
14639
+ function stringifyTNodeAttrs(tNode) {
14640
+ const results = [];
14641
+ if (tNode.attrs) {
14642
+ for (let i = 0; i < tNode.attrs.length;) {
14643
+ const attrName = tNode.attrs[i++];
14644
+ // Once we reach the first flag, we know that the list of
14645
+ // attributes is over.
14646
+ if (typeof attrName == 'number') {
14647
+ break;
14648
+ }
14649
+ const attrValue = tNode.attrs[i++];
14650
+ results.push(`${attrName}="${shorten(attrValue)}"`);
14651
+ }
14652
+ }
14653
+ return results.join(' ');
14654
+ }
14655
+ /**
14656
+ * The list of internal attributes that should be filtered out while
14657
+ * producing an error message.
14658
+ */
14659
+ const internalAttrs = new Set(['ngh', 'ng-version', 'ng-server-context']);
14660
+ /**
14661
+ * Stringifies an HTML Element's attributes
14662
+ *
14663
+ * @param rNode an HTML Element
14664
+ * @returns string
14665
+ */
14666
+ function stringifyRNodeAttrs(rNode) {
14667
+ const results = [];
14668
+ for (let i = 0; i < rNode.attributes.length; i++) {
14669
+ const attr = rNode.attributes[i];
14670
+ if (internalAttrs.has(attr.name))
14671
+ continue;
14672
+ results.push(`${attr.name}="${shorten(attr.value)}"`);
14673
+ }
14674
+ return results.join(' ');
14675
+ }
14676
+ // Methods for Describing the DOM
14677
+ /**
14678
+ * Converts a tNode to a helpful readable string value for use in error messages
14679
+ *
14680
+ * @param tNode a given TNode
14681
+ * @param innerContent the content of the node
14682
+ * @returns string
14683
+ */
14684
+ function describeTNode(tNode, innerContent = '…') {
14685
+ switch (tNode.type) {
14686
+ case 1 /* TNodeType.Text */:
14687
+ const content = tNode.value ? `(${tNode.value})` : '';
14688
+ return `#text${content}`;
14689
+ case 2 /* TNodeType.Element */:
14690
+ const attrs = stringifyTNodeAttrs(tNode);
14691
+ const tag = tNode.value.toLowerCase();
14692
+ return `<${tag}${attrs ? ' ' + attrs : ''}>${innerContent}</${tag}>`;
14693
+ case 8 /* TNodeType.ElementContainer */:
14694
+ return '<!-- ng-container -->';
14695
+ case 4 /* TNodeType.Container */:
14696
+ return '<!-- container -->';
14697
+ default:
14698
+ const typeAsString = getFriendlyStringFromTNodeType(tNode.type);
14699
+ return `#node(${typeAsString})`;
14700
+ }
14701
+ }
14702
+ /**
14703
+ * Converts an RNode to a helpful readable string value for use in error messages
14704
+ *
14705
+ * @param rNode a given RNode
14706
+ * @param innerContent the content of the node
14707
+ * @returns string
14708
+ */
14709
+ function describeRNode(rNode, innerContent = '…') {
14710
+ var _a;
14711
+ const node = rNode;
14712
+ switch (node.nodeType) {
14713
+ case Node.ELEMENT_NODE:
14714
+ const tag = node.tagName.toLowerCase();
14715
+ const attrs = stringifyRNodeAttrs(node);
14716
+ return `<${tag}${attrs ? ' ' + attrs : ''}>${innerContent}</${tag}>`;
14717
+ case Node.TEXT_NODE:
14718
+ const content = node.textContent ? shorten(node.textContent) : '';
14719
+ return `#text${content ? `(${content})` : ''}`;
14720
+ case Node.COMMENT_NODE:
14721
+ return `<!-- ${shorten((_a = node.textContent) !== null && _a !== void 0 ? _a : '')} -->`;
14722
+ default:
14723
+ return `#node(${node.nodeType})`;
14724
+ }
14725
+ }
14726
+ /**
14727
+ * Builds the string containing the expected DOM present given the LView and TNode
14728
+ * values for a readable error message
14729
+ *
14730
+ * @param lView the lView containing the DOM
14731
+ * @param tNode the tNode
14732
+ * @param isViewContainerAnchor boolean
14733
+ * @returns string
14734
+ */
14735
+ function describeExpectedDom(lView, tNode, isViewContainerAnchor) {
14736
+ const spacer = ' ';
14737
+ let content = '';
14738
+ if (tNode.prev) {
14739
+ content += spacer + '…\n';
14740
+ content += spacer + describeTNode(tNode.prev) + '\n';
14741
+ }
14742
+ else if (tNode.type && tNode.type & 12 /* TNodeType.AnyContainer */) {
14743
+ content += spacer + '…\n';
14744
+ }
14745
+ if (isViewContainerAnchor) {
14746
+ content += spacer + describeTNode(tNode) + '\n';
14747
+ content += spacer + `<!-- container --> ${AT_THIS_LOCATION}\n`;
14748
+ }
14749
+ else {
14750
+ content += spacer + describeTNode(tNode) + ` ${AT_THIS_LOCATION}\n`;
14751
+ }
14752
+ content += spacer + '…\n';
14753
+ const parentRNode = tNode.type ? getParentRElement(lView[TVIEW], tNode, lView) : null;
14754
+ if (parentRNode) {
14755
+ content = describeRNode(parentRNode, '\n' + content);
14756
+ }
14757
+ return content;
14758
+ }
14759
+ /**
14760
+ * Builds the string containing the DOM present around a given RNode for a
14761
+ * readable error message
14762
+ *
14763
+ * @param node the RNode
14764
+ * @returns string
14765
+ */
14766
+ function describeDomFromNode(node) {
14767
+ const spacer = ' ';
14768
+ let content = '';
14769
+ const currentNode = node;
14770
+ if (currentNode.previousSibling) {
14771
+ content += spacer + '…\n';
14772
+ content += spacer + describeRNode(currentNode.previousSibling) + '\n';
14773
+ }
14774
+ content += spacer + describeRNode(currentNode) + ` ${AT_THIS_LOCATION}\n`;
14775
+ if (node.nextSibling) {
14776
+ content += spacer + '…\n';
14777
+ }
14778
+ if (node.parentNode) {
14779
+ content = describeRNode(currentNode.parentNode, '\n' + content);
14780
+ }
14781
+ return content;
14782
+ }
14783
+ /**
14784
+ * Shortens the description of a given RNode by its type for readability
14785
+ *
14786
+ * @param nodeType the type of node
14787
+ * @param tagName the node tag name
14788
+ * @param textContent the text content in the node
14789
+ * @returns string
14790
+ */
14791
+ function shortRNodeDescription(nodeType, tagName, textContent) {
14792
+ switch (nodeType) {
14793
+ case Node.ELEMENT_NODE:
14794
+ return `<${tagName.toLowerCase()}>`;
14795
+ case Node.TEXT_NODE:
14796
+ const content = textContent ? ` (with the "${shorten(textContent)}" content)` : '';
14797
+ return `a text node${content}`;
14798
+ case Node.COMMENT_NODE:
14799
+ return 'a comment node';
14800
+ default:
14801
+ return `#node(nodeType=${nodeType})`;
14802
+ }
14803
+ }
14804
+ /**
14805
+ * Builds the footer hydration error message
14806
+ *
14807
+ * @param componentClassName the name of the component class
14808
+ * @returns string
14809
+ */
14810
+ function getHydrationErrorFooter(componentClassName) {
14811
+ const componentInfo = componentClassName ? `the "${componentClassName}"` : 'corresponding';
14812
+ return `To fix this problem:\n` +
14813
+ ` * check ${componentInfo} component for hydration-related issues\n` +
14814
+ ` * or skip hydration by adding the \`ngSkipHydration\` attribute ` +
14815
+ `to its host node in a template`;
14816
+ }
14817
+ /**
14818
+ * An attribute related note for hydration errors
14819
+ */
14820
+ function getHydrationAttributeNote() {
14821
+ return 'Note: attributes are only displayed to better represent the DOM' +
14822
+ ' but have no effect on hydration mismatches.\n\n';
14823
+ }
14824
+ // Node string utility functions
14825
+ /**
14826
+ * Strips all newlines out of a given string
14827
+ *
14828
+ * @param input a string to be cleared of new line characters
14829
+ * @returns
14830
+ */
14831
+ function stripNewlines(input) {
14832
+ return input.replace(/\s+/gm, '');
14833
+ }
14834
+ /**
14835
+ * Reduces a string down to a maximum length of characters with ellipsis for readability
14836
+ *
14837
+ * @param input a string input
14838
+ * @param maxLength a maximum length in characters
14839
+ * @returns string
14840
+ */
14841
+ function shorten(input, maxLength = 50) {
14842
+ if (!input) {
14843
+ return '';
14844
+ }
14845
+ input = stripNewlines(input);
14846
+ return input.length > maxLength ? `${input.substring(0, maxLength - 1)}…` : input;
14847
+ }
14848
+
14849
+ /**
14850
+ * Regexp that extracts a reference node information from the compressed node location.
14851
+ * The reference node is represented as either:
14852
+ * - a number which points to an LView slot
14853
+ * - the `b` char which indicates that the lookup should start from the `document.body`
14854
+ * - the `h` char to start lookup from the component host node (`lView[HOST]`)
14855
+ */
14856
+ const REF_EXTRACTOR_REGEXP = new RegExp(`^(\\d+)*(${REFERENCE_NODE_BODY}|${REFERENCE_NODE_HOST})*(.*)`);
14857
+ /**
14858
+ * Helper function that takes a reference node location and a set of navigation steps
14859
+ * (from the reference node) to a target node and outputs a string that represents
14860
+ * a location.
14861
+ *
14862
+ * For example, given: referenceNode = 'b' (body) and path = ['firstChild', 'firstChild',
14863
+ * 'nextSibling'], the function returns: `bf2n`.
14864
+ */
14865
+ function compressNodeLocation(referenceNode, path) {
14866
+ const result = [referenceNode];
14867
+ for (const segment of path) {
14868
+ const lastIdx = result.length - 1;
14869
+ if (lastIdx > 0 && result[lastIdx - 1] === segment) {
14870
+ // An empty string in a count slot represents 1 occurrence of an instruction.
14871
+ const value = (result[lastIdx] || 1);
14872
+ result[lastIdx] = value + 1;
14873
+ }
14874
+ else {
14875
+ // Adding a new segment to the path.
14876
+ // Using an empty string in a counter field to avoid encoding `1`s
14877
+ // into the path, since they are implicit (e.g. `f1n1` vs `fn`), so
14878
+ // it's enough to have a single char in this case.
14879
+ result.push(segment, '');
14880
+ }
14881
+ }
14882
+ return result.join('');
14883
+ }
14884
+ /**
14885
+ * Helper function that reverts the `compressNodeLocation` and transforms a given
14886
+ * string into an array where at 0th position there is a reference node info and
14887
+ * after that it contains information (in pairs) about a navigation step and the
14888
+ * number of repetitions.
14889
+ *
14890
+ * For example, the path like 'bf2n' will be transformed to:
14891
+ * ['b', 'firstChild', 2, 'nextSibling', 1].
14892
+ *
14893
+ * This information is later consumed by the code that navigates the DOM to find
14894
+ * a given node by its location.
14895
+ */
14896
+ function decompressNodeLocation(path) {
14897
+ const matches = path.match(REF_EXTRACTOR_REGEXP);
14898
+ const [_, refNodeId, refNodeName, rest] = matches;
14899
+ // If a reference node is represented by an index, transform it to a number.
14900
+ const ref = refNodeId ? parseInt(refNodeId, 10) : refNodeName;
14901
+ const steps = [];
14902
+ // Match all segments in a path.
14903
+ for (const [_, step, count] of rest.matchAll(/(f|n)(\d*)/g)) {
14904
+ const repeat = parseInt(count, 10) || 1;
14905
+ steps.push(step, repeat);
14906
+ }
14907
+ return [ref, ...steps];
14908
+ }
14909
+
14910
+ /** Whether current TNode is a first node in an <ng-container>. */
14911
+ function isFirstElementInNgContainer(tNode) {
14912
+ var _a;
14913
+ return !tNode.prev && ((_a = tNode.parent) === null || _a === void 0 ? void 0 : _a.type) === 8 /* TNodeType.ElementContainer */;
14914
+ }
14915
+ /** Returns an instruction index (subtracting HEADER_OFFSET). */
14916
+ function getNoOffsetIndex(tNode) {
14917
+ return tNode.index - HEADER_OFFSET;
14918
+ }
14919
+ /**
14920
+ * Locate a node in DOM tree that corresponds to a given TNode.
14921
+ *
14922
+ * @param hydrationInfo The hydration annotation data
14923
+ * @param tView the current tView
14924
+ * @param lView the current lView
14925
+ * @param tNode the current tNode
14926
+ * @returns an RNode that represents a given tNode
14927
+ */
14928
+ function locateNextRNode(hydrationInfo, tView, lView, tNode) {
14929
+ var _a;
14930
+ let native = null;
14931
+ const noOffsetIndex = getNoOffsetIndex(tNode);
14932
+ const nodes = hydrationInfo.data[NODES];
14933
+ if (nodes === null || nodes === void 0 ? void 0 : nodes[noOffsetIndex]) {
14934
+ // We know the exact location of the node.
14935
+ native = locateRNodeByPath(nodes[noOffsetIndex], lView);
14936
+ }
14937
+ else if (tView.firstChild === tNode) {
14938
+ // We create a first node in this view, so we use a reference
14939
+ // to the first child in this DOM segment.
14940
+ native = hydrationInfo.firstChild;
14941
+ }
14942
+ else {
14943
+ // Locate a node based on a previous sibling or a parent node.
14944
+ const previousTNodeParent = tNode.prev === null;
14945
+ const previousTNode = ((_a = tNode.prev) !== null && _a !== void 0 ? _a : tNode.parent);
14946
+ ngDevMode &&
14947
+ assertDefined(previousTNode, 'Unexpected state: current TNode does not have a connection ' +
14948
+ 'to the previous node or a parent node.');
14949
+ if (isFirstElementInNgContainer(tNode)) {
14950
+ const noOffsetParentIndex = getNoOffsetIndex(tNode.parent);
14951
+ native = getSegmentHead(hydrationInfo, noOffsetParentIndex);
14952
+ }
14953
+ else {
14954
+ let previousRElement = getNativeByTNode(previousTNode, lView);
14955
+ if (previousTNodeParent) {
14956
+ native = previousRElement.firstChild;
14957
+ }
14958
+ else {
14959
+ // If the previous node is an element, but it also has container info,
14960
+ // this means that we are processing a node like `<div #vcrTarget>`, which is
14961
+ // represented in the DOM as `<div></div>...<!--container-->`.
14962
+ // In this case, there are nodes *after* this element and we need to skip
14963
+ // all of them to reach an element that we are looking for.
14964
+ const noOffsetPrevSiblingIndex = getNoOffsetIndex(previousTNode);
14965
+ const segmentHead = getSegmentHead(hydrationInfo, noOffsetPrevSiblingIndex);
14966
+ if (previousTNode.type === 2 /* TNodeType.Element */ && segmentHead) {
14967
+ const numRootNodesToSkip = calcSerializedContainerSize(hydrationInfo, noOffsetPrevSiblingIndex);
14968
+ // `+1` stands for an anchor comment node after all the views in this container.
14969
+ const nodesToSkip = numRootNodesToSkip + 1;
14970
+ // First node after this segment.
14971
+ native = siblingAfter(nodesToSkip, segmentHead);
14972
+ }
14973
+ else {
14974
+ native = previousRElement.nextSibling;
14975
+ }
14976
+ }
14977
+ }
14978
+ }
14979
+ return native;
14980
+ }
14981
+ /**
14982
+ * Skips over a specified number of nodes and returns the next sibling node after that.
14983
+ */
14984
+ function siblingAfter(skip, from) {
14985
+ let currentNode = from;
14986
+ for (let i = 0; i < skip; i++) {
14987
+ ngDevMode && validateSiblingNodeExists(currentNode);
14988
+ currentNode = currentNode.nextSibling;
14989
+ }
14990
+ return currentNode;
14991
+ }
14992
+ /**
14993
+ * Helper function to produce a string representation of the navigation steps
14994
+ * (in terms of `nextSibling` and `firstChild` navigations). Used in error
14995
+ * messages in dev mode.
14996
+ */
14997
+ function stringifyNavigationInstructions(instructions) {
14998
+ const container = [];
14999
+ let i = 0;
15000
+ while (i < instructions.length) {
15001
+ const step = instructions[i++];
15002
+ const repeat = instructions[i++];
15003
+ for (let r = 0; r < repeat; r++) {
15004
+ container.push(step === NodeNavigationStep.FirstChild ? 'firstChild' : 'nextSibling');
15005
+ }
15006
+ }
15007
+ return container.join('.');
15008
+ }
15009
+ /**
15010
+ * Helper function that navigates from a starting point node (the `from` node)
15011
+ * using provided set of navigation instructions (within `path` argument).
15012
+ */
15013
+ function navigateToNode(from, instructions) {
15014
+ let node = from;
15015
+ let i = 0;
15016
+ while (i < instructions.length) {
15017
+ const step = instructions[i++];
15018
+ const repeat = instructions[i++];
15019
+ for (let r = 0; r < repeat; r++) {
15020
+ if (ngDevMode && !node) {
15021
+ throw nodeNotFoundAtPathError(from, stringifyNavigationInstructions(instructions));
15022
+ }
15023
+ switch (step) {
15024
+ case NodeNavigationStep.FirstChild:
15025
+ node = node.firstChild;
15026
+ break;
15027
+ case NodeNavigationStep.NextSibling:
15028
+ node = node.nextSibling;
15029
+ break;
15030
+ }
14158
15031
  }
14159
15032
  }
14160
- return ɵɵattributeInterpolateV;
15033
+ if (ngDevMode && !node) {
15034
+ throw nodeNotFoundAtPathError(from, stringifyNavigationInstructions(instructions));
15035
+ }
15036
+ return node;
14161
15037
  }
14162
-
14163
15038
  /**
14164
- * Synchronously perform change detection on a component (and possibly its sub-components).
14165
- *
14166
- * This function triggers change detection in a synchronous way on a component.
15039
+ * Locates an RNode given a set of navigation instructions (which also contains
15040
+ * a starting point node info).
15041
+ */
15042
+ function locateRNodeByPath(path, lView) {
15043
+ const [referenceNode, ...navigationInstructions] = decompressNodeLocation(path);
15044
+ let ref;
15045
+ if (referenceNode === REFERENCE_NODE_HOST) {
15046
+ ref = lView[0];
15047
+ }
15048
+ else if (referenceNode === REFERENCE_NODE_BODY) {
15049
+ ref = ɵɵresolveBody(lView[0]);
15050
+ }
15051
+ else {
15052
+ const parentElementId = Number(referenceNode);
15053
+ ref = unwrapRNode(lView[parentElementId + HEADER_OFFSET]);
15054
+ }
15055
+ return navigateToNode(ref, navigationInstructions);
15056
+ }
15057
+ /**
15058
+ * Generate a list of DOM navigation operations to get from node `start` to node `finish`.
14167
15059
  *
14168
- * @param component The component which the change detection should be performed on.
15060
+ * Note: assumes that node `start` occurs before node `finish` in an in-order traversal of the DOM
15061
+ * tree. That is, we should be able to get from `start` to `finish` purely by using `.firstChild`
15062
+ * and `.nextSibling` operations.
14169
15063
  */
14170
- function detectChanges(component) {
14171
- const view = getComponentViewByInstance(component);
14172
- detectChangesInternal(view[TVIEW], view, component);
15064
+ function navigateBetween(start, finish) {
15065
+ if (start === finish) {
15066
+ return [];
15067
+ }
15068
+ else if (start.parentElement == null || finish.parentElement == null) {
15069
+ return null;
15070
+ }
15071
+ else if (start.parentElement === finish.parentElement) {
15072
+ return navigateBetweenSiblings(start, finish);
15073
+ }
15074
+ else {
15075
+ // `finish` is a child of its parent, so the parent will always have a child.
15076
+ const parent = finish.parentElement;
15077
+ const parentPath = navigateBetween(start, parent);
15078
+ const childPath = navigateBetween(parent.firstChild, finish);
15079
+ if (!parentPath || !childPath)
15080
+ return null;
15081
+ return [
15082
+ // First navigate to `finish`'s parent
15083
+ ...parentPath,
15084
+ // Then to its first child.
15085
+ NodeNavigationStep.FirstChild,
15086
+ // And finally from that node to `finish` (maybe a no-op if we're already there).
15087
+ ...childPath,
15088
+ ];
15089
+ }
15090
+ }
15091
+ /**
15092
+ * Calculates a path between 2 sibling nodes (generates a number of `NextSibling` navigations).
15093
+ */
15094
+ function navigateBetweenSiblings(start, finish) {
15095
+ const nav = [];
15096
+ let node = null;
15097
+ for (node = start; node != null && node !== finish; node = node.nextSibling) {
15098
+ nav.push(NodeNavigationStep.NextSibling);
15099
+ }
15100
+ return node === null ? [] : nav;
15101
+ }
15102
+ /**
15103
+ * Calculates a path between 2 nodes in terms of `nextSibling` and `firstChild`
15104
+ * navigations:
15105
+ * - the `from` node is a known node, used as an starting point for the lookup
15106
+ * (the `fromNodeName` argument is a string representation of the node).
15107
+ * - the `to` node is a node that the runtime logic would be looking up,
15108
+ * using the path generated by this function.
15109
+ */
15110
+ function calcPathBetween(from, to, fromNodeName) {
15111
+ const path = navigateBetween(from, to);
15112
+ return path === null ? null : compressNodeLocation(fromNodeName, path);
15113
+ }
15114
+ /**
15115
+ * Invoked at serialization time (on the server) when a set of navigation
15116
+ * instructions needs to be generated for a TNode.
15117
+ */
15118
+ function calcPathForNode(tNode, lView) {
15119
+ const parentTNode = tNode.parent;
15120
+ let parentIndex;
15121
+ let parentRNode;
15122
+ let referenceNodeName;
15123
+ if (parentTNode === null) {
15124
+ // No parent TNode - use host element as a reference node.
15125
+ parentIndex = referenceNodeName = REFERENCE_NODE_HOST;
15126
+ parentRNode = lView[HOST];
15127
+ }
15128
+ else {
15129
+ // Use parent TNode as a reference node.
15130
+ parentIndex = parentTNode.index;
15131
+ parentRNode = unwrapRNode(lView[parentIndex]);
15132
+ referenceNodeName = renderStringify(parentIndex - HEADER_OFFSET);
15133
+ }
15134
+ let rNode = unwrapRNode(lView[tNode.index]);
15135
+ if (tNode.type & 12 /* TNodeType.AnyContainer */) {
15136
+ // For <ng-container> nodes, instead of serializing a reference
15137
+ // to the anchor comment node, serialize a location of the first
15138
+ // DOM element. Paired with the container size (serialized as a part
15139
+ // of `ngh.containers`), it should give enough information for runtime
15140
+ // to hydrate nodes in this container.
15141
+ const firstRNode = getFirstNativeNode(lView, tNode);
15142
+ // If container is not empty, use a reference to the first element,
15143
+ // otherwise, rNode would point to an anchor comment node.
15144
+ if (firstRNode) {
15145
+ rNode = firstRNode;
15146
+ }
15147
+ }
15148
+ let path = calcPathBetween(parentRNode, rNode, referenceNodeName);
15149
+ if (path === null && parentRNode !== rNode) {
15150
+ // Searching for a path between elements within a host node failed.
15151
+ // Trying to find a path to an element starting from the `document.body` instead.
15152
+ //
15153
+ // Important note: this type of reference is relatively unstable, since Angular
15154
+ // may not be able to control parts of the page that the runtime logic navigates
15155
+ // through. This is mostly needed to cover "portals" use-case (like menus, dialog boxes,
15156
+ // etc), where nodes are content-projected (including direct DOM manipulations) outside
15157
+ // of the host node. The better solution is to provide APIs to work with "portals",
15158
+ // at which point this code path would not be needed.
15159
+ const body = parentRNode.ownerDocument.body;
15160
+ path = calcPathBetween(body, rNode, REFERENCE_NODE_BODY);
15161
+ if (path === null) {
15162
+ // If the path is still empty, it's likely that this node is detached and
15163
+ // won't be found during hydration.
15164
+ throw nodeNotFoundError(lView, tNode);
15165
+ }
15166
+ }
15167
+ return path;
14173
15168
  }
14174
15169
 
14175
15170
  function templateFirstCreatePass(index, tView, lView, templateFn, decls, vars, tagName, attrsIndex, localRefsIndex) {
15171
+ var _a, _b;
14176
15172
  ngDevMode && assertFirstCreatePass(tView);
14177
15173
  ngDevMode && ngDevMode.firstCreatePass++;
14178
15174
  const tViewConsts = tView.consts;
15175
+ let ssrId = null;
15176
+ const hydrationInfo = lView[HYDRATION];
15177
+ if (hydrationInfo) {
15178
+ const noOffsetIndex = index - HEADER_OFFSET;
15179
+ ssrId = (_b = (hydrationInfo && ((_a = hydrationInfo.data[TEMPLATES]) === null || _a === void 0 ? void 0 : _a[noOffsetIndex]))) !== null && _b !== void 0 ? _b : null;
15180
+ }
14179
15181
  // TODO(pk): refactor getOrCreateTNode to have the "create" only version
14180
15182
  const tNode = getOrCreateTNode(tView, index, 4 /* TNodeType.Container */, tagName || null, getConstant(tViewConsts, attrsIndex));
14181
15183
  resolveDirectives(tView, lView, tNode, getConstant(tViewConsts, localRefsIndex));
14182
15184
  registerPostOrderHooks(tView, tNode);
14183
- const embeddedTView = tNode.tView = createTView(2 /* TViewType.Embedded */, tNode, templateFn, decls, vars, tView.directiveRegistry, tView.pipeRegistry, null, tView.schemas, tViewConsts);
15185
+ const embeddedTView = tNode.tView = createTView(2 /* TViewType.Embedded */, tNode, templateFn, decls, vars, tView.directiveRegistry, tView.pipeRegistry, null, tView.schemas, tViewConsts, ssrId);
14184
15186
  if (tView.queries !== null) {
14185
15187
  tView.queries.template(tView, tNode);
14186
15188
  embeddedTView.queries = tView.queries.embeddedTView(tNode);
@@ -14213,8 +15215,10 @@ function ɵɵtemplate(index, templateFn, decls, vars, tagName, attrsIndex, local
14213
15215
  const tNode = tView.firstCreatePass ? templateFirstCreatePass(adjustedIndex, tView, lView, templateFn, decls, vars, tagName, attrsIndex, localRefsIndex) :
14214
15216
  tView.data[adjustedIndex];
14215
15217
  setCurrentTNode(tNode, false);
14216
- const comment = lView[RENDERER].createComment(ngDevMode ? 'container' : '');
14217
- appendChild(tView, lView, comment, tNode);
15218
+ const comment = _locateOrCreateContainerAnchor(tView, lView, tNode, index);
15219
+ if (wasLastNodeCreated()) {
15220
+ appendChild(tView, lView, comment, tNode);
15221
+ }
14218
15222
  attachPatchData(comment, lView);
14219
15223
  addToViewTree(lView, lView[adjustedIndex] = createLContainer(comment, lView, comment, tNode));
14220
15224
  if (isDirectiveHost(tNode)) {
@@ -14224,6 +15228,42 @@ function ɵɵtemplate(index, templateFn, decls, vars, tagName, attrsIndex, local
14224
15228
  saveResolvedLocalsInData(lView, tNode, localRefExtractor);
14225
15229
  }
14226
15230
  }
15231
+ let _locateOrCreateContainerAnchor = createContainerAnchorImpl;
15232
+ /**
15233
+ * Regular creation mode for LContainers and their anchor (comment) nodes.
15234
+ */
15235
+ function createContainerAnchorImpl(tView, lView, tNode, index) {
15236
+ lastNodeWasCreated(true);
15237
+ return lView[RENDERER].createComment(ngDevMode ? 'container' : '');
15238
+ }
15239
+ /**
15240
+ * Enables hydration code path (to lookup existing elements in DOM)
15241
+ * in addition to the regular creation mode for LContainers and their
15242
+ * anchor (comment) nodes.
15243
+ */
15244
+ function locateOrCreateContainerAnchorImpl(tView, lView, tNode, index) {
15245
+ const hydrationInfo = lView[HYDRATION];
15246
+ const isNodeCreationMode = !hydrationInfo || isInSkipHydrationBlock$1() || isDisconnectedNode(hydrationInfo, index);
15247
+ lastNodeWasCreated(isNodeCreationMode);
15248
+ // Regular creation mode.
15249
+ if (isNodeCreationMode) {
15250
+ return createContainerAnchorImpl(tView, lView, tNode, index);
15251
+ }
15252
+ // Hydration mode, looking up existing elements in DOM.
15253
+ const currentRNode = locateNextRNode(hydrationInfo, tView, lView, tNode);
15254
+ ngDevMode && validateNodeExists(currentRNode);
15255
+ setSegmentHead(hydrationInfo, index, currentRNode);
15256
+ const viewContainerSize = calcSerializedContainerSize(hydrationInfo, index);
15257
+ const comment = siblingAfter(viewContainerSize, currentRNode);
15258
+ if (ngDevMode) {
15259
+ validateMatchingNode(comment, Node.COMMENT_NODE, null, lView, tNode);
15260
+ markRNodeAsClaimedByHydration(comment);
15261
+ }
15262
+ return comment;
15263
+ }
15264
+ function enableLocateOrCreateContainerAnchorImpl() {
15265
+ _locateOrCreateContainerAnchor = locateOrCreateContainerAnchorImpl;
15266
+ }
14227
15267
 
14228
15268
  /** Store a value in the `data` at a given `index`. */
14229
15269
  function store(tView, lView, index, value) {
@@ -14250,95 +15290,6 @@ function ɵɵreference(index) {
14250
15290
  return load(contextLView, HEADER_OFFSET + index);
14251
15291
  }
14252
15292
 
14253
- /**
14254
- * Verifies whether a given node matches an expected criteria,
14255
- * based on internal data structure state.
14256
- */
14257
- function validateMatchingNode(node, nodeType, tagName, lView, tNode) {
14258
- if (node.nodeType !== nodeType ||
14259
- (node.nodeType === Node.ELEMENT_NODE &&
14260
- node.tagName.toLowerCase() !== (tagName === null || tagName === void 0 ? void 0 : tagName.toLowerCase()))) {
14261
- // TODO: improve error message and use RuntimeError instead.
14262
- throw new Error(`Unexpected node found during hydration.`);
14263
- }
14264
- }
14265
- /**
14266
- * Verifies whether next sibling node exists.
14267
- */
14268
- function validateSiblingNodeExists(node) {
14269
- if (!node.nextSibling) {
14270
- // TODO: improve error message and use RuntimeError instead.
14271
- throw new Error(`Unexpected state: insufficient number of sibling nodes.`);
14272
- }
14273
- }
14274
-
14275
- /** Whether current TNode is a first node in an <ng-container>. */
14276
- function isFirstElementInNgContainer(tNode) {
14277
- var _a;
14278
- return !tNode.prev && ((_a = tNode.parent) === null || _a === void 0 ? void 0 : _a.type) === 8 /* TNodeType.ElementContainer */;
14279
- }
14280
- /** Returns first element from a DOM segment that corresponds to this <ng-container>. */
14281
- function getDehydratedNgContainer(hydrationInfo, tContainerNode) {
14282
- var _a;
14283
- const noOffsetIndex = tContainerNode.index - HEADER_OFFSET;
14284
- const ngContainer = (_a = hydrationInfo.ngContainers) === null || _a === void 0 ? void 0 : _a[noOffsetIndex];
14285
- ngDevMode &&
14286
- assertDefined(ngContainer, 'Unexpected state: no hydration info available for a given TNode, ' +
14287
- 'which represents an element container.');
14288
- return ngContainer;
14289
- }
14290
- /**
14291
- * Locate a node in DOM tree that corresponds to a given TNode.
14292
- *
14293
- * @param hydrationInfo The hydration annotation data
14294
- * @param tView the current tView
14295
- * @param lView the current lView
14296
- * @param tNode the current tNode
14297
- * @returns an RNode that represents a given tNode
14298
- */
14299
- function locateNextRNode(hydrationInfo, tView, lView, tNode) {
14300
- var _a, _b;
14301
- let native = null;
14302
- if (tView.firstChild === tNode) {
14303
- // We create a first node in this view, so we use a reference
14304
- // to the first child in this DOM segment.
14305
- native = hydrationInfo.firstChild;
14306
- }
14307
- else {
14308
- // Locate a node based on a previous sibling or a parent node.
14309
- const previousTNodeParent = tNode.prev === null;
14310
- const previousTNode = ((_a = tNode.prev) !== null && _a !== void 0 ? _a : tNode.parent);
14311
- ngDevMode &&
14312
- assertDefined(previousTNode, 'Unexpected state: current TNode does not have a connection ' +
14313
- 'to the previous node or a parent node.');
14314
- const previousRElement = getNativeByTNode(previousTNode, lView);
14315
- if (isFirstElementInNgContainer(tNode)) {
14316
- const ngContainer = getDehydratedNgContainer(hydrationInfo, tNode.parent);
14317
- native = (_b = ngContainer.firstChild) !== null && _b !== void 0 ? _b : null;
14318
- }
14319
- else {
14320
- if (previousTNodeParent) {
14321
- native = previousRElement.firstChild;
14322
- }
14323
- else {
14324
- native = previousRElement.nextSibling;
14325
- }
14326
- }
14327
- }
14328
- return native;
14329
- }
14330
- /**
14331
- * Skips over a specified number of nodes and returns the next sibling node after that.
14332
- */
14333
- function siblingAfter(skip, from) {
14334
- let currentNode = from;
14335
- for (let i = 0; i < skip; i++) {
14336
- ngDevMode && validateSiblingNodeExists(currentNode);
14337
- currentNode = currentNode.nextSibling;
14338
- }
14339
- return currentNode;
14340
- }
14341
-
14342
15293
  /**
14343
15294
  * The name of an attribute that can be added to the hydration boundary node
14344
15295
  * (component host node) to disable hydration for the content within that boundary.
@@ -14365,6 +15316,21 @@ function hasNgSkipHydrationAttr(tNode) {
14365
15316
  }
14366
15317
  return false;
14367
15318
  }
15319
+ /**
15320
+ * Helper function that determines if a given node is within a skip hydration block
15321
+ * by navigating up the TNode tree to see if any parent nodes have skip hydration
15322
+ * attribute.
15323
+ */
15324
+ function isInSkipHydrationBlock(tNode) {
15325
+ let currentTNode = tNode.parent;
15326
+ while (currentTNode) {
15327
+ if (hasNgSkipHydrationAttr(currentTNode)) {
15328
+ return true;
15329
+ }
15330
+ currentTNode = currentTNode.parent;
15331
+ }
15332
+ return false;
15333
+ }
14368
15334
 
14369
15335
  /**
14370
15336
  * Update a property on a selected element.
@@ -14450,7 +15416,7 @@ function ɵɵelementStart(index, name, attrsIndex, localRefsIndex) {
14450
15416
  const tNode = tView.firstCreatePass ?
14451
15417
  elementStartFirstCreatePass(adjustedIndex, tView, lView, name, attrsIndex, localRefsIndex) :
14452
15418
  tView.data[adjustedIndex];
14453
- const native = _locateOrCreateElementNode(tView, lView, tNode, renderer, name);
15419
+ const native = _locateOrCreateElementNode(tView, lView, tNode, renderer, name, index);
14454
15420
  lView[adjustedIndex] = native;
14455
15421
  const hasDirectives = isDirectiveHost(tNode);
14456
15422
  if (ngDevMode && tView.firstCreatePass) {
@@ -14533,7 +15499,7 @@ function ɵɵelement(index, name, attrsIndex, localRefsIndex) {
14533
15499
  ɵɵelementEnd();
14534
15500
  return ɵɵelement;
14535
15501
  }
14536
- let _locateOrCreateElementNode = (tView, lView, tNode, renderer, name) => {
15502
+ let _locateOrCreateElementNode = (tView, lView, tNode, renderer, name, index) => {
14537
15503
  lastNodeWasCreated(true);
14538
15504
  return createElementNode(renderer, name, getNamespace$1());
14539
15505
  };
@@ -14541,9 +15507,9 @@ let _locateOrCreateElementNode = (tView, lView, tNode, renderer, name) => {
14541
15507
  * Enables hydration code path (to lookup existing elements in DOM)
14542
15508
  * in addition to the regular creation mode of element nodes.
14543
15509
  */
14544
- function locateOrCreateElementNodeImpl(tView, lView, tNode, renderer, name) {
15510
+ function locateOrCreateElementNodeImpl(tView, lView, tNode, renderer, name, index) {
14545
15511
  const hydrationInfo = lView[HYDRATION];
14546
- const isNodeCreationMode = !hydrationInfo || isInSkipHydrationBlock();
15512
+ const isNodeCreationMode = !hydrationInfo || isInSkipHydrationBlock$1() || isDisconnectedNode(hydrationInfo, index);
14547
15513
  lastNodeWasCreated(isNodeCreationMode);
14548
15514
  // Regular creation mode.
14549
15515
  if (isNodeCreationMode) {
@@ -14551,16 +15517,33 @@ function locateOrCreateElementNodeImpl(tView, lView, tNode, renderer, name) {
14551
15517
  }
14552
15518
  // Hydration mode, looking up an existing element in DOM.
14553
15519
  const native = locateNextRNode(hydrationInfo, tView, lView, tNode);
14554
- ngDevMode &&
14555
- validateMatchingNode(native, Node.ELEMENT_NODE, name, lView, tNode);
15520
+ ngDevMode && validateMatchingNode(native, Node.ELEMENT_NODE, name, lView, tNode);
14556
15521
  ngDevMode && markRNodeAsClaimedByHydration(native);
15522
+ // This element might also be an anchor of a view container.
15523
+ if (getSerializedContainerViews(hydrationInfo, index)) {
15524
+ // Important note: this element acts as an anchor, but it's **not** a part
15525
+ // of the embedded view, so we start the segment **after** this element, taking
15526
+ // a reference to the next sibling. For example, the following template:
15527
+ // `<div #vcrTarget>` is represented in the DOM as `<div></div>...<!--container-->`,
15528
+ // so while processing a `<div>` instruction, point to the next sibling as a
15529
+ // start of a segment.
15530
+ ngDevMode && validateNodeExists(native.nextSibling);
15531
+ setSegmentHead(hydrationInfo, index, native.nextSibling);
15532
+ }
14557
15533
  // Checks if the skip hydration attribute is present during hydration so we know to
14558
15534
  // skip attempting to hydrate this block.
14559
15535
  if (hydrationInfo && hasNgSkipHydrationAttr(tNode)) {
14560
- enterSkipHydrationBlock(tNode);
14561
- // Since this isn't hydratable, we need to empty the node
14562
- // so there's no duplicate content after render
14563
- clearElementContents(renderer, native);
15536
+ if (isComponentHost(tNode)) {
15537
+ enterSkipHydrationBlock(tNode);
15538
+ // Since this isn't hydratable, we need to empty the node
15539
+ // so there's no duplicate content after render
15540
+ clearElementContents(renderer, native);
15541
+ }
15542
+ else if (ngDevMode) {
15543
+ // If this is not a component host, throw an error.
15544
+ // Hydration can be skipped on per-component basis only.
15545
+ throw invalidSkipHydrationHost(native);
15546
+ }
14564
15547
  }
14565
15548
  return native;
14566
15549
  }
@@ -14681,7 +15664,7 @@ let _locateOrCreateElementContainerNode = (tView, lView, tNode, index) => {
14681
15664
  function locateOrCreateElementContainerNode(tView, lView, tNode, index) {
14682
15665
  let comment;
14683
15666
  const hydrationInfo = lView[HYDRATION];
14684
- const isNodeCreationMode = !hydrationInfo || isInSkipHydrationBlock();
15667
+ const isNodeCreationMode = !hydrationInfo || isInSkipHydrationBlock$1();
14685
15668
  lastNodeWasCreated(isNodeCreationMode);
14686
15669
  // Regular creation mode.
14687
15670
  if (isNodeCreationMode) {
@@ -14689,21 +15672,17 @@ function locateOrCreateElementContainerNode(tView, lView, tNode, index) {
14689
15672
  }
14690
15673
  // Hydration mode, looking up existing elements in DOM.
14691
15674
  const currentRNode = locateNextRNode(hydrationInfo, tView, lView, tNode);
15675
+ ngDevMode && validateNodeExists(currentRNode);
14692
15676
  const ngContainerSize = getNgContainerSize(hydrationInfo, index);
14693
15677
  ngDevMode &&
14694
15678
  assertNumber(ngContainerSize, 'Unexpected state: hydrating an <ng-container>, ' +
14695
15679
  'but no hydration info is available.');
14696
- if (ngContainerSize > 0) {
14697
- storeNgContainerInfo(hydrationInfo, index, currentRNode);
14698
- comment = siblingAfter(ngContainerSize, currentRNode);
14699
- }
14700
- else {
14701
- // If <ng-container> has no nodes,
14702
- // the current node is an anchor (comment) node.
14703
- comment = currentRNode;
15680
+ setSegmentHead(hydrationInfo, index, currentRNode);
15681
+ comment = siblingAfter(ngContainerSize, currentRNode);
15682
+ if (ngDevMode) {
15683
+ validateMatchingNode(comment, Node.COMMENT_NODE, null, lView, tNode);
15684
+ markRNodeAsClaimedByHydration(comment);
14704
15685
  }
14705
- ngDevMode && validateMatchingNode(comment, Node.COMMENT_NODE, null, lView, tNode);
14706
- ngDevMode && markRNodeAsClaimedByHydration(comment);
14707
15686
  return comment;
14708
15687
  }
14709
15688
  function enableLocateOrCreateElementContainerNodeImpl() {
@@ -15080,7 +16059,10 @@ function ɵɵprojection(nodeIndex, selectorIndex = 0, attrs) {
15080
16059
  tProjectionNode.projection = selectorIndex;
15081
16060
  // `<ng-content>` has no content
15082
16061
  setCurrentTNodeAsNotParent();
15083
- if ((tProjectionNode.flags & 32 /* TNodeFlags.isDetached */) !== 32 /* TNodeFlags.isDetached */) {
16062
+ const hydrationInfo = lView[HYDRATION];
16063
+ const isNodeCreationMode = !hydrationInfo || isInSkipHydrationBlock$1();
16064
+ if (isNodeCreationMode &&
16065
+ (tProjectionNode.flags & 32 /* TNodeFlags.isDetached */) !== 32 /* TNodeFlags.isDetached */) {
15084
16066
  // re-distribution of projectable nodes is stored on a component's view level
15085
16067
  applyProjection(tView, lView, tProjectionNode);
15086
16068
  }
@@ -17043,7 +18025,7 @@ function ɵɵtext(index, value = '') {
17043
18025
  const tNode = tView.firstCreatePass ?
17044
18026
  getOrCreateTNode(tView, adjustedIndex, 1 /* TNodeType.Text */, value, null) :
17045
18027
  tView.data[adjustedIndex];
17046
- const textNative = _locateOrCreateTextNode(tView, lView, tNode, value);
18028
+ const textNative = _locateOrCreateTextNode(tView, lView, tNode, value, index);
17047
18029
  lView[adjustedIndex] = textNative;
17048
18030
  if (wasLastNodeCreated()) {
17049
18031
  appendChild(tView, lView, textNative, tNode);
@@ -17051,7 +18033,7 @@ function ɵɵtext(index, value = '') {
17051
18033
  // Text nodes are self closing.
17052
18034
  setCurrentTNode(tNode, false);
17053
18035
  }
17054
- let _locateOrCreateTextNode = (tView, lView, tNode, value) => {
18036
+ let _locateOrCreateTextNode = (tView, lView, tNode, value, index) => {
17055
18037
  lastNodeWasCreated(true);
17056
18038
  return createTextNode(lView[RENDERER], value);
17057
18039
  };
@@ -17059,9 +18041,9 @@ let _locateOrCreateTextNode = (tView, lView, tNode, value) => {
17059
18041
  * Enables hydration code path (to lookup existing elements in DOM)
17060
18042
  * in addition to the regular creation mode of text nodes.
17061
18043
  */
17062
- function locateOrCreateTextNodeImpl(tView, lView, tNode, value) {
18044
+ function locateOrCreateTextNodeImpl(tView, lView, tNode, value, index) {
17063
18045
  const hydrationInfo = lView[HYDRATION];
17064
- const isNodeCreationMode = !hydrationInfo || isInSkipHydrationBlock();
18046
+ const isNodeCreationMode = !hydrationInfo || isInSkipHydrationBlock$1() || isDisconnectedNode(hydrationInfo, index);
17065
18047
  lastNodeWasCreated(isNodeCreationMode);
17066
18048
  // Regular creation mode.
17067
18049
  if (isNodeCreationMode) {
@@ -20729,7 +21711,7 @@ class NgModuleFactory$1 {
20729
21711
  * @publicApi
20730
21712
  */
20731
21713
  function createNgModule(ngModule, parentInjector) {
20732
- return new NgModuleRef(ngModule, parentInjector !== null && parentInjector !== void 0 ? parentInjector : null);
21714
+ return new NgModuleRef(ngModule, parentInjector !== null && parentInjector !== void 0 ? parentInjector : null, []);
20733
21715
  }
20734
21716
  /**
20735
21717
  * The `createNgModule` function alias for backwards-compatibility.
@@ -20739,7 +21721,7 @@ function createNgModule(ngModule, parentInjector) {
20739
21721
  */
20740
21722
  const createNgModuleRef = createNgModule;
20741
21723
  class NgModuleRef extends NgModuleRef$1 {
20742
- constructor(ngModuleType, _parent) {
21724
+ constructor(ngModuleType, _parent, additionalProviders) {
20743
21725
  super();
20744
21726
  this._parent = _parent;
20745
21727
  // tslint:disable-next-line:require-internal-with-underscore
@@ -20760,7 +21742,8 @@ class NgModuleRef extends NgModuleRef$1 {
20760
21742
  { provide: NgModuleRef$1, useValue: this }, {
20761
21743
  provide: ComponentFactoryResolver$1,
20762
21744
  useValue: this.componentFactoryResolver
20763
- }
21745
+ },
21746
+ ...additionalProviders
20764
21747
  ], stringify(ngModuleType), new Set(['environment']));
20765
21748
  // We need to resolve the injector types separately from the injector creation, because
20766
21749
  // the module might be trying to use this ref in its constructor for DI which will cause a
@@ -20789,9 +21772,12 @@ class NgModuleFactory extends NgModuleFactory$1 {
20789
21772
  this.moduleType = moduleType;
20790
21773
  }
20791
21774
  create(parentInjector) {
20792
- return new NgModuleRef(this.moduleType, parentInjector);
21775
+ return new NgModuleRef(this.moduleType, parentInjector, []);
20793
21776
  }
20794
21777
  }
21778
+ function createNgModuleRefWithProviders(moduleType, parentInjector, additionalProviders) {
21779
+ return new NgModuleRef(moduleType, parentInjector, additionalProviders);
21780
+ }
20795
21781
  class EnvironmentNgModuleRefAdapter extends NgModuleRef$1 {
20796
21782
  constructor(providers, parent, source) {
20797
21783
  super();
@@ -21239,7 +22225,8 @@ function sortListeners(a, b) {
21239
22225
  * See call site for more info.
21240
22226
  */
21241
22227
  function isDirectiveDefHack(obj) {
21242
- return obj.type !== undefined && obj.template !== undefined && obj.declaredInputs !== undefined;
22228
+ return obj.type !== undefined && obj.declaredInputs !== undefined &&
22229
+ obj.findHostDirectiveDefs !== undefined;
21243
22230
  }
21244
22231
  /**
21245
22232
  * Retrieve the component `LView` from component/element.
@@ -22077,9 +23064,25 @@ const R3TemplateRef = class TemplateRef extends ViewEngineTemplateRef {
22077
23064
  this._declarationTContainer = _declarationTContainer;
22078
23065
  this.elementRef = elementRef;
22079
23066
  }
23067
+ /**
23068
+ * Returns an `ssrId` associated with a TView, which was used to
23069
+ * create this instance of the `TemplateRef`.
23070
+ *
23071
+ * @internal
23072
+ */
23073
+ get ssrId() {
23074
+ var _a;
23075
+ return ((_a = this._declarationTContainer.tView) === null || _a === void 0 ? void 0 : _a.ssrId) || null;
23076
+ }
22080
23077
  createEmbeddedView(context, injector) {
23078
+ return this.createEmbeddedViewImpl(context, injector, null);
23079
+ }
23080
+ /**
23081
+ * @internal
23082
+ */
23083
+ createEmbeddedViewImpl(context, injector, hydrationInfo) {
22081
23084
  const embeddedTView = this._declarationTContainer.tView;
22082
- const embeddedLView = createLView(this._declarationLView, embeddedTView, context, 16 /* LViewFlags.CheckAlways */, null, embeddedTView.declTNode, null, null, null, null, injector || null, null);
23085
+ const embeddedLView = createLView(this._declarationLView, embeddedTView, context, 16 /* LViewFlags.CheckAlways */, null, embeddedTView.declTNode, null, null, null, null, injector || null, hydrationInfo || null);
22083
23086
  const declarationLContainer = this._declarationLView[this._declarationTContainer.index];
22084
23087
  ngDevMode && assertLContainer(declarationLContainer);
22085
23088
  embeddedLView[DECLARATION_LCONTAINER] = declarationLContainer;
@@ -22114,6 +23117,167 @@ function createTemplateRef(hostTNode, hostLView) {
22114
23117
  return null;
22115
23118
  }
22116
23119
 
23120
+ /**
23121
+ * Removes all dehydrated views from a given LContainer:
23122
+ * both in internal data structure, as well as removing
23123
+ * corresponding DOM nodes that belong to that dehydrated view.
23124
+ */
23125
+ function removeDehydratedViews(lContainer) {
23126
+ var _a;
23127
+ const views = (_a = lContainer[DEHYDRATED_VIEWS]) !== null && _a !== void 0 ? _a : [];
23128
+ const parentLView = lContainer[PARENT];
23129
+ const renderer = parentLView[RENDERER];
23130
+ for (const view of views) {
23131
+ removeDehydratedView(view, renderer);
23132
+ ngDevMode && ngDevMode.dehydratedViewsRemoved++;
23133
+ }
23134
+ // Reset the value to an empty array to indicate that no
23135
+ // further processing of dehydrated views is needed for
23136
+ // this view container (i.e. do not trigger the lookup process
23137
+ // once again in case a `ViewContainerRef` is created later).
23138
+ lContainer[DEHYDRATED_VIEWS] = EMPTY_ARRAY;
23139
+ }
23140
+ /**
23141
+ * Helper function to remove all nodes from a dehydrated view.
23142
+ */
23143
+ function removeDehydratedView(dehydratedView, renderer) {
23144
+ let nodesRemoved = 0;
23145
+ let currentRNode = dehydratedView.firstChild;
23146
+ if (currentRNode) {
23147
+ const numNodes = dehydratedView.data[NUM_ROOT_NODES];
23148
+ while (nodesRemoved < numNodes) {
23149
+ ngDevMode && validateSiblingNodeExists(currentRNode);
23150
+ const nextSibling = currentRNode.nextSibling;
23151
+ nativeRemoveNode(renderer, currentRNode, false);
23152
+ currentRNode = nextSibling;
23153
+ nodesRemoved++;
23154
+ }
23155
+ }
23156
+ }
23157
+ /**
23158
+ * Walks over all views within this LContainer invokes dehydrated views
23159
+ * cleanup function for each one.
23160
+ */
23161
+ function cleanupLContainer(lContainer) {
23162
+ removeDehydratedViews(lContainer);
23163
+ for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) {
23164
+ cleanupLView(lContainer[i]);
23165
+ }
23166
+ }
23167
+ /**
23168
+ * Walks over `LContainer`s and components registered within
23169
+ * this LView and invokes dehydrated views cleanup function for each one.
23170
+ */
23171
+ function cleanupLView(lView) {
23172
+ const tView = lView[TVIEW];
23173
+ for (let i = HEADER_OFFSET; i < tView.bindingStartIndex; i++) {
23174
+ if (isLContainer(lView[i])) {
23175
+ const lContainer = lView[i];
23176
+ cleanupLContainer(lContainer);
23177
+ }
23178
+ else if (Array.isArray(lView[i])) {
23179
+ // This is a component, enter the `cleanupLView` recursively.
23180
+ cleanupLView(lView[i]);
23181
+ }
23182
+ }
23183
+ }
23184
+ /**
23185
+ * Walks over all views registered within the ApplicationRef and removes
23186
+ * all dehydrated views from all `LContainer`s along the way.
23187
+ */
23188
+ function cleanupDehydratedViews(appRef) {
23189
+ // Wait once an app becomes stable and cleanup all views that
23190
+ // were not claimed during the application bootstrap process.
23191
+ // The timing is similar to when we kick off serialization on the server.
23192
+ return appRef.isStable.pipe(first((isStable) => isStable)).toPromise().then(() => {
23193
+ const viewRefs = appRef._views;
23194
+ for (const viewRef of viewRefs) {
23195
+ const lView = getComponentLViewForHydration(viewRef);
23196
+ // An `lView` might be `null` if a `ViewRef` represents
23197
+ // an embedded view (not a component view).
23198
+ if (lView !== null && lView[HOST] !== null) {
23199
+ cleanupLView(lView);
23200
+ ngDevMode && ngDevMode.dehydratedViewsCleanupRuns++;
23201
+ }
23202
+ }
23203
+ });
23204
+ }
23205
+
23206
+ /**
23207
+ * Given a current DOM node and a serialized information about the views
23208
+ * in a container, walks over the DOM structure, collecting the list of
23209
+ * dehydrated views.
23210
+ */
23211
+ function locateDehydratedViewsInContainer(currentRNode, serializedViews) {
23212
+ var _a;
23213
+ const dehydratedViews = [];
23214
+ for (const serializedView of serializedViews) {
23215
+ // Repeats a view multiple times as needed, based on the serialized information
23216
+ // (for example, for *ngFor-produced views).
23217
+ for (let i = 0; i < ((_a = serializedView[MULTIPLIER]) !== null && _a !== void 0 ? _a : 1); i++) {
23218
+ const view = {
23219
+ data: serializedView,
23220
+ firstChild: null,
23221
+ };
23222
+ if (serializedView[NUM_ROOT_NODES] > 0) {
23223
+ // Keep reference to the first node in this view,
23224
+ // so it can be accessed while invoking template instructions.
23225
+ view.firstChild = currentRNode;
23226
+ // Move over to the next node after this view, which can
23227
+ // either be a first node of the next view or an anchor comment
23228
+ // node after the last view in a container.
23229
+ currentRNode = siblingAfter(serializedView[NUM_ROOT_NODES], currentRNode);
23230
+ }
23231
+ dehydratedViews.push(view);
23232
+ }
23233
+ }
23234
+ return [currentRNode, dehydratedViews];
23235
+ }
23236
+ /**
23237
+ * Reference to a function that searches for a matching dehydrated views
23238
+ * stored on a given lContainer.
23239
+ * Returns `null` by default, when hydration is not enabled.
23240
+ */
23241
+ let _findMatchingDehydratedViewImpl = (lContainer, template) => null;
23242
+ /**
23243
+ * Retrieves the next dehydrated view from the LContainer and verifies that
23244
+ * it matches a given template id (from the TView that was used to create this
23245
+ * instance of a view). If the id doesn't match, that means that we are in an
23246
+ * unexpected state and can not complete the reconciliation process. Thus,
23247
+ * all dehydrated views from this LContainer are removed (including corresponding
23248
+ * DOM nodes) and the rendering is performed as if there were no dehydrated views
23249
+ * in this container.
23250
+ */
23251
+ function findMatchingDehydratedViewImpl(lContainer, template) {
23252
+ var _a;
23253
+ const views = (_a = lContainer[DEHYDRATED_VIEWS]) !== null && _a !== void 0 ? _a : [];
23254
+ if (!template || views.length === 0) {
23255
+ return null;
23256
+ }
23257
+ const view = views[0];
23258
+ // Verify whether the first dehydrated view in the container matches
23259
+ // the template id passed to this function (that originated from a TView
23260
+ // that was used to create an instance of an embedded or component views.
23261
+ if (view.data[TEMPLATE_ID] === template) {
23262
+ // If the template id matches - extract the first view and return it.
23263
+ return views.shift();
23264
+ }
23265
+ else {
23266
+ // Otherwise, we are at the state when reconciliation can not be completed,
23267
+ // thus we remove all dehydrated views within this container (remove them
23268
+ // from internal data structures as well as delete associated elements from
23269
+ // the DOM tree).
23270
+ removeDehydratedViews(lContainer);
23271
+ return null;
23272
+ }
23273
+ }
23274
+ function enableFindMatchingDehydratedViewImpl() {
23275
+ _findMatchingDehydratedViewImpl = findMatchingDehydratedViewImpl;
23276
+ }
23277
+ function findMatchingDehydratedView(lContainer, template) {
23278
+ return _findMatchingDehydratedViewImpl(lContainer, template);
23279
+ }
23280
+
22117
23281
  /**
22118
23282
  * Represents a container where one or more views can be attached to a component.
22119
23283
  *
@@ -22198,11 +23362,13 @@ const R3ViewContainerRef = class ViewContainerRef extends VE_ViewContainerRef {
22198
23362
  index = indexOrOptions.index;
22199
23363
  injector = indexOrOptions.injector;
22200
23364
  }
22201
- const viewRef = templateRef.createEmbeddedView(context || {}, injector);
22202
- this.insert(viewRef, index);
23365
+ const hydrationInfo = findMatchingDehydratedView(this._lContainer, templateRef.ssrId);
23366
+ const viewRef = templateRef.createEmbeddedViewImpl(context || {}, injector, hydrationInfo);
23367
+ this.insertImpl(viewRef, index, !!hydrationInfo);
22203
23368
  return viewRef;
22204
23369
  }
22205
23370
  createComponent(componentFactoryOrType, indexOrOptions, injector, projectableNodes, environmentInjector) {
23371
+ var _a, _b, _c;
22206
23372
  const isComponentFactory = componentFactoryOrType && !isType(componentFactoryOrType);
22207
23373
  let index;
22208
23374
  // This function supports 2 signatures and we need to handle options correctly for both:
@@ -22269,11 +23435,17 @@ const R3ViewContainerRef = class ViewContainerRef extends VE_ViewContainerRef {
22269
23435
  environmentInjector = result;
22270
23436
  }
22271
23437
  }
22272
- const componentRef = componentFactory.create(contextInjector, projectableNodes, undefined, environmentInjector);
22273
- this.insert(componentRef.hostView, index);
23438
+ const componentDef = getComponentDef$1((_a = componentFactory.componentType) !== null && _a !== void 0 ? _a : {});
23439
+ const dehydratedView = findMatchingDehydratedView(this._lContainer, (_b = componentDef === null || componentDef === void 0 ? void 0 : componentDef.id) !== null && _b !== void 0 ? _b : null);
23440
+ const rNode = (_c = dehydratedView === null || dehydratedView === void 0 ? void 0 : dehydratedView.firstChild) !== null && _c !== void 0 ? _c : null;
23441
+ const componentRef = componentFactory.create(contextInjector, projectableNodes, rNode, environmentInjector);
23442
+ this.insertImpl(componentRef.hostView, index, !!dehydratedView);
22274
23443
  return componentRef;
22275
23444
  }
22276
23445
  insert(viewRef, index) {
23446
+ return this.insertImpl(viewRef, index, false);
23447
+ }
23448
+ insertImpl(viewRef, index, skipDomInsertion) {
22277
23449
  const lView = viewRef._lView;
22278
23450
  const tView = lView[TVIEW];
22279
23451
  if (ngDevMode && viewRef.destroyed) {
@@ -22304,11 +23476,13 @@ const R3ViewContainerRef = class ViewContainerRef extends VE_ViewContainerRef {
22304
23476
  const lContainer = this._lContainer;
22305
23477
  insertView(tView, lView, lContainer, adjustedIdx);
22306
23478
  // Physical operation of adding the DOM nodes.
22307
- const beforeNode = getBeforeNodeForView(adjustedIdx, lContainer);
22308
- const renderer = lView[RENDERER];
22309
- const parentRNode = nativeParentNode(renderer, lContainer[NATIVE]);
22310
- if (parentRNode !== null) {
22311
- addViewToContainer(tView, lContainer[T_HOST], renderer, lView, parentRNode, beforeNode);
23479
+ if (!skipDomInsertion) {
23480
+ const beforeNode = getBeforeNodeForView(adjustedIdx, lContainer);
23481
+ const renderer = lView[RENDERER];
23482
+ const parentRNode = nativeParentNode(renderer, lContainer[NATIVE]);
23483
+ if (parentRNode !== null) {
23484
+ addViewToContainer(tView, lContainer[T_HOST], renderer, lView, parentRNode, beforeNode);
23485
+ }
22312
23486
  }
22313
23487
  viewRef.attachToViewContainerRef();
22314
23488
  addToArray(getOrCreateViewRefs(lContainer), adjustedIdx, viewRef);
@@ -22365,8 +23539,6 @@ function getOrCreateViewRefs(lContainer) {
22365
23539
  /**
22366
23540
  * Creates a ViewContainerRef and stores it on the injector.
22367
23541
  *
22368
- * @param ViewContainerRefToken The ViewContainerRef type
22369
- * @param ElementRefToken The ElementRef type
22370
23542
  * @param hostTNode The node that is requesting a ViewContainerRef
22371
23543
  * @param hostLView The view to which the node belongs
22372
23544
  * @returns The ViewContainerRef instance to use
@@ -22380,31 +23552,96 @@ function createContainerRef(hostTNode, hostLView) {
22380
23552
  lContainer = slotValue;
22381
23553
  }
22382
23554
  else {
22383
- let commentNode;
22384
- // If the host is an element container, the native host element is guaranteed to be a
22385
- // comment and we can reuse that comment as anchor element for the new LContainer.
22386
- // The comment node in question is already part of the DOM structure so we don't need to append
22387
- // it again.
22388
- if (hostTNode.type & 8 /* TNodeType.ElementContainer */) {
22389
- commentNode = unwrapRNode(slotValue);
22390
- }
22391
- else {
22392
- // If the host is a regular element, we have to insert a comment node manually which will
22393
- // be used as an anchor when inserting elements. In this specific case we use low-level DOM
22394
- // manipulation to insert it.
22395
- const renderer = hostLView[RENDERER];
22396
- ngDevMode && ngDevMode.rendererCreateComment++;
22397
- commentNode = renderer.createComment(ngDevMode ? 'container' : '');
22398
- const hostNative = getNativeByTNode(hostTNode, hostLView);
22399
- const parentOfHostNative = nativeParentNode(renderer, hostNative);
22400
- nativeInsertBefore(renderer, parentOfHostNative, commentNode, nativeNextSibling(renderer, hostNative), false);
22401
- }
22402
- hostLView[hostTNode.index] = lContainer =
22403
- createLContainer(slotValue, hostLView, commentNode, hostTNode);
23555
+ // An LContainer anchor can not be `null`, but we set it here temporarily
23556
+ // and update to the actual value later in this function (see
23557
+ // `_locateOrCreateAnchorNode`).
23558
+ lContainer = createLContainer(slotValue, hostLView, null, hostTNode);
23559
+ hostLView[hostTNode.index] = lContainer;
22404
23560
  addToViewTree(hostLView, lContainer);
22405
23561
  }
23562
+ _locateOrCreateAnchorNode(lContainer, hostLView, hostTNode, slotValue);
22406
23563
  return new R3ViewContainerRef(lContainer, hostTNode, hostLView);
22407
23564
  }
23565
+ /**
23566
+ * Creates and inserts a comment node that acts as an anchor for a view container.
23567
+ *
23568
+ * If the host is a regular element, we have to insert a comment node manually which will
23569
+ * be used as an anchor when inserting elements. In this specific case we use low-level DOM
23570
+ * manipulation to insert it.
23571
+ */
23572
+ function insertAnchorNode(hostLView, hostTNode) {
23573
+ const renderer = hostLView[RENDERER];
23574
+ ngDevMode && ngDevMode.rendererCreateComment++;
23575
+ const commentNode = renderer.createComment(ngDevMode ? 'container' : '');
23576
+ const hostNative = getNativeByTNode(hostTNode, hostLView);
23577
+ const parentOfHostNative = nativeParentNode(renderer, hostNative);
23578
+ nativeInsertBefore(renderer, parentOfHostNative, commentNode, nativeNextSibling(renderer, hostNative), false);
23579
+ return commentNode;
23580
+ }
23581
+ let _locateOrCreateAnchorNode = createAnchorNode;
23582
+ /**
23583
+ * Regular creation mode: an anchor is created and
23584
+ * assigned to the `lContainer[NATIVE]` slot.
23585
+ */
23586
+ function createAnchorNode(lContainer, hostLView, hostTNode, slotValue) {
23587
+ // We already have a native element (anchor) set, return.
23588
+ if (lContainer[NATIVE])
23589
+ return;
23590
+ let commentNode;
23591
+ // If the host is an element container, the native host element is guaranteed to be a
23592
+ // comment and we can reuse that comment as anchor element for the new LContainer.
23593
+ // The comment node in question is already part of the DOM structure so we don't need to append
23594
+ // it again.
23595
+ if (hostTNode.type & 8 /* TNodeType.ElementContainer */) {
23596
+ commentNode = unwrapRNode(slotValue);
23597
+ }
23598
+ else {
23599
+ commentNode = insertAnchorNode(hostLView, hostTNode);
23600
+ }
23601
+ lContainer[NATIVE] = commentNode;
23602
+ }
23603
+ /**
23604
+ * Hydration logic that looks up:
23605
+ * - an anchor node in the DOM and stores the node in `lContainer[NATIVE]`
23606
+ * - all dehydrated views in this container and puts them into `lContainer[DEHYDRATED_VIEWS]`
23607
+ */
23608
+ function locateOrCreateAnchorNode(lContainer, hostLView, hostTNode, slotValue) {
23609
+ var _a;
23610
+ // We already have a native element (anchor) set and the process
23611
+ // of finding dehydrated views happened (so the `lContainer[DEHYDRATED_VIEWS]`
23612
+ // is not null), exit early.
23613
+ if (lContainer[NATIVE] && lContainer[DEHYDRATED_VIEWS])
23614
+ return;
23615
+ const hydrationInfo = hostLView[HYDRATION];
23616
+ const noOffsetIndex = hostTNode.index - HEADER_OFFSET;
23617
+ const isNodeCreationMode = !hydrationInfo || isInSkipHydrationBlock(hostTNode) ||
23618
+ isDisconnectedNode(hydrationInfo, noOffsetIndex);
23619
+ // Regular creation mode.
23620
+ if (isNodeCreationMode) {
23621
+ return createAnchorNode(lContainer, hostLView, hostTNode, slotValue);
23622
+ }
23623
+ // Hydration mode, looking up an anchor node and dehydrated views in DOM.
23624
+ const currentRNode = getSegmentHead(hydrationInfo, noOffsetIndex);
23625
+ const serializedViews = (_a = hydrationInfo.data[CONTAINERS]) === null || _a === void 0 ? void 0 : _a[noOffsetIndex];
23626
+ ngDevMode &&
23627
+ assertDefined(serializedViews, 'Unexpected state: no hydration info available for a given TNode, ' +
23628
+ 'which represents a view container.');
23629
+ const [commentNode, dehydratedViews] = locateDehydratedViewsInContainer(currentRNode, serializedViews);
23630
+ if (ngDevMode) {
23631
+ validateMatchingNode(commentNode, Node.COMMENT_NODE, null, hostLView, hostTNode, true);
23632
+ // Do not throw in case this node is already claimed (thus `false` as a second
23633
+ // argument). If this container is created based on an `<ng-template>`, the comment
23634
+ // node would be already claimed from the `template` instruction. If an element acts
23635
+ // as an anchor (e.g. <div #vcRef>), a separate comment node would be created/located,
23636
+ // so we need to claim it here.
23637
+ markRNodeAsClaimedByHydration(commentNode, false);
23638
+ }
23639
+ lContainer[NATIVE] = commentNode;
23640
+ lContainer[DEHYDRATED_VIEWS] = dehydratedViews;
23641
+ }
23642
+ function enableLocateOrCreateContainerRefImpl() {
23643
+ _locateOrCreateAnchorNode = locateOrCreateAnchorNode;
23644
+ }
22408
23645
 
22409
23646
  class LQuery_ {
22410
23647
  constructor(queryList) {
@@ -23382,6 +24619,7 @@ function resetCompiledComponents() {
23382
24619
  ownerNgModule = new WeakMap();
23383
24620
  verifiedNgModule = new WeakMap();
23384
24621
  moduleQueue.length = 0;
24622
+ GENERATED_COMP_IDS.clear();
23385
24623
  }
23386
24624
  /**
23387
24625
  * Computes the combined declarations of explicit declarations, as well as declarations inherited by
@@ -23972,7 +25210,7 @@ class TestBedCompiler {
23972
25210
  // every component.
23973
25211
  this.componentToModuleScope.clear();
23974
25212
  const parentInjector = this.platform.injector;
23975
- this.testModuleRef = new ɵRender3NgModuleRef(this.testModuleType, parentInjector);
25213
+ this.testModuleRef = new ɵRender3NgModuleRef(this.testModuleType, parentInjector, []);
23976
25214
  // ApplicationInitStatus.runInitializers() is marked @internal to core.
23977
25215
  // Cast it to any before accessing it.
23978
25216
  this.testModuleRef.injector.get(ApplicationInitStatus).runInitializers();
@@ -24234,10 +25472,11 @@ class TestBedCompiler {
24234
25472
  }
24235
25473
  }
24236
25474
  queueTypesFromModulesArray(arr) {
24237
- // Because we may encounter the same NgModule while processing the imports and exports of an
24238
- // NgModule tree, we cache them in this set so we can skip ones that have already been seen
24239
- // encountered. In some test setups, this caching resulted in 10X runtime improvement.
24240
- const processedNgModuleDefs = new Set();
25475
+ // Because we may encounter the same NgModule or a standalone Component while processing
25476
+ // the dependencies of an NgModule or a standalone Component, we cache them in this set so we
25477
+ // can skip ones that have already been seen encountered. In some test setups, this caching
25478
+ // resulted in 10X runtime improvement.
25479
+ const processedDefs = new Set();
24241
25480
  const queueTypesFromModulesArrayRecur = (arr) => {
24242
25481
  var _a;
24243
25482
  for (const value of arr) {
@@ -24246,10 +25485,10 @@ class TestBedCompiler {
24246
25485
  }
24247
25486
  else if (hasNgModuleDef(value)) {
24248
25487
  const def = value.ɵmod;
24249
- if (processedNgModuleDefs.has(def)) {
25488
+ if (processedDefs.has(def)) {
24250
25489
  continue;
24251
25490
  }
24252
- processedNgModuleDefs.add(def);
25491
+ processedDefs.add(def);
24253
25492
  // Look through declarations, imports, and exports, and queue
24254
25493
  // everything found there.
24255
25494
  this.queueTypeArray(maybeUnwrapFn(def.declarations), value);
@@ -24262,6 +25501,10 @@ class TestBedCompiler {
24262
25501
  else if (isStandaloneComponent(value)) {
24263
25502
  this.queueType(value, null);
24264
25503
  const def = getComponentDef(value);
25504
+ if (processedDefs.has(def)) {
25505
+ continue;
25506
+ }
25507
+ processedDefs.add(def);
24265
25508
  const dependencies = maybeUnwrapFn((_a = def.dependencies) !== null && _a !== void 0 ? _a : []);
24266
25509
  dependencies.forEach((dependency) => {
24267
25510
  // Note: in AOT, the `dependencies` might also contain regular
@@ -24400,7 +25643,7 @@ class TestBedCompiler {
24400
25643
  });
24401
25644
  const ngZone = new NgZone({ enableLongStackTrace: true });
24402
25645
  const providers = [
24403
- { provide: NgZone, useValue: ngZone },
25646
+ ɵprovideNgZoneChangeDetection(ngZone),
24404
25647
  { provide: Compiler, useFactory: () => new R3TestCompiler(this) },
24405
25648
  ...this.providers,
24406
25649
  ...this.providerOverrides,