@ -1,8 +1,6 @@
/** @import { Effect, Source, TemplateNode, } from '#client' */
import {
BLOCK _EFFECT ,
BOUNDARY _EFFECT ,
COMMENT _NODE ,
DIRTY ,
EFFECT _PRESERVED ,
EFFECT _TRANSPARENT ,
@ -53,7 +51,7 @@ import { set_signal_status } from '../../reactivity/status.js';
* } } BoundaryProps
* /
var flags = EFFECT _TRANSPARENT | EFFECT _PRESERVED | BOUNDARY _EFFECT ;
var flags = EFFECT _TRANSPARENT | EFFECT _PRESERVED ;
/ * *
* @ param { TemplateNode } node
@ -98,15 +96,10 @@ export class Boundary {
/** @type {DocumentFragment | null} */
# offscreen _fragment = null ;
/** @type {TemplateNode | null} */
# pending _anchor = null ;
# local _pending _count = 0 ;
# pending _count = 0 ;
# pending _count _update _queued = false ;
# is _creating _fallback = false ;
/** @type {Set<Effect>} */
# dirty _effects = new Set ( ) ;
@ -142,51 +135,31 @@ export class Boundary {
constructor ( node , props , children ) {
this . # anchor = node ;
this . # props = props ;
this . # children = children ;
this . parent = /** @type {Effect} */ ( active _effect ) . b ;
this . # children = ( anchor ) => {
var effect = /** @type {Effect} */ ( active _effect ) ;
this . is _pending = ! ! this . # props . pending ;
effect . b = this ;
effect . f |= BOUNDARY _EFFECT ;
this . # effect = block ( ( ) => {
/** @type {Effect} */ ( active _effect ) . b = this ;
children ( anchor ) ;
} ;
this . parent = /** @type {Effect} */ ( active _effect ) . b ;
this . # effect = block ( ( ) => {
if ( hydrating ) {
const comment = this . # hydrate _open ;
const comment = /** @type {Comment} */ ( this . # hydrate _open ) ;
hydrate _next ( ) ;
const server _rendered _pending =
/** @type {Comment} */ ( comment ) . nodeType === COMMENT _NODE &&
/** @type {Comment} */ ( comment ) . data === HYDRATION _START _ELSE ;
if ( server _rendered _pending ) {
if ( comment . data === HYDRATION _START _ELSE ) {
this . # hydrate _pending _content ( ) ;
} else {
this . # hydrate _resolved _content ( ) ;
if ( this . # pending _count === 0 ) {
this . is _pending = false ;
}
}
} else {
var anchor = this . # get _anchor ( ) ;
try {
this . # main _effect = branch ( ( ) => children ( anchor ) ) ;
} catch ( error ) {
this . error ( error ) ;
}
if ( this . # pending _count > 0 ) {
this . # show _pending _snippet ( ) ;
} else {
this . is _pending = false ;
}
this . # render ( ) ;
}
return ( ) => {
this . # pending _anchor ? . remove ( ) ;
} ;
} , flags ) ;
if ( hydrating ) {
@ -206,39 +179,75 @@ export class Boundary {
const pending = this . # props . pending ;
if ( ! pending ) return ;
this . is _pending = true ;
this . # pending _effect = branch ( ( ) => pending ( this . # anchor ) ) ;
queue _micro _task ( ( ) => {
var anchor = this . # get _anchor ( ) ;
var fragment = ( this . # offscreen _fragment = document . createDocumentFragment ( ) ) ;
var anchor = create _text ( ) ;
fragment . append ( anchor ) ;
this . # main _effect = this . # run ( ( ) => {
Batch . ensure ( ) ;
return branch ( ( ) => this . # children ( anchor ) ) ;
} ) ;
if ( this . # pending _count > 0 ) {
this . # show _pending _snippet ( ) ;
} else {
if ( this . # pending _count === 0 ) {
this . # anchor . before ( fragment ) ;
this . # offscreen _fragment = null ;
pause _effect ( /** @type {Effect} */ ( this . # pending _effect ) , ( ) => {
this . # pending _effect = null ;
} ) ;
this . is _pending = false ;
this . # resolve ( ) ;
}
} ) ;
}
# get _anchor ( ) {
var anchor = this . # anchor ;
# render ( ) {
try {
this . is _pending = this . has _pending _snippet ( ) ;
this . # pending _count = 0 ;
this . # local _pending _count = 0 ;
this . # main _effect = branch ( ( ) => {
this . # children ( this . # anchor ) ;
} ) ;
if ( this . is _pending ) {
this . # pending _anchor = create _text ( ) ;
this . # anchor . before ( this . # pending _anchor ) ;
if ( this . # pending _count > 0 ) {
var fragment = ( this . # offscreen _fragment = document . createDocumentFragment ( ) ) ;
move _effect ( this . # main _effect , fragment ) ;
anchor = this . # pending _anchor ;
const pending = /** @type {(anchor: Node) => void} */ ( this . # props . pending ) ;
this . # pending _effect = branch ( ( ) => pending ( this . # anchor ) ) ;
} else {
this . # resolve ( ) ;
}
} catch ( error ) {
this . error ( error ) ;
}
}
# resolve ( ) {
this . is _pending = false ;
// any effects that were previously deferred should be rescheduled —
// after the next traversal (which will happen immediately, due to the
// same update that brought us here) the effects will be flushed
for ( const e of this . # dirty _effects ) {
set _signal _status ( e , DIRTY ) ;
schedule _effect ( e ) ;
}
for ( const e of this . # maybe _dirty _effects ) {
set _signal _status ( e , MAYBE _DIRTY ) ;
schedule _effect ( e ) ;
}
return anchor ;
this . # dirty _effects . clear ( ) ;
this . # maybe _dirty _effects . clear ( ) ;
}
/ * *
@ -262,7 +271,8 @@ export class Boundary {
}
/ * *
* @ param { ( ) => Effect | null } fn
* @ template T
* @ param { ( ) => T } fn
* /
# run ( fn ) {
var previous _effect = active _effect ;
@ -285,20 +295,6 @@ export class Boundary {
}
}
# show _pending _snippet ( ) {
const pending = /** @type {(anchor: Node) => void} */ ( this . # props . pending ) ;
if ( this . # main _effect !== null ) {
this . # offscreen _fragment = document . createDocumentFragment ( ) ;
this . # offscreen _fragment . append ( /** @type {TemplateNode} */ ( this . # pending _anchor ) ) ;
move _effect ( this . # main _effect , this . # offscreen _fragment ) ;
}
if ( this . # pending _effect === null ) {
this . # pending _effect = branch ( ( ) => pending ( this . # anchor ) ) ;
}
}
/ * *
* Updates the pending count associated with the currently visible pending snippet ,
* if any , such that we can replace the snippet with content once work is done
@ -317,24 +313,7 @@ export class Boundary {
this . # pending _count += d ;
if ( this . # pending _count === 0 ) {
this . is _pending = false ;
// any effects that were encountered and deferred during traversal
// should be rescheduled — after the next traversal (which will happen
// immediately, due to the same update that brought us here)
// the effects will be flushed
for ( const e of this . # dirty _effects ) {
set _signal _status ( e , DIRTY ) ;
schedule _effect ( e ) ;
}
for ( const e of this . # maybe _dirty _effects ) {
set _signal _status ( e , MAYBE _DIRTY ) ;
schedule _effect ( e ) ;
}
this . # dirty _effects . clear ( ) ;
this . # maybe _dirty _effects . clear ( ) ;
this . # resolve ( ) ;
if ( this . # pending _effect ) {
pause _effect ( this . # pending _effect , ( ) => {
@ -383,7 +362,7 @@ export class Boundary {
// If we have nothing to capture the error, or if we hit an error while
// rendering the fallback, re-throw for another boundary to handle
if ( this . # is _creating _fallback || ( ! onerror && ! failed ) ) {
if ( ! onerror && ! failed ) {
throw error ;
}
@ -423,31 +402,18 @@ export class Boundary {
e . svelte _boundary _reset _onerror ( ) ;
}
// If the failure happened while flushing effects, current_batch can be null
Batch . ensure ( ) ;
this . # local _pending _count = 0 ;
if ( this . # failed _effect !== null ) {
pause _effect ( this . # failed _effect , ( ) => {
this . # failed _effect = null ;
} ) ;
}
// we intentionally do not try to find the nearest pending boundary. If this boundary has one, we'll render it on reset
// but it would be really weird to show the parent's boundary on a child reset.
this . is _pending = this . has _pending _snippet ( ) ;
this . # run ( ( ) => {
// If the failure happened while flushing effects, current_batch can be null
Batch . ensure ( ) ;
this . # main _effect = this . # run ( ( ) => {
this . # is _creating _fallback = false ;
return branch ( ( ) => this . # children ( this . # anchor ) ) ;
this . # render ( ) ;
} ) ;
if ( this . # pending _count > 0 ) {
this . # show _pending _snippet ( ) ;
} else {
this . is _pending = false ;
}
} ;
queue _micro _task ( ( ) => {
@ -462,10 +428,16 @@ export class Boundary {
if ( failed ) {
this . # failed _effect = this . # run ( ( ) => {
Batch . ensure ( ) ;
this . # is _creating _fallback = true ;
try {
return branch ( ( ) => {
// errors in `failed` snippets cause the boundary to error again
// TODO Svelte 6: revisit this decision, most likely better to go to parent boundary instead
var effect = /** @type {Effect} */ ( active _effect ) ;
effect . b = this ;
effect . f |= BOUNDARY _EFFECT ;
failed (
this . # anchor ,
( ) => error ,
@ -475,8 +447,6 @@ export class Boundary {
} catch ( error ) {
invoke _error _boundary ( error , /** @type {Effect} */ ( this . # effect . parent ) ) ;
return null ;
} finally {
this . # is _creating _fallback = false ;
}
} ) ;
}
@ -484,10 +454,6 @@ export class Boundary {
}
}
export function get _boundary ( ) {
return /** @type {Boundary} */ ( /** @type {Effect} */ ( active _effect ) . b ) ;
}
export function pending ( ) {
if ( active _effect === null ) {
e . effect _pending _outside _reaction ( ) ;