@@ -157,13 +157,29 @@ export function processChildren(
157
157
asFragment = false ,
158
158
disableNestedFragments = false ,
159
159
disableComment = false ,
160
+ asDynamic = false ,
160
161
) : void {
162
+ if ( asDynamic ) {
163
+ context . pushStringPart ( `<!--[[-->` )
164
+ }
161
165
if ( asFragment ) {
162
166
context . pushStringPart ( `<!--[-->` )
163
167
}
168
+
164
169
const { children } = parent
165
170
for ( let i = 0 ; i < children . length ; i ++ ) {
166
171
const child = children [ i ]
172
+ if ( shouldProcessAsDynamic ( parent , child ) ) {
173
+ processChildren (
174
+ { children : [ child ] } ,
175
+ context ,
176
+ asFragment ,
177
+ disableNestedFragments ,
178
+ disableComment ,
179
+ true ,
180
+ )
181
+ continue
182
+ }
167
183
switch ( child . type ) {
168
184
case NodeTypes . ELEMENT :
169
185
switch ( child . tagType ) {
@@ -237,6 +253,9 @@ export function processChildren(
237
253
if ( asFragment ) {
238
254
context . pushStringPart ( `<!--]-->` )
239
255
}
256
+ if ( asDynamic ) {
257
+ context . pushStringPart ( `<!--]]-->` )
258
+ }
240
259
}
241
260
242
261
export function processChildrenAsStatement (
@@ -249,3 +268,72 @@ export function processChildrenAsStatement(
249
268
processChildren ( parent , childContext , asFragment )
250
269
return createBlockStatement ( childContext . body )
251
270
}
271
+
272
+ const isStaticElement = ( c : TemplateChildNode ) : boolean =>
273
+ c . type === NodeTypes . ELEMENT && c . tagType !== ElementTypes . COMPONENT
274
+
275
+ /**
276
+ * Check if a node should be processed as dynamic.
277
+ * This is primarily used in Vapor mode hydration to wrap dynamic parts
278
+ * with markers (`<!--[[-->` and `<!--]]-->`).
279
+ *
280
+ * <element>
281
+ * <element/> // Static previous sibling
282
+ * <Comp/> // Dynamic node (current)
283
+ * <Comp/> // Dynamic next sibling
284
+ * <element/> // Static next sibling
285
+ * </element>
286
+ */
287
+ function shouldProcessAsDynamic (
288
+ parent : { tag ?: string ; children : TemplateChildNode [ ] } ,
289
+ node : TemplateChildNode ,
290
+ ) : boolean {
291
+ // 1. Must be a dynamic node type
292
+ if ( isStaticElement ( node ) ) return false
293
+ // 2. Must be inside a parent element
294
+ if ( ! parent . tag ) return false
295
+
296
+ const children = parent . children
297
+ const len = children . length
298
+ const index = children . indexOf ( node )
299
+
300
+ // 3. Check for a static previous sibling
301
+ let hasStaticPreviousSibling = false
302
+ if ( index > 0 ) {
303
+ for ( let i = index - 1 ; i >= 0 ; i -- ) {
304
+ if ( isStaticElement ( children [ i ] ) ) {
305
+ hasStaticPreviousSibling = true
306
+ break
307
+ }
308
+ }
309
+ }
310
+ if ( ! hasStaticPreviousSibling ) return false
311
+
312
+ // 4. Check for a static next sibling
313
+ let hasStaticNextSibling = false
314
+ if ( index > - 1 && index < len - 1 ) {
315
+ for ( let i = index + 1 ; i < len ; i ++ ) {
316
+ if ( isStaticElement ( children [ i ] ) ) {
317
+ hasStaticNextSibling = true
318
+ break
319
+ }
320
+ }
321
+ }
322
+ if ( ! hasStaticNextSibling ) return false
323
+
324
+ // 5. Check for a consecutive dynamic sibling (immediately before or after)
325
+ let hasConsecutiveDynamicNodes = false
326
+ if ( index > 0 && ! isStaticElement ( children [ index - 1 ] ) ) {
327
+ hasConsecutiveDynamicNodes = true
328
+ }
329
+ if (
330
+ ! hasConsecutiveDynamicNodes &&
331
+ index < len - 1 &&
332
+ ! isStaticElement ( children [ index + 1 ] )
333
+ ) {
334
+ hasConsecutiveDynamicNodes = true
335
+ }
336
+
337
+ // Only process as dynamic if all conditions are met
338
+ return hasConsecutiveDynamicNodes
339
+ }
0 commit comments