@@ -13,25 +13,26 @@ import {Injector} from '../di';
1313import { TransferState } from '../transfer_state' ;
1414import { removeListenersFromBlocks } from '../event_delegation_utils' ;
1515import { cleanupLContainer } from './cleanup' ;
16- import { TNode } from '../render3/interfaces/node' ;
17- import { HEADER_OFFSET , TView } from '../render3/interfaces/view' ;
18- import { DeferBlock , TDeferBlockDetails } from '../defer/interfaces' ;
19- import { getDeferBlockDataIndex , getTDeferBlockDetails , isTDeferBlockDetails } from '../defer/utils' ;
16+ import { DeferBlock } from '../defer/interfaces' ;
2017import { whenStable , ApplicationRef } from '../application/application_ref' ;
2118
2219/**
2320 * Finds first hydrated parent `@defer` block for a given block id.
2421 * If there are any dehydrated `@defer` blocks found along the way,
2522 * they are also stored and returned from the function (as a list of ids).
23+ * Note: This is utilizing serialized information to navigate up the tree
2624 */
2725export function findFirstHydratedParentDeferBlock ( deferBlockId : string , injector : Injector ) {
2826 const deferBlockRegistry = injector . get ( DeferBlockRegistry ) ;
2927 const transferState = injector . get ( TransferState ) ;
3028 const deferBlockParents = transferState . get ( NGH_DEFER_BLOCKS_KEY , { } ) ;
3129 const dehydratedBlocks : string [ ] = [ ] ;
3230
33- let deferBlock = deferBlockRegistry . get ( deferBlockId ) ?? null ;
31+ let deferBlock = deferBlockRegistry . get ( deferBlockId ) ;
3432 let currentBlockId : string | null = deferBlockId ;
33+ // at each level we check if the registry has the given defer block id
34+ // - if it does, we know it was already hydrated and can stop here
35+ // - if it does not, we continue on
3536 while ( ! deferBlock ) {
3637 dehydratedBlocks . unshift ( currentBlockId ) ;
3738 currentBlockId = deferBlockParents [ currentBlockId ] [ DEFER_PARENT_BLOCK_ID ] ;
@@ -42,24 +43,21 @@ export function findFirstHydratedParentDeferBlock(deferBlockId: string, injector
4243}
4344
4445/**
45- * Hydrates a defer block by block name through jsaction code paths
46- */
47- let _hydrateFromBlockNameImpl : typeof hydrateFromBlockNameImpl = ( ) => {
48- return Promise . resolve ( { deferBlock : null , hydratedBlocks : new Set < string > ( ) } ) ;
49- } ;
50-
51- /**
52- * Hydrates a defer block by block name using non jsaction code paths
46+ * The core mechanism for incremental hydration. This recursively triggers
47+ * hydration for all the blocks in the tree that need to be hydrated and keeps
48+ * track of all those blocks that were hydrated along the way.
49+ *
50+ * @param injector
51+ * @param blockName
52+ * @param onTriggerFn The function that triggers the block and fetches deps
53+ * @param hydratedBlocks The set of blocks currently being hydrated in the tree
54+ * @returns
5355 */
54- let _incrementallyHydrateFromBlockNameImpl : typeof incrementallyHydrateFromBlockNameImpl = ( ) => {
55- return Promise . resolve ( ) ;
56- } ;
57-
58- async function hydrateFromBlockNameImpl (
56+ export async function hydrateFromBlockName (
5957 injector : Injector ,
6058 blockName : string ,
61- onTriggerFn : ( deferBlock : any ) => void ,
62- hydratedBlocks : Set < string > ,
59+ onTriggerFn : ( deferBlock : DeferBlock ) => void ,
60+ hydratedBlocks : Set < string > = new Set ( ) ,
6361) : Promise < {
6462 deferBlock : DeferBlock | null ;
6563 hydratedBlocks : Set < string > ;
@@ -74,80 +72,48 @@ async function hydrateFromBlockNameImpl(
7472 injector ,
7573 ) ;
7674 if ( deferBlock && blockId ) {
75+ // Step 2: Add the current block to the tracking sets to prevent
76+ // attempting to trigger hydration on a block more than once
77+ // simulataneously.
7778 hydratedBlocks . add ( blockId ) ;
7879 deferBlockRegistry . hydrating . add ( blockId ) ;
7980
81+ // Step 3: Run the actual trigger function to fetch dependencies
8082 await onTriggerFn ( deferBlock ) ;
83+
84+ // Step 4: Recursively trigger, fetch, and hydrate from the top of the hierarchy down
8185 let hydratedBlock : DeferBlock | null = deferBlock ;
8286 for ( const dehydratedBlock of dehydratedBlocks ) {
83- const hydratedInfo = await hydrateFromBlockNameImpl (
87+ const hydratedInfo = await hydrateFromBlockName (
8488 injector ,
8589 dehydratedBlock ,
8690 onTriggerFn ,
8791 hydratedBlocks ,
8892 ) ;
8993 hydratedBlock = hydratedInfo . deferBlock ;
9094 }
91- // this is going to be the wrong defer block. We need to get the final one.
95+ // TODO(incremental-hydration): this is likely where we want to do Step 5: some cleanup work in the
96+ // DeferBlockRegistry.
9297 return { deferBlock : hydratedBlock , hydratedBlocks} ;
9398 } else {
9499 // TODO(incremental-hydration): this is likely an error, consider producing a `console.error`.
95100 return { deferBlock : null , hydratedBlocks} ;
96101 }
97102}
98103
99- /**
100- * Sets the implementation for the `retrieveDeferBlockData` function.
101- */
102- export function enableHydrateFromBlockNameImpl ( ) {
103- _hydrateFromBlockNameImpl = hydrateFromBlockNameImpl ;
104- _incrementallyHydrateFromBlockNameImpl = incrementallyHydrateFromBlockNameImpl ;
105- }
106-
107- export async function hydrateFromBlockName (
108- injector : Injector ,
109- blockName : string ,
110- onTriggerFn : ( deferBlock : any ) => void ,
111- ) : Promise < {
112- deferBlock : DeferBlock | null ;
113- hydratedBlocks : Set < string > ;
114- } > {
115- return await _hydrateFromBlockNameImpl ( injector , blockName , onTriggerFn , new Set < string > ( ) ) ;
116- }
117-
118- async function incrementallyHydrateFromBlockNameImpl (
104+ export async function incrementallyHydrateFromBlockName (
119105 injector : Injector ,
120106 blockName : string ,
121- triggerFn : ( deferBlock : any ) => void ,
107+ triggerFn : ( deferBlock : DeferBlock ) => void ,
122108) : Promise < void > {
123109 const { deferBlock, hydratedBlocks} = await hydrateFromBlockName ( injector , blockName , triggerFn ) ;
124- removeListenersFromBlocks ( [ ...hydratedBlocks ] , injector ) ;
125110 if ( deferBlock !== null ) {
111+ // hydratedBlocks is a set, and needs to be converted to an array
112+ // for removing listeners
113+ removeListenersFromBlocks ( [ ...hydratedBlocks ] , injector ) ;
126114 cleanupLContainer ( deferBlock . lContainer ) ;
127- const appRef = injector . get ( ApplicationRef ) ;
128- await whenStable ( appRef ) ;
129- }
130- }
131-
132- export function incrementallyHydrateFromBlockName (
133- injector : Injector ,
134- blockName : string ,
135- triggerFn : ( deferBlock : any ) => void ,
136- ) : Promise < void > {
137- return _incrementallyHydrateFromBlockNameImpl ( injector , blockName , triggerFn ) ;
138- }
139-
140- /**
141- * Whether a given TNode represents a defer block.
142- */
143- export function isDeferBlock ( tView : TView , tNode : TNode ) : boolean {
144- let tDetails : TDeferBlockDetails | null = null ;
145- const slotIndex = getDeferBlockDataIndex ( tNode . index ) ;
146- // Check if a slot index is in the reasonable range.
147- // Note: we do `-1` on the right border, since defer block details are stored
148- // in the `n+1` slot, see `getDeferBlockDataIndex` for more info.
149- if ( HEADER_OFFSET < slotIndex && slotIndex < tView . bindingStartIndex ) {
150- tDetails = getTDeferBlockDetails ( tView , tNode ) ;
115+ // we need to wait for app stability here so we don't continue before
116+ // the hydration process has finished, which could result in problems
117+ await whenStable ( injector . get ( ApplicationRef ) ) ;
151118 }
152- return ! ! tDetails && isTDeferBlockDetails ( tDetails ) ;
153119}
0 commit comments