From 7014c63625c7c9f83322931600a52c82ba8b107a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Tue, 7 May 2024 14:52:49 +0900 Subject: [PATCH] fix(css/modules): Allow any order of composes (#8930) **Related issue:** - Closes #8240 --- Cargo.lock | 1 + crates/swc_css_modules/Cargo.toml | 1 + crates/swc_css_modules/src/lib.rs | 67 ++++--- crates/swc_css_modules/tests/fixture.rs | 8 +- .../tests/fixture/issue-8240.compiled.css | 3 + .../tests/fixture/issue-8240.css | 8 + .../tests/fixture/issue-8240.transform.json | 32 ++++ .../issue-7737/issue-7737.transform.json | 168 +++++++++--------- .../composes-1/source.transform.json | 84 ++++----- .../composes-2/source.transform.json | 84 ++++----- 10 files changed, 264 insertions(+), 192 deletions(-) create mode 100644 crates/swc_css_modules/tests/fixture/issue-8240.compiled.css create mode 100644 crates/swc_css_modules/tests/fixture/issue-8240.css create mode 100644 crates/swc_css_modules/tests/fixture/issue-8240.transform.json diff --git a/Cargo.lock b/Cargo.lock index c0a7b910f661..1dfae89d4945 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4099,6 +4099,7 @@ dependencies = [ name = "swc_css_modules" version = "0.29.35" dependencies = [ + "indexmap 2.1.0", "rustc-hash", "serde", "serde_json", diff --git a/crates/swc_css_modules/Cargo.toml b/crates/swc_css_modules/Cargo.toml index 3a7d1f6004db..c384f439887d 100644 --- a/crates/swc_css_modules/Cargo.toml +++ b/crates/swc_css_modules/Cargo.toml @@ -26,6 +26,7 @@ swc_css_parser = { version = "0.150.33", path = "../swc_css_parser" } swc_css_visit = { version = "0.139.22", path = "../swc_css_visit" } [dev-dependencies] +indexmap = { workspace = true, features = ["serde"] } serde_json = { workspace = true } swc_css_compat = { version = "0.27.35", path = "../swc_css_compat" } diff --git a/crates/swc_css_modules/src/lib.rs b/crates/swc_css_modules/src/lib.rs index a0cc0a8c1536..a408691839f0 100644 --- a/crates/swc_css_modules/src/lib.rs +++ b/crates/swc_css_modules/src/lib.rs @@ -60,6 +60,50 @@ pub fn compile<'a>(ss: &mut Stylesheet, config: impl 'a + TransformConfig) -> Tr ss.visit_mut_with(&mut compiler); + fn add(result: &mut TransformResult, data: &Data, key: &JsWord, composes: &[CssClassName]) { + let mut extra_classes = vec![]; + { + let class_names = result.renamed.entry(key.clone()).or_default(); + + class_names.extend(composes.iter().cloned()); + } + + for composed_class_name in composes.iter() { + if let CssClassName::Local { name } = composed_class_name { + if let Some(original_class_name) = data.renamed_to_orig.get(&name.value) { + extra_classes.extend( + result + .renamed + .entry(original_class_name.clone()) + .or_default() + .split_at(1) + .1 + .to_vec(), + ); + } + } + } + + { + let class_names = result.renamed.entry(key.clone()).or_default(); + + class_names.extend(extra_classes); + } + } + + let composes = compiler.data.composes_inherit.take(); + + for (key, composes) in &composes { + add(&mut compiler.result, &compiler.data, key, composes); + } + for (key, composes) in &composes { + add(&mut compiler.result, &compiler.data, key, composes); + } + compiler.result.renamed.iter_mut().for_each(|(_, v)| { + v.sort(); + v.dedup(); + }); + compiler.result } @@ -76,6 +120,7 @@ where struct Data { /// Context for `composes` composes_for_current: Option>, + composes_inherit: Vec<(JsWord, Vec)>, renamed_to_orig: FxHashMap, orig_to_renamed: FxHashMap, @@ -214,27 +259,7 @@ where .cloned(); if let Some(key) = key { - let mut renamed = self.result.renamed.clone(); - let class_names = self.result.renamed.entry(key).or_default(); - - class_names.extend(composes.clone()); - - for composed_class_name in composes.iter() { - if let CssClassName::Local { name } = composed_class_name { - if let Some(original_class_name) = - self.data.renamed_to_orig.get(&name.value) - { - class_names.extend( - renamed - .entry(original_class_name.clone()) - .or_default() - .split_at(1) - .1 - .to_vec(), - ); - } - } - } + self.data.composes_inherit.push((key, composes.clone())); } } } diff --git a/crates/swc_css_modules/tests/fixture.rs b/crates/swc_css_modules/tests/fixture.rs index 051ee818062e..6294a8e81bb8 100644 --- a/crates/swc_css_modules/tests/fixture.rs +++ b/crates/swc_css_modules/tests/fixture.rs @@ -1,6 +1,6 @@ use std::path::PathBuf; -use rustc_hash::FxHashMap; +use indexmap::IndexMap; use serde::Serialize; use swc_atoms::JsWord; use swc_css_codegen::{ @@ -89,7 +89,7 @@ fn compile(input: PathBuf) { .unwrap(); if !transform_result.renamed.is_empty() { - let transformed_classes = &transform_result + let mut transformed_classes = transform_result .renamed .into_iter() .map(|(k, v)| { @@ -113,7 +113,9 @@ fn compile(input: PathBuf) { .collect::>(), ) }) - .collect::>(); + .collect::>(); + + transformed_classes.sort_keys(); NormalizedOutput::compare_json_to_file( &transformed_classes, diff --git a/crates/swc_css_modules/tests/fixture/issue-8240.compiled.css b/crates/swc_css_modules/tests/fixture/issue-8240.compiled.css new file mode 100644 index 000000000000..6c866ff78ea1 --- /dev/null +++ b/crates/swc_css_modules/tests/fixture/issue-8240.compiled.css @@ -0,0 +1,3 @@ +.__local__root-class {} +.__local__chain1 {} +.__local__chain2 {} diff --git a/crates/swc_css_modules/tests/fixture/issue-8240.css b/crates/swc_css_modules/tests/fixture/issue-8240.css new file mode 100644 index 000000000000..00e99b1a5b04 --- /dev/null +++ b/crates/swc_css_modules/tests/fixture/issue-8240.css @@ -0,0 +1,8 @@ +.root-class { + composes: chain1; +} +.chain1 { + composes: chain2; +} +.chain2 { +} \ No newline at end of file diff --git a/crates/swc_css_modules/tests/fixture/issue-8240.transform.json b/crates/swc_css_modules/tests/fixture/issue-8240.transform.json new file mode 100644 index 000000000000..6306d9a900e5 --- /dev/null +++ b/crates/swc_css_modules/tests/fixture/issue-8240.transform.json @@ -0,0 +1,32 @@ +{ + "chain1": [ + { + "name": "__local__chain1", + "type": "local" + }, + { + "name": "__local__chain2", + "type": "local" + } + ], + "chain2": [ + { + "name": "__local__chain2", + "type": "local" + } + ], + "root-class": [ + { + "name": "__local__root-class", + "type": "local" + }, + { + "name": "__local__chain1", + "type": "local" + }, + { + "name": "__local__chain2", + "type": "local" + } + ] +} diff --git a/crates/swc_css_modules/tests/fixture/modules/issue-7737/issue-7737.transform.json b/crates/swc_css_modules/tests/fixture/modules/issue-7737/issue-7737.transform.json index eb2ecd0fc44b..315945e49cce 100644 --- a/crates/swc_css_modules/tests/fixture/modules/issue-7737/issue-7737.transform.json +++ b/crates/swc_css_modules/tests/fixture/modules/issue-7737/issue-7737.transform.json @@ -1,86 +1,86 @@ { - "chain2": [ - { - "type": "local", - "name": "__local__chain2" - }, - { - "type": "global", - "name": "e" - }, - { - "type": "import", - "name": "f", - "from": "./f.css" - } - ], - "chain1": [ - { - "type": "local", - "name": "__local__chain1" - }, - { - "type": "local", - "name": "__local__chain2" - }, - { - "type": "global", - "name": "c" - }, - { - "type": "import", - "name": "d", - "from": "./d.css" - }, - { - "type": "global", - "name": "e" - }, - { - "type": "import", - "name": "f", - "from": "./f.css" - } - ], - "root-class": [ - { - "type": "local", - "name": "__local__root-class" - }, - { - "type": "local", - "name": "__local__chain1" - }, - { - "type": "global", - "name": "a" - }, - { - "type": "import", - "name": "b", - "from": "./b.css" - }, - { - "type": "local", - "name": "__local__chain2" - }, - { - "type": "global", - "name": "c" - }, - { - "type": "import", - "name": "d", - "from": "./d.css" - }, - { - "type": "global", - "name": "e" - }, - { - "type": "import", - "name": "f", - "from": "./f.css" - } - ] + "chain1": [ + { + "name": "__local__chain1", + "type": "local" + }, + { + "name": "__local__chain2", + "type": "local" + }, + { + "name": "e", + "type": "global" + }, + { + "name": "c", + "type": "global" + }, + { + "from": "./f.css", + "name": "f", + "type": "import" + }, + { + "from": "./d.css", + "name": "d", + "type": "import" + } + ], + "chain2": [ + { + "name": "__local__chain2", + "type": "local" + }, + { + "name": "e", + "type": "global" + }, + { + "from": "./f.css", + "name": "f", + "type": "import" + } + ], + "root-class": [ + { + "name": "__local__chain2", + "type": "local" + }, + { + "name": "__local__root-class", + "type": "local" + }, + { + "name": "__local__chain1", + "type": "local" + }, + { + "name": "e", + "type": "global" + }, + { + "name": "c", + "type": "global" + }, + { + "name": "a", + "type": "global" + }, + { + "from": "./f.css", + "name": "f", + "type": "import" + }, + { + "from": "./d.css", + "name": "d", + "type": "import" + }, + { + "from": "./b.css", + "name": "b", + "type": "import" + } + ] } diff --git a/crates/swc_css_modules/tests/fixture/modules/tests-cases/composes-1/source.transform.json b/crates/swc_css_modules/tests/fixture/modules/tests-cases/composes-1/source.transform.json index 6aa2e89e42ea..2f1ba300001e 100644 --- a/crates/swc_css_modules/tests/fixture/modules/tests-cases/composes-1/source.transform.json +++ b/crates/swc_css_modules/tests/fixture/modules/tests-cases/composes-1/source.transform.json @@ -1,44 +1,44 @@ { - "c1": [ - { - "type": "local", - "name": "__local__c1" - }, - { - "type": "import", - "name": "c2", - "from": "./file.css" - } - ], - "c3": [ - { - "type": "local", - "name": "__local__c3" - }, - { - "type": "local", - "name": "__local__c1" - }, - { - "type": "import", - "name": "c2", - "from": "./file.css" - } - ], - "c5": [ - { - "type": "local", - "name": "__local__c5" - }, - { - "type": "import", - "name": "c2", - "from": "./file.css" - }, - { - "type": "import", - "name": "c4", - "from": "./file.css" - } - ] + "c1": [ + { + "name": "__local__c1", + "type": "local" + }, + { + "from": "./file.css", + "name": "c2", + "type": "import" + } + ], + "c3": [ + { + "name": "__local__c3", + "type": "local" + }, + { + "name": "__local__c1", + "type": "local" + }, + { + "from": "./file.css", + "name": "c2", + "type": "import" + } + ], + "c5": [ + { + "name": "__local__c5", + "type": "local" + }, + { + "from": "./file.css", + "name": "c2", + "type": "import" + }, + { + "from": "./file.css", + "name": "c4", + "type": "import" + } + ] } diff --git a/crates/swc_css_modules/tests/fixture/modules/tests-cases/composes-2/source.transform.json b/crates/swc_css_modules/tests/fixture/modules/tests-cases/composes-2/source.transform.json index c7fcc9f0b744..ffc56ec7cec8 100644 --- a/crates/swc_css_modules/tests/fixture/modules/tests-cases/composes-2/source.transform.json +++ b/crates/swc_css_modules/tests/fixture/modules/tests-cases/composes-2/source.transform.json @@ -1,44 +1,44 @@ { - "c1": [ - { - "type": "local", - "name": "__local__c1" - }, - { - "type": "import", - "name": "c-2", - "from": "./file.css" - } - ], - "c3": [ - { - "type": "local", - "name": "__local__c3" - }, - { - "type": "local", - "name": "__local__c1" - }, - { - "type": "import", - "name": "c-2", - "from": "./file.css" - } - ], - "c5": [ - { - "type": "local", - "name": "__local__c5" - }, - { - "type": "import", - "name": "c-2", - "from": "./file.css" - }, - { - "type": "import", - "name": "c4", - "from": "./file.css" - } - ] + "c1": [ + { + "name": "__local__c1", + "type": "local" + }, + { + "from": "./file.css", + "name": "c-2", + "type": "import" + } + ], + "c3": [ + { + "name": "__local__c3", + "type": "local" + }, + { + "name": "__local__c1", + "type": "local" + }, + { + "from": "./file.css", + "name": "c-2", + "type": "import" + } + ], + "c5": [ + { + "name": "__local__c5", + "type": "local" + }, + { + "from": "./file.css", + "name": "c-2", + "type": "import" + }, + { + "from": "./file.css", + "name": "c4", + "type": "import" + } + ] }