@ -1,5 +1,3 @@
// Handle hydration
import { schedule _task } from './task.js' ;
import { empty } from './operations.js' ;
@ -9,66 +7,79 @@ import { empty } from './operations.js';
* /
export let hydrating = false ;
/** @param {boolean} value */
export function set _hydrating ( value ) {
hydrating = value ;
}
/ * *
* Array of nodes to traverse for hydration . This will be null if we ' re not hydrating , but for
* the sake of simplicity we ' re not going to use ` null ` checks everywhere and instead rely on
* the ` hydrating ` flag to tell whether or not we ' re in hydration mode at which point this is set .
* @ type { import ( '../types.js' ) . TemplateNode [ ] }
* @ type { import ( ' #client ') . TemplateNode [ ] }
* /
export let current_hydration _fragment = /** @type {any} */ ( null ) ;
export let hydrate_nodes = /** @type {any} */ ( null ) ;
/ * *
* @ param { null | import ( ' ../types.js') . TemplateNode [ ] } fragment
* @ param { null | import ( ' #client') . TemplateNode [ ] } nodes
* @ returns { void }
* /
export function set _current _hydration _fragment ( fragment ) {
hydrating = fragment !== null ;
current _hydration _fragment = /** @type {import('../types.js').TemplateNode[]} */ ( fragment ) ;
export function set _hydrate _nodes ( nodes ) {
hydrate _nodes = /** @type {import('#client').TemplateNode[]} */ ( nodes ) ;
}
/ * *
* @ param { Node | null } first
* @ param { boolean } [ insert _text ] Whether to insert an empty text node if ` nodes ` is empty
* /
export function update _hydrate _nodes ( first , insert _text ) {
const nodes = get _hydrate _nodes ( first , insert _text ) ;
set _hydrate _nodes ( nodes ) ;
return nodes ;
}
/ * *
* Returns all nodes between the first ` <!--ssr:...--> ` comment tag pair encountered .
* @ param { Node | null } node
* @ param { boolean } [ insert _text ] Whether to insert an empty text node if the fragment is empty
* @ returns { import ( '../types.js' ) . TemplateNode [ ] | null }
* @ param { boolean } [ insert _text ] Whether to insert an empty text node if ` nodes ` is empty
* @ returns { import ( ' #client ') . TemplateNode [ ] | null }
* /
export function get _hydration _fragment ( node , insert _text = false ) {
/** @type {import(' ../types.js ').TemplateNode[]} */
const fragment = [ ] ;
function get _hydrate _nodes ( node , insert _text = false ) {
/** @type {import(' #client ').TemplateNode[]} */
var nodes = [ ] ;
/** @type {null | Node} */
let current _node = node ;
var current _node = /** @type {null | import('#client').TemplateNode} */ ( node ) ;
/** @type {null | string} */
let target _depth = null ;
var target _depth = null ;
while ( current _node !== null ) {
const node _type = current _node . nodeType ;
const next _sibling = current _node . nextSibling ;
if ( node _type === 8 ) {
const data = /** @type {Comment} */ ( current _node ) . data ;
if ( current _node . nodeType === 8 ) {
var data = /** @type {Comment} */ ( current _node ) . data ;
if ( data . startsWith ( 'ssr:' ) ) {
const depth = data . slice ( 4 ) ;
var depth = data . slice ( 4 ) ;
if ( target _depth === null ) {
target _depth = depth ;
} else if ( depth === target _depth ) {
if ( insert _text && fragment . length === 0 ) {
const text = empty ( ) ;
fragment . push ( text ) ;
/** @type {Node} */ ( current _node . parentNode ) . insertBefore ( text , current _node ) ;
if ( insert _text && nodes . length === 0 ) {
var text = empty ( ) ;
nodes . push ( text ) ;
current _node . before ( text ) ;
}
return fragment ;
return nodes ;
} else {
fragment. push ( /** @type {Text | Comment | Element} */ ( current _node ) ) ;
nodes. push ( current _node ) ;
}
current _node = next _sibling ;
continue ;
}
} else if ( target _depth !== null ) {
nodes . push ( current _node ) ;
}
if ( target _depth !== null ) {
fragment . push ( /** @type {Text | Comment | Element} */ ( current _node ) ) ;
}
current _node = next _sibling ;
current _node = /** @type {null | import('#client').TemplateNode} */ ( current _node . nextSibling ) ;
}
return null ;
}
@ -81,18 +92,39 @@ export function hydrate_block_anchor(node) {
if ( node . nodeType === 8 ) {
// @ts-ignore
let fragment = node . $$fragment ;
if ( fragment === undefined ) {
fragment = get _hydration _fragment ( node ) ;
let nodes = node . $$fragment ;
if ( nodes === undefined ) {
nodes = get _hydrate _nodes ( node ) ;
} else {
schedule _task ( ( ) => {
// @ts-expect-error clean up memory
node . $$fragment = undefined ;
} ) ;
}
set _ current_hydration _fragment ( fragment ) ;
set _ hydrate_nodes ( nodes ) ;
} else {
const first _child = /** @type {Element | null} */ ( node . firstChild ) ;
set _current _hydration _fragment ( first _child === null ? [ ] : [ first _child ] ) ;
set _hydrate _nodes ( first _child === null ? [ ] : [ first _child ] ) ;
}
}
/ * *
* Expects to only be called in hydration mode
* @ param { Node } node
* @ returns { Node }
* /
export function capture _fragment _from _node ( node ) {
if (
node . nodeType === 8 &&
/** @type {Comment} */ ( node ) . data . startsWith ( 'ssr:' ) &&
hydrate _nodes [ hydrate _nodes . length - 1 ] !== node
) {
const nodes = /** @type {Node[]} */ ( get _hydrate _nodes ( node ) ) ;
const last _child = nodes [ nodes . length - 1 ] || node ;
const target = /** @type {Node} */ ( last _child . nextSibling ) ;
// @ts-ignore
target . $$fragment = nodes ;
return target ;
}
return node ;
}