@angular/core 19.2.0-rc.0 → 19.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/fesm2022/core.mjs +461 -269
  2. package/fesm2022/core.mjs.map +1 -1
  3. package/fesm2022/primitives/event-dispatch.mjs +1 -1
  4. package/fesm2022/primitives/signals.mjs +1 -1
  5. package/fesm2022/rxjs-interop.mjs +1 -1
  6. package/fesm2022/testing.mjs +696 -5
  7. package/fesm2022/testing.mjs.map +1 -1
  8. package/index.d.ts +195 -19
  9. package/package.json +1 -1
  10. package/primitives/event-dispatch/index.d.ts +1 -1
  11. package/primitives/signals/index.d.ts +1 -1
  12. package/rxjs-interop/index.d.ts +1 -1
  13. package/schematics/bundles/{apply_import_manager-a930fcf1.js → apply_import_manager-a4e62ded.js} +2 -2
  14. package/schematics/bundles/checker-2eecc677.js +2 -2
  15. package/schematics/bundles/cleanup-unused-imports.js +4 -4
  16. package/schematics/bundles/{compiler_host-c280a924.js → compiler_host-f313eac9.js} +1 -1
  17. package/schematics/bundles/control-flow-migration.js +2 -2
  18. package/schematics/bundles/explicit-standalone-flag.js +4 -4
  19. package/schematics/bundles/{imports-abe29092.js → imports-31a38653.js} +1 -1
  20. package/schematics/bundles/index-3891dd55.js +2 -2
  21. package/schematics/bundles/{index-24a2ad1e.js → index-afc3f749.js} +2 -2
  22. package/schematics/bundles/inject-migration.js +6 -6
  23. package/schematics/bundles/{leading_space-d190b83b.js → leading_space-6e7a8ec6.js} +1 -1
  24. package/schematics/bundles/{migrate_ts_type_references-71b3a951.js → migrate_ts_type_references-1abf1f5f.js} +4 -4
  25. package/schematics/bundles/{ng_decorators-e699c081.js → ng_decorators-6878e227.js} +2 -2
  26. package/schematics/bundles/{nodes-a535b2be.js → nodes-ffdce442.js} +1 -1
  27. package/schematics/bundles/output-migration.js +5 -5
  28. package/schematics/bundles/pending-tasks.js +4 -4
  29. package/schematics/bundles/program-24da9092.js +10 -10
  30. package/schematics/bundles/{project_paths-b073c4d6.js → project_paths-64bc3947.js} +1 -1
  31. package/schematics/bundles/{project_tsconfig_paths-e9ccccbf.js → project_tsconfig_paths-6c9cde78.js} +1 -1
  32. package/schematics/bundles/{property_name-7c8433f5.js → property_name-42030525.js} +1 -1
  33. package/schematics/bundles/provide-initializer.js +4 -4
  34. package/schematics/bundles/route-lazy-loading.js +4 -4
  35. package/schematics/bundles/self-closing-tags-migration.js +6 -6
  36. package/schematics/bundles/signal-input-migration.js +7 -7
  37. package/schematics/bundles/signal-queries-migration.js +7 -7
  38. package/schematics/bundles/signals.js +7 -7
  39. package/schematics/bundles/standalone-migration.js +6 -6
  40. package/testing/index.d.ts +297 -1
package/fesm2022/core.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Angular v19.2.0-rc.0
2
+ * @license Angular v19.2.0
3
3
  * (c) 2010-2024 Google LLC. https://angular.io/
4
4
  * License: MIT
5
5
  */
@@ -967,8 +967,9 @@ function stringifyTypeFromDebugInfo(debugInfo) {
967
967
 
968
968
  /** Called when directives inject each other (creating a circular dependency) */
969
969
  function throwCyclicDependencyError(token, path) {
970
- const depPath = path ? `. Dependency path: ${path.join(' > ')} > ${token}` : '';
971
- throw new RuntimeError(-200 /* RuntimeErrorCode.CYCLIC_DI_DEPENDENCY */, ngDevMode ? `Circular dependency in DI detected for ${token}${depPath}` : token);
970
+ throw new RuntimeError(-200 /* RuntimeErrorCode.CYCLIC_DI_DEPENDENCY */, ngDevMode
971
+ ? `Circular dependency in DI detected for ${token}${path ? `. Dependency path: ${path.join(' > ')} > ${token}` : ''}`
972
+ : token);
972
973
  }
973
974
  function throwMixedMultiProviderError() {
974
975
  throw new Error(`Cannot mix multi providers and regular providers`);
@@ -7106,23 +7107,6 @@ function unwrapElementRef(value) {
7106
7107
  return value instanceof ElementRef ? value.nativeElement : value;
7107
7108
  }
7108
7109
 
7109
- const markedFeatures = new Set();
7110
- // tslint:disable:ban
7111
- /**
7112
- * A guarded `performance.mark` for feature marking.
7113
- *
7114
- * This method exists because while all supported browser and node.js version supported by Angular
7115
- * support performance.mark API. This is not the case for other environments such as JSDOM and
7116
- * Cloudflare workers.
7117
- */
7118
- function performanceMarkFeature(feature) {
7119
- if (markedFeatures.has(feature)) {
7120
- return;
7121
- }
7122
- markedFeatures.add(feature);
7123
- performance?.mark?.('mark_feature_usage', { detail: { feature } });
7124
- }
7125
-
7126
7110
  /**
7127
7111
  * Checks if the given `value` is a reactive `Signal`.
7128
7112
  */
@@ -7145,7 +7129,6 @@ function ɵunwrapWritableSignal(value) {
7145
7129
  * Create a `Signal` that can be set or updated directly.
7146
7130
  */
7147
7131
  function signal(initialValue, options) {
7148
- performanceMarkFeature('NgSignals');
7149
7132
  const signalFn = createSignal$1(initialValue);
7150
7133
  const node = signalFn[SIGNAL$1];
7151
7134
  if (options?.equal) {
@@ -8625,6 +8608,23 @@ var TracingAction;
8625
8608
  */
8626
8609
  const TracingService = new InjectionToken(ngDevMode ? 'TracingService' : '');
8627
8610
 
8611
+ const markedFeatures = new Set();
8612
+ // tslint:disable:ban
8613
+ /**
8614
+ * A guarded `performance.mark` for feature marking.
8615
+ *
8616
+ * This method exists because while all supported browser and node.js version supported by Angular
8617
+ * support performance.mark API. This is not the case for other environments such as JSDOM and
8618
+ * Cloudflare workers.
8619
+ */
8620
+ function performanceMarkFeature(feature) {
8621
+ if (markedFeatures.has(feature)) {
8622
+ return;
8623
+ }
8624
+ markedFeatures.add(feature);
8625
+ performance?.mark?.('mark_feature_usage', { detail: { feature } });
8626
+ }
8627
+
8628
8628
  /**
8629
8629
  * Asserts that the current stack frame is not within a reactive context. Useful
8630
8630
  * to disallow certain code from running inside a reactive context (see {@link toSignal}).
@@ -12559,15 +12559,6 @@ function executeTemplate(tView, lView, templateFn, rf, context) {
12559
12559
  profiler(postHookType, context);
12560
12560
  }
12561
12561
  }
12562
- /**
12563
- * Creates directive instances.
12564
- */
12565
- function createDirectivesInstancesInInstruction(tView, lView, tNode) {
12566
- if (!getBindingsEnabled())
12567
- return;
12568
- attachPatchData(getNativeByTNode(tNode, lView), lView);
12569
- createDirectivesInstances(tView, lView, tNode);
12570
- }
12571
12562
  /**
12572
12563
  * Creates directive instances.
12573
12564
  */
@@ -12685,17 +12676,15 @@ function mapPropName(name) {
12685
12676
  }
12686
12677
  function elementPropertyInternal(tView, tNode, lView, propName, value, renderer, sanitizer, nativeOnly) {
12687
12678
  ngDevMode && assertNotSame(value, NO_CHANGE, 'Incoming value should never be NO_CHANGE.');
12688
- let inputData = tNode.inputs;
12689
- let dataValue;
12690
- if (!nativeOnly && inputData != null && (dataValue = inputData[propName])) {
12691
- setInputsForProperty(tView, lView, dataValue, propName, value);
12692
- if (isComponentHost(tNode))
12693
- markDirtyIfOnPush(lView, tNode.index);
12694
- if (ngDevMode) {
12695
- setNgReflectProperties(lView, tView, tNode, dataValue, value);
12679
+ if (!nativeOnly) {
12680
+ const hasSetInput = setAllInputsForProperty(tNode, tView, lView, propName, value);
12681
+ if (hasSetInput) {
12682
+ isComponentHost(tNode) && markDirtyIfOnPush(lView, tNode.index);
12683
+ ngDevMode && setNgReflectProperties(lView, tView, tNode, propName, value);
12684
+ return; // Stop propcessing if we've matched at least one input.
12696
12685
  }
12697
12686
  }
12698
- else if (tNode.type & 3 /* TNodeType.AnyRNode */) {
12687
+ if (tNode.type & 3 /* TNodeType.AnyRNode */) {
12699
12688
  const element = getNativeByTNode(tNode, lView);
12700
12689
  propName = mapPropName(propName);
12701
12690
  if (ngDevMode) {
@@ -12744,14 +12733,25 @@ function setNgReflectProperty(lView, tNode, attrName, value) {
12744
12733
  renderer.setValue(element, textContent);
12745
12734
  }
12746
12735
  }
12747
- function setNgReflectProperties(lView, tView, tNode, inputConfig, value) {
12748
- if (tNode.type & (3 /* TNodeType.AnyRNode */ | 4 /* TNodeType.Container */)) {
12749
- // Note: we set the private name of the input as the reflected property, not the public one.
12750
- for (let i = 0; i < inputConfig.length; i += 2) {
12751
- const index = inputConfig[i];
12752
- const lookupName = inputConfig[i + 1];
12736
+ function setNgReflectProperties(lView, tView, tNode, publicName, value) {
12737
+ if (!(tNode.type & (3 /* TNodeType.AnyRNode */ | 4 /* TNodeType.Container */))) {
12738
+ return;
12739
+ }
12740
+ const inputConfig = tNode.inputs?.[publicName];
12741
+ const hostInputConfig = tNode.hostDirectiveInputs?.[publicName];
12742
+ if (hostInputConfig) {
12743
+ for (let i = 0; i < hostInputConfig.length; i += 2) {
12744
+ const index = hostInputConfig[i];
12745
+ const publicName = hostInputConfig[i + 1];
12746
+ const def = tView.data[index];
12747
+ setNgReflectProperty(lView, tNode, def.inputs[publicName][0], value);
12748
+ }
12749
+ }
12750
+ // Note: we set the private name of the input as the reflected property, not the public one.
12751
+ if (inputConfig) {
12752
+ for (const index of inputConfig) {
12753
12753
  const def = tView.data[index];
12754
- setNgReflectProperty(lView, tNode, def.inputs[lookupName][0], value);
12754
+ setNgReflectProperty(lView, tNode, def.inputs[publicName][0], value);
12755
12755
  }
12756
12756
  }
12757
12757
  }
@@ -12920,7 +12920,7 @@ function storePropertyBindingMetadata(tData, tNode, propertyName, bindingIndex,
12920
12920
  // Since we don't have a concept of the "first update pass" we need to check for presence of the
12921
12921
  // binding meta-data to decide if one should be stored (or if was stored already).
12922
12922
  if (tData[bindingIndex] === null) {
12923
- if (tNode.inputs == null || !tNode.inputs[propertyName]) {
12923
+ if (!tNode.inputs?.[propertyName] && !tNode.hostDirectiveInputs?.[propertyName]) {
12924
12924
  const propBindingIdxs = tNode.propertyBindings || (tNode.propertyBindings = []);
12925
12925
  propBindingIdxs.push(bindingIndex);
12926
12926
  let bindingMetadata = propertyName;
@@ -12955,23 +12955,88 @@ function handleError(lView, error) {
12955
12955
  errorHandler && errorHandler.handleError(error);
12956
12956
  }
12957
12957
  /**
12958
- * Set the inputs of directives at the current node to corresponding value.
12958
+ * Set all directive inputs with the specific public name on the node.
12959
12959
  *
12960
- * @param tView The current TView
12961
- * @param lView the `LView` which contains the directives.
12962
- * @param inputs mapping between the public "input" name and privately-known,
12963
- * possibly minified, property names to write to.
12960
+ * @param tNode TNode on which the input is being set.
12961
+ * @param tView Current TView
12962
+ * @param lView `LView` which contains the directives.
12963
+ * @param publicName Public name of the input being set.
12964
+ * @param value Value to set.
12965
+ */
12966
+ function setAllInputsForProperty(tNode, tView, lView, publicName, value) {
12967
+ const inputs = tNode.inputs?.[publicName];
12968
+ const hostDirectiveInputs = tNode.hostDirectiveInputs?.[publicName];
12969
+ let hasMatch = false;
12970
+ if (hostDirectiveInputs) {
12971
+ for (let i = 0; i < hostDirectiveInputs.length; i += 2) {
12972
+ const index = hostDirectiveInputs[i];
12973
+ ngDevMode && assertIndexInRange(lView, index);
12974
+ const publicName = hostDirectiveInputs[i + 1];
12975
+ const def = tView.data[index];
12976
+ writeToDirectiveInput(def, lView[index], publicName, value);
12977
+ hasMatch = true;
12978
+ }
12979
+ }
12980
+ if (inputs) {
12981
+ for (const index of inputs) {
12982
+ ngDevMode && assertIndexInRange(lView, index);
12983
+ const instance = lView[index];
12984
+ const def = tView.data[index];
12985
+ writeToDirectiveInput(def, instance, publicName, value);
12986
+ hasMatch = true;
12987
+ }
12988
+ }
12989
+ return hasMatch;
12990
+ }
12991
+ /**
12992
+ * Sets an input value only on a specific directive and its host directives.
12993
+ * @param tNode TNode on which the input is being set.
12994
+ * @param tView Current TView
12995
+ * @param lView `LView` which contains the directives.
12996
+ * @param target Directive on which to set the input.
12997
+ * @param publicName Public name of the input being set.
12964
12998
  * @param value Value to set.
12965
12999
  */
12966
- function setInputsForProperty(tView, lView, inputs, publicName, value) {
12967
- for (let i = 0; i < inputs.length; i += 2) {
12968
- const index = inputs[i];
12969
- ngDevMode && assertIndexInRange(lView, index);
12970
- const privateName = inputs[i + 1];
12971
- const instance = lView[index];
12972
- const def = tView.data[index];
12973
- writeToDirectiveInput(def, instance, privateName, value);
13000
+ function setDirectiveInput(tNode, tView, lView, target, publicName, value) {
13001
+ let hostIndex = null;
13002
+ let hostDirectivesStart = null;
13003
+ let hostDirectivesEnd = null;
13004
+ let hasSet = false;
13005
+ if (ngDevMode && !tNode.directiveToIndex?.has(target.type)) {
13006
+ throw new Error(`Node does not have a directive with type ${target.type.name}`);
13007
+ }
13008
+ const data = tNode.directiveToIndex.get(target.type);
13009
+ if (typeof data === 'number') {
13010
+ hostIndex = data;
13011
+ }
13012
+ else {
13013
+ [hostIndex, hostDirectivesStart, hostDirectivesEnd] = data;
13014
+ }
13015
+ if (hostDirectivesStart !== null &&
13016
+ hostDirectivesEnd !== null &&
13017
+ tNode.hostDirectiveInputs?.hasOwnProperty(publicName)) {
13018
+ const hostDirectiveInputs = tNode.hostDirectiveInputs[publicName];
13019
+ for (let i = 0; i < hostDirectiveInputs.length; i += 2) {
13020
+ const index = hostDirectiveInputs[i];
13021
+ if (index >= hostDirectivesStart && index <= hostDirectivesEnd) {
13022
+ ngDevMode && assertIndexInRange(lView, index);
13023
+ const def = tView.data[index];
13024
+ const hostDirectivePublicName = hostDirectiveInputs[i + 1];
13025
+ writeToDirectiveInput(def, lView[index], hostDirectivePublicName, value);
13026
+ hasSet = true;
13027
+ }
13028
+ else if (index > hostDirectivesEnd) {
13029
+ // Directives here are in ascending order so we can stop looking once we're past the range.
13030
+ break;
13031
+ }
13032
+ }
13033
+ }
13034
+ if (hostIndex !== null) {
13035
+ ngDevMode && assertIndexInRange(lView, hostIndex);
13036
+ writeToDirectiveInput(target, lView[hostIndex], publicName, value);
13037
+ hasSet = true;
12974
13038
  }
13039
+ return hasSet;
12975
13040
  }
12976
13041
 
12977
13042
  function renderComponent(hostLView, componentHostIdx) {
@@ -15624,9 +15689,12 @@ function createTNode(tView, tParent, type, index, value, attrs) {
15624
15689
  attrs: attrs,
15625
15690
  mergedAttrs: null,
15626
15691
  localNames: null,
15627
- initialInputs: undefined,
15692
+ initialInputs: null,
15628
15693
  inputs: null,
15694
+ hostDirectiveInputs: null,
15629
15695
  outputs: null,
15696
+ hostDirectiveOutputs: null,
15697
+ directiveToIndex: null,
15630
15698
  tView: null,
15631
15699
  next: null,
15632
15700
  prev: null,
@@ -15934,7 +16002,7 @@ function createIcuIterator(tIcu, lView) {
15934
16002
  * - the `b` char which indicates that the lookup should start from the `document.body`
15935
16003
  * - the `h` char to start lookup from the component host node (`lView[HOST]`)
15936
16004
  */
15937
- const REF_EXTRACTOR_REGEXP = new RegExp(`^(\\d+)*(${REFERENCE_NODE_BODY}|${REFERENCE_NODE_HOST})*(.*)`);
16005
+ const REF_EXTRACTOR_REGEXP = /* @__PURE__ */ new RegExp(`^(\\d+)*(${REFERENCE_NODE_BODY}|${REFERENCE_NODE_HOST})*(.*)`);
15938
16006
  /**
15939
16007
  * Helper function that takes a reference node location and a set of navigation steps
15940
16008
  * (from the reference node) to a target node and outputs a string that represents
@@ -16814,6 +16882,18 @@ function removeDehydratedViews(lContainer) {
16814
16882
  // once again in case a `ViewContainerRef` is created later).
16815
16883
  lContainer[DEHYDRATED_VIEWS] = retainedViews;
16816
16884
  }
16885
+ function removeDehydratedViewList(deferBlock) {
16886
+ const { lContainer } = deferBlock;
16887
+ const dehydratedViews = lContainer[DEHYDRATED_VIEWS];
16888
+ if (dehydratedViews === null)
16889
+ return;
16890
+ const parentLView = lContainer[PARENT];
16891
+ const renderer = parentLView[RENDERER];
16892
+ for (const view of dehydratedViews) {
16893
+ removeDehydratedView(view, renderer);
16894
+ ngDevMode && ngDevMode.dehydratedViewsRemoved++;
16895
+ }
16896
+ }
16817
16897
  /**
16818
16898
  * Helper function to remove all nodes from a dehydrated view.
16819
16899
  */
@@ -17453,14 +17533,22 @@ function ɵɵinvalidFactory() {
17453
17533
  * Resolve the matched directives on a node.
17454
17534
  */
17455
17535
  function resolveDirectives(tView, lView, tNode, localRefs, directiveMatcher) {
17456
- // Please make sure to have explicit type for `exportsMap`. Inferred type triggers bug in
17457
- // tsickle.
17536
+ // Please make sure to have explicit type for `exportsMap`. Inferred type triggers bug in tsickle.
17458
17537
  ngDevMode && assertFirstCreatePass(tView);
17459
17538
  const exportsMap = localRefs === null ? null : { '': -1 };
17460
17539
  const matchedDirectiveDefs = directiveMatcher(tView, tNode);
17461
17540
  if (matchedDirectiveDefs !== null) {
17462
- const [directiveDefs, hostDirectiveDefs] = resolveHostDirectives(tView, tNode, matchedDirectiveDefs);
17463
- initializeDirectives(tView, lView, tNode, directiveDefs, exportsMap, hostDirectiveDefs);
17541
+ let directiveDefs;
17542
+ let hostDirectiveDefs = null;
17543
+ let hostDirectiveRanges = null;
17544
+ const hostDirectiveResolution = resolveHostDirectives(matchedDirectiveDefs);
17545
+ if (hostDirectiveResolution === null) {
17546
+ directiveDefs = matchedDirectiveDefs;
17547
+ }
17548
+ else {
17549
+ [directiveDefs, hostDirectiveDefs, hostDirectiveRanges] = hostDirectiveResolution;
17550
+ }
17551
+ initializeDirectives(tView, lView, tNode, directiveDefs, exportsMap, hostDirectiveDefs, hostDirectiveRanges);
17464
17552
  }
17465
17553
  if (exportsMap !== null && localRefs !== null) {
17466
17554
  cacheMatchingLocalNames(tNode, localRefs, exportsMap);
@@ -17479,40 +17567,63 @@ function cacheMatchingLocalNames(tNode, localRefs, exportsMap) {
17479
17567
  localNames.push(localRefs[i], index);
17480
17568
  }
17481
17569
  }
17482
- function resolveHostDirectives(tView, tNode, matches) {
17483
- const allDirectiveDefs = [];
17570
+ function resolveHostDirectives(matches) {
17571
+ let componentDef = null;
17572
+ let hasHostDirectives = false;
17573
+ for (let i = 0; i < matches.length; i++) {
17574
+ const def = matches[i];
17575
+ if (i === 0 && isComponentDef(def)) {
17576
+ componentDef = def;
17577
+ }
17578
+ if (def.findHostDirectiveDefs !== null) {
17579
+ hasHostDirectives = true;
17580
+ break;
17581
+ }
17582
+ }
17583
+ if (!hasHostDirectives) {
17584
+ return null;
17585
+ }
17586
+ let allDirectiveDefs = null;
17484
17587
  let hostDirectiveDefs = null;
17588
+ let hostDirectiveRanges = null;
17589
+ // Components are inserted at the front of the matches array so that their lifecycle
17590
+ // hooks run before any directive lifecycle hooks. This appears to be for ViewEngine
17591
+ // compatibility. This logic doesn't make sense with host directives, because it
17592
+ // would allow the host directives to undo any overrides the host may have made.
17593
+ // To handle this case, the host directives of components are inserted at the beginning
17594
+ // of the array, followed by the component. As such, the insertion order is as follows:
17595
+ // 1. Host directives belonging to the selector-matched component.
17596
+ // 2. Selector-matched component.
17597
+ // 3. Host directives belonging to selector-matched directives.
17598
+ // 4. Selector-matched dir
17485
17599
  for (const def of matches) {
17486
17600
  if (def.findHostDirectiveDefs !== null) {
17487
- // TODO(pk): probably could return matches instead of taking in an array to fill in?
17601
+ allDirectiveDefs ??= [];
17488
17602
  hostDirectiveDefs ??= new Map();
17489
- // Components are inserted at the front of the matches array so that their lifecycle
17490
- // hooks run before any directive lifecycle hooks. This appears to be for ViewEngine
17491
- // compatibility. This logic doesn't make sense with host directives, because it
17492
- // would allow the host directives to undo any overrides the host may have made.
17493
- // To handle this case, the host directives of components are inserted at the beginning
17494
- // of the array, followed by the component. As such, the insertion order is as follows:
17495
- // 1. Host directives belonging to the selector-matched component.
17496
- // 2. Selector-matched component.
17497
- // 3. Host directives belonging to selector-matched directives.
17498
- // 4. Selector-matched directives.
17499
- def.findHostDirectiveDefs(def, allDirectiveDefs, hostDirectiveDefs);
17603
+ hostDirectiveRanges ??= new Map();
17604
+ resolveHostDirectivesForDef(def, allDirectiveDefs, hostDirectiveRanges, hostDirectiveDefs);
17500
17605
  }
17501
- if (isComponentDef(def)) {
17606
+ // Component definition needs to be pushed early to maintain the correct ordering.
17607
+ if (def === componentDef) {
17608
+ allDirectiveDefs ??= [];
17502
17609
  allDirectiveDefs.push(def);
17503
- markAsComponentHost(tView, tNode, allDirectiveDefs.length - 1);
17504
17610
  }
17505
17611
  }
17506
- if (isComponentHost(tNode)) {
17507
- allDirectiveDefs.push(...matches.slice(1));
17508
- }
17509
- else {
17510
- allDirectiveDefs.push(...matches);
17511
- }
17512
- if (ngDevMode) {
17513
- assertNoDuplicateDirectives(allDirectiveDefs);
17612
+ if (allDirectiveDefs !== null) {
17613
+ allDirectiveDefs.push(...(componentDef === null ? matches : matches.slice(1)));
17614
+ ngDevMode && assertNoDuplicateDirectives(allDirectiveDefs);
17615
+ return [allDirectiveDefs, hostDirectiveDefs, hostDirectiveRanges];
17514
17616
  }
17515
- return [allDirectiveDefs, hostDirectiveDefs];
17617
+ return null;
17618
+ }
17619
+ function resolveHostDirectivesForDef(def, allDirectiveDefs, hostDirectiveRanges, hostDirectiveDefs) {
17620
+ ngDevMode && assertDefined(def.findHostDirectiveDefs, 'Expected host directive resolve function');
17621
+ const start = allDirectiveDefs.length;
17622
+ // TODO(pk): probably could return matches instead of taking in an array to fill in?
17623
+ def.findHostDirectiveDefs(def, allDirectiveDefs, hostDirectiveDefs);
17624
+ // Note that these indexes are within the offset by `directiveStart`. We can't do the
17625
+ // offsetting here, because `directiveStart` hasn't been initialized on the TNode yet.
17626
+ hostDirectiveRanges.set(def, [start, allDirectiveDefs.length - 1]);
17516
17627
  }
17517
17628
  /**
17518
17629
  * Marks a given TNode as a component's host. This consists of:
@@ -17526,37 +17637,62 @@ function markAsComponentHost(tView, hostTNode, componentOffset) {
17526
17637
  (tView.components ??= []).push(hostTNode.index);
17527
17638
  }
17528
17639
  /** Initializes the data structures necessary for a list of directives to be instantiated. */
17529
- function initializeDirectives(tView, lView, tNode, directives, exportsMap, hostDirectiveDefs) {
17640
+ function initializeDirectives(tView, lView, tNode, directives, exportsMap, hostDirectiveDefs, hostDirectiveRanges) {
17530
17641
  ngDevMode && assertFirstCreatePass(tView);
17642
+ const directivesLength = directives.length;
17643
+ let hasSeenComponent = false;
17531
17644
  // Publishes the directive types to DI so they can be injected. Needs to
17532
17645
  // happen in a separate pass before the TNode flags have been initialized.
17533
- for (let i = 0; i < directives.length; i++) {
17534
- diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, lView), tView, directives[i].type);
17646
+ for (let i = 0; i < directivesLength; i++) {
17647
+ const def = directives[i];
17648
+ if (!hasSeenComponent && isComponentDef(def)) {
17649
+ hasSeenComponent = true;
17650
+ markAsComponentHost(tView, tNode, i);
17651
+ }
17652
+ diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, lView), tView, def.type);
17535
17653
  }
17536
- initTNodeFlags(tNode, tView.data.length, directives.length);
17654
+ initTNodeFlags(tNode, tView.data.length, directivesLength);
17537
17655
  // When the same token is provided by several directives on the same node, some rules apply in
17538
17656
  // the viewEngine:
17539
17657
  // - viewProviders have priority over providers
17540
17658
  // - the last directive in NgModule.declarations has priority over the previous one
17541
17659
  // So to match these rules, the order in which providers are added in the arrays is very
17542
17660
  // important.
17543
- for (let i = 0; i < directives.length; i++) {
17661
+ for (let i = 0; i < directivesLength; i++) {
17544
17662
  const def = directives[i];
17545
17663
  if (def.providersResolver)
17546
17664
  def.providersResolver(def);
17547
17665
  }
17548
17666
  let preOrderHooksFound = false;
17549
17667
  let preOrderCheckHooksFound = false;
17550
- let directiveIdx = allocExpando(tView, lView, directives.length, null);
17668
+ let directiveIdx = allocExpando(tView, lView, directivesLength, null);
17551
17669
  ngDevMode &&
17552
17670
  assertSame(directiveIdx, tNode.directiveStart, 'TNode.directiveStart should point to just allocated space');
17553
- for (let i = 0; i < directives.length; i++) {
17671
+ // If there's at least one directive, we'll have to track it so initialize the map.
17672
+ if (directivesLength > 0) {
17673
+ tNode.directiveToIndex = new Map();
17674
+ }
17675
+ for (let i = 0; i < directivesLength; i++) {
17554
17676
  const def = directives[i];
17555
17677
  // Merge the attrs in the order of matches. This assumes that the first directive is the
17556
17678
  // component itself, so that the component has the least priority.
17557
17679
  tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, def.hostAttrs);
17558
17680
  configureViewWithDirective(tView, tNode, lView, directiveIdx, def);
17559
17681
  saveNameToExportMap(directiveIdx, def, exportsMap);
17682
+ // If a directive has host directives, we need to track both its index and the range within
17683
+ // the host directives are declared. Host directives are not tracked, but should be resolved
17684
+ // by looking up the host and getting its indexes from there.
17685
+ if (hostDirectiveRanges !== null && hostDirectiveRanges.has(def)) {
17686
+ const [start, end] = hostDirectiveRanges.get(def);
17687
+ tNode.directiveToIndex.set(def.type, [
17688
+ directiveIdx,
17689
+ start + tNode.directiveStart,
17690
+ end + tNode.directiveStart,
17691
+ ]);
17692
+ }
17693
+ else if (hostDirectiveDefs === null || !hostDirectiveDefs.has(def)) {
17694
+ tNode.directiveToIndex.set(def.type, directiveIdx);
17695
+ }
17560
17696
  if (def.contentQueries !== null)
17561
17697
  tNode.flags |= 4 /* TNodeFlags.hasContentQuery */;
17562
17698
  if (def.hostBindings !== null || def.hostAttrs !== null || def.hostVars !== 0)
@@ -17584,87 +17720,84 @@ function initializeDirectives(tView, lView, tNode, directives, exportsMap, hostD
17584
17720
  * Initializes data structures required to work with directive inputs and outputs.
17585
17721
  * Initialization is done for all directives matched on a given TNode.
17586
17722
  */
17587
- function initializeInputAndOutputAliases(tView, tNode, hostDirectiveDefinitionMap) {
17723
+ function initializeInputAndOutputAliases(tView, tNode, hostDirectiveDefs) {
17588
17724
  ngDevMode && assertFirstCreatePass(tView);
17589
- const start = tNode.directiveStart;
17590
- const end = tNode.directiveEnd;
17591
- const tViewData = tView.data;
17592
- const tNodeAttrs = tNode.attrs;
17593
- const inputsFromAttrs = [];
17594
- let inputsStore = null;
17595
- let outputsStore = null;
17596
- for (let directiveIndex = start; directiveIndex < end; directiveIndex++) {
17597
- const directiveDef = tViewData[directiveIndex];
17598
- const aliasData = hostDirectiveDefinitionMap
17599
- ? hostDirectiveDefinitionMap.get(directiveDef)
17600
- : null;
17601
- const aliasedInputs = aliasData ? aliasData.inputs : null;
17602
- const aliasedOutputs = aliasData ? aliasData.outputs : null;
17603
- inputsStore = captureNodeBindings(0 /* CaptureNodeBindingMode.Inputs */, directiveDef.inputs, directiveIndex, inputsStore, aliasedInputs);
17604
- outputsStore = captureNodeBindings(1 /* CaptureNodeBindingMode.Outputs */, directiveDef.outputs, directiveIndex, outputsStore, aliasedOutputs);
17605
- // Do not use unbound attributes as inputs to structural directives, since structural
17606
- // directive inputs can only be set using microsyntax (e.g. `<div *dir="exp">`).
17607
- const initialInputs = inputsStore !== null && tNodeAttrs !== null && !isInlineTemplate(tNode)
17608
- ? generateInitialInputs(inputsStore, directiveIndex, tNodeAttrs)
17609
- : null;
17610
- inputsFromAttrs.push(initialInputs);
17611
- }
17612
- if (inputsStore !== null) {
17613
- if (inputsStore.hasOwnProperty('class')) {
17614
- tNode.flags |= 8 /* TNodeFlags.hasClassInput */;
17725
+ for (let index = tNode.directiveStart; index < tNode.directiveEnd; index++) {
17726
+ const directiveDef = tView.data[index];
17727
+ if (hostDirectiveDefs === null || !hostDirectiveDefs.has(directiveDef)) {
17728
+ setupSelectorMatchedInputsOrOutputs(0 /* BindingType.Inputs */, tNode, directiveDef, index);
17729
+ setupSelectorMatchedInputsOrOutputs(1 /* BindingType.Outputs */, tNode, directiveDef, index);
17730
+ setupInitialInputs(tNode, index, false);
17615
17731
  }
17616
- if (inputsStore.hasOwnProperty('style')) {
17617
- tNode.flags |= 16 /* TNodeFlags.hasStyleInput */;
17732
+ else {
17733
+ const hostDirectiveDef = hostDirectiveDefs.get(directiveDef);
17734
+ setupHostDirectiveInputsOrOutputs(0 /* BindingType.Inputs */, tNode, hostDirectiveDef, index);
17735
+ setupHostDirectiveInputsOrOutputs(1 /* BindingType.Outputs */, tNode, hostDirectiveDef, index);
17736
+ setupInitialInputs(tNode, index, true);
17618
17737
  }
17619
17738
  }
17620
- tNode.initialInputs = inputsFromAttrs;
17621
- tNode.inputs = inputsStore;
17622
- tNode.outputs = outputsStore;
17623
17739
  }
17624
- function captureNodeBindings(mode, aliasMap, directiveIndex, bindingsResult, hostDirectiveAliasMap) {
17625
- for (let publicName in aliasMap) {
17626
- if (!aliasMap.hasOwnProperty(publicName)) {
17627
- continue;
17628
- }
17629
- const value = aliasMap[publicName];
17630
- if (value === undefined) {
17631
- continue;
17632
- }
17633
- bindingsResult ??= {};
17634
- // If there are no host directive mappings, we want to remap using the alias map from the
17635
- // definition itself. If there is an alias map, it has two functions:
17636
- // 1. It serves as an allowlist of bindings that are exposed by the host directives. Only the
17637
- // ones inside the host directive map will be exposed on the host.
17638
- // 2. The public name of the property is aliased using the host directive alias map, rather
17639
- // than the alias map from the definition.
17640
- let finalPublicName = publicName;
17641
- if (hostDirectiveAliasMap !== null) {
17642
- // If there is no mapping, it's not part of the allowlist and this input/output
17643
- // is not captured and should be ignored.
17644
- if (!hostDirectiveAliasMap.hasOwnProperty(publicName)) {
17645
- continue;
17740
+ /**
17741
+ * Sets up the input/output bindings for a directive that was matched in the template through its
17742
+ * selector. This method is called repeatedly to build up all of the available inputs on a node.
17743
+ *
17744
+ * @param mode Whether inputs or outputs are being contructed.
17745
+ * @param tNode Node on which the bindings are being set up.
17746
+ * @param def Directive definition for which the bindings are being set up.
17747
+ * @param directiveIndex Index at which the directive instance will be stored in the LView.
17748
+ */
17749
+ function setupSelectorMatchedInputsOrOutputs(mode, tNode, def, directiveIndex) {
17750
+ const aliasMap = mode === 0 /* BindingType.Inputs */ ? def.inputs : def.outputs;
17751
+ for (const publicName in aliasMap) {
17752
+ if (aliasMap.hasOwnProperty(publicName)) {
17753
+ let bindings;
17754
+ if (mode === 0 /* BindingType.Inputs */) {
17755
+ bindings = tNode.inputs ??= {};
17646
17756
  }
17647
- finalPublicName = hostDirectiveAliasMap[publicName];
17648
- }
17649
- if (mode === 0 /* CaptureNodeBindingMode.Inputs */) {
17650
- addPropertyBinding(bindingsResult, directiveIndex, finalPublicName, publicName);
17757
+ else {
17758
+ bindings = tNode.outputs ??= {};
17759
+ }
17760
+ bindings[publicName] ??= [];
17761
+ bindings[publicName].push(directiveIndex);
17762
+ setShadowStylingInputFlags(tNode, publicName);
17651
17763
  }
17652
- else {
17653
- addPropertyBinding(bindingsResult, directiveIndex, finalPublicName, value);
17764
+ }
17765
+ }
17766
+ /**
17767
+ * Sets up input/output bindings that were defined through host directives on a specific node.
17768
+ * @param mode Whether inputs or outputs are being contructed.
17769
+ * @param tNode Node on which the bindings are being set up.
17770
+ * @param config Host directive definition that is being set up.
17771
+ * @param directiveIndex Index at which the directive instance will be stored in the LView.
17772
+ */
17773
+ function setupHostDirectiveInputsOrOutputs(mode, tNode, config, directiveIndex) {
17774
+ const aliasMap = mode === 0 /* BindingType.Inputs */ ? config.inputs : config.outputs;
17775
+ for (const initialName in aliasMap) {
17776
+ if (aliasMap.hasOwnProperty(initialName)) {
17777
+ const publicName = aliasMap[initialName];
17778
+ let bindings;
17779
+ if (mode === 0 /* BindingType.Inputs */) {
17780
+ bindings = tNode.hostDirectiveInputs ??= {};
17781
+ }
17782
+ else {
17783
+ bindings = tNode.hostDirectiveOutputs ??= {};
17784
+ }
17785
+ bindings[publicName] ??= [];
17786
+ bindings[publicName].push(directiveIndex, initialName);
17787
+ setShadowStylingInputFlags(tNode, publicName);
17654
17788
  }
17655
17789
  }
17656
- return bindingsResult;
17657
17790
  }
17658
- function addPropertyBinding(bindings, directiveIndex, publicName, lookupName) {
17659
- if (bindings.hasOwnProperty(publicName)) {
17660
- bindings[publicName].push(directiveIndex, lookupName);
17791
+ function setShadowStylingInputFlags(tNode, publicName) {
17792
+ if (publicName === 'class') {
17793
+ tNode.flags |= 8 /* TNodeFlags.hasClassInput */;
17661
17794
  }
17662
- else {
17663
- bindings[publicName] = [directiveIndex, lookupName];
17795
+ else if (publicName === 'style') {
17796
+ tNode.flags |= 16 /* TNodeFlags.hasStyleInput */;
17664
17797
  }
17665
17798
  }
17666
17799
  /**
17667
- * Generates initialInputData for a node and stores it in the template's static storage
17800
+ * Sets up the initialInputData for a node and stores it in the template's static storage
17668
17801
  * so subsequent template invocations don't have to recalculate it.
17669
17802
  *
17670
17803
  * initialInputData is an array containing values that need to be set as input properties
@@ -17674,11 +17807,21 @@ function addPropertyBinding(bindings, directiveIndex, publicName, lookupName) {
17674
17807
  *
17675
17808
  * <my-component name="Bess"></my-component>
17676
17809
  *
17677
- * @param inputs Input alias map that was generated from the directive def inputs.
17810
+ * @param tNode TNode on which to set up the initial inputs.
17678
17811
  * @param directiveIndex Index of the directive that is currently being processed.
17679
- * @param attrs Static attrs on this node.
17680
17812
  */
17681
- function generateInitialInputs(inputs, directiveIndex, attrs) {
17813
+ function setupInitialInputs(tNode, directiveIndex, isHostDirective) {
17814
+ const { attrs, inputs, hostDirectiveInputs } = tNode;
17815
+ if (attrs === null ||
17816
+ (!isHostDirective && inputs === null) ||
17817
+ (isHostDirective && hostDirectiveInputs === null) ||
17818
+ // Do not use unbound attributes as inputs to structural directives, since structural
17819
+ // directive inputs can only be set using microsyntax (e.g. `<div *dir="exp">`).
17820
+ isInlineTemplate(tNode)) {
17821
+ tNode.initialInputs ??= [];
17822
+ tNode.initialInputs.push(null);
17823
+ return;
17824
+ }
17682
17825
  let inputsToStore = null;
17683
17826
  let i = 0;
17684
17827
  while (i < attrs.length) {
@@ -17693,26 +17836,38 @@ function generateInitialInputs(inputs, directiveIndex, attrs) {
17693
17836
  i += 2;
17694
17837
  continue;
17695
17838
  }
17696
- // If we hit any other attribute markers, we're done anyway. None of those are valid inputs.
17697
- if (typeof attrName === 'number')
17839
+ else if (typeof attrName === 'number') {
17840
+ // If we hit any other attribute markers, we're done anyway. None of those are valid inputs.
17698
17841
  break;
17699
- if (inputs.hasOwnProperty(attrName)) {
17842
+ }
17843
+ if (!isHostDirective && inputs.hasOwnProperty(attrName)) {
17700
17844
  // Find the input's public name from the input store. Note that we can be found easier
17701
17845
  // through the directive def, but we want to do it using the inputs store so that it can
17702
17846
  // account for host directive aliases.
17703
17847
  const inputConfig = inputs[attrName];
17704
- for (let j = 0; j < inputConfig.length; j += 2) {
17705
- if (inputConfig[j] === directiveIndex) {
17848
+ for (const index of inputConfig) {
17849
+ if (index === directiveIndex) {
17706
17850
  inputsToStore ??= [];
17707
- inputsToStore.push(inputConfig[j + 1], attrs[i + 1]);
17851
+ inputsToStore.push(attrName, attrs[i + 1]);
17708
17852
  // A directive can't have multiple inputs with the same name so we can break here.
17709
17853
  break;
17710
17854
  }
17711
17855
  }
17712
17856
  }
17857
+ else if (isHostDirective && hostDirectiveInputs.hasOwnProperty(attrName)) {
17858
+ const config = hostDirectiveInputs[attrName];
17859
+ for (let j = 0; j < config.length; j += 2) {
17860
+ if (config[j] === directiveIndex) {
17861
+ inputsToStore ??= [];
17862
+ inputsToStore.push(config[j + 1], attrs[i + 1]);
17863
+ break;
17864
+ }
17865
+ }
17866
+ }
17713
17867
  i += 2;
17714
17868
  }
17715
- return inputsToStore;
17869
+ tNode.initialInputs ??= [];
17870
+ tNode.initialInputs.push(inputsToStore);
17716
17871
  }
17717
17872
  /**
17718
17873
  * Setup directive for instantiation.
@@ -17972,7 +18127,7 @@ class ComponentFactory extends ComponentFactory$1 {
17972
18127
  const cmpDef = this.componentDef;
17973
18128
  ngDevMode && verifyNotAnOrphanComponent(cmpDef);
17974
18129
  const tAttributes = rootSelectorOrNode
17975
- ? ['ng-version', '19.2.0-rc.0']
18130
+ ? ['ng-version', '19.2.0']
17976
18131
  : // Extract attributes and classes from the first selector only to match VE behavior.
17977
18132
  extractAttrsAndClassesFromSelector(this.componentDef.selectors[0]);
17978
18133
  // Create the root view. Uses empty TView and ContentTemplate.
@@ -18061,29 +18216,24 @@ class ComponentRef extends ComponentRef$1 {
18061
18216
  this.componentType = componentType;
18062
18217
  }
18063
18218
  setInput(name, value) {
18064
- const inputData = this._tNode.inputs;
18065
- let dataValue;
18066
- if (inputData !== null && (dataValue = inputData[name])) {
18067
- this.previousInputValues ??= new Map();
18068
- // Do not set the input if it is the same as the last value
18069
- // This behavior matches `bindingUpdated` when binding inputs in templates.
18070
- if (this.previousInputValues.has(name) &&
18071
- Object.is(this.previousInputValues.get(name), value)) {
18072
- return;
18073
- }
18074
- const lView = this._rootLView;
18075
- setInputsForProperty(lView[TVIEW], lView, dataValue, name, value);
18076
- this.previousInputValues.set(name, value);
18077
- const childComponentLView = getComponentLViewByIndex(this._tNode.index, lView);
18078
- markViewDirty(childComponentLView, 1 /* NotificationSource.SetInput */);
18219
+ const tNode = this._tNode;
18220
+ this.previousInputValues ??= new Map();
18221
+ // Do not set the input if it is the same as the last value
18222
+ // This behavior matches `bindingUpdated` when binding inputs in templates.
18223
+ if (this.previousInputValues.has(name) &&
18224
+ Object.is(this.previousInputValues.get(name), value)) {
18225
+ return;
18079
18226
  }
18080
- else {
18081
- if (ngDevMode) {
18082
- const cmpNameForError = stringifyForError(this.componentType);
18083
- let message = `Can't set value of the '${name}' input on the '${cmpNameForError}' component. `;
18084
- message += `Make sure that the '${name}' property is annotated with @Input() or a mapped @Input('${name}') exists.`;
18085
- reportUnknownPropertyError(message);
18086
- }
18227
+ const lView = this._rootLView;
18228
+ const hasSetInput = setAllInputsForProperty(tNode, lView[TVIEW], lView, name, value);
18229
+ this.previousInputValues.set(name, value);
18230
+ const childComponentLView = getComponentLViewByIndex(tNode.index, lView);
18231
+ markViewDirty(childComponentLView, 1 /* NotificationSource.SetInput */);
18232
+ if (ngDevMode && !hasSetInput) {
18233
+ const cmpNameForError = stringifyForError(this.componentType);
18234
+ let message = `Can't set value of the '${name}' input on the '${cmpNameForError}' component. `;
18235
+ message += `Make sure that the '${name}' property is annotated with @Input() or a mapped @Input('${name}') exists.`;
18236
+ reportUnknownPropertyError(message);
18087
18237
  }
18088
18238
  }
18089
18239
  get injector() {
@@ -20705,7 +20855,7 @@ function declareTemplate(declarationLView, declarationTView, index, templateFn,
20705
20855
  // In client-only mode, this function is a noop.
20706
20856
  populateDehydratedViewsInLContainer(lContainer, tNode, declarationLView);
20707
20857
  if (isDirectiveHost(tNode)) {
20708
- createDirectivesInstancesInInstruction(declarationTView, declarationLView, tNode);
20858
+ createDirectivesInstances(declarationTView, declarationLView, tNode);
20709
20859
  }
20710
20860
  if (localRefsIndex != null) {
20711
20861
  saveResolvedLocalsInData(declarationLView, tNode, localRefExtractor);
@@ -21240,8 +21390,9 @@ function renderDeferBlockState(newState, tNode, lContainer, skipTimerScheduling
21240
21390
  }
21241
21391
  }
21242
21392
  function findMatchingDehydratedViewForDeferBlock(lContainer, lDetails) {
21243
- // Find matching view based on serialized defer block state.
21244
- return (lContainer[DEHYDRATED_VIEWS]?.find((view) => view.data[DEFER_BLOCK_STATE$1] === lDetails[DEFER_BLOCK_STATE]) ?? null);
21393
+ const dehydratedViewIx = lContainer[DEHYDRATED_VIEWS]?.findIndex((view) => view.data[DEFER_BLOCK_STATE$1] === lDetails[DEFER_BLOCK_STATE]) ?? -1;
21394
+ const dehydratedView = dehydratedViewIx > -1 ? lContainer[DEHYDRATED_VIEWS][dehydratedViewIx] : null;
21395
+ return { dehydratedView, dehydratedViewIx };
21245
21396
  }
21246
21397
  /**
21247
21398
  * Applies changes to the DOM to reflect a given state.
@@ -21273,21 +21424,23 @@ function applyDeferBlockState(newState, lDetails, lContainer, tNode, hostLView)
21273
21424
  injector = createDeferBlockInjector(hostLView[INJECTOR], tDetails, providers);
21274
21425
  }
21275
21426
  }
21276
- const dehydratedView = findMatchingDehydratedViewForDeferBlock(lContainer, lDetails);
21277
- // Erase dehydrated view info, so that it's not removed later
21278
- // by post-hydration cleanup process.
21279
- lContainer[DEHYDRATED_VIEWS] = null;
21427
+ const { dehydratedView, dehydratedViewIx } = findMatchingDehydratedViewForDeferBlock(lContainer, lDetails);
21280
21428
  const embeddedLView = createAndRenderEmbeddedLView(hostLView, activeBlockTNode, null, {
21281
21429
  injector,
21282
21430
  dehydratedView,
21283
21431
  });
21284
21432
  addLViewToLContainer(lContainer, embeddedLView, viewIndex, shouldAddViewToDom(activeBlockTNode, dehydratedView));
21285
21433
  markViewDirty(embeddedLView, 2 /* NotificationSource.DeferBlockStateUpdate */);
21286
- // TODO(incremental-hydration):
21287
- // - what if we had some views in `lContainer[DEHYDRATED_VIEWS]`, but
21288
- // we didn't find a view that matches the expected state?
21289
- // - for example, handle a situation when a block was in the "completed" state
21290
- // on the server, but the loading failing on the client. How do we reconcile and cleanup?
21434
+ if (dehydratedViewIx > -1) {
21435
+ // Erase dehydrated view info in a given LContainer, so that the view is not
21436
+ // removed later by post-hydration cleanup process (which iterates over all
21437
+ // dehydrated views in component tree). This clears only the dehydrated view
21438
+ // that was found for this render, which in most cases will be the only view.
21439
+ // In the case that there was control flow that changed, there may be either
21440
+ // more than one or the views would not match up due to the server rendered
21441
+ // content being a different branch of the control flow.
21442
+ lContainer[DEHYDRATED_VIEWS]?.splice(dehydratedViewIx, 1);
21443
+ }
21291
21444
  if ((newState === DeferBlockState.Complete || newState === DeferBlockState.Error) &&
21292
21445
  Array.isArray(lDetails[ON_COMPLETE_FNS])) {
21293
21446
  for (const callback of lDetails[ON_COMPLETE_FNS]) {
@@ -24136,16 +24289,36 @@ async function triggerHydrationFromBlockName(injector, blockName, replayQueuedEv
24136
24289
  await parentBlockPromise;
24137
24290
  }
24138
24291
  // Actually do the triggering and hydration of the queue of blocks
24139
- for (const dehydratedBlockId of hydrationQueue) {
24140
- await triggerResourceLoadingForHydration(dehydratedBlockId, dehydratedBlockRegistry);
24141
- await nextRender(injector);
24142
- // TODO(incremental-hydration): assert (in dev mode) that a defer block is present in the dehydrated registry
24143
- // at this point. If not - it means that the block has not been hydrated, for example due to different
24144
- // `@if` conditions on the client and the server. If we detect this case, we should also do the cleanup
24145
- // of all child block (promises, registry state, etc).
24146
- // TODO(incremental-hydration): call `rejectFn` when lDetails[DEFER_BLOCK_STATE] is `DeferBlockState.Error`.
24147
- blocksBeingHydrated.get(dehydratedBlockId).resolve();
24148
- // TODO(incremental-hydration): consider adding a wait for stability here
24292
+ for (let blockQueueIdx = 0; blockQueueIdx < hydrationQueue.length; blockQueueIdx++) {
24293
+ const dehydratedBlockId = hydrationQueue[blockQueueIdx];
24294
+ const dehydratedDeferBlock = dehydratedBlockRegistry.get(dehydratedBlockId);
24295
+ if (dehydratedDeferBlock != null) {
24296
+ // trigger the block resources and await next render for hydration. This should result
24297
+ // in the next block ɵɵdefer instruction being called and that block being added to the dehydrated registry.
24298
+ await triggerResourceLoadingForHydration(dehydratedDeferBlock);
24299
+ await nextRender(injector);
24300
+ // if the content has changed since server rendering, we need to check for the expected block
24301
+ // being in the registry or if errors occurred. In that case, we need to clean up the remaining expected
24302
+ // content that won't be rendered or fetched.
24303
+ if (deferBlockHasErrored(dehydratedDeferBlock)) {
24304
+ // Either the expected block has not yet had its ɵɵdefer instruction called or the block errored out when fetching
24305
+ // resources. In the former case, either we're hydrating too soon or the client and server differ. In both cases,
24306
+ // we need to clean up child content and promises.
24307
+ removeDehydratedViewList(dehydratedDeferBlock);
24308
+ cleanupRemainingHydrationQueue(hydrationQueue.slice(blockQueueIdx), dehydratedBlockRegistry);
24309
+ break;
24310
+ }
24311
+ // The defer block has not errored and we've finished fetching resources and rendering.
24312
+ // At this point it is safe to resolve the hydration promise.
24313
+ blocksBeingHydrated.get(dehydratedBlockId).resolve();
24314
+ }
24315
+ else {
24316
+ // The expected block has not yet had its ɵɵdefer instruction called. This is likely due to content changing between
24317
+ // client and server. We need to clean up the dehydrated DOM in the container since it no longer is valid.
24318
+ cleanupParentContainer(blockQueueIdx, hydrationQueue, dehydratedBlockRegistry);
24319
+ cleanupRemainingHydrationQueue(hydrationQueue.slice(blockQueueIdx), dehydratedBlockRegistry);
24320
+ break;
24321
+ }
24149
24322
  }
24150
24323
  // Await hydration completion for the requested block.
24151
24324
  await blocksBeingHydrated.get(blockName)?.promise;
@@ -24158,6 +24331,33 @@ async function triggerHydrationFromBlockName(injector, blockName, replayQueuedEv
24158
24331
  // Cleanup after hydration of all affected defer blocks.
24159
24332
  cleanupHydratedDeferBlocks(dehydratedBlockRegistry.get(blockName), hydrationQueue, dehydratedBlockRegistry, injector.get(ApplicationRef));
24160
24333
  }
24334
+ function deferBlockHasErrored(deferBlock) {
24335
+ return (getLDeferBlockDetails(deferBlock.lView, deferBlock.tNode)[DEFER_BLOCK_STATE] ===
24336
+ DeferBlockState.Error);
24337
+ }
24338
+ /**
24339
+ * Clean up the parent container of a block where content changed between server and client.
24340
+ * The parent of a block going through `triggerHydrationFromBlockName` will contain the
24341
+ * dehydrated content that needs to be cleaned up. So we have to do the clean up from that location
24342
+ * in the tree.
24343
+ */
24344
+ function cleanupParentContainer(currentBlockIdx, hydrationQueue, dehydratedBlockRegistry) {
24345
+ // If a parent block exists, it's in the hydration queue in front of the current block.
24346
+ const parentDeferBlockIdx = currentBlockIdx - 1;
24347
+ const parentDeferBlock = parentDeferBlockIdx > -1
24348
+ ? dehydratedBlockRegistry.get(hydrationQueue[parentDeferBlockIdx])
24349
+ : null;
24350
+ if (parentDeferBlock) {
24351
+ cleanupLContainer(parentDeferBlock.lContainer);
24352
+ }
24353
+ }
24354
+ function cleanupRemainingHydrationQueue(hydrationQueue, dehydratedBlockRegistry) {
24355
+ const blocksBeingHydrated = dehydratedBlockRegistry.hydrating;
24356
+ for (const dehydratedBlockId in hydrationQueue) {
24357
+ blocksBeingHydrated.get(dehydratedBlockId)?.reject();
24358
+ }
24359
+ dehydratedBlockRegistry.cleanup(hydrationQueue);
24360
+ }
24161
24361
  /**
24162
24362
  * Generates a new promise for every defer block in the hydrating queue
24163
24363
  */
@@ -24170,18 +24370,8 @@ function populateHydratingStateForQueue(registry, queue) {
24170
24370
  function nextRender(injector) {
24171
24371
  return new Promise((resolveFn) => afterNextRender(resolveFn, { injector }));
24172
24372
  }
24173
- async function triggerResourceLoadingForHydration(dehydratedBlockId, dehydratedBlockRegistry) {
24174
- const deferBlock = dehydratedBlockRegistry.get(dehydratedBlockId);
24175
- // Since we trigger hydration for nested defer blocks in a sequence (parent -> child),
24176
- // there is a chance that a defer block may not be present at hydration time. For example,
24177
- // when a nested block was in an `@if` condition, which has changed.
24178
- if (deferBlock === null) {
24179
- // TODO(incremental-hydration): handle the cleanup for cases when
24180
- // defer block is no longer present during hydration (e.g. `@if` condition
24181
- // has changed during hydration/rendering).
24182
- return;
24183
- }
24184
- const { tNode, lView } = deferBlock;
24373
+ async function triggerResourceLoadingForHydration(dehydratedBlock) {
24374
+ const { tNode, lView } = dehydratedBlock;
24185
24375
  const lDetails = getLDeferBlockDetails(lView, tNode);
24186
24376
  return new Promise((resolve) => {
24187
24377
  onDeferBlockCompletion(lDetails, resolve);
@@ -26234,10 +26424,8 @@ function ɵɵproperty(propName, value, sanitizer) {
26234
26424
  * directive input.
26235
26425
  */
26236
26426
  function setDirectiveInputsWhichShadowsStyling(tView, tNode, lView, value, isClassBased) {
26237
- const inputs = tNode.inputs;
26238
- const property = isClassBased ? 'class' : 'style';
26239
26427
  // We support both 'class' and `className` hence the fallback.
26240
- setInputsForProperty(tView, lView, inputs[property], property, value);
26428
+ setAllInputsForProperty(tNode, tView, lView, isClassBased ? 'class' : 'style', value);
26241
26429
  }
26242
26430
 
26243
26431
  /**
@@ -28061,12 +28249,12 @@ function ɵɵelementStart(index, name, attrsIndex, localRefsIndex) {
28061
28249
  // any immediate children of a component or template container must be pre-emptively
28062
28250
  // monkey-patched with the component view data so that the element can be inspected
28063
28251
  // later on using any element discovery utility methods (see `element_discovery.ts`)
28064
- if (getElementDepthCount() === 0) {
28252
+ if (getElementDepthCount() === 0 || hasDirectives) {
28065
28253
  attachPatchData(native, lView);
28066
28254
  }
28067
28255
  increaseElementDepthCount();
28068
28256
  if (hasDirectives) {
28069
- createDirectivesInstancesInInstruction(tView, lView, tNode);
28257
+ createDirectivesInstances(tView, lView, tNode);
28070
28258
  executeContentQueries(tView, tNode, lView);
28071
28259
  }
28072
28260
  if (localRefsIndex !== null) {
@@ -28238,7 +28426,7 @@ function ɵɵelementContainerStart(index, attrsIndex, localRefsIndex) {
28238
28426
  }
28239
28427
  attachPatchData(comment, lView);
28240
28428
  if (isDirectiveHost(tNode)) {
28241
- createDirectivesInstancesInInstruction(tView, lView, tNode);
28429
+ createDirectivesInstances(tView, lView, tNode);
28242
28430
  executeContentQueries(tView, tNode, lView);
28243
28431
  }
28244
28432
  if (localRefsIndex != null) {
@@ -30293,7 +30481,7 @@ function findExistingListener(tView, lView, eventName, tNodeIdx) {
30293
30481
  function listenerInternal(tView, lView, renderer, tNode, eventName, listenerFn, eventTargetResolver) {
30294
30482
  const isTNodeDirectiveHost = isDirectiveHost(tNode);
30295
30483
  const firstCreatePass = tView.firstCreatePass;
30296
- const tCleanup = firstCreatePass && getOrCreateTViewCleanup(tView);
30484
+ const tCleanup = firstCreatePass ? getOrCreateTViewCleanup(tView) : null;
30297
30485
  const context = lView[CONTEXT];
30298
30486
  // When the ɵɵlistener instruction was generated and is executed we know that there is either a
30299
30487
  // native listener or a directive output on this element. As such we we know that we will have to
@@ -30358,29 +30546,37 @@ function listenerInternal(tView, lView, renderer, tNode, eventName, listenerFn,
30358
30546
  // ancestors are marked dirty when an event occurs.
30359
30547
  listenerFn = wrapListener(tNode, lView, context, listenerFn);
30360
30548
  }
30361
- // subscribe to directive outputs
30362
- const outputs = tNode.outputs;
30363
- let props;
30364
- if (processOutputs && outputs !== null && (props = outputs[eventName])) {
30365
- const propsLength = props.length;
30366
- if (propsLength) {
30367
- for (let i = 0; i < propsLength; i += 2) {
30368
- const index = props[i];
30369
- ngDevMode && assertIndexInRange(lView, index);
30370
- const minifiedName = props[i + 1];
30371
- const directiveInstance = lView[index];
30372
- const output = directiveInstance[minifiedName];
30373
- if (ngDevMode && !isOutputSubscribable(output)) {
30374
- throw new Error(`@Output ${minifiedName} not initialized in '${directiveInstance.constructor.name}'.`);
30375
- }
30376
- const subscription = output.subscribe(listenerFn);
30377
- const idx = lCleanup.length;
30378
- lCleanup.push(listenerFn, subscription);
30379
- tCleanup && tCleanup.push(eventName, tNode.index, idx, -(idx + 1));
30549
+ if (processOutputs) {
30550
+ const outputConfig = tNode.outputs?.[eventName];
30551
+ const hostDirectiveOutputConfig = tNode.hostDirectiveOutputs?.[eventName];
30552
+ if (hostDirectiveOutputConfig && hostDirectiveOutputConfig.length) {
30553
+ for (let i = 0; i < hostDirectiveOutputConfig.length; i += 2) {
30554
+ const index = hostDirectiveOutputConfig[i];
30555
+ const lookupName = hostDirectiveOutputConfig[i + 1];
30556
+ listenToOutput(tNode, tView, lView, index, lookupName, eventName, listenerFn, lCleanup, tCleanup);
30557
+ }
30558
+ }
30559
+ if (outputConfig && outputConfig.length) {
30560
+ for (const index of outputConfig) {
30561
+ listenToOutput(tNode, tView, lView, index, eventName, eventName, listenerFn, lCleanup, tCleanup);
30380
30562
  }
30381
30563
  }
30382
30564
  }
30383
30565
  }
30566
+ function listenToOutput(tNode, tView, lView, index, lookupName, eventName, listenerFn, lCleanup, tCleanup) {
30567
+ ngDevMode && assertIndexInRange(lView, index);
30568
+ const instance = lView[index];
30569
+ const def = tView.data[index];
30570
+ const propertyName = def.outputs[lookupName];
30571
+ const output = instance[propertyName];
30572
+ if (ngDevMode && !isOutputSubscribable(output)) {
30573
+ throw new Error(`@Output ${propertyName} not initialized in '${instance.constructor.name}'.`);
30574
+ }
30575
+ const subscription = output.subscribe(listenerFn);
30576
+ const idx = lCleanup.length;
30577
+ lCleanup.push(listenerFn, subscription);
30578
+ tCleanup && tCleanup.push(eventName, tNode.index, idx, -(idx + 1));
30579
+ }
30384
30580
  function executeListenerWithErrorHandling(lView, context, listenerFn, e) {
30385
30581
  const prevConsumer = setActiveConsumer$1(null);
30386
30582
  try {
@@ -33544,11 +33740,12 @@ function executeWithInvalidateFallback(importMeta, id, callback) {
33544
33740
  callback();
33545
33741
  }
33546
33742
  catch (e) {
33547
- const errorMessage = e.message;
33743
+ const error = e;
33548
33744
  // If we have all the necessary information and APIs to send off the invalidation
33549
33745
  // request, send it before rethrowing so the dev server can decide what to do.
33550
- if (id !== null && errorMessage) {
33551
- importMeta?.hot?.send?.('angular:invalidate', { id, message: errorMessage, error: true });
33746
+ if (id !== null && error.message) {
33747
+ const toLog = error.message + (error.stack ? '\n' + error.stack : '');
33748
+ importMeta?.hot?.send?.('angular:invalidate', { id, message: toLog, error: true });
33552
33749
  }
33553
33750
  // Throw the error in case the page doesn't get refreshed.
33554
33751
  throw e;
@@ -34978,7 +35175,7 @@ class Version {
34978
35175
  /**
34979
35176
  * @publicApi
34980
35177
  */
34981
- const VERSION = new Version('19.2.0-rc.0');
35178
+ const VERSION = new Version('19.2.0');
34982
35179
 
34983
35180
  /**
34984
35181
  * Combination of NgModuleFactory and ComponentFactories.
@@ -35812,10 +36009,9 @@ class ImagePerformanceWarning {
35812
36009
  window = null;
35813
36010
  observer = null;
35814
36011
  options = inject(IMAGE_CONFIG);
35815
- isBrowser = inject(PLATFORM_ID) === 'browser';
35816
36012
  lcpImageUrl;
35817
36013
  start() {
35818
- if (!this.isBrowser ||
36014
+ if ((typeof ngServerMode !== 'undefined' && ngServerMode) ||
35819
36015
  typeof PerformanceObserver === 'undefined' ||
35820
36016
  (this.options?.disableImageSizeWarning && this.options?.disableImageLazyLoadWarning)) {
35821
36017
  return;
@@ -35823,7 +36019,7 @@ class ImagePerformanceWarning {
35823
36019
  this.observer = this.initPerformanceObserver();
35824
36020
  const doc = getDocument();
35825
36021
  const win = doc.defaultView;
35826
- if (typeof win !== 'undefined') {
36022
+ if (win) {
35827
36023
  this.window = win;
35828
36024
  // Wait to avoid race conditions where LCP image triggers
35829
36025
  // load event before it's recorded by the performance observer
@@ -40428,7 +40624,6 @@ function untracked(nonReactiveReadsFn) {
40428
40624
  * Create a computed `Signal` which derives a reactive value from an expression.
40429
40625
  */
40430
40626
  function computed(computation, options) {
40431
- performanceMarkFeature('NgSignals');
40432
40627
  const getter = createComputed$1(computation);
40433
40628
  if (options?.equal) {
40434
40629
  getter[SIGNAL$1].equal = options.equal;
@@ -40526,7 +40721,6 @@ function effect$1() { }
40526
40721
  * Create a global `Effect` for the given reactive function.
40527
40722
  */
40528
40723
  function microtaskEffect(effectFn, options) {
40529
- performanceMarkFeature('NgSignals');
40530
40724
  ngDevMode &&
40531
40725
  assertNotInReactiveContext(effect$1, 'Call `effect` outside of a reactive context. For example, schedule the ' +
40532
40726
  'effect inside the component constructor.');
@@ -40599,7 +40793,6 @@ function effect(effectFn, options) {
40599
40793
  }
40600
40794
  return microtaskEffect(effectFn, options);
40601
40795
  }
40602
- performanceMarkFeature('NgSignals');
40603
40796
  ngDevMode &&
40604
40797
  assertNotInReactiveContext(effect, 'Call `effect` outside of a reactive context. For example, schedule the ' +
40605
40798
  'effect inside the component constructor.');
@@ -40796,7 +40989,6 @@ var ResourceStatus;
40796
40989
 
40797
40990
  const identityFn = (v) => v;
40798
40991
  function linkedSignal(optionsOrComputation, options) {
40799
- performanceMarkFeature('NgSignals');
40800
40992
  if (typeof optionsOrComputation === 'function') {
40801
40993
  const getter = createLinkedSignal$1(optionsOrComputation, (identityFn), options?.equal);
40802
40994
  return upgradeLinkedSignalGetter(getter);