Repository: swc-project/swc (rust)

Issue:
CSS module :global function syntax
### Describe the bug

Currently SWC css module pass cannot handle cases like this 

```
.a:global(.b .c).d {
  color: red
}
```

SWC ignores `.d`

I tried fix this, but current algorithm seems hard to implement

[lightningcss demo](https://lightningcss.dev/playground/index.html#%7B%22minify%22%3Atrue%2C%22customMedia%22%3Atrue%2C%22cssModules%22%3Atrue%2C%22analyzeDependencies%22%3Afalse%2C%22targets%22%3A%7B%22chrome%22%3A6225920%7D%2C%22include%22%3A0%2C%22exclude%22%3A0%2C%22source%22%3A%22.a%3Aglobal(.b%20.c).d%20%7B%5Cn%20%20color%3A%20red%5Cn%7D%22%2C%22visitorEnabled%22%3Afalse%2C%22visitor%22%3A%22%7B%5Cn%20%20Color(color)%20%7B%5Cn%20%20%20%20if%20(color.type%20%3D%3D%3D%20'rgb')%20%7B%5Cn%20%20%20%20%20%20color.g%20%3D%200%3B%5Cn%20%20%20%20%20%20return%20color%3B%5Cn%20%20%20%20%7D%5Cn%20%20%7D%5Cn%7D%22%2C%22unusedSymbols%22%3A%5B%5D%2C%22version%22%3A%22local%22%7D)

### Input code

_No response_

### Config

_No response_

### Playground link (or link to the minimal reproduction)

None

### SWC Info output

_No response_

### Expected behavior

.d should be renamed

### Actual behavior

.d stay as is

### Version

1.3.95

### Additional context

_No response_

Pull request:
fix(css/modules): Fix handling of `:global()` selector
**Related issue:**

 - Closes #8179

Patch:
diff --git a/crates/swc_css_modules/src/lib.rs b/crates/swc_css_modules/src/lib.rs
index 7263b17a2d..7455fa0cbf 100644
--- a/crates/swc_css_modules/src/lib.rs
+++ b/crates/swc_css_modules/src/lib.rs
@@ -476,7 +476,7 @@ where
 
         'complex: for mut n in n.children.take() {
             if let ComplexSelectorChildren::CompoundSelector(selector) = &mut n {
-                for (sel_index, sel) in selector.subclass_selectors.iter_mut().enumerate() {
+                for sel in selector.subclass_selectors.iter_mut() {
                     match sel {
                         SubclassSelector::Class(..) | SubclassSelector::Id(..) => {
                             if !self.data.is_global_mode {
@@ -489,7 +489,14 @@ where
                                 );
                             }
                         }
-                        SubclassSelector::PseudoClass(class_sel) => match &*class_sel.name.value {
+
+                        _ => {}
+                    }
+                }
+
+                for (sel_index, sel) in selector.subclass_selectors.iter_mut().enumerate() {
+                    if let SubclassSelector::PseudoClass(class_sel) = sel {
+                        match &*class_sel.name.value {
                             "local" => {
                                 if let Some(children) = &mut class_sel.children {
                                     if let Some(PseudoClassSelectorChildren::ComplexSelector(
@@ -508,7 +515,8 @@ where
                                             complex_selector.children.clone();
                                         prepend_left_subclass_selectors(
                                             &mut complex_selector_children,
-                                            selector.subclass_selectors.split_at(sel_index),
+                                            &mut selector.subclass_selectors,
+                                            sel_index,
                                         );
                                         new_children.extend(complex_selector_children);
 
@@ -537,7 +545,8 @@ where
                                             complex_selector.children.clone();
                                         prepend_left_subclass_selectors(
                                             &mut complex_selector_children,
-                                            selector.subclass_selectors.split_at(sel_index),
+                                            &mut selector.subclass_selectors,
+                                            sel_index,
                                         );
                                         new_children.extend(complex_selector_children);
                                     }
@@ -554,8 +563,7 @@ where
                                 continue 'complex;
                             }
                             _ => {}
-                        },
-                        _ => {}
+                        }
                     }
                 }
             }
@@ -661,16 +669,22 @@ fn process_local<C>(
 
 fn prepend_left_subclass_selectors(
     complex_selector_children: &mut [ComplexSelectorChildren],
-    sels: (&[SubclassSelector], &[SubclassSelector]),
+    sels: &mut Vec<SubclassSelector>,
+    mut sel_index: usize,
 ) {
-    if let Some(ComplexSelectorChildren::CompoundSelector(first)) =
-        complex_selector_children.get_mut(0)
+    sels.remove(sel_index);
+
+    for c in complex_selector_children
+        .iter_mut()
+        .filter_map(|c| c.as_mut_compound_selector())
     {
-        first.subclass_selectors = [
-            sels.0.to_vec(),
-            first.subclass_selectors.take(),
-            sels.1[1..].to_vec(),
-        ]
-        .concat();
+        c.subclass_selectors.splice(0..0, sels.drain(..sel_index));
+
+        if sels.len() > sel_index {
+            c.subclass_selectors
+                .extend(sels[..sel_index + 1].iter().cloned());
+        }
+
+        sel_index = 0;
     }
 }


Test patch:
diff --git a/crates/swc_css_modules/tests/fixture/issue-8179.compiled.css b/crates/swc_css_modules/tests/fixture/issue-8179.compiled.css
new file mode 100644
index 0000000000..5a38183517
--- /dev/null
+++ b/crates/swc_css_modules/tests/fixture/issue-8179.compiled.css
@@ -0,0 +1,3 @@
+.__local__a.b .c.__local__d {
+  color: red;
+}
diff --git a/crates/swc_css_modules/tests/fixture/issue-8179.css b/crates/swc_css_modules/tests/fixture/issue-8179.css
new file mode 100644
index 0000000000..381d9a1023
--- /dev/null
+++ b/crates/swc_css_modules/tests/fixture/issue-8179.css
@@ -0,0 +1,3 @@
+.a:global(.b .c).d {
+    color: red;
+}
diff --git a/crates/swc_css_modules/tests/fixture/issue-8179.transform.json b/crates/swc_css_modules/tests/fixture/issue-8179.transform.json
new file mode 100644
index 0000000000..25719f1d2d
--- /dev/null
+++ b/crates/swc_css_modules/tests/fixture/issue-8179.transform.json
@@ -0,0 +1,14 @@
+{
+    "a": [
+        {
+            "type": "local",
+            "name": "__local__a"
+        }
+    ],
+    "d": [
+        {
+            "type": "local",
+            "name": "__local__d"
+        }
+    ]
+}
diff --git a/crates/swc_css_modules/tests/fixture/issues/issuse-7447.compiled.css b/crates/swc_css_modules/tests/fixture/issues/issuse-7447.compiled.css
index bc0f3dc897..f69f586e69 100644
--- a/crates/swc_css_modules/tests/fixture/issues/issuse-7447.compiled.css
+++ b/crates/swc_css_modules/tests/fixture/issues/issuse-7447.compiled.css
@@ -1,6 +1,6 @@
 .a:hover {
   color: blue;
 }
-.__local__a.b {
+.__local__a.__local__b {
   color: red;
 }
diff --git a/crates/swc_css_modules/tests/fixture/issues/issuse-7447.transform.json b/crates/swc_css_modules/tests/fixture/issues/issuse-7447.transform.json
index 1d62625f4f..3d1d05e060 100644
--- a/crates/swc_css_modules/tests/fixture/issues/issuse-7447.transform.json
+++ b/crates/swc_css_modules/tests/fixture/issues/issuse-7447.transform.json
@@ -1,8 +1,14 @@
 {
-  "a": [
-    {
-      "type": "local",
-      "name": "__local__a"
-    }
-  ]
+    "b": [
+        {
+            "type": "local",
+            "name": "__local__b"
+        }
+    ],
+    "a": [
+        {
+            "type": "local",
+            "name": "__local__a"
+        }
+    ]
 }