From f6d3f0a6ec4b5bc294fb5c19cb213290b82a2f35 Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Thu, 3 Jul 2025 15:54:06 +0900 Subject: [PATCH 1/3] Support nested selector --- .changeset/large-poems-deny.md | 6 + libs/extractor/src/lib.rs | 195 ++++++++++++++++++ ...tor__tests__extract_nested_selector-2.snap | 22 ++ ...tor__tests__extract_nested_selector-3.snap | 35 ++++ ...tor__tests__extract_nested_selector-4.snap | 22 ++ ...tor__tests__extract_nested_selector-5.snap | 22 ++ ...tor__tests__extract_nested_selector-6.snap | 22 ++ ...tor__tests__extract_nested_selector-7.snap | 35 ++++ ...actor__tests__extract_nested_selector.snap | 22 ++ .../extractor__tests__nested_theme_props.snap | 48 +++++ libs/extractor/src/style_extractor.rs | 10 +- packages/react/src/types/props/index.ts | 6 +- 12 files changed, 436 insertions(+), 9 deletions(-) create mode 100644 .changeset/large-poems-deny.md create mode 100644 libs/extractor/src/snapshots/extractor__tests__extract_nested_selector-2.snap create mode 100644 libs/extractor/src/snapshots/extractor__tests__extract_nested_selector-3.snap create mode 100644 libs/extractor/src/snapshots/extractor__tests__extract_nested_selector-4.snap create mode 100644 libs/extractor/src/snapshots/extractor__tests__extract_nested_selector-5.snap create mode 100644 libs/extractor/src/snapshots/extractor__tests__extract_nested_selector-6.snap create mode 100644 libs/extractor/src/snapshots/extractor__tests__extract_nested_selector-7.snap create mode 100644 libs/extractor/src/snapshots/extractor__tests__extract_nested_selector.snap create mode 100644 libs/extractor/src/snapshots/extractor__tests__nested_theme_props.snap diff --git a/.changeset/large-poems-deny.md b/.changeset/large-poems-deny.md new file mode 100644 index 00000000..c11ae6b7 --- /dev/null +++ b/.changeset/large-poems-deny.md @@ -0,0 +1,6 @@ +--- +"@devup-ui/react": patch +"@devup-ui/wasm": patch +--- + +Support nested selector diff --git a/libs/extractor/src/lib.rs b/libs/extractor/src/lib.rs index 9bd42945..f9aa7ad3 100644 --- a/libs/extractor/src/lib.rs +++ b/libs/extractor/src/lib.rs @@ -1353,6 +1353,170 @@ import clsx from 'clsx' )); } + #[test] + #[serial] + fn extract_nested_selector() { + reset_class_map(); + assert_debug_snapshot!(ToBTreeSet::from( + extract( + "test.tsx", + r#"import {Box} from '@devup-ui/core' + + "#, + ExtractOption { + package: "@devup-ui/core".to_string(), + css_file: None + } + ) + .unwrap() + )); + + reset_class_map(); + assert_debug_snapshot!(ToBTreeSet::from( + extract( + "test.tsx", + r#"import {Box} from '@devup-ui/core' + + "#, + ExtractOption { + package: "@devup-ui/core".to_string(), + css_file: None + } + ) + .unwrap() + )); + + reset_class_map(); + assert_debug_snapshot!(ToBTreeSet::from( + extract( + "test.tsx", + r#"import {Box} from '@devup-ui/core' + + "#, + ExtractOption { + package: "@devup-ui/core".to_string(), + css_file: None + } + ) + .unwrap() + )); + + reset_class_map(); + assert_debug_snapshot!(ToBTreeSet::from( + extract( + "test.tsx", + r#"import {Box} from '@devup-ui/core' + + "#, + ExtractOption { + package: "@devup-ui/core".to_string(), + css_file: None + } + ) + .unwrap() + )); + + reset_class_map(); + assert_debug_snapshot!(ToBTreeSet::from( + extract( + "test.tsx", + r#"import {Box} from '@devup-ui/core' + + "#, + ExtractOption { + package: "@devup-ui/core".to_string(), + css_file: None + } + ) + .unwrap() + )); + + reset_class_map(); + assert_debug_snapshot!(ToBTreeSet::from( + extract( + "test.tsx", + r#"import {Box} from '@devup-ui/core' + + "#, + ExtractOption { + package: "@devup-ui/core".to_string(), + css_file: None + } + ) + .unwrap() + )); + + reset_class_map(); + assert_debug_snapshot!(ToBTreeSet::from( + extract( + "test.tsx", + r#"import {Box} from '@devup-ui/core' + + "#, + ExtractOption { + package: "@devup-ui/core".to_string(), + css_file: None + } + ) + .unwrap() + )); + } + #[test] #[serial] fn extract_conditional_selector() { @@ -2802,6 +2966,37 @@ import {Button} from '@devup/ui' )); } + #[test] + #[serial] + fn nested_theme_props() { + reset_class_map(); + assert_debug_snapshot!(ToBTreeSet::from( + extract( + "test.jsx", + r#"import {Box} from '@devup-ui/core' + + "#, + ExtractOption { + package: "@devup-ui/core".to_string(), + css_file: None + } + ) + .unwrap() + )); + } + #[test] #[serial] fn template_literal_props() { diff --git a/libs/extractor/src/snapshots/extractor__tests__extract_nested_selector-2.snap b/libs/extractor/src/snapshots/extractor__tests__extract_nested_selector-2.snap new file mode 100644 index 00000000..32839d06 --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__extract_nested_selector-2.snap @@ -0,0 +1,22 @@ +--- +source: libs/extractor/src/lib.rs +expression: "ToBTreeSet::from(extract(\"test.tsx\",\nr#\"import {Box} from '@devup-ui/core'\n \n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())" +--- +ToBTreeSet { + styles: { + Static( + ExtractStaticStyle { + property: "color", + value: "blue", + level: 0, + selector: Some( + Selector( + "&:hover::placeholder, &:hover:active", + ), + ), + style_order: None, + }, + ), + }, + code: "import \"@devup-ui/core/devup-ui.css\";\n
;\n", +} diff --git a/libs/extractor/src/snapshots/extractor__tests__extract_nested_selector-3.snap b/libs/extractor/src/snapshots/extractor__tests__extract_nested_selector-3.snap new file mode 100644 index 00000000..e592a8ae --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__extract_nested_selector-3.snap @@ -0,0 +1,35 @@ +--- +source: libs/extractor/src/lib.rs +expression: "ToBTreeSet::from(extract(\"test.tsx\",\nr#\"import {Box} from '@devup-ui/core'\n \n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())" +--- +ToBTreeSet { + styles: { + Static( + ExtractStaticStyle { + property: "color", + value: "blue", + level: 0, + selector: Some( + Selector( + "&:hover::placeholder, &:hover:active", + ), + ), + style_order: None, + }, + ), + Static( + ExtractStaticStyle { + property: "color", + value: "red", + level: 0, + selector: Some( + Selector( + "&:hover::placeholder", + ), + ), + style_order: None, + }, + ), + }, + code: "import \"@devup-ui/core/devup-ui.css\";\n
;\n", +} diff --git a/libs/extractor/src/snapshots/extractor__tests__extract_nested_selector-4.snap b/libs/extractor/src/snapshots/extractor__tests__extract_nested_selector-4.snap new file mode 100644 index 00000000..4aea7d29 --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__extract_nested_selector-4.snap @@ -0,0 +1,22 @@ +--- +source: libs/extractor/src/lib.rs +expression: "ToBTreeSet::from(extract(\"test.tsx\",\nr#\"import {Box} from '@devup-ui/core'\n \n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())" +--- +ToBTreeSet { + styles: { + Static( + ExtractStaticStyle { + property: "color", + value: "red", + level: 0, + selector: Some( + Selector( + "&:hover::placeholder:active", + ), + ), + style_order: None, + }, + ), + }, + code: "import \"@devup-ui/core/devup-ui.css\";\n
;\n", +} diff --git a/libs/extractor/src/snapshots/extractor__tests__extract_nested_selector-5.snap b/libs/extractor/src/snapshots/extractor__tests__extract_nested_selector-5.snap new file mode 100644 index 00000000..85af7854 --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__extract_nested_selector-5.snap @@ -0,0 +1,22 @@ +--- +source: libs/extractor/src/lib.rs +expression: "ToBTreeSet::from(extract(\"test.tsx\",\nr#\"import {Box} from '@devup-ui/core'\n \n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())" +--- +ToBTreeSet { + styles: { + Static( + ExtractStaticStyle { + property: "color", + value: "red", + level: 0, + selector: Some( + Selector( + "&::placeholder:active", + ), + ), + style_order: None, + }, + ), + }, + code: "import \"@devup-ui/core/devup-ui.css\";\n
;\n", +} diff --git a/libs/extractor/src/snapshots/extractor__tests__extract_nested_selector-6.snap b/libs/extractor/src/snapshots/extractor__tests__extract_nested_selector-6.snap new file mode 100644 index 00000000..7522cf8c --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__extract_nested_selector-6.snap @@ -0,0 +1,22 @@ +--- +source: libs/extractor/src/lib.rs +expression: "ToBTreeSet::from(extract(\"test.tsx\",\nr#\"import {Box} from '@devup-ui/core'\n \n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())" +--- +ToBTreeSet { + styles: { + Static( + ExtractStaticStyle { + property: "color", + value: "red", + level: 0, + selector: Some( + Selector( + "&::placeholder:active:hover", + ), + ), + style_order: None, + }, + ), + }, + code: "import \"@devup-ui/core/devup-ui.css\";\n
;\n", +} diff --git a/libs/extractor/src/snapshots/extractor__tests__extract_nested_selector-7.snap b/libs/extractor/src/snapshots/extractor__tests__extract_nested_selector-7.snap new file mode 100644 index 00000000..dbff2f26 --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__extract_nested_selector-7.snap @@ -0,0 +1,35 @@ +--- +source: libs/extractor/src/lib.rs +expression: "ToBTreeSet::from(extract(\"test.tsx\",\nr#\"import {Box} from '@devup-ui/core'\n \n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())" +--- +ToBTreeSet { + styles: { + Static( + ExtractStaticStyle { + property: "color", + value: "blue", + level: 0, + selector: Some( + Selector( + "&::placeholder:active:hover", + ), + ), + style_order: None, + }, + ), + Static( + ExtractStaticStyle { + property: "color", + value: "red", + level: 0, + selector: Some( + Selector( + "&::placeholder:active", + ), + ), + style_order: None, + }, + ), + }, + code: "import \"@devup-ui/core/devup-ui.css\";\n
;\n", +} diff --git a/libs/extractor/src/snapshots/extractor__tests__extract_nested_selector.snap b/libs/extractor/src/snapshots/extractor__tests__extract_nested_selector.snap new file mode 100644 index 00000000..19ce0505 --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__extract_nested_selector.snap @@ -0,0 +1,22 @@ +--- +source: libs/extractor/src/lib.rs +expression: "ToBTreeSet::from(extract(\"test.tsx\",\nr#\"import {Box} from '@devup-ui/core'\n \n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())" +--- +ToBTreeSet { + styles: { + Static( + ExtractStaticStyle { + property: "color", + value: "red", + level: 0, + selector: Some( + Selector( + "&:hover::placeholder", + ), + ), + style_order: None, + }, + ), + }, + code: "import \"@devup-ui/core/devup-ui.css\";\n
;\n", +} diff --git a/libs/extractor/src/snapshots/extractor__tests__nested_theme_props.snap b/libs/extractor/src/snapshots/extractor__tests__nested_theme_props.snap new file mode 100644 index 00000000..186162f1 --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__nested_theme_props.snap @@ -0,0 +1,48 @@ +--- +source: libs/extractor/src/lib.rs +expression: "ToBTreeSet::from(extract(\"test.jsx\",\nr#\"import {Box} from '@devup-ui/core'\n \n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())" +--- +ToBTreeSet { + styles: { + Static( + ExtractStaticStyle { + property: "color", + value: "blue", + level: 0, + selector: Some( + Selector( + ":root[data-theme=dark] &:active", + ), + ), + style_order: None, + }, + ), + Static( + ExtractStaticStyle { + property: "color", + value: "green", + level: 0, + selector: Some( + Selector( + ":root[data-theme=dark] &:active::placeholder", + ), + ), + style_order: None, + }, + ), + Static( + ExtractStaticStyle { + property: "color", + value: "red", + level: 0, + selector: Some( + Selector( + ":root[data-theme=dark] &:hover", + ), + ), + style_order: None, + }, + ), + }, + code: "import \"@devup-ui/core/devup-ui.css\";\n
;\n", +} diff --git a/libs/extractor/src/style_extractor.rs b/libs/extractor/src/style_extractor.rs index e3964609..5ff38fc6 100644 --- a/libs/extractor/src/style_extractor.rs +++ b/libs/extractor/src/style_extractor.rs @@ -75,6 +75,8 @@ pub fn extract_style_from_expression<'a>( ) -> ExtractResult<'a> { let mut typo = false; + println!("name: {:?}, selector: {:?}", name, selector); + if name.is_none() && selector.is_none() { let mut style_order = None; let mut style_vars = None; @@ -273,13 +275,9 @@ pub fn extract_style_from_expression<'a>( level, Some( &if let Some(selector) = selector { - format!( - "{}{}", - selector.to_string().split("&").collect::>()[0], - name - ) + name.replace("&", &selector.to_string()) } else { - name + name.to_string() } .as_str() .into(), diff --git a/packages/react/src/types/props/index.ts b/packages/react/src/types/props/index.ts index d3b03cf6..760f2f82 100644 --- a/packages/react/src/types/props/index.ts +++ b/packages/react/src/types/props/index.ts @@ -62,12 +62,12 @@ export interface DevupCommonProps DevupUiTransitionProps, DevupUiUiProps, DevupUiViewTransitionProps, + DevupSelectorProps, + DevupThemeSelectorProps, DevupUiSvgProps {} export interface DevupProps - extends DevupCommonProps, - DevupSelectorProps, - DevupThemeSelectorProps { + extends DevupCommonProps { as?: T styleVars?: Record } From b3317eeebd2f62381c43b8fe8ce3cc482c2a3373 Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Thu, 3 Jul 2025 16:02:42 +0900 Subject: [PATCH 2/3] Rm println! --- libs/extractor/src/style_extractor.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/libs/extractor/src/style_extractor.rs b/libs/extractor/src/style_extractor.rs index 5ff38fc6..bbea932b 100644 --- a/libs/extractor/src/style_extractor.rs +++ b/libs/extractor/src/style_extractor.rs @@ -75,8 +75,6 @@ pub fn extract_style_from_expression<'a>( ) -> ExtractResult<'a> { let mut typo = false; - println!("name: {:?}, selector: {:?}", name, selector); - if name.is_none() && selector.is_none() { let mut style_order = None; let mut style_vars = None; From f1bd5078a536dcb2309cf514c2e67848221d6892 Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Thu, 3 Jul 2025 16:05:41 +0900 Subject: [PATCH 3/3] Rm to_string --- libs/extractor/src/style_extractor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/extractor/src/style_extractor.rs b/libs/extractor/src/style_extractor.rs index bbea932b..82e3241c 100644 --- a/libs/extractor/src/style_extractor.rs +++ b/libs/extractor/src/style_extractor.rs @@ -275,7 +275,7 @@ pub fn extract_style_from_expression<'a>( &if let Some(selector) = selector { name.replace("&", &selector.to_string()) } else { - name.to_string() + name } .as_str() .into(),