fix :host and :global css scoping (#5957)

pull/5970/head
Tan Li Hau 4 years ago committed by GitHub
parent ebbdb4277c
commit 44f41c9edd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,5 +1,10 @@
# Svelte changelog
## Unreleased
* Fix scoping of selectors with `:global()` and `~` sibling combinators ([#5499](https://github.com/sveltejs/svelte/issues/5499))
* Fix removal of `:host` selectors as unused when compiling to a custom element ([#5946](https://github.com/sveltejs/svelte/issues/5946))
## 3.32.1
* Warn when using `module` variables reactively, and close weird reactivity loophole ([#5847](https://github.com/sveltejs/svelte/pull/5847))

@ -84,7 +84,7 @@ export default class Selector {
while (i--) {
const selector = block.selectors[i];
if (selector.type === 'PseudoElementSelector' || selector.type === 'PseudoClassSelector') {
if (selector.name !== 'root') {
if (selector.name !== 'root' && selector.name !== 'host') {
if (i === 0) code.prependRight(selector.start, attr);
}
continue;
@ -162,7 +162,10 @@ function apply_selector(blocks: Block[], node: Element, to_encapsulate: any[]):
if (!block) return false;
if (!node) {
return block.global && blocks.every(block => block.global);
return (
(block.global && blocks.every(block => block.global)) ||
(block.host && blocks.length === 0)
);
}
switch (block_might_apply_to_node(block, node)) {
@ -182,6 +185,11 @@ function apply_selector(blocks: Block[], node: Element, to_encapsulate: any[]):
continue;
}
if (ancestor_block.host) {
to_encapsulate.push({ node, block });
return true;
}
let parent = node;
while (parent = get_element_parent(parent)) {
if (block_might_apply_to_node(ancestor_block, parent) !== BlockAppliesToNode.NotPossible) {
@ -211,6 +219,19 @@ function apply_selector(blocks: Block[], node: Element, to_encapsulate: any[]):
} else if (block.combinator.name === '+' || block.combinator.name === '~') {
const siblings = 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) {
return false;
}
to_encapsulate.push({ node, block });
return true;
}
for (const possible_sibling of siblings.keys()) {
if (apply_selector(blocks.slice(), possible_sibling, to_encapsulate)) {
to_encapsulate.push({ node, block });
@ -236,6 +257,10 @@ function block_might_apply_to_node(block: Block, node: Element): BlockAppliesToN
const selector = block.selectors[i];
const name = typeof selector.name === 'string' && selector.name.replace(/\\(.)/g, '$1');
if (selector.type === 'PseudoClassSelector' && name === 'host') {
return BlockAppliesToNode.NotPossible;
}
if (selector.type === 'PseudoClassSelector' || selector.type === 'PseudoElementSelector') {
continue;
}
@ -541,6 +566,7 @@ function loop_child(children: INode[], adjacent_only: boolean) {
class Block {
global: boolean;
host: boolean;
combinator: CssNode;
selectors: CssNode[]
start: number;
@ -550,6 +576,7 @@ class Block {
constructor(combinator: CssNode) {
this.combinator = combinator;
this.global = false;
this.host = false;
this.selectors = [];
this.start = null;
@ -562,6 +589,7 @@ class Block {
if (this.selectors.length === 0) {
this.start = selector.start;
this.global = selector.type === 'PseudoClassSelector' && selector.name === 'global';
this.host = selector.type === 'PseudoClassSelector' && selector.name === 'host';
}
this.selectors.push(selector);

@ -0,0 +1,27 @@
export default {
warnings: [
{
code: 'css-unused-selector',
message: 'Unused CSS selector ":host > span"',
pos: 147,
start: {
character: 147,
column: 1,
line: 18
},
end: {
character: 159,
column: 13,
line: 18
},
frame: `
16: }
17:
18: :host > span {
^
19: color: red;
20: }
`
}
]
};

@ -0,0 +1 @@
:host h1.svelte-xyz{color:red}:host>h1.svelte-xyz{color:red}:host>.svelte-xyz{color:red}:host span.svelte-xyz{color:red}

@ -0,0 +1,27 @@
<style>
:host h1 {
color: red;
}
:host > h1 {
color: red;
}
:host > * {
color: red;
}
:host span {
color: red;
}
:host > span {
color: red;
}
</style>
<h1>Hello!</h1>
<div>
<span>World!</span>
</div>

@ -0,0 +1,50 @@
export default {
warnings: [
{
code: 'css-unused-selector',
message: 'Unused CSS selector ":global(input) + span"',
pos: 239,
start: {
character: 239,
column: 2,
line: 9
},
end: {
character: 260,
column: 23,
line: 9
},
frame: `
7: :global(input) ~ p { color: red; }
8:
9: :global(input) + span { color: red; }
^
10: :global(input) ~ span { color: red; }
11: </style>
`
},
{
code: 'css-unused-selector',
message: 'Unused CSS selector ":global(input) ~ span"',
pos: 279,
start: {
character: 279,
column: 2,
line: 10
},
end: {
character: 300,
column: 23,
line: 10
},
frame: `
8:
9: :global(input) + span { color: red; }
10: :global(input) ~ span { color: red; }
^
11: </style>
12:
`
}
]
};

@ -0,0 +1 @@
input+div.svelte-xyz{color:red}input~div.svelte-xyz{color:red}input+h1.svelte-xyz{color:red}input~h1.svelte-xyz{color:red}input+p.svelte-xyz{color:red}input~p.svelte-xyz{color:red}

@ -0,0 +1,21 @@
<style>
:global(input) + div { color: red; }
:global(input) ~ div { color: red; }
:global(input) + h1 { color: red; }
:global(input) ~ h1 { color: red; }
:global(input) + p { color: red; }
:global(input) ~ p { color: red; }
:global(input) + span { color: red; }
:global(input) ~ span { color: red; }
</style>
<h1>Hello!</h1>
<div>
<span>World!</span>
</div>
{#each [] as _}
<p />
{/each}
Loading…
Cancel
Save