@angular/core 16.0.0-next.1 → 16.0.0-next.2

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 (54) hide show
  1. package/esm2020/src/application_config.mjs +21 -0
  2. package/esm2020/src/application_init.mjs +21 -30
  3. package/esm2020/src/application_ref.mjs +31 -31
  4. package/esm2020/src/application_tokens.mjs +2 -13
  5. package/esm2020/src/change_detection/change_detection.mjs +2 -2
  6. package/esm2020/src/change_detection/constants.mjs +1 -49
  7. package/esm2020/src/core.mjs +4 -3
  8. package/esm2020/src/core_private_export.mjs +5 -7
  9. package/esm2020/src/core_render3_private_export.mjs +2 -1
  10. package/esm2020/src/debug/debug_node.mjs +1 -5
  11. package/esm2020/src/hydration/annotate.mjs +140 -0
  12. package/esm2020/src/hydration/api.mjs +120 -0
  13. package/esm2020/src/hydration/error_handling.mjs +29 -0
  14. package/esm2020/src/hydration/interfaces.mjs +10 -0
  15. package/esm2020/src/hydration/node_lookup_utils.mjs +75 -0
  16. package/esm2020/src/hydration/skip_hydration.mjs +34 -0
  17. package/esm2020/src/hydration/tokens.mjs +25 -0
  18. package/esm2020/src/hydration/utils.mjs +131 -0
  19. package/esm2020/src/linker/template_ref.mjs +4 -4
  20. package/esm2020/src/render3/component_ref.mjs +16 -9
  21. package/esm2020/src/render3/i18n/i18n_util.mjs +3 -3
  22. package/esm2020/src/render3/instructions/element.mjs +48 -6
  23. package/esm2020/src/render3/instructions/element_container.mjs +54 -9
  24. package/esm2020/src/render3/instructions/listener.mjs +3 -3
  25. package/esm2020/src/render3/instructions/shared.mjs +25 -8
  26. package/esm2020/src/render3/instructions/template.mjs +2 -2
  27. package/esm2020/src/render3/instructions/text.mjs +36 -5
  28. package/esm2020/src/render3/interfaces/node.mjs +1 -1
  29. package/esm2020/src/render3/interfaces/renderer_dom.mjs +1 -1
  30. package/esm2020/src/render3/interfaces/view.mjs +3 -2
  31. package/esm2020/src/render3/jit/directive.mjs +1 -2
  32. package/esm2020/src/render3/node_manipulation.mjs +12 -1
  33. package/esm2020/src/render3/state.mjs +45 -1
  34. package/esm2020/src/transfer_state.mjs +15 -10
  35. package/esm2020/src/util/lang.mjs +1 -11
  36. package/esm2020/src/util/ng_dev_mode.mjs +3 -1
  37. package/esm2020/src/version.mjs +1 -1
  38. package/esm2020/testing/src/logger.mjs +3 -3
  39. package/esm2020/testing/src/ng_zone_mock.mjs +3 -3
  40. package/fesm2015/core.mjs +1127 -517
  41. package/fesm2015/core.mjs.map +1 -1
  42. package/fesm2015/testing.mjs +677 -90
  43. package/fesm2015/testing.mjs.map +1 -1
  44. package/fesm2020/core.mjs +1119 -515
  45. package/fesm2020/core.mjs.map +1 -1
  46. package/fesm2020/testing.mjs +671 -90
  47. package/fesm2020/testing.mjs.map +1 -1
  48. package/index.d.ts +174 -92
  49. package/package.json +2 -2
  50. package/schematics/migrations/relative-link-resolution/bundle.js +7 -7
  51. package/schematics/migrations/router-link-with-href/bundle.js +10 -10
  52. package/schematics/ng-generate/standalone-migration/bundle.js +693 -668
  53. package/schematics/ng-generate/standalone-migration/bundle.js.map +4 -4
  54. package/testing/index.d.ts +1 -1
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Angular v16.0.0-next.1
2
+ * @license Angular v16.0.0-next.2
3
3
  * (c) 2010-2022 Google LLC. https://angular.io/
4
4
  * License: MIT
5
5
  */
@@ -1635,6 +1635,8 @@ function ngDevModeResetPerfCounters() {
1635
1635
  rendererAppendChild: 0,
1636
1636
  rendererInsertBefore: 0,
1637
1637
  rendererCreateComment: 0,
1638
+ hydratedNodes: 0,
1639
+ hydratedComponents: 0,
1638
1640
  };
1639
1641
  // Make sure to refer to ngDevMode as ['ngDevMode'] for closure.
1640
1642
  const allowNgDevModeTrue = locationString.indexOf('ngDevMode=false') === -1;
@@ -2114,54 +2116,6 @@ var ChangeDetectionStrategy;
2114
2116
  */
2115
2117
  ChangeDetectionStrategy[ChangeDetectionStrategy["Default"] = 1] = "Default";
2116
2118
  })(ChangeDetectionStrategy || (ChangeDetectionStrategy = {}));
2117
- /**
2118
- * Defines the possible states of the default change detector.
2119
- * @see `ChangeDetectorRef`
2120
- */
2121
- var ChangeDetectorStatus;
2122
- (function (ChangeDetectorStatus) {
2123
- /**
2124
- * A state in which, after calling `detectChanges()`, the change detector
2125
- * state becomes `Checked`, and must be explicitly invoked or reactivated.
2126
- */
2127
- ChangeDetectorStatus[ChangeDetectorStatus["CheckOnce"] = 0] = "CheckOnce";
2128
- /**
2129
- * A state in which change detection is skipped until the change detector mode
2130
- * becomes `CheckOnce`.
2131
- */
2132
- ChangeDetectorStatus[ChangeDetectorStatus["Checked"] = 1] = "Checked";
2133
- /**
2134
- * A state in which change detection continues automatically until explicitly
2135
- * deactivated.
2136
- */
2137
- ChangeDetectorStatus[ChangeDetectorStatus["CheckAlways"] = 2] = "CheckAlways";
2138
- /**
2139
- * A state in which a change detector sub tree is not a part of the main tree and
2140
- * should be skipped.
2141
- */
2142
- ChangeDetectorStatus[ChangeDetectorStatus["Detached"] = 3] = "Detached";
2143
- /**
2144
- * Indicates that the change detector encountered an error checking a binding
2145
- * or calling a directive lifecycle method and is now in an inconsistent state. Change
2146
- * detectors in this state do not detect changes.
2147
- */
2148
- ChangeDetectorStatus[ChangeDetectorStatus["Errored"] = 4] = "Errored";
2149
- /**
2150
- * Indicates that the change detector has been destroyed.
2151
- */
2152
- ChangeDetectorStatus[ChangeDetectorStatus["Destroyed"] = 5] = "Destroyed";
2153
- })(ChangeDetectorStatus || (ChangeDetectorStatus = {}));
2154
- /**
2155
- * Reports whether a given strategy is currently the default for change detection.
2156
- * @param changeDetectionStrategy The strategy to check.
2157
- * @returns True if the given strategy is the current default, false otherwise.
2158
- * @see `ChangeDetectorStatus`
2159
- * @see `ChangeDetectorRef`
2160
- */
2161
- function isDefaultChangeDetectionStrategy(changeDetectionStrategy) {
2162
- return changeDetectionStrategy == null ||
2163
- changeDetectionStrategy === ChangeDetectionStrategy.Default;
2164
- }
2165
2119
 
2166
2120
  /**
2167
2121
  * Defines the CSS styles encapsulation policies for the {@link Component} decorator's
@@ -2559,6 +2513,7 @@ const QUERIES = 19;
2559
2513
  const ID = 20;
2560
2514
  const EMBEDDED_VIEW_INJECTOR = 21;
2561
2515
  const ON_DESTROY_HOOKS = 22;
2516
+ const HYDRATION = 23;
2562
2517
  /**
2563
2518
  * Size of LView's header. Necessary to adjust for it when setting slots.
2564
2519
  *
@@ -2566,7 +2521,7 @@ const ON_DESTROY_HOOKS = 22;
2566
2521
  * instruction index into `LView` index. All other indexes should be in the `LView` index space and
2567
2522
  * there should be no need to refer to `HEADER_OFFSET` anywhere else.
2568
2523
  */
2569
- const HEADER_OFFSET = 23;
2524
+ const HEADER_OFFSET = 24;
2570
2525
  // Note: This hack is necessary so we don't erroneously get a circular dependency
2571
2526
  // failure based on types.
2572
2527
  const unusedValueExportToPlacateAjd$4 = 1;
@@ -3055,6 +3010,7 @@ function storeLViewOnDestroy(lView, onDestroyCallback) {
3055
3010
  const instructionState = {
3056
3011
  lFrame: createLFrame(null),
3057
3012
  bindingsEnabled: true,
3013
+ skipHydrationRootTNode: null,
3058
3014
  };
3059
3015
  /**
3060
3016
  * In this mode, any changes in bindings will throw an ExpressionChangedAfterChecked error.
@@ -3085,6 +3041,21 @@ function decreaseElementDepthCount() {
3085
3041
  function getBindingsEnabled() {
3086
3042
  return instructionState.bindingsEnabled;
3087
3043
  }
3044
+ /**
3045
+ * Returns true if currently inside a skip hydration block.
3046
+ * @returns boolean
3047
+ */
3048
+ function isInSkipHydrationBlock() {
3049
+ return instructionState.skipHydrationRootTNode !== null;
3050
+ }
3051
+ /**
3052
+ * Returns true if this is the root TNode of the skip hydration block.
3053
+ * @param tNode the current TNode
3054
+ * @returns boolean
3055
+ */
3056
+ function isSkipHydrationRootTNode(tNode) {
3057
+ return instructionState.skipHydrationRootTNode === tNode;
3058
+ }
3088
3059
  /**
3089
3060
  * Enables directive matching on elements.
3090
3061
  *
@@ -3107,6 +3078,13 @@ function getBindingsEnabled() {
3107
3078
  function ɵɵenableBindings() {
3108
3079
  instructionState.bindingsEnabled = true;
3109
3080
  }
3081
+ /**
3082
+ * Sets a flag to specify that the TNode is in a skip hydration block.
3083
+ * @param tNode the current TNode
3084
+ */
3085
+ function enterSkipHydrationBlock(tNode) {
3086
+ instructionState.skipHydrationRootTNode = tNode;
3087
+ }
3110
3088
  /**
3111
3089
  * Disables directive matching on element.
3112
3090
  *
@@ -3129,6 +3107,12 @@ function ɵɵenableBindings() {
3129
3107
  function ɵɵdisableBindings() {
3130
3108
  instructionState.bindingsEnabled = false;
3131
3109
  }
3110
+ /**
3111
+ * Clears the root skip hydration node when leaving a skip hydration block.
3112
+ */
3113
+ function leaveSkipHydrationBlock() {
3114
+ instructionState.skipHydrationRootTNode = null;
3115
+ }
3132
3116
  /**
3133
3117
  * Return the current `LView`.
3134
3118
  */
@@ -3553,6 +3537,21 @@ function namespaceHTMLInternal() {
3553
3537
  function getNamespace$1() {
3554
3538
  return instructionState.lFrame.currentNamespace;
3555
3539
  }
3540
+ let _wasLastNodeCreated = true;
3541
+ /**
3542
+ * Retrieves a global flag that indicates whether the most recent DOM node
3543
+ * was created or hydrated.
3544
+ */
3545
+ function wasLastNodeCreated() {
3546
+ return _wasLastNodeCreated;
3547
+ }
3548
+ /**
3549
+ * Sets a global flag to indicate whether the most recent DOM node
3550
+ * was created or hydrated.
3551
+ */
3552
+ function lastNodeWasCreated(flag) {
3553
+ _wasLastNodeCreated = flag;
3554
+ }
3556
3555
 
3557
3556
  /**
3558
3557
  * Adds all directive lifecycle hooks from the given `DirectiveDef` to the given `TView`.
@@ -6607,6 +6606,17 @@ function nativeRemoveNode(renderer, rNode, isHostElement) {
6607
6606
  nativeRemoveChild(renderer, nativeParent, rNode, isHostElement);
6608
6607
  }
6609
6608
  }
6609
+ /**
6610
+ * Removes the contents of a given RElement using a given renderer.
6611
+ *
6612
+ * @param renderer A renderer to be used
6613
+ * @param rElement the native RElement to be cleared
6614
+ */
6615
+ function clearElementContents(renderer, rElement) {
6616
+ while (rElement.firstChild) {
6617
+ nativeRemoveChild(renderer, rElement, rElement.firstChild, false);
6618
+ }
6619
+ }
6610
6620
  /**
6611
6621
  * Performs the operation of `action` on the node. Typically this involves inserting or removing
6612
6622
  * nodes on the LView or projection boundary.
@@ -8586,6 +8596,328 @@ function forEachSingleProvider(providers, fn) {
8586
8596
  }
8587
8597
  }
8588
8598
 
8599
+ /**
8600
+ * A [DI token](guide/glossary#di-token "DI token definition") representing a unique string ID, used
8601
+ * primarily for prefixing application attributes and CSS styles when
8602
+ * {@link ViewEncapsulation#Emulated ViewEncapsulation.Emulated} is being used.
8603
+ *
8604
+ * BY default, the value is randomly generated and assigned to the application by Angular.
8605
+ * To provide a custom ID value, use a DI provider <!-- TODO: provider --> to configure
8606
+ * the root {@link Injector} that uses this token.
8607
+ *
8608
+ * @publicApi
8609
+ */
8610
+ const APP_ID = new InjectionToken('AppId', {
8611
+ providedIn: 'root',
8612
+ factory: _appIdRandomProviderFactory,
8613
+ });
8614
+ function _appIdRandomProviderFactory() {
8615
+ return `${_randomChar()}${_randomChar()}${_randomChar()}`;
8616
+ }
8617
+ /**
8618
+ * Providers that generate a random `APP_ID_TOKEN`.
8619
+ * @publicApi
8620
+ */
8621
+ const APP_ID_RANDOM_PROVIDER = {
8622
+ provide: APP_ID,
8623
+ useFactory: _appIdRandomProviderFactory,
8624
+ deps: [],
8625
+ };
8626
+ function _randomChar() {
8627
+ return String.fromCharCode(97 + Math.floor(Math.random() * 25));
8628
+ }
8629
+ /**
8630
+ * A function that is executed when a platform is initialized.
8631
+ * @publicApi
8632
+ */
8633
+ const PLATFORM_INITIALIZER = new InjectionToken('Platform Initializer');
8634
+ /**
8635
+ * A token that indicates an opaque platform ID.
8636
+ * @publicApi
8637
+ */
8638
+ const PLATFORM_ID = new InjectionToken('Platform ID', {
8639
+ providedIn: 'platform',
8640
+ factory: () => 'unknown', // set a default platform name, when none set explicitly
8641
+ });
8642
+ /**
8643
+ * A [DI token](guide/glossary#di-token "DI token definition") that indicates the root directory of
8644
+ * the application
8645
+ * @publicApi
8646
+ */
8647
+ const PACKAGE_ROOT_URL = new InjectionToken('Application Packages Root URL');
8648
+ // We keep this token here, rather than the animations package, so that modules that only care
8649
+ // about which animations module is loaded (e.g. the CDK) can retrieve it without having to
8650
+ // include extra dependencies. See #44970 for more context.
8651
+ /**
8652
+ * A [DI token](guide/glossary#di-token "DI token definition") that indicates which animations
8653
+ * module has been loaded.
8654
+ * @publicApi
8655
+ */
8656
+ const ANIMATION_MODULE_TYPE = new InjectionToken('AnimationModuleType');
8657
+
8658
+ function escapeTransferStateContent(text) {
8659
+ const escapedText = {
8660
+ '&': '&a;',
8661
+ '"': '&q;',
8662
+ '\'': '&s;',
8663
+ '<': '&l;',
8664
+ '>': '&g;',
8665
+ };
8666
+ return text.replace(/[&"'<>]/g, s => escapedText[s]);
8667
+ }
8668
+ function unescapeTransferStateContent(text) {
8669
+ const unescapedText = {
8670
+ '&a;': '&',
8671
+ '&q;': '"',
8672
+ '&s;': '\'',
8673
+ '&l;': '<',
8674
+ '&g;': '>',
8675
+ };
8676
+ return text.replace(/&[^;]+;/g, s => unescapedText[s]);
8677
+ }
8678
+ /**
8679
+ * Create a `StateKey<T>` that can be used to store value of type T with `TransferState`.
8680
+ *
8681
+ * Example:
8682
+ *
8683
+ * ```
8684
+ * const COUNTER_KEY = makeStateKey<number>('counter');
8685
+ * let value = 10;
8686
+ *
8687
+ * transferState.set(COUNTER_KEY, value);
8688
+ * ```
8689
+ *
8690
+ * @publicApi
8691
+ */
8692
+ function makeStateKey(key) {
8693
+ return key;
8694
+ }
8695
+ function initTransferState() {
8696
+ const transferState = new TransferState();
8697
+ transferState.store = retrieveTransferredState(getDocument(), inject$1(APP_ID));
8698
+ return transferState;
8699
+ }
8700
+ /**
8701
+ * A key value store that is transferred from the application on the server side to the application
8702
+ * on the client side.
8703
+ *
8704
+ * The `TransferState` is available as an injectable token.
8705
+ * On the client, just inject this token using DI and use it, it will be lazily initialized.
8706
+ * On the server it's already included if `renderApplication` function is used. Otherwise, import
8707
+ * the `ServerTransferStateModule` module to make the `TransferState` available.
8708
+ *
8709
+ * The values in the store are serialized/deserialized using JSON.stringify/JSON.parse. So only
8710
+ * boolean, number, string, null and non-class objects will be serialized and deserialized in a
8711
+ * non-lossy manner.
8712
+ *
8713
+ * @publicApi
8714
+ */
8715
+ class TransferState {
8716
+ constructor() {
8717
+ /** @internal */
8718
+ this.store = {};
8719
+ this.onSerializeCallbacks = {};
8720
+ }
8721
+ /**
8722
+ * Get the value corresponding to a key. Return `defaultValue` if key is not found.
8723
+ */
8724
+ get(key, defaultValue) {
8725
+ return this.store[key] !== undefined ? this.store[key] : defaultValue;
8726
+ }
8727
+ /**
8728
+ * Set the value corresponding to a key.
8729
+ */
8730
+ set(key, value) {
8731
+ this.store[key] = value;
8732
+ }
8733
+ /**
8734
+ * Remove a key from the store.
8735
+ */
8736
+ remove(key) {
8737
+ delete this.store[key];
8738
+ }
8739
+ /**
8740
+ * Test whether a key exists in the store.
8741
+ */
8742
+ hasKey(key) {
8743
+ return this.store.hasOwnProperty(key);
8744
+ }
8745
+ /**
8746
+ * Indicates whether the state is empty.
8747
+ */
8748
+ get isEmpty() {
8749
+ return Object.keys(this.store).length === 0;
8750
+ }
8751
+ /**
8752
+ * Register a callback to provide the value for a key when `toJson` is called.
8753
+ */
8754
+ onSerialize(key, callback) {
8755
+ this.onSerializeCallbacks[key] = callback;
8756
+ }
8757
+ /**
8758
+ * Serialize the current state of the store to JSON.
8759
+ */
8760
+ toJson() {
8761
+ // Call the onSerialize callbacks and put those values into the store.
8762
+ for (const key in this.onSerializeCallbacks) {
8763
+ if (this.onSerializeCallbacks.hasOwnProperty(key)) {
8764
+ try {
8765
+ this.store[key] = this.onSerializeCallbacks[key]();
8766
+ }
8767
+ catch (e) {
8768
+ console.warn('Exception in onSerialize callback: ', e);
8769
+ }
8770
+ }
8771
+ }
8772
+ return JSON.stringify(this.store);
8773
+ }
8774
+ }
8775
+ /** @nocollapse */
8776
+ TransferState.ɵprov =
8777
+ /** @pureOrBreakMyCode */ ɵɵdefineInjectable({
8778
+ token: TransferState,
8779
+ providedIn: 'root',
8780
+ factory: initTransferState,
8781
+ });
8782
+ function retrieveTransferredState(doc, appId) {
8783
+ // Locate the script tag with the JSON data transferred from the server.
8784
+ // The id of the script tag is set to the Angular appId + 'state'.
8785
+ const script = doc.getElementById(appId + '-state');
8786
+ let initialState = {};
8787
+ if (script && script.textContent) {
8788
+ try {
8789
+ // Avoid using any here as it triggers lint errors in google3 (any is not allowed).
8790
+ initialState = JSON.parse(unescapeTransferStateContent(script.textContent));
8791
+ }
8792
+ catch (e) {
8793
+ console.warn('Exception while restoring TransferState for app ' + appId, e);
8794
+ }
8795
+ }
8796
+ return initialState;
8797
+ }
8798
+
8799
+ /* Represents a key in NghDom that holds information about <ng-container>s. */
8800
+ const ELEMENT_CONTAINERS = 'e';
8801
+
8802
+ /**
8803
+ * The name of the key used in the TransferState collection,
8804
+ * where hydration information is located.
8805
+ */
8806
+ const TRANSFER_STATE_TOKEN_ID = '__ɵnghData__';
8807
+ /**
8808
+ * Lookup key used to reference DOM hydration data (ngh) in `TransferState`.
8809
+ */
8810
+ const NGH_DATA_KEY = makeStateKey(TRANSFER_STATE_TOKEN_ID);
8811
+ /**
8812
+ * The name of the attribute that would be added to host component
8813
+ * nodes and contain a reference to a particular slot in transferred
8814
+ * state that contains the necessary hydration info for this component.
8815
+ */
8816
+ const NGH_ATTR_NAME = 'ngh';
8817
+ /**
8818
+ * Reference to a function that reads `ngh` attribute value from a given RNode
8819
+ * and retrieves hydration information from the TransferState using that value
8820
+ * as an index. Returns `null` by default, when hydration is not enabled.
8821
+ *
8822
+ * @param rNode Component's host element.
8823
+ * @param injector Injector that this component has access to.
8824
+ */
8825
+ let _retrieveHydrationInfoImpl = (rNode, injector) => null;
8826
+ function retrieveHydrationInfoImpl(rNode, injector) {
8827
+ const nghAttrValue = rNode.getAttribute(NGH_ATTR_NAME);
8828
+ if (nghAttrValue == null)
8829
+ return null;
8830
+ let data = {};
8831
+ // An element might have an empty `ngh` attribute value (e.g. `<comp ngh="" />`),
8832
+ // which means that no special annotations are required. Do not attempt to read
8833
+ // from the TransferState in this case.
8834
+ if (nghAttrValue !== '') {
8835
+ const transferState = injector.get(TransferState, null, { optional: true });
8836
+ if (transferState !== null) {
8837
+ const nghData = transferState.get(NGH_DATA_KEY, []);
8838
+ // The nghAttrValue is always a number referencing an index
8839
+ // in the hydration TransferState data.
8840
+ data = nghData[Number(nghAttrValue)];
8841
+ // If the `ngh` attribute exists and has a non-empty value,
8842
+ // the hydration info *must* be present in the TransferState.
8843
+ // If there is no data for some reasons, this is an error.
8844
+ ngDevMode && assertDefined(data, 'Unable to retrieve hydration info from the TransferState.');
8845
+ }
8846
+ }
8847
+ const dehydratedView = {
8848
+ data,
8849
+ firstChild: rNode.firstChild ?? null,
8850
+ };
8851
+ // The `ngh` attribute is cleared from the DOM node now
8852
+ // that the data has been retrieved.
8853
+ rNode.removeAttribute(NGH_ATTR_NAME);
8854
+ // Note: don't check whether this node was claimed for hydration,
8855
+ // because this node might've been previously claimed while processing
8856
+ // template instructions.
8857
+ ngDevMode && markRNodeAsClaimedByHydration(rNode, /* checkIfAlreadyClaimed */ false);
8858
+ ngDevMode && ngDevMode.hydratedComponents++;
8859
+ return dehydratedView;
8860
+ }
8861
+ /**
8862
+ * Sets the implementation for the `retrieveNghInfo` function.
8863
+ */
8864
+ function enableRetrieveHydrationInfoImpl() {
8865
+ _retrieveHydrationInfoImpl = retrieveHydrationInfoImpl;
8866
+ }
8867
+ /**
8868
+ * Retrieves hydration info by reading the value from the `ngh` attribute
8869
+ * and accessing a corresponding slot in TransferState storage.
8870
+ */
8871
+ function retrieveHydrationInfo(rNode, injector) {
8872
+ return _retrieveHydrationInfoImpl(rNode, injector);
8873
+ }
8874
+ /**
8875
+ * Retrieves an instance of a component LView from a given ViewRef.
8876
+ * Returns an instance of a component LView or `null` in case of an embedded view.
8877
+ */
8878
+ function getComponentLViewForHydration(viewRef) {
8879
+ // Reading an internal field from `ViewRef` instance.
8880
+ let lView = viewRef._lView;
8881
+ const tView = lView[TVIEW];
8882
+ // A registered ViewRef might represent an instance of an
8883
+ // embedded view, in which case we do not need to annotate it.
8884
+ if (tView.type === 2 /* TViewType.Embedded */) {
8885
+ return null;
8886
+ }
8887
+ // Check if it's a root view and if so, retrieve component's
8888
+ // LView from the first slot after the header.
8889
+ if (isRootView(lView)) {
8890
+ lView = lView[HEADER_OFFSET];
8891
+ }
8892
+ return lView;
8893
+ }
8894
+ /**
8895
+ * Marks a node as "claimed" by hydration process.
8896
+ * This is needed to make assessments in tests whether
8897
+ * the hydration process handled all nodes.
8898
+ */
8899
+ function markRNodeAsClaimedByHydration(node, checkIfAlreadyClaimed = true) {
8900
+ if (!ngDevMode) {
8901
+ throw new Error('Calling `markRNodeAsClaimedByHydration` in prod mode ' +
8902
+ 'is not supported and likely a mistake.');
8903
+ }
8904
+ if (checkIfAlreadyClaimed && isRNodeClaimedForHydration(node)) {
8905
+ throw new Error('Trying to claim a node, which was claimed already.');
8906
+ }
8907
+ node.__claimed = true;
8908
+ ngDevMode.hydratedNodes++;
8909
+ }
8910
+ function isRNodeClaimedForHydration(node) {
8911
+ return !!node.__claimed;
8912
+ }
8913
+ function storeNgContainerInfo(hydrationInfo, index, firstChild) {
8914
+ hydrationInfo.ngContainers ?? (hydrationInfo.ngContainers = {});
8915
+ hydrationInfo.ngContainers[index] = { firstChild };
8916
+ }
8917
+ function getNgContainerSize(hydrationInfo, index) {
8918
+ return hydrationInfo.data[ELEMENT_CONTAINERS]?.[index] ?? null;
8919
+ }
8920
+
8589
8921
  /**
8590
8922
  * Represents a component created by a `ComponentFactory`.
8591
8923
  * Provides access to the component instance and related objects,
@@ -8765,7 +9097,7 @@ class Version {
8765
9097
  /**
8766
9098
  * @publicApi
8767
9099
  */
8768
- const VERSION = new Version('16.0.0-next.1');
9100
+ const VERSION = new Version('16.0.0-next.2');
8769
9101
 
8770
9102
  // This default value is when checking the hierarchy for a token.
8771
9103
  //
@@ -8846,6 +9178,23 @@ class ErrorHandler {
8846
9178
  }
8847
9179
  }
8848
9180
 
9181
+ const NG_DEV_MODE = typeof ngDevMode === 'undefined' || !!ngDevMode;
9182
+ /**
9183
+ * Internal token that specifies whether hydration is enabled.
9184
+ */
9185
+ const IS_HYDRATION_FEATURE_ENABLED = new InjectionToken(NG_DEV_MODE ? 'IS_HYDRATION_FEATURE_ENABLED' : '');
9186
+ // By default (in client rendering mode), we remove all the contents
9187
+ // of the host element and render an application after that.
9188
+ const PRESERVE_HOST_CONTENT_DEFAULT = false;
9189
+ /**
9190
+ * Internal token that indicates whether host element content should be
9191
+ * retained during the bootstrap.
9192
+ */
9193
+ const PRESERVE_HOST_CONTENT = new InjectionToken(NG_DEV_MODE ? 'PRESERVE_HOST_CONTENT' : '', {
9194
+ providedIn: 'root',
9195
+ factory: () => PRESERVE_HOST_CONTENT_DEFAULT,
9196
+ });
9197
+
8849
9198
  function normalizeDebugBindingName(name) {
8850
9199
  // Attribute names with `$` (eg `x-y$`) are valid per spec, but unsupported by some browsers
8851
9200
  name = camelCaseToDashCase(name.replace(/[$@]/g, '_'));
@@ -10496,7 +10845,7 @@ function renderChildComponents(hostLView, components) {
10496
10845
  renderComponent(hostLView, components[i]);
10497
10846
  }
10498
10847
  }
10499
- function createLView(parentLView, tView, context, flags, host, tHostNode, rendererFactory, renderer, sanitizer, injector, embeddedViewInjector) {
10848
+ function createLView(parentLView, tView, context, flags, host, tHostNode, rendererFactory, renderer, sanitizer, injector, embeddedViewInjector, hydrationInfo) {
10500
10849
  const lView = tView.blueprint.slice();
10501
10850
  lView[HOST] = host;
10502
10851
  lView[FLAGS] = flags | 4 /* LViewFlags.CreationMode */ | 64 /* LViewFlags.Attached */ | 8 /* LViewFlags.FirstLViewPass */;
@@ -10516,6 +10865,7 @@ function createLView(parentLView, tView, context, flags, host, tHostNode, render
10516
10865
  lView[INJECTOR$1] = injector || parentLView && parentLView[INJECTOR$1] || null;
10517
10866
  lView[T_HOST] = tHostNode;
10518
10867
  lView[ID] = getUniqueLViewId();
10868
+ lView[HYDRATION] = hydrationInfo;
10519
10869
  lView[EMBEDDED_VIEW_INJECTOR] = embeddedViewInjector;
10520
10870
  ngDevMode &&
10521
10871
  assertEqual(tView.type == 2 /* TViewType.Embedded */ ? parentLView !== null : true, true, 'Embedded views must have parentLView');
@@ -10957,10 +11307,19 @@ function createViewBlueprint(bindingStartIndex, initialViewLength) {
10957
11307
  * @param rendererFactory Factory function to create renderer instance.
10958
11308
  * @param elementOrSelector Render element or CSS selector to locate the element.
10959
11309
  * @param encapsulation View Encapsulation defined for component that requests host element.
10960
- */
10961
- function locateHostElement(renderer, elementOrSelector, encapsulation) {
10962
- // When using native Shadow DOM, do not clear host element to allow native slot projection
10963
- const preserveContent = encapsulation === ViewEncapsulation.ShadowDom;
11310
+ * @param injector Root view injector instance.
11311
+ */
11312
+ function locateHostElement(renderer, elementOrSelector, encapsulation, injector) {
11313
+ // Note: we use default value for the `PRESERVE_HOST_CONTENT` here even though it's a
11314
+ // tree-shakable one (providedIn:'root'). This code path can be triggered during dynamic component
11315
+ // creation (after calling ViewContainerRef.createComponent) when an injector instance can be
11316
+ // provided. The injector instance might be disconnected from the main DI tree, thus the
11317
+ // `PRESERVE_HOST_CONTENT` woild not be able to instantiate. In this case, the default value will
11318
+ // be used.
11319
+ const preserveHostContent = injector.get(PRESERVE_HOST_CONTENT, PRESERVE_HOST_CONTENT_DEFAULT);
11320
+ // When using native Shadow DOM, do not clear host element to allow native slot
11321
+ // projection.
11322
+ const preserveContent = preserveHostContent || encapsulation === ViewEncapsulation.ShadowDom;
10964
11323
  return renderer.selectRootElement(elementOrSelector, preserveContent);
10965
11324
  }
10966
11325
  /**
@@ -11017,7 +11376,7 @@ function createTNode(tView, tParent, type, index, value, attrs) {
11017
11376
  initialInputs: undefined,
11018
11377
  inputs: null,
11019
11378
  outputs: null,
11020
- tViews: null,
11379
+ tView: null,
11021
11380
  next: null,
11022
11381
  prev: null,
11023
11382
  projectionNext: null,
@@ -11560,7 +11919,7 @@ function addComponentLogic(lView, hostTNode, def) {
11560
11919
  // Only component views should be added to the view tree directly. Embedded views are
11561
11920
  // accessed through their containers because they may be removed / re-added later.
11562
11921
  const rendererFactory = lView[RENDERER_FACTORY];
11563
- const componentView = addToViewTree(lView, createLView(lView, tView, null, def.onPush ? 32 /* LViewFlags.Dirty */ : 16 /* LViewFlags.CheckAlways */, native, hostTNode, rendererFactory, rendererFactory.createRenderer(native, def), null, null, null));
11922
+ const componentView = addToViewTree(lView, createLView(lView, tView, null, def.onPush ? 32 /* LViewFlags.Dirty */ : 16 /* LViewFlags.CheckAlways */, native, hostTNode, rendererFactory, rendererFactory.createRenderer(native, def), null, null, null, null));
11564
11923
  // Component view will always be created before any injected LContainers,
11565
11924
  // so this is a regular element, wrap it with the component view
11566
11925
  lView[hostTNode.index] = componentView;
@@ -11805,6 +12164,11 @@ function renderComponent(hostLView, componentHostIdx) {
11805
12164
  const componentView = getComponentLViewByIndex(componentHostIdx, hostLView);
11806
12165
  const componentTView = componentView[TVIEW];
11807
12166
  syncViewWithBlueprint(componentTView, componentView);
12167
+ const hostRNode = componentView[HOST];
12168
+ // Populate an LView with hydration info retrieved from the DOM via TransferState.
12169
+ if (hostRNode !== null && componentView[HYDRATION] === null) {
12170
+ componentView[HYDRATION] = retrieveHydrationInfo(hostRNode, componentView[INJECTOR$1]);
12171
+ }
11808
12172
  renderView(componentTView, componentView, componentView[CONTEXT]);
11809
12173
  }
11810
12174
  /**
@@ -12505,13 +12869,13 @@ class ComponentFactory extends ComponentFactory$1 {
12505
12869
  // dynamically. Default to 'div' if this component did not specify any tag name in its selector.
12506
12870
  const elementName = this.componentDef.selectors[0][0] || 'div';
12507
12871
  const hostRNode = rootSelectorOrNode ?
12508
- locateHostElement(hostRenderer, rootSelectorOrNode, this.componentDef.encapsulation) :
12872
+ locateHostElement(hostRenderer, rootSelectorOrNode, this.componentDef.encapsulation, rootViewInjector) :
12509
12873
  createElementNode(hostRenderer, elementName, getNamespace(elementName));
12510
12874
  const rootFlags = this.componentDef.onPush ? 32 /* LViewFlags.Dirty */ | 256 /* LViewFlags.IsRoot */ :
12511
12875
  16 /* LViewFlags.CheckAlways */ | 256 /* LViewFlags.IsRoot */;
12512
12876
  // Create the root view. Uses empty TView and ContentTemplate.
12513
12877
  const rootTView = createTView(0 /* TViewType.Root */, null, null, 1, 0, null, null, null, null, null);
12514
- const rootLView = createLView(null, rootTView, null, rootFlags, null, null, rendererFactory, hostRenderer, sanitizer, rootViewInjector, null);
12878
+ const rootLView = createLView(null, rootTView, null, rootFlags, null, null, rendererFactory, hostRenderer, sanitizer, rootViewInjector, null, null);
12515
12879
  // rootView is the parent when bootstrapping
12516
12880
  // TODO(misko): it looks like we are entering view here but we don't really need to as
12517
12881
  // `renderView` does that. However as the code is written it is needed because
@@ -12622,7 +12986,7 @@ function createRootComponentTNode(lView, rNode) {
12622
12986
  /**
12623
12987
  * Creates the root component view and the root component node.
12624
12988
  *
12625
- * @param rNode Render host element.
12989
+ * @param hostRNode Render host element.
12626
12990
  * @param rootComponentDef ComponentDef
12627
12991
  * @param rootView The parent view where the host node is stored
12628
12992
  * @param rendererFactory Factory to be used for creating child renderers.
@@ -12631,11 +12995,17 @@ function createRootComponentTNode(lView, rNode) {
12631
12995
  *
12632
12996
  * @returns Component view created
12633
12997
  */
12634
- function createRootComponentView(tNode, rNode, rootComponentDef, rootDirectives, rootView, rendererFactory, hostRenderer, sanitizer) {
12998
+ function createRootComponentView(tNode, hostRNode, rootComponentDef, rootDirectives, rootView, rendererFactory, hostRenderer, sanitizer) {
12635
12999
  const tView = rootView[TVIEW];
12636
- applyRootComponentStyling(rootDirectives, tNode, rNode, hostRenderer);
12637
- const viewRenderer = rendererFactory.createRenderer(rNode, rootComponentDef);
12638
- const componentView = createLView(rootView, getOrCreateComponentTView(rootComponentDef), null, rootComponentDef.onPush ? 32 /* LViewFlags.Dirty */ : 16 /* LViewFlags.CheckAlways */, rootView[tNode.index], tNode, rendererFactory, viewRenderer, sanitizer || null, null, null);
13000
+ applyRootComponentStyling(rootDirectives, tNode, hostRNode, hostRenderer);
13001
+ // Hydration info is on the host element and needs to be retreived
13002
+ // and passed to the component LView.
13003
+ let hydrationInfo = null;
13004
+ if (hostRNode !== null) {
13005
+ hydrationInfo = retrieveHydrationInfo(hostRNode, rootView[INJECTOR$1]);
13006
+ }
13007
+ const viewRenderer = rendererFactory.createRenderer(hostRNode, rootComponentDef);
13008
+ const componentView = createLView(rootView, getOrCreateComponentTView(rootComponentDef), null, rootComponentDef.onPush ? 32 /* LViewFlags.Dirty */ : 16 /* LViewFlags.CheckAlways */, rootView[tNode.index], tNode, rendererFactory, viewRenderer, sanitizer || null, null, null, hydrationInfo);
12639
13009
  if (tView.firstCreatePass) {
12640
13010
  markAsComponentHost(tView, tNode, rootDirectives.length - 1);
12641
13011
  }
@@ -13797,7 +14167,7 @@ function templateFirstCreatePass(index, tView, lView, templateFn, decls, vars, t
13797
14167
  const tNode = getOrCreateTNode(tView, index, 4 /* TNodeType.Container */, tagName || null, getConstant(tViewConsts, attrsIndex));
13798
14168
  resolveDirectives(tView, lView, tNode, getConstant(tViewConsts, localRefsIndex));
13799
14169
  registerPostOrderHooks(tView, tNode);
13800
- const embeddedTView = tNode.tViews = createTView(2 /* TViewType.Embedded */, tNode, templateFn, decls, vars, tView.directiveRegistry, tView.pipeRegistry, null, tView.schemas, tViewConsts);
14170
+ const embeddedTView = tNode.tView = createTView(2 /* TViewType.Embedded */, tNode, templateFn, decls, vars, tView.directiveRegistry, tView.pipeRegistry, null, tView.schemas, tViewConsts);
13801
14171
  if (tView.queries !== null) {
13802
14172
  tView.queries.template(tView, tNode);
13803
14173
  embeddedTView.queries = tView.queries.embeddedTView(tNode);
@@ -13867,6 +14237,119 @@ function ɵɵreference(index) {
13867
14237
  return load(contextLView, HEADER_OFFSET + index);
13868
14238
  }
13869
14239
 
14240
+ /**
14241
+ * Verifies whether a given node matches an expected criteria,
14242
+ * based on internal data structure state.
14243
+ */
14244
+ function validateMatchingNode(node, nodeType, tagName, lView, tNode) {
14245
+ if (node.nodeType !== nodeType ||
14246
+ (node.nodeType === Node.ELEMENT_NODE &&
14247
+ node.tagName.toLowerCase() !== tagName?.toLowerCase())) {
14248
+ // TODO: improve error message and use RuntimeError instead.
14249
+ throw new Error(`Unexpected node found during hydration.`);
14250
+ }
14251
+ }
14252
+ /**
14253
+ * Verifies whether next sibling node exists.
14254
+ */
14255
+ function validateSiblingNodeExists(node) {
14256
+ if (!node.nextSibling) {
14257
+ // TODO: improve error message and use RuntimeError instead.
14258
+ throw new Error(`Unexpected state: insufficient number of sibling nodes.`);
14259
+ }
14260
+ }
14261
+
14262
+ /** Whether current TNode is a first node in an <ng-container>. */
14263
+ function isFirstElementInNgContainer(tNode) {
14264
+ return !tNode.prev && tNode.parent?.type === 8 /* TNodeType.ElementContainer */;
14265
+ }
14266
+ /** Returns first element from a DOM segment that corresponds to this <ng-container>. */
14267
+ function getDehydratedNgContainer(hydrationInfo, tContainerNode) {
14268
+ const noOffsetIndex = tContainerNode.index - HEADER_OFFSET;
14269
+ const ngContainer = hydrationInfo.ngContainers?.[noOffsetIndex];
14270
+ ngDevMode &&
14271
+ assertDefined(ngContainer, 'Unexpected state: no hydration info available for a given TNode, ' +
14272
+ 'which represents an element container.');
14273
+ return ngContainer;
14274
+ }
14275
+ /**
14276
+ * Locate a node in DOM tree that corresponds to a given TNode.
14277
+ *
14278
+ * @param hydrationInfo The hydration annotation data
14279
+ * @param tView the current tView
14280
+ * @param lView the current lView
14281
+ * @param tNode the current tNode
14282
+ * @returns an RNode that represents a given tNode
14283
+ */
14284
+ function locateNextRNode(hydrationInfo, tView, lView, tNode) {
14285
+ let native = null;
14286
+ if (tView.firstChild === tNode) {
14287
+ // We create a first node in this view, so we use a reference
14288
+ // to the first child in this DOM segment.
14289
+ native = hydrationInfo.firstChild;
14290
+ }
14291
+ else {
14292
+ // Locate a node based on a previous sibling or a parent node.
14293
+ const previousTNodeParent = tNode.prev === null;
14294
+ const previousTNode = (tNode.prev ?? tNode.parent);
14295
+ ngDevMode &&
14296
+ assertDefined(previousTNode, 'Unexpected state: current TNode does not have a connection ' +
14297
+ 'to the previous node or a parent node.');
14298
+ const previousRElement = getNativeByTNode(previousTNode, lView);
14299
+ if (isFirstElementInNgContainer(tNode)) {
14300
+ const ngContainer = getDehydratedNgContainer(hydrationInfo, tNode.parent);
14301
+ native = ngContainer.firstChild ?? null;
14302
+ }
14303
+ else {
14304
+ if (previousTNodeParent) {
14305
+ native = previousRElement.firstChild;
14306
+ }
14307
+ else {
14308
+ native = previousRElement.nextSibling;
14309
+ }
14310
+ }
14311
+ }
14312
+ return native;
14313
+ }
14314
+ /**
14315
+ * Skips over a specified number of nodes and returns the next sibling node after that.
14316
+ */
14317
+ function siblingAfter(skip, from) {
14318
+ let currentNode = from;
14319
+ for (let i = 0; i < skip; i++) {
14320
+ ngDevMode && validateSiblingNodeExists(currentNode);
14321
+ currentNode = currentNode.nextSibling;
14322
+ }
14323
+ return currentNode;
14324
+ }
14325
+
14326
+ /**
14327
+ * The name of an attribute that can be added to the hydration boundary node
14328
+ * (component host node) to disable hydration for the content within that boundary.
14329
+ */
14330
+ const SKIP_HYDRATION_ATTR_NAME = 'ngSkipHydration';
14331
+ /**
14332
+ * Helper function to check if a given node has the 'ngSkipHydration' attribute
14333
+ */
14334
+ function hasNgSkipHydrationAttr(tNode) {
14335
+ const SKIP_HYDRATION_ATTR_NAME_LOWER_CASE = SKIP_HYDRATION_ATTR_NAME.toLowerCase();
14336
+ const attrs = tNode.mergedAttrs;
14337
+ if (attrs === null)
14338
+ return false;
14339
+ // only ever look at the attribute name and skip the values
14340
+ for (let i = 0; i < attrs.length; i += 2) {
14341
+ const value = attrs[i];
14342
+ // This is a marker, which means that the static attributes section is over,
14343
+ // so we can exit early.
14344
+ if (typeof value === 'number')
14345
+ return false;
14346
+ if (typeof value === 'string' && value.toLowerCase() === SKIP_HYDRATION_ATTR_NAME_LOWER_CASE) {
14347
+ return true;
14348
+ }
14349
+ }
14350
+ return false;
14351
+ }
14352
+
13870
14353
  /**
13871
14354
  * Update a property on a selected element.
13872
14355
  *
@@ -13951,14 +14434,15 @@ function ɵɵelementStart(index, name, attrsIndex, localRefsIndex) {
13951
14434
  const tNode = tView.firstCreatePass ?
13952
14435
  elementStartFirstCreatePass(adjustedIndex, tView, lView, name, attrsIndex, localRefsIndex) :
13953
14436
  tView.data[adjustedIndex];
13954
- const native = lView[adjustedIndex] = createElementNode(renderer, name, getNamespace$1());
14437
+ const native = _locateOrCreateElementNode(tView, lView, tNode, renderer, name);
14438
+ lView[adjustedIndex] = native;
13955
14439
  const hasDirectives = isDirectiveHost(tNode);
13956
14440
  if (ngDevMode && tView.firstCreatePass) {
13957
14441
  validateElementIsKnown(native, lView, tNode.value, tView.schemas, hasDirectives);
13958
14442
  }
13959
14443
  setCurrentTNode(tNode, true);
13960
14444
  setupStaticAttributes(renderer, native, tNode);
13961
- if ((tNode.flags & 32 /* TNodeFlags.isDetached */) !== 32 /* TNodeFlags.isDetached */) {
14445
+ if ((tNode.flags & 32 /* TNodeFlags.isDetached */) !== 32 /* TNodeFlags.isDetached */ && wasLastNodeCreated()) {
13962
14446
  // In the i18n case, the translation may have removed this element, so only add it if it is not
13963
14447
  // detached. See `TNodeType.Placeholder` and `LFrame.inI18n` for more context.
13964
14448
  appendChild(tView, lView, native, tNode);
@@ -13998,6 +14482,9 @@ function ɵɵelementEnd() {
13998
14482
  }
13999
14483
  const tNode = currentTNode;
14000
14484
  ngDevMode && assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */);
14485
+ if (isSkipHydrationRootTNode(tNode)) {
14486
+ leaveSkipHydrationBlock();
14487
+ }
14001
14488
  decreaseElementDepthCount();
14002
14489
  const tView = getTView();
14003
14490
  if (tView.firstCreatePass) {
@@ -14030,6 +14517,40 @@ function ɵɵelement(index, name, attrsIndex, localRefsIndex) {
14030
14517
  ɵɵelementEnd();
14031
14518
  return ɵɵelement;
14032
14519
  }
14520
+ let _locateOrCreateElementNode = (tView, lView, tNode, renderer, name) => {
14521
+ lastNodeWasCreated(true);
14522
+ return createElementNode(renderer, name, getNamespace$1());
14523
+ };
14524
+ /**
14525
+ * Enables hydration code path (to lookup existing elements in DOM)
14526
+ * in addition to the regular creation mode of element nodes.
14527
+ */
14528
+ function locateOrCreateElementNodeImpl(tView, lView, tNode, renderer, name) {
14529
+ const hydrationInfo = lView[HYDRATION];
14530
+ const isNodeCreationMode = !hydrationInfo || isInSkipHydrationBlock();
14531
+ lastNodeWasCreated(isNodeCreationMode);
14532
+ // Regular creation mode.
14533
+ if (isNodeCreationMode) {
14534
+ return createElementNode(renderer, name, getNamespace$1());
14535
+ }
14536
+ // Hydration mode, looking up an existing element in DOM.
14537
+ const native = locateNextRNode(hydrationInfo, tView, lView, tNode);
14538
+ ngDevMode &&
14539
+ validateMatchingNode(native, Node.ELEMENT_NODE, name, lView, tNode);
14540
+ ngDevMode && markRNodeAsClaimedByHydration(native);
14541
+ // Checks if the skip hydration attribute is present during hydration so we know to
14542
+ // skip attempting to hydrate this block.
14543
+ if (hydrationInfo && hasNgSkipHydrationAttr(tNode)) {
14544
+ enterSkipHydrationBlock(tNode);
14545
+ // Since this isn't hydratable, we need to empty the node
14546
+ // so there's no duplicate content after render
14547
+ clearElementContents(renderer, native);
14548
+ }
14549
+ return native;
14550
+ }
14551
+ function enableLocateOrCreateElementNodeImpl() {
14552
+ _locateOrCreateElementNode = locateOrCreateElementNodeImpl;
14553
+ }
14033
14554
 
14034
14555
  function elementContainerStartFirstCreatePass(index, tView, lView, attrsIndex, localRefsIndex) {
14035
14556
  ngDevMode && ngDevMode.firstCreatePass++;
@@ -14075,10 +14596,12 @@ function ɵɵelementContainerStart(index, attrsIndex, localRefsIndex) {
14075
14596
  tView.data[adjustedIndex];
14076
14597
  setCurrentTNode(tNode, true);
14077
14598
  ngDevMode && ngDevMode.rendererCreateComment++;
14078
- const native = lView[adjustedIndex] =
14079
- lView[RENDERER].createComment(ngDevMode ? 'ng-container' : '');
14080
- appendChild(tView, lView, native, tNode);
14081
- attachPatchData(native, lView);
14599
+ const comment = _locateOrCreateElementContainerNode(tView, lView, tNode, index);
14600
+ lView[adjustedIndex] = comment;
14601
+ if (wasLastNodeCreated()) {
14602
+ appendChild(tView, lView, comment, tNode);
14603
+ }
14604
+ attachPatchData(comment, lView);
14082
14605
  if (isDirectiveHost(tNode)) {
14083
14606
  createDirectivesInstances(tView, lView, tNode);
14084
14607
  executeContentQueries(tView, tNode, lView);
@@ -14130,6 +14653,46 @@ function ɵɵelementContainer(index, attrsIndex, localRefsIndex) {
14130
14653
  ɵɵelementContainerEnd();
14131
14654
  return ɵɵelementContainer;
14132
14655
  }
14656
+ let _locateOrCreateElementContainerNode = (tView, lView, tNode, index) => {
14657
+ lastNodeWasCreated(true);
14658
+ return createCommentNode(lView[RENDERER], ngDevMode ? 'ng-container' : '');
14659
+ };
14660
+ /**
14661
+ * Enables hydration code path (to lookup existing elements in DOM)
14662
+ * in addition to the regular creation mode of comment nodes that
14663
+ * represent <ng-container>'s anchor.
14664
+ */
14665
+ function locateOrCreateElementContainerNode(tView, lView, tNode, index) {
14666
+ let comment;
14667
+ const hydrationInfo = lView[HYDRATION];
14668
+ const isNodeCreationMode = !hydrationInfo || isInSkipHydrationBlock();
14669
+ lastNodeWasCreated(isNodeCreationMode);
14670
+ // Regular creation mode.
14671
+ if (isNodeCreationMode) {
14672
+ return createCommentNode(lView[RENDERER], ngDevMode ? 'ng-container' : '');
14673
+ }
14674
+ // Hydration mode, looking up existing elements in DOM.
14675
+ const currentRNode = locateNextRNode(hydrationInfo, tView, lView, tNode);
14676
+ const ngContainerSize = getNgContainerSize(hydrationInfo, index);
14677
+ ngDevMode &&
14678
+ assertNumber(ngContainerSize, 'Unexpected state: hydrating an <ng-container>, ' +
14679
+ 'but no hydration info is available.');
14680
+ if (ngContainerSize > 0) {
14681
+ storeNgContainerInfo(hydrationInfo, index, currentRNode);
14682
+ comment = siblingAfter(ngContainerSize, currentRNode);
14683
+ }
14684
+ else {
14685
+ // If <ng-container> has no nodes,
14686
+ // the current node is an anchor (comment) node.
14687
+ comment = currentRNode;
14688
+ }
14689
+ ngDevMode && validateMatchingNode(comment, Node.COMMENT_NODE, null, lView, tNode);
14690
+ ngDevMode && markRNodeAsClaimedByHydration(comment);
14691
+ return comment;
14692
+ }
14693
+ function enableLocateOrCreateElementContainerNodeImpl() {
14694
+ _locateOrCreateElementContainerNode = locateOrCreateElementContainerNode;
14695
+ }
14133
14696
 
14134
14697
  /**
14135
14698
  * Returns the current OpaqueViewState instance.
@@ -14158,16 +14721,6 @@ function isPromise(obj) {
14158
14721
  function isSubscribable(obj) {
14159
14722
  return !!obj && typeof obj.subscribe === 'function';
14160
14723
  }
14161
- /**
14162
- * Determine if the argument is an Observable
14163
- *
14164
- * Strictly this tests that the `obj` is `Subscribable`, since `Observable`
14165
- * types need additional methods, such as `lift()`. But it is adequate for our
14166
- * needs since within the Angular framework code we only ever need to use the
14167
- * `subscribe()` method, and RxJS has mechanisms to wrap `Subscribable` objects
14168
- * into `Observable` as needed.
14169
- */
14170
- const isObservable = isSubscribable;
14171
14724
 
14172
14725
  /**
14173
14726
  * Adds an event listener to the current node.
@@ -14330,7 +14883,7 @@ function listenerInternal(tView, lView, renderer, tNode, eventName, listenerFn,
14330
14883
  const minifiedName = props[i + 1];
14331
14884
  const directiveInstance = lView[index];
14332
14885
  const output = directiveInstance[minifiedName];
14333
- if (ngDevMode && !isObservable(output)) {
14886
+ if (ngDevMode && !isSubscribable(output)) {
14334
14887
  throw new Error(`@Output ${minifiedName} not initialized in '${directiveInstance.constructor.name}'.`);
14335
14888
  }
14336
14889
  const subscription = output.subscribe(listenerFn);
@@ -16474,11 +17027,39 @@ function ɵɵtext(index, value = '') {
16474
17027
  const tNode = tView.firstCreatePass ?
16475
17028
  getOrCreateTNode(tView, adjustedIndex, 1 /* TNodeType.Text */, value, null) :
16476
17029
  tView.data[adjustedIndex];
16477
- const textNative = lView[adjustedIndex] = createTextNode(lView[RENDERER], value);
16478
- appendChild(tView, lView, textNative, tNode);
17030
+ const textNative = _locateOrCreateTextNode(tView, lView, tNode, value);
17031
+ lView[adjustedIndex] = textNative;
17032
+ if (wasLastNodeCreated()) {
17033
+ appendChild(tView, lView, textNative, tNode);
17034
+ }
16479
17035
  // Text nodes are self closing.
16480
17036
  setCurrentTNode(tNode, false);
16481
17037
  }
17038
+ let _locateOrCreateTextNode = (tView, lView, tNode, value) => {
17039
+ lastNodeWasCreated(true);
17040
+ return createTextNode(lView[RENDERER], value);
17041
+ };
17042
+ /**
17043
+ * Enables hydration code path (to lookup existing elements in DOM)
17044
+ * in addition to the regular creation mode of text nodes.
17045
+ */
17046
+ function locateOrCreateTextNodeImpl(tView, lView, tNode, value) {
17047
+ const hydrationInfo = lView[HYDRATION];
17048
+ const isNodeCreationMode = !hydrationInfo || isInSkipHydrationBlock();
17049
+ lastNodeWasCreated(isNodeCreationMode);
17050
+ // Regular creation mode.
17051
+ if (isNodeCreationMode) {
17052
+ return createTextNode(lView[RENDERER], value);
17053
+ }
17054
+ // Hydration mode, looking up an existing element in DOM.
17055
+ const textNative = locateNextRNode(hydrationInfo, tView, lView, tNode);
17056
+ ngDevMode && validateMatchingNode(textNative, Node.TEXT_NODE, null, lView, tNode);
17057
+ ngDevMode && markRNodeAsClaimedByHydration(textNative);
17058
+ return textNative;
17059
+ }
17060
+ function enableLocateOrCreateTextNodeImpl() {
17061
+ _locateOrCreateTextNode = locateOrCreateTextNodeImpl;
17062
+ }
16482
17063
 
16483
17064
  /**
16484
17065
  *
@@ -18156,7 +18737,7 @@ function getTIcu(tView, index) {
18156
18737
  if (value === null || typeof value === 'string')
18157
18738
  return null;
18158
18739
  if (ngDevMode &&
18159
- !(value.hasOwnProperty('tViews') || value.hasOwnProperty('currentCaseLViewIndex'))) {
18740
+ !(value.hasOwnProperty('tView') || value.hasOwnProperty('currentCaseLViewIndex'))) {
18160
18741
  throwError('We expect to get \'null\'|\'TIcu\'|\'TIcuContainer\', but got: ' + value);
18161
18742
  }
18162
18743
  // Here the `value.hasOwnProperty('currentCaseLViewIndex')` is a polymorphic read as it can be
@@ -18185,7 +18766,7 @@ function getTIcu(tView, index) {
18185
18766
  function setTIcu(tView, index, tIcu) {
18186
18767
  const tNode = tView.data[index];
18187
18768
  ngDevMode &&
18188
- assertEqual(tNode === null || tNode.hasOwnProperty('tViews'), true, 'We expect to get \'null\'|\'TIcuContainer\'');
18769
+ assertEqual(tNode === null || tNode.hasOwnProperty('tView'), true, 'We expect to get \'null\'|\'TIcuContainer\'');
18189
18770
  if (tNode === null) {
18190
18771
  tView.data[index] = tIcu;
18191
18772
  }
@@ -21480,8 +22061,8 @@ const R3TemplateRef = class TemplateRef extends ViewEngineTemplateRef {
21480
22061
  this.elementRef = elementRef;
21481
22062
  }
21482
22063
  createEmbeddedView(context, injector) {
21483
- const embeddedTView = this._declarationTContainer.tViews;
21484
- const embeddedLView = createLView(this._declarationLView, embeddedTView, context, 16 /* LViewFlags.CheckAlways */, null, embeddedTView.declTNode, null, null, null, null, injector || null);
22064
+ const embeddedTView = this._declarationTContainer.tView;
22065
+ const embeddedLView = createLView(this._declarationLView, embeddedTView, context, 16 /* LViewFlags.CheckAlways */, null, embeddedTView.declTNode, null, null, null, null, injector || null, null);
21485
22066
  const declarationLContainer = this._declarationLView[this._declarationTContainer.index];
21486
22067
  ngDevMode && assertLContainer(declarationLContainer);
21487
22068
  embeddedLView[DECLARATION_LCONTAINER] = declarationLContainer;
@@ -21510,7 +22091,7 @@ function injectTemplateRef() {
21510
22091
  */
21511
22092
  function createTemplateRef(hostTNode, hostLView) {
21512
22093
  if (hostTNode.type & 4 /* TNodeType.Container */) {
21513
- ngDevMode && assertDefined(hostTNode.tViews, 'TView must be allocated');
22094
+ ngDevMode && assertDefined(hostTNode.tView, 'TView must be allocated');
21514
22095
  return new R3TemplateRef(hostLView, hostTNode, createElementRef(hostTNode, hostLView));
21515
22096
  }
21516
22097
  return null;