@ -5,9 +5,12 @@ import { regex_ends_with_whitespace, regex_starts_with_whitespace } from '../../
import { get _attribute _chunks , is _text _attribute } from '../../../utils/ast.js' ;
/** @typedef {NODE_PROBABLY_EXISTS | NODE_DEFINITELY_EXISTS} NodeExistsValue */
/** @typedef {FORWARD | BACKWARD} Direction */
const NODE _PROBABLY _EXISTS = 0 ;
const NODE _DEFINITELY _EXISTS = 1 ;
const FORWARD = 0 ;
const BACKWARD = 1 ;
const whitelist _attribute _selector = new Map ( [
[ 'details' , [ 'open' ] ] ,
@ -43,6 +46,27 @@ const nesting_selector = {
}
} ;
/** @type {Compiler.AST.CSS.RelativeSelector} */
const any _selector = {
type : 'RelativeSelector' ,
start : - 1 ,
end : - 1 ,
combinator : null ,
selectors : [
{
type : 'TypeSelector' ,
name : '*' ,
start : - 1 ,
end : - 1
}
] ,
metadata : {
is _global : false ,
is _global _like : false ,
scoped : false
}
} ;
/ * *
* Snippets encountered already ( avoids infinite loops )
* @ type { Set < Compiler . AST . SnippetBlock > }
@ -72,7 +96,8 @@ export function prune(stylesheet, element) {
apply _selector (
selectors ,
/** @type {Compiler.AST.CSS.Rule} */ ( node . metadata . rule ) ,
element
element ,
BACKWARD
)
) {
node . metadata . used = true ;
@ -159,16 +184,17 @@ function truncate(node) {
* @ param { Compiler . AST . CSS . RelativeSelector [ ] } relative _selectors
* @ param { Compiler . AST . CSS . Rule } rule
* @ param { Compiler . AST . RegularElement | Compiler . AST . SvelteElement } element
* @ param { Direction } direction
* @ returns { boolean }
* /
function apply _selector ( relative _selectors , rule , element ) {
const paren t_selectors = relative _selectors . slice ( ) ;
const relative _selector = paren t_selectors . pop ( ) ;
function apply _selector ( relative _selectors , rule , element , direction ) {
const res t_selectors = relative _selectors . slice ( ) ;
const relative _selector = direction === FORWARD ? rest _selectors . shift ( ) : res t_selectors . pop ( ) ;
const matched =
! ! relative _selector &&
relative _selector _might _apply _to _node ( relative _selector , rule , element ) &&
apply _combinator ( relative _selector , paren t_selectors , rule , element ) ;
relative _selector _might _apply _to _node ( relative _selector , rule , element , direction ) &&
apply _combinator ( relative _selector , res t_selectors , rule , element , direction ) ;
if ( matched ) {
if ( ! is _outer _global ( relative _selector ) ) {
@ -183,76 +209,63 @@ function apply_selector(relative_selectors, rule, element) {
/ * *
* @ param { Compiler . AST . CSS . RelativeSelector } relative _selector
* @ param { Compiler . AST . CSS . RelativeSelector [ ] } paren t_selectors
* @ param { Compiler . AST . CSS . RelativeSelector [ ] } res t_selectors
* @ param { Compiler . AST . CSS . Rule } rule
* @ param { Compiler . AST . RegularElement | Compiler . AST . SvelteElement | Compiler . AST . RenderTag | Compiler . AST . Component | Compiler . AST . SvelteComponent | Compiler . AST . SvelteSelf } node
* @ param { Direction } direction
* @ returns { boolean }
* /
function apply _combinator ( relative _selector , paren t_selectors , rule , node ) {
if ( ! relative _selector . combinator ) return true ;
const name = relative _selector . combinator . nam e;
function apply _combinator ( relative _selector , res t_selectors , rule , node , direction ) {
const combinator =
direction == FORWARD ? rest _selectors [ 0 ] ? . combinator : relative _selector . combinator ;
if ( ! combinator ) return tru e;
switch ( name) {
switch ( combinator. name) {
case ' ' :
case '>' : {
const is _adjacent = combinator . name === '>' ;
const parents =
direction === FORWARD
? get _descendant _elements ( node , is _adjacent )
: get _ancestor _elements ( node , is _adjacent ) ;
let parent _matched = false ;
const path = node . metadata . path ;
let i = path . length ;
while ( i -- ) {
const parent = path [ i ] ;
if ( parent . type === 'SnippetBlock' ) {
if ( seen . has ( parent ) ) {
parent _matched = true ;
} else {
seen . add ( parent ) ;
for ( const site of parent . metadata . sites ) {
if ( apply _combinator ( relative _selector , parent _selectors , rule , site ) ) {
for ( const parent of parents ) {
if ( apply _selector ( rest _selectors , rule , parent , direction ) ) {
parent _matched = true ;
}
}
}
break ;
}
if ( parent . type === 'RegularElement' || parent . type === 'SvelteElement' ) {
if ( apply _selector ( parent _selectors , rule , parent ) ) {
parent _matched = true ;
}
if ( name === '>' ) return parent _matched ;
}
}
return parent _matched || parent _selectors . every ( ( selector ) => is _global ( selector , rule ) ) ;
return (
parent _matched ||
( direction === BACKWARD &&
( ! is _adjacent || parents . length === 0 ) &&
rest _selectors . every ( ( selector ) => is _global ( selector , rule ) ) )
) ;
}
case '+' :
case '~' : {
const siblings = get _possible _element _siblings ( node , name === '+' ) ;
const siblings = get _possible _element _siblings ( node , direction, combinator . name === '+' ) ;
let sibling _matched = false ;
for ( const possible _sibling of siblings . keys ( ) ) {
if ( possible _sibling . type === 'RenderTag' || possible _sibling . type === 'SlotElement' ) {
// `{@render foo()}<p>foo</p>` with `:global(.x) + p` is a match
if ( parent_selectors . length === 1 && paren t_selectors [ 0 ] . metadata . is _global ) {
if ( rest_selectors . length === 1 && res t_selectors [ 0 ] . metadata . is _global ) {
sibling _matched = true ;
}
} else if ( apply _selector ( paren t_selectors , rule , possible _sibling ) ) {
} else if ( apply _selector ( res t_selectors , rule , possible _sibling , direction ) ) {
sibling _matched = true ;
}
}
return (
sibling _matched ||
( get _element _parent ( node ) === null &&
parent _selectors . every ( ( selector ) => is _global ( selector , rule ) ) )
( direction === BACKWARD &&
get _element _parent ( node ) === null &&
rest _selectors . every ( ( selector ) => is _global ( selector , rule ) ) )
) ;
}
@ -313,9 +326,10 @@ const regex_backslash_and_following_character = /\\(.)/g;
* @ param { Compiler . AST . CSS . RelativeSelector } relative _selector
* @ param { Compiler . AST . CSS . Rule } rule
* @ param { Compiler . AST . RegularElement | Compiler . AST . SvelteElement } element
* @ param { Direction } direction
* @ returns { boolean }
* /
function relative _selector _might _apply _to _node ( relative _selector , rule , element ) {
function relative _selector _might _apply _to _node ( relative _selector , rule , element , direction ) {
// Sort :has(...) selectors in one bucket and everything else into another
const has _selectors = [ ] ;
const other _selectors = [ ] ;
@ -331,13 +345,6 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element)
// If we're called recursively from a :has(...) selector, we're on the way of checking if the other selectors match.
// In that case ignore this check (because we just came from this) to avoid an infinite loop.
if ( has _selectors . length > 0 ) {
/** @type {Array<Compiler.AST.RegularElement | Compiler.AST.SvelteElement>} */
const child _elements = [ ] ;
/** @type {Array<Compiler.AST.RegularElement | Compiler.AST.SvelteElement>} */
const descendant _elements = [ ] ;
/** @type {Array<Compiler.AST.RegularElement | Compiler.AST.SvelteElement>} */
let sibling _elements ; // do them lazy because it's rarely used and expensive to calculate
// If this is a :has inside a global selector, we gotta include the element itself, too,
// because the global selector might be for an element that's outside the component,
// e.g. :root:has(.scoped), :global(.foo):has(.scoped), or :root { &:has(.scoped) {} }
@ -353,46 +360,6 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element)
)
)
) ;
if ( include _self ) {
child _elements . push ( element ) ;
descendant _elements . push ( element ) ;
}
const seen = new Set ( ) ;
/ * *
* @ param { Compiler . AST . SvelteNode } node
* @ param { { is _child : boolean } } state
* /
function walk _children ( node , state ) {
walk ( node , state , {
_ ( node , context ) {
if ( node . type === 'RegularElement' || node . type === 'SvelteElement' ) {
descendant _elements . push ( node ) ;
if ( context . state . is _child ) {
child _elements . push ( node ) ;
context . state . is _child = false ;
context . next ( ) ;
context . state . is _child = true ;
} else {
context . next ( ) ;
}
} else if ( node . type === 'RenderTag' ) {
for ( const snippet of node . metadata . snippets ) {
if ( seen . has ( snippet ) ) continue ;
seen . add ( snippet ) ;
walk _children ( snippet . body , context . state ) ;
}
} else {
context . next ( ) ;
}
}
} ) ;
}
walk _children ( element . fragment , { is _child : true } ) ;
// :has(...) is special in that it means "look downwards in the CSS tree". Since our matching algorithm goes
// upwards and back-to-front, we need to first check the selectors inside :has(...), then check the rest of the
@ -403,36 +370,33 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element)
let matched = false ;
for ( const complex _selector of complex _selectors ) {
const selectors = truncate ( complex _selector ) ;
const left _most _combinator = selectors [ 0 ] ? . combinator ? ? descendant _combinator ;
// In .x:has(> y), we want to search for y, ignoring the left-most combinator
// (else it would try to walk further up and fail because there are no selectors left)
if ( selectors . length > 0 ) {
selectors [ 0 ] = {
... selectors [ 0 ] ,
combinator : null
} ;
const [ first , ... rest ] = truncate ( complex _selector ) ;
// if it was just a :global(...)
if ( ! first ) {
complex _selector . metadata . used = true ;
matched = true ;
continue ;
}
const descendants =
left _most _combinator . name === '+' || left _most _combinator . name === '~'
? ( sibling _elements ? ? = get _following _sibling _elements ( element , include _self ) )
: left _most _combinator . name === '>'
? child _elements
: descendant _elements ;
let selector _matched = false ;
// Iterate over all descendant elements and check if the selector inside :has matches
for ( const element of descendants ) {
if (
selectors . length === 0 /* is :global(...) */ ||
( element . metadata . scoped && selector _matched ) ||
apply _selector ( selectors , rule , element )
) {
if ( include _self ) {
const selector _including _self = [
first . combinator ? { ... first , combinator : null } : first ,
... rest
] ;
if ( apply _selector ( selector _including _self , rule , element , FORWARD ) ) {
complex _selector . metadata . used = true ;
selector _matched = matched = true ;
matched = true ;
}
}
const selector _excluding _self = [
any _selector ,
first . combinator ? first : { ... first , combinator : descendant _combinator } ,
... rest
] ;
if ( apply _selector ( selector _excluding _self , rule , element , FORWARD ) ) {
complex _selector . metadata . used = true ;
matched = true ;
}
}
@ -458,7 +422,7 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element)
) {
const args = selector . args ;
const complex _selector = args . children [ 0 ] ;
return apply _selector ( complex _selector . children , rule , element );
return apply _selector ( complex _selector . children , rule , element , BACKWARD );
}
// We came across a :global, everything beyond it is global and therefore a potential match
@ -507,7 +471,7 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element)
if ( is _global ) {
complex _selector . metadata . used = true ;
matched = true ;
} else if ( apply _selector ( relative , rule , element )) {
} else if ( apply _selector ( relative , rule , element , BACKWARD )) {
complex _selector . metadata . used = true ;
matched = true ;
} else if ( complex _selector . children . length > 1 && ( name == 'is' || name == 'where' ) ) {
@ -591,7 +555,7 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element)
for ( const complex _selector of parent . prelude . children ) {
if (
apply _selector ( get _relative _selectors ( complex _selector ) , parent , element ) ||
apply _selector ( get _relative _selectors ( complex _selector ) , parent , element , direction ) ||
complex _selector . children . every ( ( s ) => is _global ( s , parent ) )
) {
complex _selector . metadata . used = true ;
@ -612,80 +576,6 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element)
return true ;
}
/ * *
* @ param { Compiler . AST . RegularElement | Compiler . AST . SvelteElement } element
* @ param { boolean } include _self
* /
function get _following _sibling _elements ( element , include _self ) {
const path = element . metadata . path ;
let i = path . length ;
/** @type {Compiler.AST.SvelteNode} */
let start = element ;
let nodes = /** @type {Compiler.AST.SvelteNode[]} */ (
/** @type {Compiler.AST.Fragment} */ ( path [ 0 ] ) . nodes
) ;
// find the set of nodes to walk...
while ( i -- ) {
const node = path [ i ] ;
if ( node . type === 'RegularElement' || node . type === 'SvelteElement' ) {
nodes = node . fragment . nodes ;
break ;
}
if ( node . type !== 'Fragment' ) {
start = node ;
}
}
/** @type {Array<Compiler.AST.RegularElement | Compiler.AST.SvelteElement>} */
const siblings = [ ] ;
// ...then walk them, starting from the node containing the element in question
// skipping nodes that appears before the element
const seen = new Set ( ) ;
let skip = true ;
/** @param {Compiler.AST.SvelteNode} node */
function get _siblings ( node ) {
walk ( node , null , {
RegularElement ( node ) {
if ( node === element ) {
skip = false ;
if ( include _self ) siblings . push ( node ) ;
} else if ( ! skip ) {
siblings . push ( node ) ;
}
} ,
SvelteElement ( node ) {
if ( node === element ) {
skip = false ;
if ( include _self ) siblings . push ( node ) ;
} else if ( ! skip ) {
siblings . push ( node ) ;
}
} ,
RenderTag ( node ) {
for ( const snippet of node . metadata . snippets ) {
if ( seen . has ( snippet ) ) continue ;
seen . add ( snippet ) ;
get _siblings ( snippet . body ) ;
}
}
} ) ;
}
for ( const node of nodes . slice ( nodes . indexOf ( start ) ) ) {
get _siblings ( node ) ;
}
return siblings ;
}
/ * *
* @ param { any } operator
* @ param { any } expected _value
@ -822,6 +712,84 @@ function unquote(str) {
return str ;
}
/ * *
* @ param { Compiler . AST . RegularElement | Compiler . AST . SvelteElement | Compiler . AST . RenderTag | Compiler . AST . Component | Compiler . AST . SvelteComponent | Compiler . AST . SvelteSelf } node
* @ param { boolean } adjacent _only
* @ param { Set < Compiler . AST . SnippetBlock > } seen
* /
function get _ancestor _elements ( node , adjacent _only , seen = new Set ( ) ) {
/** @type {Array<Compiler.AST.RegularElement | Compiler.AST.SvelteElement>} */
const ancestors = [ ] ;
const path = node . metadata . path ;
let i = path . length ;
while ( i -- ) {
const parent = path [ i ] ;
if ( parent . type === 'SnippetBlock' ) {
if ( ! seen . has ( parent ) ) {
seen . add ( parent ) ;
for ( const site of parent . metadata . sites ) {
ancestors . push ( ... get _ancestor _elements ( site , adjacent _only , seen ) ) ;
}
}
break ;
}
if ( parent . type === 'RegularElement' || parent . type === 'SvelteElement' ) {
ancestors . push ( parent ) ;
if ( adjacent _only ) {
break ;
}
}
}
return ancestors ;
}
/ * *
* @ param { Compiler . AST . RegularElement | Compiler . AST . SvelteElement | Compiler . AST . RenderTag | Compiler . AST . Component | Compiler . AST . SvelteComponent | Compiler . AST . SvelteSelf } node
* @ param { boolean } adjacent _only
* @ param { Set < Compiler . AST . SnippetBlock > } seen
* /
function get _descendant _elements ( node , adjacent _only , seen = new Set ( ) ) {
/** @type {Array<Compiler.AST.RegularElement | Compiler.AST.SvelteElement>} */
const descendants = [ ] ;
/ * *
* @ param { Compiler . AST . SvelteNode } node
* /
function walk _children ( node ) {
walk ( node , null , {
_ ( node , context ) {
if ( node . type === 'RegularElement' || node . type === 'SvelteElement' ) {
descendants . push ( node ) ;
if ( ! adjacent _only ) {
context . next ( ) ;
}
} else if ( node . type === 'RenderTag' ) {
for ( const snippet of node . metadata . snippets ) {
if ( seen . has ( snippet ) ) continue ;
seen . add ( snippet ) ;
walk _children ( snippet . body ) ;
}
} else {
context . next ( ) ;
}
}
} ) ;
}
walk _children ( node . type === 'RenderTag' ? node : node . fragment ) ;
return descendants ;
}
/ * *
* @ param { Compiler . AST . RegularElement | Compiler . AST . SvelteElement | Compiler . AST . RenderTag | Compiler . AST . Component | Compiler . AST . SvelteComponent | Compiler . AST . SvelteSelf } node
* @ returns { Compiler . AST . RegularElement | Compiler . AST . SvelteElement | null }
@ -843,11 +811,12 @@ function get_element_parent(node) {
/ * *
* @ param { Compiler . AST . RegularElement | Compiler . AST . SvelteElement | Compiler . AST . RenderTag | Compiler . AST . Component | Compiler . AST . SvelteComponent | Compiler . AST . SvelteSelf } node
* @ param { Direction } direction
* @ param { boolean } adjacent _only
* @ param { Set < Compiler . AST . SnippetBlock > } seen
* @ returns { Map < Compiler . AST . RegularElement | Compiler . AST . SvelteElement | Compiler . AST . SlotElement | Compiler . AST . RenderTag , NodeExistsValue > }
* /
function get _possible _element _siblings ( node , adjacent_only , seen = new Set ( ) ) {
function get _possible _element _siblings ( node , direction, adjacent_only , seen = new Set ( ) ) {
/** @type {Map<Compiler.AST.RegularElement | Compiler.AST.SvelteElement | Compiler.AST.SlotElement | Compiler.AST.RenderTag, NodeExistsValue>} */
const result = new Map ( ) ;
const path = node . metadata . path ;
@ -859,9 +828,9 @@ function get_possible_element_siblings(node, adjacent_only, seen = new Set()) {
while ( i -- ) {
const fragment = /** @type {Compiler.AST.Fragment} */ ( path [ i -- ] ) ;
let j = fragment . nodes . indexOf ( current ) ;
let j = fragment . nodes . indexOf ( current ) + ( direction === FORWARD ? 1 : - 1 ) ;
while ( j -- ) {
while ( j >= 0 && j < fragment . nodes . length ) {
const node = fragment . nodes [ j ] ;
if ( node . type === 'RegularElement' ) {
@ -876,23 +845,30 @@ function get_possible_element_siblings(node, adjacent_only, seen = new Set()) {
return result ;
}
}
// Special case: slots, render tags and svelte:element tags could resolve to no siblings,
// so we want to continue until we find a definite sibling even with the adjacent-only combinator
} else if ( is _block ( node ) ) {
if ( node . type === 'SlotElement' ) {
result . set ( node , NODE _PROBABLY _EXISTS ) ;
}
const possible _last _child = get _possible _ last_child ( node , adjacent _only ) ;
const possible _last _child = get _possible _ nested_siblings ( node , direction , adjacent _only ) ;
add _to _map ( possible _last _child , result ) ;
if ( adjacent _only && has _definite _elements ( possible _last _child ) ) {
return result ;
}
} else if ( node . type === ' RenderTag' || node . type === ' SvelteElement') {
} else if ( node . type === ' SvelteElement') {
result . set ( node , NODE _PROBABLY _EXISTS ) ;
// Special case: slots, render tags and svelte:element tags could resolve to no siblings,
// so we want to continue until we find a definite sibling even with the adjacent-only combinator
} else if ( node . type === 'RenderTag' ) {
result . set ( node , NODE _PROBABLY _EXISTS ) ;
for ( const snippet of node . metadata . snippets ) {
add _to _map ( get _possible _nested _siblings ( snippet , direction , adjacent _only ) , result ) ;
}
}
j = direction === FORWARD ? j + 1 : j - 1 ;
}
current = path [ i ] ;
if ( ! current ) break ;
@ -910,7 +886,7 @@ function get_possible_element_siblings(node, adjacent_only, seen = new Set()) {
seen . add ( current ) ;
for ( const site of current . metadata . sites ) {
const siblings = get _possible _element _siblings ( site , adjacent_only , seen ) ;
const siblings = get _possible _element _siblings ( site , direction, adjacent_only , seen ) ;
add _to _map ( siblings , result ) ;
if ( adjacent _only && current . metadata . sites . size === 1 && has _definite _elements ( siblings ) ) {
@ -923,7 +899,7 @@ function get_possible_element_siblings(node, adjacent_only, seen = new Set()) {
if ( current . type === 'EachBlock' && fragment === current . body ) {
// `{#each ...}<a /><b />{/each}` — `<b>` can be previous sibling of `<a />`
add _to _map ( get _possible _ last_child ( current , adjacent _only ) , result ) ;
add _to _map ( get _possible _ nested_siblings ( current , direction , adjacent _only ) , result ) ;
}
}
@ -931,11 +907,13 @@ function get_possible_element_siblings(node, adjacent_only, seen = new Set()) {
}
/ * *
* @ param { Compiler . AST . EachBlock | Compiler . AST . IfBlock | Compiler . AST . AwaitBlock | Compiler . AST . KeyBlock | Compiler . AST . SlotElement } node
* @ param { Compiler . AST . EachBlock | Compiler . AST . IfBlock | Compiler . AST . AwaitBlock | Compiler . AST . KeyBlock | Compiler . AST . SlotElement | Compiler . AST . SnippetBlock } node
* @ param { Direction } direction
* @ param { boolean } adjacent _only
* @ param { Set < Compiler . AST . SnippetBlock > } seen
* @ returns { Map < Compiler . AST . RegularElement | Compiler . AST . SvelteElement , NodeExistsValue > }
* /
function get _possible _ last_child ( node , adjacent _only ) {
function get _possible _ nested_siblings ( node , direction , adjacent _only , seen = new Set ( ) ) {
/** @type {Array<Compiler.AST.Fragment | undefined | null>} */
let fragments = [ ] ;
@ -956,12 +934,20 @@ function get_possible_last_child(node, adjacent_only) {
case 'SlotElement' :
fragments . push ( node . fragment ) ;
break ;
case 'SnippetBlock' :
if ( seen . has ( node ) ) {
return new Map ( ) ;
}
seen . add ( node ) ;
fragments . push ( node . body ) ;
break ;
}
/** @type {Map<Compiler.AST.RegularElement | Compiler.AST.SvelteElement, NodeExistsValue>} NodeMap */
const result = new Map ( ) ;
let exhaustive = node . type !== 'SlotElement' ;
let exhaustive = node . type !== 'SlotElement' && node . type !== 'SnippetBlock' ;
for ( const fragment of fragments ) {
if ( fragment == null ) {
@ -969,7 +955,7 @@ function get_possible_last_child(node, adjacent_only) {
continue ;
}
const map = loop _child ( fragment . nodes , adjacent_only ) ;
const map = loop _child ( fragment . nodes , direction, adjacent_only , seen ) ;
exhaustive && = has _definite _elements ( map ) ;
add _to _map ( map , result ) ;
@ -1012,27 +998,28 @@ function add_to_map(from, to) {
}
/ * *
* @ param { NodeExistsValue | undefined } exist1
* @ param { NodeExistsValue } exist1
* @ param { NodeExistsValue | undefined } exist2
* @ returns { NodeExistsValue }
* /
function higher _existence ( exist1 , exist2 ) {
// @ts-expect-error TODO figure out if this is a bug
if ( exist1 === undefined || exist2 === undefined ) return exist1 || exist2 ;
if ( exist2 === undefined ) return exist1 ;
return exist1 > exist2 ? exist1 : exist2 ;
}
/ * *
* @ param { Compiler . AST . SvelteNode [ ] } children
* @ param { Direction } direction
* @ param { boolean } adjacent _only
* @ param { Set < Compiler . AST . SnippetBlock > } seen
* /
function loop _child ( children , adjacent_only ) {
function loop _child ( children , direction, adjacent_only , seen ) {
/** @type {Map<Compiler.AST.RegularElement | Compiler.AST.SvelteElement, NodeExistsValue>} */
const result = new Map ( ) ;
let i = children. length ;
let i = direction === FORWARD ? 0 : children. length - 1 ;
while ( i -- ) {
while ( i >= 0 && i < children . length ) {
const child = children [ i ] ;
if ( child . type === 'RegularElement' ) {
@ -1042,13 +1029,19 @@ function loop_child(children, adjacent_only) {
}
} else if ( child . type === 'SvelteElement' ) {
result . set ( child , NODE _PROBABLY _EXISTS ) ;
} else if ( child . type === 'RenderTag' ) {
for ( const snippet of child . metadata . snippets ) {
add _to _map ( get _possible _nested _siblings ( snippet , direction , adjacent _only , seen ) , result ) ;
}
} else if ( is _block ( child ) ) {
const child _result = get _possible _last _child ( child , adjacent _only ) ;
const child _result = get _possible _ nested_siblings ( child , direction , adjacent _only , seen ) ;
add _to _map ( child _result , result ) ;
if ( adjacent _only && has _definite _elements ( child _result ) ) {
break ;
}
}
i = direction === FORWARD ? i + 1 : i - 1 ;
}
return result ;