Skip to content

Commit 9de149c

Browse files
authored
fix: applying :global for +,~ sibling combinator when slots are present. fixes #9274 (#9282)
fixes #9274
1 parent a6bf91c commit 9de149c

File tree

5 files changed

+64
-9
lines changed

5 files changed

+64
-9
lines changed

.changeset/swift-falcons-fetch.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: applying :global for +,~ sibling combinator when slots are present

packages/svelte/src/compiler/compile/css/Selector.js

+21-9
Original file line numberDiff line numberDiff line change
@@ -291,14 +291,17 @@ function apply_selector(blocks, node, to_encapsulate) {
291291
}
292292
return false;
293293
} else if (block.combinator.name === '+' || block.combinator.name === '~') {
294-
const siblings = get_possible_element_siblings(node, block.combinator.name === '+');
294+
const [siblings, has_slot_sibling] = get_possible_element_siblings(
295+
node,
296+
block.combinator.name === '+'
297+
);
295298
let has_match = false;
296299
// NOTE: if we have :global(), we couldn't figure out what is selected within `:global` due to the
297300
// css-tree limitation that does not parse the inner selector of :global
298301
// so unless we are sure there will be no sibling to match, we will consider it as matched
299302
const has_global = blocks.some((block) => block.global);
300303
if (has_global) {
301-
if (siblings.size === 0 && get_element_parent(node) !== null) {
304+
if (siblings.size === 0 && get_element_parent(node) !== null && !has_slot_sibling) {
302305
return false;
303306
}
304307
to_encapsulate.push({ node, block });
@@ -542,13 +545,15 @@ function get_element_parent(node) {
542545
* <h1>Heading 1</h1>
543546
* <h2>Heading 2</h2>
544547
* @param {import('../nodes/interfaces.js').INode} node
545-
* @returns {import('../nodes/interfaces.js').INode}
548+
* @returns {[import('../nodes/interfaces.js').INode, boolean]}
546549
*/
547550
function find_previous_sibling(node) {
548551
/** @type {import('../nodes/interfaces.js').INode} */
549552
let current_node = node;
553+
let has_slot_sibling = false;
550554
do {
551555
if (current_node.type === 'Slot') {
556+
has_slot_sibling = true;
552557
const slot_children = current_node.children;
553558
if (slot_children.length > 0) {
554559
current_node = slot_children.slice(-1)[0]; // go to its last child first
@@ -560,21 +565,24 @@ function find_previous_sibling(node) {
560565
}
561566
current_node = current_node.prev;
562567
} while (current_node && current_node.type === 'Slot');
563-
return current_node;
568+
return [current_node, has_slot_sibling];
564569
}
565570

566571
/**
567572
* @param {import('../nodes/interfaces.js').INode} node
568573
* @param {boolean} adjacent_only
569-
* @returns {Map<import('../nodes/Element.js').default, NodeExistsValue>}
574+
* @returns {[Map<import('../nodes/Element.js').default, NodeExistsValue>, boolean]}
570575
*/
571576
function get_possible_element_siblings(node, adjacent_only) {
572577
/** @type {Map<import('../nodes/Element.js').default, NodeExistsValue>} */
573578
const result = new Map();
574579

575580
/** @type {import('../nodes/interfaces.js').INode} */
576581
let prev = node;
577-
while ((prev = find_previous_sibling(prev))) {
582+
let has_slot_sibling = false;
583+
let slot_sibling_found = false;
584+
while (([prev, slot_sibling_found] = find_previous_sibling(prev)) && prev) {
585+
has_slot_sibling = has_slot_sibling || slot_sibling_found;
578586
if (prev.type === 'Element') {
579587
if (
580588
!prev.attributes.find(
@@ -590,7 +598,7 @@ function get_possible_element_siblings(node, adjacent_only) {
590598
const possible_last_child = get_possible_last_child(prev, adjacent_only);
591599
add_to_map(possible_last_child, result);
592600
if (adjacent_only && has_definite_elements(possible_last_child)) {
593-
return result;
601+
return [result, has_slot_sibling];
594602
}
595603
}
596604
}
@@ -605,7 +613,11 @@ function get_possible_element_siblings(node, adjacent_only) {
605613
parent.type === 'ElseBlock' ||
606614
parent.type === 'AwaitBlock')
607615
) {
608-
const possible_siblings = get_possible_element_siblings(parent, adjacent_only);
616+
const [possible_siblings, slot_sibling_found] = get_possible_element_siblings(
617+
parent,
618+
adjacent_only
619+
);
620+
has_slot_sibling = has_slot_sibling || slot_sibling_found;
609621
add_to_map(possible_siblings, result);
610622
if (parent.type === 'EachBlock') {
611623
// first child of each block can select the last child of each block as previous sibling
@@ -623,7 +635,7 @@ function get_possible_element_siblings(node, adjacent_only) {
623635
}
624636
}
625637
}
626-
return result;
638+
return [result, has_slot_sibling];
627639
}
628640

629641
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
export default {
2+
warnings: [
3+
{
4+
code: 'css-unused-selector',
5+
message: 'Unused CSS selector ":global(input) ~ p"',
6+
pos: 160,
7+
start: {
8+
character: 160,
9+
column: 1,
10+
line: 11
11+
},
12+
end: {
13+
character: 178,
14+
column: 19,
15+
line: 11
16+
},
17+
frame: ` 9: :global(input) ~ span { color: red; }
18+
10: /* no match */
19+
11: :global(input) ~ p { color: red; }
20+
^
21+
12: </style>
22+
`
23+
}
24+
]
25+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
input+span.svelte-xyz{color:red}input~span.svelte-xyz{color:red}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<div>
2+
<slot />
3+
<span>Hello</span>
4+
</div>
5+
6+
<style>
7+
/* match */
8+
:global(input) + span { color: red; }
9+
:global(input) ~ span { color: red; }
10+
/* no match */
11+
:global(input) ~ p { color: red; }
12+
</style>

0 commit comments

Comments
 (0)