@ -1,12 +1,9 @@
/** @import { Effect, Source, TemplateNode } from '#client' */
import { DEV } from 'esm-env' ;
/** @import { Source, TemplateNode } from '#client' */
import { is _promise } from '../../../shared/utils.js' ;
import { block , branch , pause _effect , resume _effect } from '../../reactivity/effects.js' ;
import { block } from '../../reactivity/effects.js' ;
import { internal _set , mutable _source , source } from '../../reactivity/sources.js' ;
import { set _active _effect , set _active _reaction } from '../../runtime.js' ;
import {
hydrate _next ,
hydrate _node ,
hydrating ,
skip _nodes ,
set _hydrate _node ,
@ -14,15 +11,10 @@ import {
} from '../hydration.js' ;
import { queue _micro _task } from '../task.js' ;
import { HYDRATION _START _ELSE , UNINITIALIZED } from '../../../../constants.js' ;
import {
component _context ,
dev _stack ,
is _runes ,
set _component _context ,
set _dev _current _component _function ,
set _dev _stack
} from '../../context.js' ;
import { is _runes } from '../../context.js' ;
import { flushSync , is _flushing _sync } from '../../reactivity/batch.js' ;
import { BranchManager } from './branches.js' ;
import { capture , unset _context } from '../../reactivity/async.js' ;
const PENDING = 0 ;
const THEN = 1 ;
@ -33,7 +25,7 @@ const CATCH = 2;
/ * *
* @ template V
* @ param { TemplateNode } node
* @ param { ( ( ) => Promise < V > ) } get _input
* @ param { ( ( ) => any ) } get _input
* @ param { null | ( ( anchor : Node ) => void ) } pending _fn
* @ param { null | ( ( anchor : Node , value : Source < V > ) => void ) } then _fn
* @ param { null | ( ( anchor : Node , error : unknown ) => void ) } catch _fn
@ -44,149 +36,94 @@ export function await_block(node, get_input, pending_fn, then_fn, catch_fn) {
hydrate _next ( ) ;
}
var anchor = node ;
var runes = is _runes ( ) ;
var active _component _context = component _context ;
/** @type {any} */
var component _function = DEV ? component _context ? . function : null ;
var dev _original _stack = DEV ? dev _stack : null ;
/** @type {V | Promise<V> | typeof UNINITIALIZED} */
var input = UNINITIALIZED ;
/** @type {Effect | null} */
var pending _effect ;
/** @type {Effect | null} */
var then _effect ;
/** @type {Effect | null} */
var catch _effect ;
var input _source = runes
? source ( /** @type {V} */ ( undefined ) )
: mutable _source ( /** @type {V} */ ( undefined ) , false , false ) ;
var error _source = runes ? source ( undefined ) : mutable _source ( undefined , false , false ) ;
var resolved = false ;
/ * *
* @ param { AwaitState } state
* @ param { boolean } restore
* /
function update ( state , restore ) {
resolved = true ;
if ( restore ) {
set _active _effect ( effect ) ;
set _active _reaction ( effect ) ; // TODO do we need both?
set _component _context ( active _component _context ) ;
if ( DEV ) {
set _dev _current _component _function ( component _function ) ;
set _dev _stack ( dev _original _stack ) ;
}
}
try {
if ( state === PENDING && pending _fn ) {
if ( pending _effect ) resume _effect ( pending _effect ) ;
else pending _effect = branch ( ( ) => pending _fn ( anchor ) ) ;
}
if ( state === THEN && then _fn ) {
if ( then _effect ) resume _effect ( then _effect ) ;
else then _effect = branch ( ( ) => then _fn ( anchor , input _source ) ) ;
}
if ( state === CATCH && catch _fn ) {
if ( catch _effect ) resume _effect ( catch _effect ) ;
else catch _effect = branch ( ( ) => catch _fn ( anchor , error _source ) ) ;
}
if ( state !== PENDING && pending _effect ) {
pause _effect ( pending _effect , ( ) => ( pending _effect = null ) ) ;
}
if ( state !== THEN && then _effect ) {
pause _effect ( then _effect , ( ) => ( then _effect = null ) ) ;
}
if ( state !== CATCH && catch _effect ) {
pause _effect ( catch _effect , ( ) => ( catch _effect = null ) ) ;
}
} finally {
if ( restore ) {
if ( DEV ) {
set _dev _current _component _function ( null ) ;
set _dev _stack ( null ) ;
}
set _component _context ( null ) ;
set _active _reaction ( null ) ;
set _active _effect ( null ) ;
var v = /** @type {V} */ ( UNINITIALIZED ) ;
var value = runes ? source ( v ) : mutable _source ( v , false , false ) ;
var error = runes ? source ( v ) : mutable _source ( v , false , false ) ;
// without this, the DOM does not update until two ticks after the promise
// resolves, which is unexpected behaviour (and somewhat irksome to test)
if ( ! is _flushing _sync ) flushSync ( ) ;
}
}
}
var branches = new BranchManager ( node ) ;
var effect = block ( ( ) => {
if ( input === ( input = get _input ( ) ) ) return ;
block ( ( ) => {
var input = get _input ( ) ;
var destroyed = false ;
/** Whether or not there was a hydration mismatch. Needs to be a `let` or else it isn't treeshaken out */
// @ts-ignore coercing ` anchor ` to a `Comment` causes TypeScript and Prettier to fight
let mismatch = hydrating && is _promise ( input ) === ( anchor . data === HYDRATION _START _ELSE ) ;
// @ts-ignore coercing `node` to a `Comment` causes TypeScript and Prettier to fight
let mismatch = hydrating && is _promise ( input ) === ( node . data === HYDRATION _START _ELSE ) ;
if ( mismatch ) {
// Hydration mismatch: remove everything inside the anchor and start fresh
anchor = skip _nodes ( ) ;
set _hydrate _node ( anchor ) ;
set _hydrate _node ( skip _nodes ( ) ) ;
set _hydrating ( false ) ;
mismatch = true ;
}
if ( is _promise ( input ) ) {
var promise = input ;
var restore = capture ( ) ;
var resolved = false ;
/ * *
* @ param { ( ) => void } fn
* /
const resolve = ( fn ) => {
if ( destroyed ) return ;
resolved = true ;
restore ( ) ;
if ( hydrating ) {
// `restore()` could set `hydrating` to `true`, which we very much
// don't want — we want to restore everything _except_ this
set _hydrating ( false ) ;
}
resolved = false ;
try {
fn ( ) ;
} finally {
unset _context ( ) ;
promise . then (
( value ) => {
if ( promise !== input ) return ;
// we technically could use `set` here since it's on the next microtick
// but let's use internal_set for consistency and just to be safe
internal _set ( input _source , value ) ;
update ( THEN , true ) ;
// without this, the DOM does not update until two ticks after the promise
// resolves, which is unexpected behaviour (and somewhat irksome to test)
if ( ! is _flushing _sync ) flushSync ( ) ;
}
} ;
input . then (
( v ) => {
resolve ( ( ) => {
internal _set ( value , v ) ;
branches . ensure ( THEN , then _fn && ( ( target ) => then _fn ( target , value ) ) ) ;
} ) ;
} ,
( error ) => {
if ( promise !== input ) return ;
// we technically could use `set` here since it's on the next microtick
// but let's use internal_set for consistency and just to be safe
internal _set ( error _source , error ) ;
update ( CATCH , true ) ;
if ( ! catch _fn ) {
// Rethrow the error if no catch block exists
throw error _source . v ;
}
( e ) => {
resolve ( ( ) => {
internal _set ( error , e ) ;
branches . ensure ( THEN , catch _fn && ( ( target ) => catch _fn ( target , error ) ) ) ;
if ( ! catch _fn ) {
// Rethrow the error if no catch block exists
throw error . v ;
}
} ) ;
}
) ;
if ( hydrating ) {
if ( pending _fn ) {
pending _effect = branch ( ( ) => pending _fn ( anchor ) ) ;
}
branches . ensure ( PENDING , pending _fn ) ;
} else {
// Wait a microtask before checking if we should show the pending state as
// the promise might have resolved by the next microtask.
// the promise might have resolved by the n
queue _micro _task ( ( ) => {
if ( ! resolved ) update ( PENDING , true ) ;
if ( ! resolved ) {
resolve ( ( ) => {
branches . ensure ( PENDING , pending _fn ) ;
} ) ;
}
} ) ;
}
} else {
internal _set ( input _source , input ) ;
update( THEN , false ) ;
internal _set ( valu e, input ) ;
branches. ensure ( THEN , then _fn && ( ( target ) => then _fn ( target , value ) ) ) ;
}
if ( mismatch ) {
@ -194,11 +131,8 @@ export function await_block(node, get_input, pending_fn, then_fn, catch_fn) {
set _hydrating ( true ) ;
}
// Set the input to something else, in order to disable the promise callbacks
return ( ) => ( input = UNINITIALIZED ) ;
return ( ) => {
destroyed = true ;
} ;
} ) ;
if ( hydrating ) {
anchor = hydrate _node ;
}
}