@@ -167,9 +167,11 @@ export function processChildren(
167
167
}
168
168
169
169
const { children } = parent
170
+ processChildrenDynamicInfo ( children )
171
+
170
172
for ( let i = 0 ; i < children . length ; i ++ ) {
171
173
const child = children [ i ]
172
- if ( shouldProcessAsDynamic ( parent , child ) ) {
174
+ if ( shouldProcessChildAsDynamic ( parent , child ) ) {
173
175
processChildren (
174
176
{ children : [ child ] } ,
175
177
context ,
@@ -274,87 +276,127 @@ const isStaticChildNode = (c: TemplateChildNode): boolean =>
274
276
c . type === NodeTypes . TEXT ||
275
277
c . type === NodeTypes . COMMENT
276
278
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
+ }
297
285
298
- const children = parent . children . filter (
286
+ function processChildrenDynamicInfo (
287
+ children : ( TemplateChildNode & { _ssrDynamicInfo ?: DynamicInfo } ) [ ] ,
288
+ ) : void {
289
+ const filteredChildren = children . filter (
299
290
child => ! ( child . type === NodeTypes . TEXT && ! child . content . trim ( ) ) ,
300
291
)
301
- const len = children . length
302
- const index = children . indexOf ( node )
303
292
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
310
313
break
311
314
}
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 ++
312
322
}
313
- }
314
- if ( ! hasStaticPreviousSibling ) return false
323
+ info . hasStaticPrevious = foundStaticPrev
324
+ info . prevDynamicCount = dynamicCountPrev
315
325
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
322
333
break
323
334
}
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 ++
324
342
}
343
+ info . hasStaticNext = foundStaticNext
344
+ info . nextDynamicCount = dynamicCountNext
325
345
}
326
- if ( ! hasStaticNextSibling ) return false
346
+ }
327
347
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
332
378
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
341
382
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
350
392
351
- dynamicNodeCount = 1 + prevDynamicCount + nextDynamicCount
393
+ const dynamicNodeCount = 1 + prevDynamicCount + nextDynamicCount
352
394
353
- // For two consecutive dynamic nodes, mark both as dynamic
395
+ // For two consecutive dynamic nodes, mark the second one as dynamic
354
396
if ( dynamicNodeCount === 2 ) {
355
- return prevDynamicCount > 0 || nextDynamicCount > 0
397
+ return prevDynamicCount > 0
356
398
}
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
358
400
else if ( dynamicNodeCount >= 3 ) {
359
401
return prevDynamicCount > 0 && nextDynamicCount > 0
360
402
}
0 commit comments