|
|
@ -881,116 +881,63 @@ function get_element_parent(node) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Finds the given node's previous sibling in the DOM
|
|
|
|
* @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement} element
|
|
|
|
*
|
|
|
|
|
|
|
|
* The Svelte `<slot>` is just a placeholder and is not actually real. Any children nodes
|
|
|
|
|
|
|
|
* in `<slot>` are 'flattened' and considered as the same level as the `<slot>`'s siblings
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* e.g.
|
|
|
|
|
|
|
|
* ```html
|
|
|
|
|
|
|
|
* <h1>Heading 1</h1>
|
|
|
|
|
|
|
|
* <slot>
|
|
|
|
|
|
|
|
* <h2>Heading 2</h2>
|
|
|
|
|
|
|
|
* </slot>
|
|
|
|
|
|
|
|
* ```
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* is considered to look like:
|
|
|
|
|
|
|
|
* ```html
|
|
|
|
|
|
|
|
* <h1>Heading 1</h1>
|
|
|
|
|
|
|
|
* <h2>Heading 2</h2>
|
|
|
|
|
|
|
|
* ```
|
|
|
|
|
|
|
|
* @param {Compiler.SvelteNode} node
|
|
|
|
|
|
|
|
* @returns {Compiler.SvelteNode}
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
function find_previous_sibling(node) {
|
|
|
|
|
|
|
|
/** @type {Compiler.SvelteNode} */
|
|
|
|
|
|
|
|
let current_node = node;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while (
|
|
|
|
|
|
|
|
// @ts-expect-error TODO
|
|
|
|
|
|
|
|
!current_node.prev &&
|
|
|
|
|
|
|
|
// @ts-expect-error TODO
|
|
|
|
|
|
|
|
current_node.parent?.type === 'SlotElement'
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
// @ts-expect-error TODO
|
|
|
|
|
|
|
|
current_node = current_node.parent;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// @ts-expect-error
|
|
|
|
|
|
|
|
current_node = current_node.prev;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while (current_node?.type === 'SlotElement') {
|
|
|
|
|
|
|
|
const slot_children = current_node.fragment.nodes;
|
|
|
|
|
|
|
|
if (slot_children.length > 0) {
|
|
|
|
|
|
|
|
current_node = slot_children[slot_children.length - 1];
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return current_node;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* @param {Compiler.SvelteNode} node
|
|
|
|
|
|
|
|
* @param {boolean} adjacent_only
|
|
|
|
* @param {boolean} adjacent_only
|
|
|
|
* @returns {Map<Compiler.AST.RegularElement | Compiler.AST.SvelteElement | Compiler.AST.SlotElement | Compiler.AST.RenderTag, NodeExistsValue>}
|
|
|
|
* @returns {Map<Compiler.AST.RegularElement | Compiler.AST.SvelteElement | Compiler.AST.SlotElement | Compiler.AST.RenderTag, NodeExistsValue>}
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
function get_possible_element_siblings(node, adjacent_only) {
|
|
|
|
function get_possible_element_siblings(element, adjacent_only) {
|
|
|
|
/** @type {Map<Compiler.AST.RegularElement | Compiler.AST.SvelteElement | Compiler.AST.SlotElement | Compiler.AST.RenderTag, NodeExistsValue>} */
|
|
|
|
/** @type {Map<Compiler.AST.RegularElement | Compiler.AST.SvelteElement | Compiler.AST.SlotElement | Compiler.AST.RenderTag, NodeExistsValue>} */
|
|
|
|
const result = new Map();
|
|
|
|
const result = new Map();
|
|
|
|
|
|
|
|
const path = element.metadata.path;
|
|
|
|
|
|
|
|
|
|
|
|
/** @type {Compiler.SvelteNode} */
|
|
|
|
/** @type {Compiler.SvelteNode} */
|
|
|
|
let prev = node;
|
|
|
|
let current = element;
|
|
|
|
while ((prev = find_previous_sibling(prev))) {
|
|
|
|
|
|
|
|
if (prev.type === 'RegularElement') {
|
|
|
|
let i = path.length;
|
|
|
|
const has_slot_attribute = prev.attributes.some(
|
|
|
|
|
|
|
|
(attr) => attr.type === 'Attribute' && attr.name.toLowerCase() === 'slot'
|
|
|
|
while (i--) {
|
|
|
|
);
|
|
|
|
const fragment = /** @type {Compiler.AST.Fragment} */ (path[i--]);
|
|
|
|
|
|
|
|
let j = fragment.nodes.indexOf(current);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while (j--) {
|
|
|
|
|
|
|
|
const node = fragment.nodes[j];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (node.type === 'RegularElement') {
|
|
|
|
|
|
|
|
const has_slot_attribute = node.attributes.some(
|
|
|
|
|
|
|
|
(attr) => attr.type === 'Attribute' && attr.name.toLowerCase() === 'slot'
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
if (!has_slot_attribute) {
|
|
|
|
if (!has_slot_attribute) {
|
|
|
|
result.set(prev, NODE_DEFINITELY_EXISTS);
|
|
|
|
result.set(node, NODE_DEFINITELY_EXISTS);
|
|
|
|
|
|
|
|
|
|
|
|
if (adjacent_only) {
|
|
|
|
if (adjacent_only) {
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} 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);
|
|
|
|
|
|
|
|
add_to_map(possible_last_child, result);
|
|
|
|
|
|
|
|
if (adjacent_only && has_definite_elements(possible_last_child)) {
|
|
|
|
return result;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (node.type === 'RenderTag' || 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 (is_block(prev)) {
|
|
|
|
|
|
|
|
const possible_last_child = get_possible_last_child(prev, adjacent_only);
|
|
|
|
|
|
|
|
add_to_map(possible_last_child, result);
|
|
|
|
|
|
|
|
if (adjacent_only && has_definite_elements(possible_last_child)) {
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (
|
|
|
|
|
|
|
|
prev.type === 'SlotElement' ||
|
|
|
|
|
|
|
|
prev.type === 'RenderTag' ||
|
|
|
|
|
|
|
|
prev.type === 'SvelteElement'
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
result.set(prev, 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
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** @type {Compiler.SvelteNode | null} */
|
|
|
|
current = path[i];
|
|
|
|
let parent = node;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while (
|
|
|
|
if (!current || !is_block(current)) break;
|
|
|
|
// @ts-expect-error TODO
|
|
|
|
|
|
|
|
(parent = parent?.parent) &&
|
|
|
|
|
|
|
|
is_block(parent)
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
const possible_siblings = get_possible_element_siblings(parent, adjacent_only);
|
|
|
|
|
|
|
|
add_to_map(possible_siblings, result);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// @ts-expect-error
|
|
|
|
if (current.type === 'EachBlock' && fragment === current.body) {
|
|
|
|
if (parent.type === 'EachBlock' && !parent.fallback?.nodes.includes(node)) {
|
|
|
|
|
|
|
|
// `{#each ...}<a /><b />{/each}` — `<b>` can be previous sibling of `<a />`
|
|
|
|
// `{#each ...}<a /><b />{/each}` — `<b>` can be previous sibling of `<a />`
|
|
|
|
add_to_map(get_possible_last_child(parent, adjacent_only), result);
|
|
|
|
add_to_map(get_possible_last_child(current, adjacent_only), result);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (adjacent_only && has_definite_elements(possible_siblings)) {
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -998,7 +945,7 @@ function get_possible_element_siblings(node, adjacent_only) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* @param {Compiler.AST.EachBlock | Compiler.AST.IfBlock | Compiler.AST.AwaitBlock | Compiler.AST.KeyBlock} node
|
|
|
|
* @param {Compiler.AST.EachBlock | Compiler.AST.IfBlock | Compiler.AST.AwaitBlock | Compiler.AST.KeyBlock | Compiler.AST.SlotElement} node
|
|
|
|
* @param {boolean} adjacent_only
|
|
|
|
* @param {boolean} adjacent_only
|
|
|
|
* @returns {Map<Compiler.AST.RegularElement, NodeExistsValue>}
|
|
|
|
* @returns {Map<Compiler.AST.RegularElement, NodeExistsValue>}
|
|
|
|
*/
|
|
|
|
*/
|
|
|
@ -1022,6 +969,7 @@ function get_possible_last_child(node, adjacent_only) {
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 'KeyBlock':
|
|
|
|
case 'KeyBlock':
|
|
|
|
|
|
|
|
case 'SlotElement':
|
|
|
|
fragments.push(node.fragment);
|
|
|
|
fragments.push(node.fragment);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -1029,7 +977,7 @@ function get_possible_last_child(node, adjacent_only) {
|
|
|
|
/** @type {NodeMap} */
|
|
|
|
/** @type {NodeMap} */
|
|
|
|
const result = new Map();
|
|
|
|
const result = new Map();
|
|
|
|
|
|
|
|
|
|
|
|
let exhaustive = true;
|
|
|
|
let exhaustive = node.type !== 'SlotElement';
|
|
|
|
|
|
|
|
|
|
|
|
for (const fragment of fragments) {
|
|
|
|
for (const fragment of fragments) {
|
|
|
|
if (fragment == null) {
|
|
|
|
if (fragment == null) {
|
|
|
@ -1121,13 +1069,14 @@ function loop_child(children, adjacent_only) {
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* @param {Compiler.SvelteNode} node
|
|
|
|
* @param {Compiler.SvelteNode} node
|
|
|
|
* @returns {node is Compiler.AST.IfBlock | Compiler.AST.EachBlock | Compiler.AST.AwaitBlock | Compiler.AST.KeyBlock}
|
|
|
|
* @returns {node is Compiler.AST.IfBlock | Compiler.AST.EachBlock | Compiler.AST.AwaitBlock | Compiler.AST.KeyBlock | Compiler.AST.SlotElement}
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
function is_block(node) {
|
|
|
|
function is_block(node) {
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
node.type === 'IfBlock' ||
|
|
|
|
node.type === 'IfBlock' ||
|
|
|
|
node.type === 'EachBlock' ||
|
|
|
|
node.type === 'EachBlock' ||
|
|
|
|
node.type === 'AwaitBlock' ||
|
|
|
|
node.type === 'AwaitBlock' ||
|
|
|
|
node.type === 'KeyBlock'
|
|
|
|
node.type === 'KeyBlock' ||
|
|
|
|
|
|
|
|
node.type === 'SlotElement'
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|