Skip to content

Commit

Permalink
fix: applying :global for +,~ sibling combinator when slots are prese…
Browse files Browse the repository at this point in the history
…nt. fixes #9274 (#9282)

fixes #9274
  • Loading branch information
kelvinsjk committed Apr 9, 2024
1 parent a6bf91c commit 9de149c
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 9 deletions.
5 changes: 5 additions & 0 deletions .changeset/swift-falcons-fetch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte': patch
---

fix: applying :global for +,~ sibling combinator when slots are present
30 changes: 21 additions & 9 deletions packages/svelte/src/compiler/compile/css/Selector.js
Original file line number Diff line number Diff line change
Expand Up @@ -291,14 +291,17 @@ function apply_selector(blocks, node, to_encapsulate) {
}
return false;
} else if (block.combinator.name === '+' || block.combinator.name === '~') {
const siblings = get_possible_element_siblings(node, block.combinator.name === '+');
const [siblings, has_slot_sibling] = get_possible_element_siblings(
node,
block.combinator.name === '+'
);
let has_match = false;
// NOTE: if we have :global(), we couldn't figure out what is selected within `:global` due to the
// css-tree limitation that does not parse the inner selector of :global
// so unless we are sure there will be no sibling to match, we will consider it as matched
const has_global = blocks.some((block) => block.global);
if (has_global) {
if (siblings.size === 0 && get_element_parent(node) !== null) {
if (siblings.size === 0 && get_element_parent(node) !== null && !has_slot_sibling) {
return false;
}
to_encapsulate.push({ node, block });
Expand Down Expand Up @@ -542,13 +545,15 @@ function get_element_parent(node) {
* <h1>Heading 1</h1>
* <h2>Heading 2</h2>
* @param {import('../nodes/interfaces.js').INode} node
* @returns {import('../nodes/interfaces.js').INode}
* @returns {[import('../nodes/interfaces.js').INode, boolean]}
*/
function find_previous_sibling(node) {
/** @type {import('../nodes/interfaces.js').INode} */
let current_node = node;
let has_slot_sibling = false;
do {
if (current_node.type === 'Slot') {
has_slot_sibling = true;
const slot_children = current_node.children;
if (slot_children.length > 0) {
current_node = slot_children.slice(-1)[0]; // go to its last child first
Expand All @@ -560,21 +565,24 @@ function find_previous_sibling(node) {
}
current_node = current_node.prev;
} while (current_node && current_node.type === 'Slot');
return current_node;
return [current_node, has_slot_sibling];
}

/**
* @param {import('../nodes/interfaces.js').INode} node
* @param {boolean} adjacent_only
* @returns {Map<import('../nodes/Element.js').default, NodeExistsValue>}
* @returns {[Map<import('../nodes/Element.js').default, NodeExistsValue>, boolean]}
*/
function get_possible_element_siblings(node, adjacent_only) {
/** @type {Map<import('../nodes/Element.js').default, NodeExistsValue>} */
const result = new Map();

/** @type {import('../nodes/interfaces.js').INode} */
let prev = node;
while ((prev = find_previous_sibling(prev))) {
let has_slot_sibling = false;
let slot_sibling_found = false;
while (([prev, slot_sibling_found] = find_previous_sibling(prev)) && prev) {
has_slot_sibling = has_slot_sibling || slot_sibling_found;
if (prev.type === 'Element') {
if (
!prev.attributes.find(
Expand All @@ -590,7 +598,7 @@ function get_possible_element_siblings(node, adjacent_only) {
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;
return [result, has_slot_sibling];
}
}
}
Expand All @@ -605,7 +613,11 @@ function get_possible_element_siblings(node, adjacent_only) {
parent.type === 'ElseBlock' ||
parent.type === 'AwaitBlock')
) {
const possible_siblings = get_possible_element_siblings(parent, adjacent_only);
const [possible_siblings, slot_sibling_found] = get_possible_element_siblings(
parent,
adjacent_only
);
has_slot_sibling = has_slot_sibling || slot_sibling_found;
add_to_map(possible_siblings, result);
if (parent.type === 'EachBlock') {
// first child of each block can select the last child of each block as previous sibling
Expand All @@ -623,7 +635,7 @@ function get_possible_element_siblings(node, adjacent_only) {
}
}
}
return result;
return [result, has_slot_sibling];
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export default {
warnings: [
{
code: 'css-unused-selector',
message: 'Unused CSS selector ":global(input) ~ p"',
pos: 160,
start: {
character: 160,
column: 1,
line: 11
},
end: {
character: 178,
column: 19,
line: 11
},
frame: ` 9: :global(input) ~ span { color: red; }
10: /* no match */
11: :global(input) ~ p { color: red; }
^
12: </style>
`
}
]
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
input+span.svelte-xyz{color:red}input~span.svelte-xyz{color:red}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<div>
<slot />
<span>Hello</span>
</div>

<style>
/* match */
:global(input) + span { color: red; }
:global(input) ~ span { color: red; }
/* no match */
:global(input) ~ p { color: red; }
</style>

0 comments on commit 9de149c

Please sign in to comment.