diff --git a/libs/extractor/src/lib.rs b/libs/extractor/src/lib.rs index 5780de0a..839e1d92 100644 --- a/libs/extractor/src/lib.rs +++ b/libs/extractor/src/lib.rs @@ -8233,4 +8233,80 @@ keyframes({ .unwrap() )); } + + #[test] + #[serial] + fn test_dot_notation_theme_variables() { + // Test that dot notation theme variables (e.g., $primary.100) are correctly extracted + 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_dir: "@devup-ui/core".to_string(), + single_css: true, + import_main_css: false + } + ) + .unwrap() + )); + + // Test multiple dot notation variables + 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_dir: "@devup-ui/core".to_string(), + single_css: true, + import_main_css: false + } + ) + .unwrap() + )); + + // Test deep nested dot notation + 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_dir: "@devup-ui/core".to_string(), + single_css: true, + import_main_css: false + } + ) + .unwrap() + )); + + // Test dot notation in border shorthand + 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_dir: "@devup-ui/core".to_string(), + single_css: true, + import_main_css: false + } + ) + .unwrap() + )); + } } diff --git a/libs/extractor/src/snapshots/extractor__tests__dot_notation_theme_variables-2.snap b/libs/extractor/src/snapshots/extractor__tests__dot_notation_theme_variables-2.snap new file mode 100644 index 00000000..c284a7c0 --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__dot_notation_theme_variables-2.snap @@ -0,0 +1,27 @@ +--- +source: libs/extractor/src/lib.rs +expression: "ToBTreeSet::from(extract(\"test.tsx\",\nr#\"import {Box} from '@devup-ui/core'\n \n \"#,\nExtractOption\n{\n package: \"@devup-ui/core\".to_string(), css_dir:\n \"@devup-ui/core\".to_string(), single_css: true, import_main_css: false\n}).unwrap())" +--- +ToBTreeSet { + styles: { + Static( + ExtractStaticStyle { + property: "background", + value: "$gray.200", + level: 0, + selector: None, + style_order: None, + }, + ), + Static( + ExtractStaticStyle { + property: "color", + value: "$blue.500", + level: 0, + selector: None, + style_order: None, + }, + ), + }, + code: "import \"@devup-ui/core/devup-ui.css\";\n
;\n", +} diff --git a/libs/extractor/src/snapshots/extractor__tests__dot_notation_theme_variables-3.snap b/libs/extractor/src/snapshots/extractor__tests__dot_notation_theme_variables-3.snap new file mode 100644 index 00000000..89adcef8 --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__dot_notation_theme_variables-3.snap @@ -0,0 +1,18 @@ +--- +source: libs/extractor/src/lib.rs +expression: "ToBTreeSet::from(extract(\"test.tsx\",\nr#\"import {Box} from '@devup-ui/core'\n \n \"#,\nExtractOption\n{\n package: \"@devup-ui/core\".to_string(), css_dir:\n \"@devup-ui/core\".to_string(), single_css: true, import_main_css: false\n}).unwrap())" +--- +ToBTreeSet { + styles: { + Static( + ExtractStaticStyle { + property: "background", + value: "$color.brand.primary.100", + level: 0, + selector: None, + style_order: None, + }, + ), + }, + code: "import \"@devup-ui/core/devup-ui.css\";\n
;\n", +} diff --git a/libs/extractor/src/snapshots/extractor__tests__dot_notation_theme_variables-4.snap b/libs/extractor/src/snapshots/extractor__tests__dot_notation_theme_variables-4.snap new file mode 100644 index 00000000..7f73c5f6 --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__dot_notation_theme_variables-4.snap @@ -0,0 +1,18 @@ +--- +source: libs/extractor/src/lib.rs +expression: "ToBTreeSet::from(extract(\"test.tsx\",\nr#\"import {Box} from '@devup-ui/core'\n \n \"#,\nExtractOption\n{\n package: \"@devup-ui/core\".to_string(), css_dir:\n \"@devup-ui/core\".to_string(), single_css: true, import_main_css: false\n}).unwrap())" +--- +ToBTreeSet { + styles: { + Static( + ExtractStaticStyle { + property: "border", + value: "1px solid $border.primary", + level: 0, + selector: None, + style_order: None, + }, + ), + }, + code: "import \"@devup-ui/core/devup-ui.css\";\n
;\n", +} diff --git a/libs/extractor/src/snapshots/extractor__tests__dot_notation_theme_variables.snap b/libs/extractor/src/snapshots/extractor__tests__dot_notation_theme_variables.snap new file mode 100644 index 00000000..f13e9ac6 --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__dot_notation_theme_variables.snap @@ -0,0 +1,18 @@ +--- +source: libs/extractor/src/lib.rs +expression: "ToBTreeSet::from(extract(\"test.tsx\",\nr#\"import {Box} from '@devup-ui/core'\n \n \"#,\nExtractOption\n{\n package: \"@devup-ui/core\".to_string(), css_dir:\n \"@devup-ui/core\".to_string(), single_css: true, import_main_css: false\n}).unwrap())" +--- +ToBTreeSet { + styles: { + Static( + ExtractStaticStyle { + property: "background", + value: "$primary.100", + level: 0, + selector: None, + style_order: None, + }, + ), + }, + code: "import \"@devup-ui/core/devup-ui.css\";\n
;\n", +} diff --git a/libs/sheet/src/lib.rs b/libs/sheet/src/lib.rs index 9ffe2f67..d9c7bb7a 100644 --- a/libs/sheet/src/lib.rs +++ b/libs/sheet/src/lib.rs @@ -72,7 +72,7 @@ impl ExtractStyle for StyleSheetProperty { } } -static VAR_RE: Lazy = Lazy::new(|| Regex::new(r"\$\w+").unwrap()); +static VAR_RE: Lazy = Lazy::new(|| Regex::new(r"\$[\w.]+").unwrap()); static INTERFACE_KEY_RE: Lazy = Lazy::new(|| Regex::new(r"^[a-zA-Z_$][a-zA-Z0-9_$]*$").unwrap()); @@ -88,7 +88,7 @@ fn convert_theme_variable_value(value: &str) -> String { if value.contains("$") { VAR_RE .replace_all(value, |caps: ®ex::Captures| { - format!("var(--{})", &caps[0][1..]) + format!("var(--{})", &caps[0][1..].replace('.', "-")) }) .to_string() } else { @@ -690,20 +690,22 @@ mod tests { use extractor::{ExtractOption, extract}; use insta::assert_debug_snapshot; - #[test] - fn test_convert_theme_variable_value() { - assert_eq!(convert_theme_variable_value("1px"), "1px"); - assert_eq!(convert_theme_variable_value("$var"), "var(--var)"); - - assert_eq!( - convert_theme_variable_value("$var $var"), - "var(--var) var(--var)" - ); - - assert_eq!( - convert_theme_variable_value("1px solid $red"), - "1px solid var(--red)" - ); + use rstest::rstest; + + #[rstest] + #[case("1px", "1px")] + #[case("$var", "var(--var)")] + #[case("$var $var", "var(--var) var(--var)")] + #[case("1px solid $red", "1px solid var(--red)")] + // Test dot notation theme variables (e.g., $primary.100) + // Dots should be converted to dashes for CSS variable names + #[case("$primary.100", "var(--primary-100)")] + #[case("$gray.200 $blue.500", "var(--gray-200) var(--blue-500)")] + #[case("1px solid $border.primary", "1px solid var(--border-primary)")] + // Test deep nested dot notation + #[case("$color.brand.primary.100", "var(--color-brand-primary-100)")] + fn test_convert_theme_variable_value(#[case] input: &str, #[case] expected: &str) { + assert_eq!(convert_theme_variable_value(input), expected); } #[test]