@@ -80,11 +80,35 @@ export function diff(
8080 if (
8181 oldVNode . _flags & MODE_SUSPENDED &&
8282 // @ts -expect-error This is 1 or 0 (true or false)
83- ( isHydrating = oldVNode . _flags & MODE_HYDRATE ) &&
84- oldVNode . _component . _excess
83+ ( isHydrating = oldVNode . _flags & MODE_HYDRATE )
8584 ) {
86- excessDomChildren = oldVNode . _component . _excess ;
87- oldDom = excessDomChildren [ 0 ] ;
85+ let startMarker = oldVNode . _component . _excess [ 0 ] ;
86+ if (
87+ startMarker &&
88+ startMarker . nodeType == 8 &&
89+ startMarker . data . startsWith ( '$s' )
90+ ) {
91+ // Deferred restoration: re-scan current DOM from the stored start marker.
92+ // This ensures we always hydrate against the most up-to-date DOM state,
93+ // even if a streaming SSR patcher replaced the content between markers.
94+ excessDomChildren = [ ] ;
95+ let depth = 1 ;
96+ let node = startMarker . nextSibling ;
97+ while ( node && depth > 0 ) {
98+ if ( node . nodeType == 8 ) {
99+ if ( node . data . startsWith ( '$s' ) ) depth ++ ;
100+ else if ( node . data . startsWith ( '/$s' ) ) {
101+ if ( -- depth == 0 ) break ;
102+ }
103+ }
104+ excessDomChildren . push ( node ) ;
105+ node = node . nextSibling ;
106+ }
107+ oldDom = excessDomChildren [ 0 ] ;
108+ } else {
109+ excessDomChildren = oldVNode . _component . _excess ;
110+ oldDom = excessDomChildren [ 0 ] ;
111+ }
88112 oldVNode . _component . _excess = NULL ;
89113 }
90114
@@ -311,46 +335,47 @@ export function diff(
311335 if ( isHydrating || excessDomChildren != NULL ) {
312336 if ( e . then ) {
313337 let commentMarkersToFind = 0 ,
314- done ;
338+ done ,
339+ startMarker ;
315340
316341 newVNode . _flags |= isHydrating
317342 ? MODE_HYDRATE | MODE_SUSPENDED
318343 : MODE_SUSPENDED ;
319344
320- newVNode . _component . _excess = [ ] ;
321345 for ( let i = 0 ; i < excessDomChildren . length ; i ++ ) {
322346 let child = excessDomChildren [ i ] ;
323347 if ( child == NULL || done ) continue ;
324348
325- // When we encounter a boundary with $s we are opening
326- // a boundary, this implies that we need to bump
327- // the amount of markers we need to find before closing
328- // the outer boundary.
329- // We exclude the open and closing marker from
330- // the future excessDomChildren but any nested one
331- // needs to be included for future suspensions.
349+ // When we encounter a $s boundary marker we are opening a
350+ // suspended region. Track nesting depth to find the matching
351+ // close marker. We null out ALL nodes in the region so the
352+ // parent diff doesn't try to remove them; the children will be
353+ // re-scanned from the stored start marker on resume.
332354 if ( child . nodeType == 8 ) {
333- if ( child . data == '$s' ) {
334- if ( commentMarkersToFind ) {
335- newVNode . _component . _excess . push ( child ) ;
355+ if ( child . data . startsWith ( '$s' ) ) {
356+ if ( ! commentMarkersToFind ) {
357+ // Store outermost start marker for deferred restoration
358+ startMarker = child ;
336359 }
337360 commentMarkersToFind ++ ;
338- } else if ( child . data == '/$s' ) {
339- commentMarkersToFind -- ;
340- if ( commentMarkersToFind ) {
341- newVNode . _component . _excess . push ( child ) ;
361+ } else if ( child . data . startsWith ( '/$s' ) ) {
362+ if ( -- commentMarkersToFind == 0 ) {
363+ done = true ;
364+ oldDom = excessDomChildren [ i ] ;
342365 }
343- done = commentMarkersToFind == 0 ;
344- oldDom = excessDomChildren [ i ] ;
345366 }
346367 excessDomChildren [ i ] = NULL ;
347368 } else if ( commentMarkersToFind ) {
348- newVNode . _component . _excess . push ( child ) ;
349369 excessDomChildren [ i ] = NULL ;
350370 }
351371 }
352372
353- if ( ! done ) {
373+ if ( done ) {
374+ // Store only the start marker; children are re-scanned on resume
375+ // so we always hydrate against the current DOM state.
376+ // TODO: consider just storing in _dom and getting rid of _excess altogether?
377+ newVNode . _component . _excess = [ startMarker ] ;
378+ } else {
354379 while ( oldDom && oldDom . nodeType == 8 && oldDom . nextSibling ) {
355380 oldDom = oldDom . nextSibling ;
356381 }
0 commit comments