Repository: sveltejs/svelte (JavaScript)
Date: 2021-07-24 02:35:27+00:00
Task type: Bug Fix

Issue:
compiler sometimes sometimes tries to encapsulate :global(...)
### Describe the bug

There's a bug in how some css selectors are compiled, such that the class that the compiler adds (to scope things to the module) is prepended to the contents of `:global(...)`.

### Reproduction

```css
:global(div) > div {
	color: red;
}
```

compiles to:

```css
.svelte-lxuwwediv>div.svelte-lxuwwe{color:red}
```

This bug is about the extra `.svelte-lxuwwe` at the beginning that's smashed right up against the first `div`.

Here's a repl showing this in action: https://svelte.dev/repl/9b67838a3ad04a52b3a21255f84051c2?version=3.38.3


### Logs

_No response_

### System Info

```shell
System:
    OS: Linux 5.10 Alpine Linux
    CPU: (8) x64 Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz
    Memory: 235.31 MB / 7.43 GB
    Container: Yes
    Shell: 1.32.1 - /bin/ash
  Binaries:
    Node: 16.2.0 - /usr/local/bin/node
    Yarn: 1.22.5 - /usr/local/bin/yarn
    npm: 7.13.0 - /usr/local/bin/npm
  npmPackages:
    rollup: ^2.3.4 => 2.49.0
    svelte: ^3.0.0 => 3.38.2
```


### Severity

annoyance


Issue discussion:
https://svelte.dev/repl/9b67838a3ad04a52b3a21255f84051c2?version=3.40.2 Hm. In 3.40.2, correct-looking CSS is getting generated, but the scoping class is only getting added to the outer div in the DOM and not the inner one, so the text still isn't red.

@tanhauhau Do you think there's anything that can reasonably be done about this? I don't think we want the compiler to be looking inside the `:global(...)` and seeing which elements within the current component that might match, but I'm not sure how else we'd get _both_ divs to have scoping classes added to them. I'm wondering whether the remainder of this issue should be a won't-fix.
This is the fix I was looking for! I just wanted get rid of that corruption at the beginning of the css output.

I only put the outer div in the same component to make a simpler test case, I don't need that test case to work, I think the point of :global() is to span between different components.

It seems like it would be tricky to decide if you want to make stuff inside :global() match stuff in the current component, and possibly tricky to implement. I suggest having that discussion/fix in a different issue (if it needs to happen at all) so it's easier to find/link/etc.

Solution patch:
diff --git a/src/compiler/compile/css/Selector.ts b/src/compiler/compile/css/Selector.ts
--- a/src/compiler/compile/css/Selector.ts
+++ b/src/compiler/compile/css/Selector.ts
@@ -227,7 +227,8 @@ function apply_selector(blocks: Block[], node: Element, to_encapsulate: Array<{
 
 			return false;
 		} else if (block.combinator.name === '>') {
-			if (apply_selector(blocks, get_element_parent(node), to_encapsulate)) {
+			const has_global_parent = blocks.every(block => block.global);
+			if (has_global_parent || apply_selector(blocks, get_element_parent(node), to_encapsulate)) {
 				to_encapsulate.push({ node, block });
 				return true;
 			}


Test patch:
diff --git a/test/css/samples/global-with-child-combinator-2/_config.js b/test/css/samples/global-with-child-combinator-2/_config.js
new file mode 100644
--- /dev/null
+++ b/test/css/samples/global-with-child-combinator-2/_config.js
@@ -0,0 +1,27 @@
+export default {
+	warnings: [
+		{
+			code: 'css-unused-selector',
+			end: {
+				character: 111,
+				column: 21,
+				line: 8
+			},
+			frame: `
+			 6:     color: red;
+			 7:   }
+			 8:   a:global(.foo) > div {
+			      ^
+			 9:     color: red;
+			10:   }
+			`,
+			message: 'Unused CSS selector "a:global(.foo) > div"',
+			pos: 91,
+			start: {
+				character: 91,
+				column: 1,
+				line: 8
+			}
+		}
+	]
+};
diff --git a/test/css/samples/global-with-child-combinator-2/expected.css b/test/css/samples/global-with-child-combinator-2/expected.css
new file mode 100644
--- /dev/null
+++ b/test/css/samples/global-with-child-combinator-2/expected.css
@@ -0,0 +1 @@
+div>div.svelte-xyz.svelte-xyz{color:red}div.svelte-xyz.foo>div.svelte-xyz{color:red}
\ No newline at end of file
diff --git a/test/css/samples/global-with-child-combinator-2/expected.html b/test/css/samples/global-with-child-combinator-2/expected.html
new file mode 100644
--- /dev/null
+++ b/test/css/samples/global-with-child-combinator-2/expected.html
@@ -0,0 +1,3 @@
+<div class="svelte-xyz">
+  <div class="svelte-xyz"></div>
+</div>
\ No newline at end of file
diff --git a/test/css/samples/global-with-child-combinator-2/input.svelte b/test/css/samples/global-with-child-combinator-2/input.svelte
new file mode 100644
--- /dev/null
+++ b/test/css/samples/global-with-child-combinator-2/input.svelte
@@ -0,0 +1,15 @@
+<style>
+	:global(div) > div {
+		color: red;
+	}
+	div:global(.foo) > div {
+		color: red;
+	}
+	a:global(.foo) > div {
+		color: red;
+	}
+</style>
+
+<div>
+	<div />
+</div>
\ No newline at end of file
diff --git a/test/css/samples/global-with-child-combinator-3/_config.js b/test/css/samples/global-with-child-combinator-3/_config.js
new file mode 100644
--- /dev/null
+++ b/test/css/samples/global-with-child-combinator-3/_config.js
@@ -0,0 +1,3 @@
+export default {
+	warnings: []
+};
diff --git a/test/css/samples/global-with-child-combinator-3/expected.css b/test/css/samples/global-with-child-combinator-3/expected.css
new file mode 100644
--- /dev/null
+++ b/test/css/samples/global-with-child-combinator-3/expected.css
@@ -0,0 +1 @@
+a>b>div.svelte-xyz{color:red}
\ No newline at end of file
diff --git a/test/css/samples/global-with-child-combinator-3/expected.html b/test/css/samples/global-with-child-combinator-3/expected.html
new file mode 100644
--- /dev/null
+++ b/test/css/samples/global-with-child-combinator-3/expected.html
@@ -0,0 +1,3 @@
+<div class="svelte-xyz">
+  <div class="svelte-xyz"></div>
+</div>
\ No newline at end of file
diff --git a/test/css/samples/global-with-child-combinator-3/input.svelte b/test/css/samples/global-with-child-combinator-3/input.svelte
new file mode 100644
--- /dev/null
+++ b/test/css/samples/global-with-child-combinator-3/input.svelte
@@ -0,0 +1,9 @@
+<style>
+	:global(a) > :global(b) > div {
+		color: red;
+	}
+</style>
+
+<div>
+	<div />
+</div>
\ No newline at end of file
diff --git a/test/css/samples/global-with-child-combinator/expected.html b/test/css/samples/global-with-child-combinator/expected.html
new file mode 100644
--- /dev/null
+++ b/test/css/samples/global-with-child-combinator/expected.html
@@ -0,0 +1,3 @@
+<div class="svelte-xyz">
+  <div class="svelte-xyz"></div>
+</div>
\ No newline at end of file