@@ -167,9 +167,11 @@ export function processChildren(
167167 }
168168
169169 const { children } = parent
170+ processChildrenDynamicInfo ( children )
171+
170172 for ( let i = 0 ; i < children . length ; i ++ ) {
171173 const child = children [ i ]
172- if ( shouldProcessAsDynamic ( parent , child ) ) {
174+ if ( shouldProcessChildAsDynamic ( parent , child ) ) {
173175 processChildren (
174176 { children : [ child ] } ,
175177 context ,
@@ -274,87 +276,127 @@ const isStaticChildNode = (c: TemplateChildNode): boolean =>
274276 c . type === NodeTypes . TEXT ||
275277 c . type === NodeTypes . COMMENT
276278
277- /**
278- * Check if a node should be processed as dynamic.
279- * This is primarily used in Vapor mode hydration to wrap dynamic parts
280- * with markers (`<!--[[-->` and `<!--]]-->`).
281- *
282- * <element>
283- * <element/> // Static previous sibling
284- * <Comp/> // Dynamic node (current)
285- * <Comp/> // Dynamic next sibling
286- * <element/> // Static next sibling
287- * </element>
288- */
289- function shouldProcessAsDynamic (
290- parent : { tag ?: string ; children : TemplateChildNode [ ] } ,
291- node : TemplateChildNode ,
292- ) : boolean {
293- // 1. Must be a dynamic node type
294- if ( isStaticChildNode ( node ) ) return false
295- // 2. Must be inside a parent element
296- if ( ! parent . tag ) return false
279+ interface DynamicInfo {
280+ hasStaticPrevious : boolean
281+ hasStaticNext : boolean
282+ prevDynamicCount : number
283+ nextDynamicCount : number
284+ }
297285
298- const children = parent . children . filter (
286+ function processChildrenDynamicInfo (
287+ children : ( TemplateChildNode & { _ssrDynamicInfo ?: DynamicInfo } ) [ ] ,
288+ ) : void {
289+ const filteredChildren = children . filter (
299290 child => ! ( child . type === NodeTypes . TEXT && ! child . content . trim ( ) ) ,
300291 )
301- const len = children . length
302- const index = children . indexOf ( node )
303292
304- // 3. Check for a static previous sibling
305- let hasStaticPreviousSibling = false
306- if ( index > 0 ) {
307- for ( let i = index - 1 ; i >= 0 ; i -- ) {
308- if ( isStaticChildNode ( children [ i ] ) ) {
309- hasStaticPreviousSibling = true
293+ for ( let i = 0 ; i < filteredChildren . length ; i ++ ) {
294+ const child = filteredChildren [ i ]
295+ if ( isStaticChildNode ( child ) ) continue
296+
297+ child . _ssrDynamicInfo = {
298+ hasStaticPrevious : false ,
299+ hasStaticNext : false ,
300+ prevDynamicCount : 0 ,
301+ nextDynamicCount : 0 ,
302+ }
303+
304+ const info = child . _ssrDynamicInfo
305+
306+ // Calculate the previous static and dynamic node counts
307+ let foundStaticPrev = false
308+ let dynamicCountPrev = 0
309+ for ( let j = i - 1 ; j >= 0 ; j -- ) {
310+ const prevChild = filteredChildren [ j ]
311+ if ( isStaticChildNode ( prevChild ) ) {
312+ foundStaticPrev = true
310313 break
311314 }
315+ // if the previous child has dynamic info, use it
316+ else if ( prevChild . _ssrDynamicInfo ) {
317+ foundStaticPrev = prevChild . _ssrDynamicInfo . hasStaticPrevious
318+ dynamicCountPrev = prevChild . _ssrDynamicInfo . prevDynamicCount + 1
319+ break
320+ }
321+ dynamicCountPrev ++
312322 }
313- }
314- if ( ! hasStaticPreviousSibling ) return false
323+ info . hasStaticPrevious = foundStaticPrev
324+ info . prevDynamicCount = dynamicCountPrev
315325
316- // 4. Check for a static next sibling
317- let hasStaticNextSibling = false
318- if ( index > - 1 && index < len - 1 ) {
319- for ( let i = index + 1 ; i < len ; i ++ ) {
320- if ( isStaticChildNode ( children [ i ] ) ) {
321- hasStaticNextSibling = true
326+ // Calculate the number of static and dynamic nodes afterwards
327+ let foundStaticNext = false
328+ let dynamicCountNext = 0
329+ for ( let j = i + 1 ; j < filteredChildren . length ; j ++ ) {
330+ const nextChild = filteredChildren [ j ]
331+ if ( isStaticChildNode ( nextChild ) ) {
332+ foundStaticNext = true
322333 break
323334 }
335+ // if the next child has dynamic info, use it
336+ else if ( nextChild . _ssrDynamicInfo ) {
337+ foundStaticNext = nextChild . _ssrDynamicInfo . hasStaticNext
338+ dynamicCountNext = nextChild . _ssrDynamicInfo . nextDynamicCount + 1
339+ break
340+ }
341+ dynamicCountNext ++
324342 }
343+ info . hasStaticNext = foundStaticNext
344+ info . nextDynamicCount = dynamicCountNext
325345 }
326- if ( ! hasStaticNextSibling ) return false
346+ }
327347
328- // 5. Calculate the number and location of continuous dynamic nodes
329- let dynamicNodeCount = 1 // The current node is counted as one
330- let prevDynamicCount = 0
331- let nextDynamicCount = 0
348+ /**
349+ * Check if a node should be processed as dynamic.
350+ * This is primarily used in Vapor mode hydration to wrap dynamic parts
351+ * with markers (`<!--[[-->` and `<!--]]-->`).
352+ * The purpose is to distinguish the boundaries of nodes during hydration
353+ *
354+ * 1. two consecutive dynamic nodes should only wrap the second one
355+ * <element>
356+ * <element/> // Static node
357+ * <Comp/> // Dynamic node -> should NOT be wrapped
358+ * <Comp/> // Dynamic node -> should be wrapped
359+ * <element/> // Static node
360+ * </element>
361+ *
362+ * 2. three or more consecutive dynamic nodes should only wrap the
363+ * middle nodes, leaving the first and last static.
364+ * <element>
365+ * <element/> // Static node
366+ * <Comp/> // Dynamic node -> should NOT be wrapped
367+ * <Comp/> // Dynamic node -> should be wrapped
368+ * <Comp/> // Dynamic node -> should be wrapped
369+ * <Comp/> // Dynamic node -> should NOT be wrapped
370+ * <element/> // Static node
371+ */
372+ function shouldProcessChildAsDynamic (
373+ parent : { tag ?: string ; children : TemplateChildNode [ ] } ,
374+ node : TemplateChildNode & { _ssrDynamicInfo ?: DynamicInfo } ,
375+ ) : boolean {
376+ // must be inside a parent element
377+ if ( ! parent . tag ) return false
332378
333- // Count consecutive dynamic nodes forward
334- for ( let i = index - 1 ; i >= 0 ; i -- ) {
335- if ( ! isStaticChildNode ( children [ i ] ) ) {
336- prevDynamicCount ++
337- } else {
338- break
339- }
340- }
379+ // must has dynamic info
380+ const { _ssrDynamicInfo : info } = node
381+ if ( ! info ) return false
341382
342- // Count consecutive dynamic nodes backwards
343- for ( let i = index + 1 ; i < len ; i ++ ) {
344- if ( ! isStaticChildNode ( children [ i ] ) ) {
345- nextDynamicCount ++
346- } else {
347- break
348- }
349- }
383+ const {
384+ hasStaticPrevious,
385+ hasStaticNext,
386+ prevDynamicCount,
387+ nextDynamicCount,
388+ } = info
389+
390+ // must have static nodes on both sides
391+ if ( ! hasStaticPrevious || ! hasStaticNext ) return false
350392
351- dynamicNodeCount = 1 + prevDynamicCount + nextDynamicCount
393+ const dynamicNodeCount = 1 + prevDynamicCount + nextDynamicCount
352394
353- // For two consecutive dynamic nodes, mark both as dynamic
395+ // For two consecutive dynamic nodes, mark the second one as dynamic
354396 if ( dynamicNodeCount === 2 ) {
355- return prevDynamicCount > 0 || nextDynamicCount > 0
397+ return prevDynamicCount > 0
356398 }
357- // For three or more dynamic nodes, only mark the intermediate nodes as dynamic
399+ // For three or more dynamic nodes, mark the intermediate node as dynamic
358400 else if ( dynamicNodeCount >= 3 ) {
359401 return prevDynamicCount > 0 && nextDynamicCount > 0
360402 }
0 commit comments