Skip to content

Commit bf78e86

Browse files
committed
refactor(runtime-vapor): prevent duplicate fragment fallback processing and improve invalid fragment detection.
1 parent 2f0676f commit bf78e86

File tree

1 file changed

+40
-17
lines changed

1 file changed

+40
-17
lines changed

packages/runtime-vapor/src/fragment.ts

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -151,20 +151,31 @@ export class DynamicFragment extends VaporFragment {
151151
this.render(render, transition, parent, instance)
152152

153153
if (this.fallback) {
154-
// set fallback for nested fragments
155-
const hasNestedFragment = isFragment(this.nodes)
156-
if (hasNestedFragment) {
157-
setFragmentFallback(this.nodes as VaporFragment, this.fallback)
154+
// Find the deepest invalid fragment
155+
let invalidFragment: VaporFragment | null = null
156+
if (isFragment(this.nodes)) {
157+
setFragmentFallback(
158+
this.nodes,
159+
this.fallback,
160+
(frag: VaporFragment) => {
161+
if (!isValidBlock(frag.nodes)) {
162+
invalidFragment = frag
163+
}
164+
},
165+
)
166+
}
167+
168+
// Check self validity (when no nested fragment or nested is valid)
169+
if (!invalidFragment && !isValidBlock(this.nodes)) {
170+
invalidFragment = this
158171
}
159172

160-
const invalidFragment = findInvalidFragment(this)
161173
if (invalidFragment) {
162174
parent && remove(this.nodes, parent)
163175
const scope = this.scope || (this.scope = new EffectScope())
164176
scope.run(() => {
165-
// for nested fragments, render invalid fragment's fallback
166-
if (hasNestedFragment) {
167-
renderFragmentFallback(invalidFragment)
177+
if (invalidFragment !== this) {
178+
renderFragmentFallback(invalidFragment!)
168179
} else {
169180
this.nodes = this.fallback!() || []
170181
}
@@ -294,10 +305,28 @@ export class DynamicFragment extends VaporFragment {
294305
}
295306
}
296307

308+
// Track which fallback has been set on each fragment
309+
// to avoid duplicate chaining when nested DynamicFragments call setFragmentFallback
310+
const processedFallbacks = new WeakMap<VaporFragment, BlockFn>()
311+
297312
export function setFragmentFallback(
298313
fragment: VaporFragment,
299314
fallback: BlockFn,
315+
onFragment?: (frag: VaporFragment) => void,
300316
): void {
317+
if (processedFallbacks.get(fragment) === fallback) {
318+
// Already processed with this fallback, skip re-setting
319+
// but still recurse since nodes may have changed
320+
if (onFragment) onFragment(fragment)
321+
if (isFragment(fragment.nodes)) {
322+
setFragmentFallback(fragment.nodes, fragment.fallback!, onFragment)
323+
}
324+
return
325+
}
326+
327+
// Mark as processed
328+
processedFallbacks.set(fragment, fallback)
329+
301330
if (fragment.fallback) {
302331
const originalFallback = fragment.fallback
303332
// if the original fallback also renders invalid blocks,
@@ -313,8 +342,10 @@ export function setFragmentFallback(
313342
fragment.fallback = fallback
314343
}
315344

345+
if (onFragment) onFragment(fragment)
346+
316347
if (isFragment(fragment.nodes)) {
317-
setFragmentFallback(fragment.nodes, fragment.fallback)
348+
setFragmentFallback(fragment.nodes, fragment.fallback, onFragment)
318349
}
319350
}
320351

@@ -328,14 +359,6 @@ function renderFragmentFallback(fragment: VaporFragment): void {
328359
}
329360
}
330361

331-
function findInvalidFragment(fragment: VaporFragment): VaporFragment | null {
332-
if (isValidBlock(fragment.nodes)) return null
333-
334-
return isFragment(fragment.nodes)
335-
? findInvalidFragment(fragment.nodes) || fragment
336-
: fragment
337-
}
338-
339362
export function isFragment(val: NonNullable<unknown>): val is VaporFragment {
340363
return val instanceof VaporFragment
341364
}

0 commit comments

Comments
 (0)