From 2dd81f52763cb2840142ca2738a9254f1419e205 Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Wed, 2 Jul 2025 00:39:37 +0900 Subject: [PATCH 1/2] Impl styleVars --- .changeset/small-buckets-joke.md | 6 + bindings/devup-ui-wasm/src/lib.rs | 6 +- libs/css/src/lib.rs | 26 +-- libs/extractor/src/extract_style/mod.rs | 2 +- libs/extractor/src/lib.rs | 176 ++++++++++++++++++ libs/extractor/src/prop_modify_utils.rs | 143 +++++++++++++- .../extractor__tests__style_variables-10.snap | 8 + .../extractor__tests__style_variables-2.snap | 8 + .../extractor__tests__style_variables-3.snap | 8 + .../extractor__tests__style_variables-4.snap | 8 + .../extractor__tests__style_variables-5.snap | 8 + .../extractor__tests__style_variables-6.snap | 8 + .../extractor__tests__style_variables-7.snap | 18 ++ .../extractor__tests__style_variables-8.snap | 8 + .../extractor__tests__style_variables-9.snap | 8 + .../extractor__tests__style_variables.snap | 8 + libs/extractor/src/style_extractor.rs | 2 +- libs/extractor/src/utils.rs | 8 +- libs/extractor/src/visit.rs | 36 ++-- libs/sheet/src/lib.rs | 5 +- libs/sheet/src/theme.rs | 14 +- packages/react/src/types/props/index.ts | 1 + 22 files changed, 461 insertions(+), 54 deletions(-) create mode 100644 .changeset/small-buckets-joke.md create mode 100644 libs/extractor/src/snapshots/extractor__tests__style_variables-10.snap create mode 100644 libs/extractor/src/snapshots/extractor__tests__style_variables-2.snap create mode 100644 libs/extractor/src/snapshots/extractor__tests__style_variables-3.snap create mode 100644 libs/extractor/src/snapshots/extractor__tests__style_variables-4.snap create mode 100644 libs/extractor/src/snapshots/extractor__tests__style_variables-5.snap create mode 100644 libs/extractor/src/snapshots/extractor__tests__style_variables-6.snap create mode 100644 libs/extractor/src/snapshots/extractor__tests__style_variables-7.snap create mode 100644 libs/extractor/src/snapshots/extractor__tests__style_variables-8.snap create mode 100644 libs/extractor/src/snapshots/extractor__tests__style_variables-9.snap create mode 100644 libs/extractor/src/snapshots/extractor__tests__style_variables.snap diff --git a/.changeset/small-buckets-joke.md b/.changeset/small-buckets-joke.md new file mode 100644 index 00000000..01ca948b --- /dev/null +++ b/.changeset/small-buckets-joke.md @@ -0,0 +1,6 @@ +--- +"@devup-ui/wasm": patch +"@devup-ui/react": patch +--- + +Implement styleVars diff --git a/bindings/devup-ui-wasm/src/lib.rs b/bindings/devup-ui-wasm/src/lib.rs index 6cbfb559..50442fb7 100644 --- a/bindings/devup-ui-wasm/src/lib.rs +++ b/bindings/devup-ui-wasm/src/lib.rs @@ -201,20 +201,20 @@ pub fn get_theme_interface( color_interface_name, color_keys .into_iter() - .map(|key| format!("${}:null;", key)) + .map(|key| format!("${key}:null;")) .collect::>() .join(""), typography_interface_name, typography_keys .into_iter() - .map(|key| format!("{}:null;", key)) + .map(|key| format!("{key}:null;")) .collect::>() .join(""), theme_interface_name, theme_keys .into_iter() // key to pascal - .map(|key| format!("{}:null;", key)) + .map(|key| format!("{key}:null;")) .collect::>() .join("") ) diff --git a/libs/css/src/lib.rs b/libs/css/src/lib.rs index 54fb9d20..fb76049d 100644 --- a/libs/css/src/lib.rs +++ b/libs/css/src/lib.rs @@ -21,7 +21,7 @@ static SELECTOR_ORDER_MAP: Lazy> = Lazy::new(|| { .into_iter() .enumerate() { - map.insert(format!("&:{}", selector), idx as u8); + map.insert(format!("&:{selector}"), idx as u8); } map }); @@ -54,8 +54,8 @@ fn get_selector_order(selector: &str) -> u8 { let t = if selector.chars().filter(|c| c == &'&').count() == 1 { selector .split('&') - .last() - .map(|a| format!("&{}", a)) + .next_back() + .map(|a| format!("&{a}")) .unwrap_or(selector.to_string()) } else { selector.to_string() @@ -129,7 +129,7 @@ impl Display for StyleSelector { "{}", match self { StyleSelector::Selector(value) => value.to_string(), - StyleSelector::Media(value) => format!("@{}", value), + StyleSelector::Media(value) => format!("@{value}"), } ) } @@ -138,11 +138,11 @@ impl Display for StyleSelector { pub fn merge_selector(class_name: &str, selector: Option<&StyleSelector>) -> String { if let Some(selector) = selector { match selector { - StyleSelector::Selector(value) => value.replace("&", &format!(".{}", class_name)), - StyleSelector::Media(_) => format!(".{}", class_name), + StyleSelector::Selector(value) => value.replace("&", &format!(".{class_name}")), + StyleSelector::Media(_) => format!(".{class_name}"), } } else { - format!(".{}", class_name) + format!(".{class_name}") } } @@ -359,7 +359,7 @@ fn optimize_color(value: &str) -> String { } } - format!("#{}", ret) + format!("#{ret}") } pub fn optimize_value(value: &str) -> String { @@ -377,13 +377,13 @@ pub fn optimize_value(value: &str) -> String { } // remove ; from dynamic value for str_symbol in ["", "`", "\"", "'"] { - if ret.ends_with(&format!(";{}", str_symbol)) { + if ret.ends_with(&format!(";{str_symbol}")) { ret = format!( "{}{}", ret[..ret.len() - str_symbol.len() - 1].trim_end_matches(';'), str_symbol ); - } else if ret.ends_with(&format!(";{})", str_symbol)) { + } else if ret.ends_with(&format!(";{str_symbol})")) { ret = format!( "{}{})", ret[..ret.len() - str_symbol.len() - 2].trim_end_matches(';'), @@ -427,7 +427,7 @@ pub fn sheet_to_classname( style_order.unwrap_or(255) ); let mut map = GLOBAL_CLASS_MAP.lock().unwrap(); - map.get(&key).map(|v| format!("d{}", v)).unwrap_or_else(|| { + map.get(&key).map(|v| format!("d{v}")).unwrap_or_else(|| { let len = map.len(); map.insert(key, len as i32); format!("d{}", map.len() - 1) @@ -442,7 +442,7 @@ pub fn css_to_classname(css: &str) -> String { format!("css-{}", hasher.finish()) } else { let mut map = GLOBAL_CLASS_MAP.lock().unwrap(); - map.get(css).map(|v| format!("d{}", v)).unwrap_or_else(|| { + map.get(css).map(|v| format!("d{v}")).unwrap_or_else(|| { let len = map.len(); map.insert(css.to_string(), len as i32); format!("d{}", map.len() - 1) @@ -469,7 +469,7 @@ pub fn sheet_to_variable_name(property: &str, level: u8, selector: Option<&str>) let key = format!("{}-{}-{}", property, level, selector.unwrap_or("").trim()); let mut map = GLOBAL_CLASS_MAP.lock().unwrap(); map.get(&key) - .map(|v| format!("--d{}", v)) + .map(|v| format!("--d{v}")) .unwrap_or_else(|| { let len = map.len(); map.insert(key, len as i32); diff --git a/libs/extractor/src/extract_style/mod.rs b/libs/extractor/src/extract_style/mod.rs index d001aa2d..269768b3 100644 --- a/libs/extractor/src/extract_style/mod.rs +++ b/libs/extractor/src/extract_style/mod.rs @@ -220,7 +220,7 @@ impl ExtractStyleValue { ExtractStyleValue::Dynamic(style) => style.extract(), ExtractStyleValue::Css(css) => css.extract(), ExtractStyleValue::Typography(typo) => { - StyleProperty::ClassName(format!("typo-{}", typo)) + StyleProperty::ClassName(format!("typo-{typo}")) } } } diff --git a/libs/extractor/src/lib.rs b/libs/extractor/src/lib.rs index faa0ca94..6fb7818d 100644 --- a/libs/extractor/src/lib.rs +++ b/libs/extractor/src/lib.rs @@ -3084,4 +3084,180 @@ export { .unwrap() )); } + + #[test] + #[serial] + fn style_variables() { + 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() + )); + + 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() + )); + + 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() + )); + + 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() + )); + + 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() + )); + + 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() + )); + + 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() + )); + + 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() + )); + + 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() + )); + + 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() + )); + } } diff --git a/libs/extractor/src/prop_modify_utils.rs b/libs/extractor/src/prop_modify_utils.rs index 97b01386..1669590d 100644 --- a/libs/extractor/src/prop_modify_utils.rs +++ b/libs/extractor/src/prop_modify_utils.rs @@ -1,13 +1,16 @@ +use crate::ExtractStyleProp; use crate::gen_class_name::{ apply_class_name_attribute, gen_class_names, merge_expression_for_class_name, }; use crate::gen_style::{apply_style_attribute, gen_styles}; -use crate::ExtractStyleProp; -use oxc_allocator::CloneIn; +use oxc_allocator::{CloneIn, Vec}; +use oxc_ast::AstBuilder; use oxc_ast::ast::JSXAttributeItem::Attribute; use oxc_ast::ast::JSXAttributeName::Identifier; -use oxc_ast::ast::{Expression, JSXAttributeItem, ObjectPropertyKind, PropertyKey, PropertyKind}; -use oxc_ast::AstBuilder; +use oxc_ast::ast::{ + Expression, JSXAttribute, JSXAttributeItem, JSXAttributeValue, JSXExpression, + ObjectPropertyKind, PropertyKey, PropertyKind, TemplateElementValue, +}; use oxc_span::SPAN; /// modify object props @@ -120,6 +123,7 @@ pub fn modify_props<'a>( props: &mut oxc_allocator::Vec>, styles: &mut [ExtractStyleProp<'a>], style_order: Option, + style_vars: Option>, ) { let mut class_name_prop = None; let mut style_prop = None; @@ -163,19 +167,140 @@ pub fn modify_props<'a>( // should modify style prop if let Some(ex) = gen_styles(ast_builder, styles) { - let mut attr = if let Some(style_prop) = style_prop { - style_prop - } else { + let mut attr = style_prop.unwrap_or_else(|| { ast_builder.alloc_jsx_attribute( SPAN, Identifier(ast_builder.alloc_jsx_identifier(SPAN, "style")), None, ) - }; + }); apply_style_attribute(ast_builder, &mut attr, ex); + if let Some(mut style_vars) = style_vars { + merge_style_vars(ast_builder, &mut attr, &mut style_vars); + } props.push(Attribute(attr)); - } else if let Some(style_prop) = style_prop { + } else if let Some(mut style_prop) = style_prop { // re add class name prop if not modified + + if let Some(mut style_vars) = style_vars { + merge_style_vars(ast_builder, &mut style_prop, &mut style_vars); + } props.push(Attribute(style_prop)) + } else if let Some(mut style_vars) = style_vars { + let mut attr = style_prop.unwrap_or_else(|| { + ast_builder.alloc_jsx_attribute( + SPAN, + Identifier(ast_builder.alloc_jsx_identifier(SPAN, "style")), + None, + ) + }); + merge_style_vars(ast_builder, &mut attr, &mut style_vars); + props.push(Attribute(attr)); + } +} + +/// Priority: dynamic style, style, styleVars +fn merge_style_vars<'a>( + ast_builder: &AstBuilder<'a>, + style_prop: &mut JSXAttribute<'a>, + style_vars: &mut Expression<'a>, +) { + convert_style_vars(ast_builder, style_vars); + if let Some(ref mut value) = style_prop.value { + if let JSXAttributeValue::ExpressionContainer(container) = value { + style_prop.value = Some(JSXAttributeValue::ExpressionContainer( + ast_builder.alloc_jsx_expression_container( + SPAN, + JSXExpression::ObjectExpression( + ast_builder.alloc_object_expression( + SPAN, + Vec::from_array_in( + [ + ObjectPropertyKind::SpreadProperty( + ast_builder.alloc_spread_element( + SPAN, + style_vars.clone_in(ast_builder.allocator), + ), + ), + ObjectPropertyKind::SpreadProperty( + ast_builder.alloc_spread_element( + SPAN, + container + .expression + .clone_in(ast_builder.allocator) + .into_expression(), + ), + ), + ], + ast_builder.allocator, + ), + ), + ), + ), + )); + } + } else { + style_prop.value = Some(JSXAttributeValue::ExpressionContainer( + ast_builder.alloc_jsx_expression_container( + SPAN, + style_vars.clone_in(ast_builder.allocator).into(), + ), + )); + } +} + +pub fn convert_style_vars<'a>(ast_builder: &AstBuilder<'a>, style_vars: &mut Expression<'a>) { + if let Expression::ObjectExpression(obj) = style_vars { + for idx in (0..obj.properties.len()).rev() { + let mut prop = obj.properties.remove(idx); + + if let ObjectPropertyKind::ObjectProperty(prop) = &mut prop { + let name = match &prop.key { + PropertyKey::StaticIdentifier(ident) => ident.name, + PropertyKey::StringLiteral(ident) => ident.value, + etc => { + obj.properties.insert( + idx, + ObjectPropertyKind::ObjectProperty(ast_builder.alloc_object_property( + SPAN, + PropertyKind::Init, + PropertyKey::TemplateLiteral(ast_builder.alloc_template_literal( + SPAN, + Vec::from_array_in( + [ast_builder.template_element( + SPAN, + TemplateElementValue { + raw: ast_builder.atom("--"), + cooked: None, + }, + false, + )], + ast_builder.allocator, + ), + Vec::from_array_in( + [etc.to_expression().clone_in(ast_builder.allocator)], + ast_builder.allocator, + ), + )), + prop.value.clone_in(ast_builder.allocator), + false, + false, + true, + )), + ); + continue; + } + }; + + if !name.starts_with("--") { + prop.key = PropertyKey::StringLiteral(ast_builder.alloc_string_literal( + SPAN, + ast_builder.atom(&format!("--{name}")), + None, + )); + } + } + obj.properties.insert(idx, prop); + } } } diff --git a/libs/extractor/src/snapshots/extractor__tests__style_variables-10.snap b/libs/extractor/src/snapshots/extractor__tests__style_variables-10.snap new file mode 100644 index 00000000..ad03901c --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__style_variables-10.snap @@ -0,0 +1,8 @@ +--- +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: {}, + code: "
;\n", +} diff --git a/libs/extractor/src/snapshots/extractor__tests__style_variables-2.snap b/libs/extractor/src/snapshots/extractor__tests__style_variables-2.snap new file mode 100644 index 00000000..dc4c9b76 --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__style_variables-2.snap @@ -0,0 +1,8 @@ +--- +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: {}, + code: "
;\n", +} diff --git a/libs/extractor/src/snapshots/extractor__tests__style_variables-3.snap b/libs/extractor/src/snapshots/extractor__tests__style_variables-3.snap new file mode 100644 index 00000000..f3a975e9 --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__style_variables-3.snap @@ -0,0 +1,8 @@ +--- +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: {}, + code: "
;\n", +} diff --git a/libs/extractor/src/snapshots/extractor__tests__style_variables-4.snap b/libs/extractor/src/snapshots/extractor__tests__style_variables-4.snap new file mode 100644 index 00000000..ac6afdd5 --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__style_variables-4.snap @@ -0,0 +1,8 @@ +--- +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: {}, + code: "
;\n", +} diff --git a/libs/extractor/src/snapshots/extractor__tests__style_variables-5.snap b/libs/extractor/src/snapshots/extractor__tests__style_variables-5.snap new file mode 100644 index 00000000..440aed1e --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__style_variables-5.snap @@ -0,0 +1,8 @@ +--- +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: {}, + code: "
;\n", +} diff --git a/libs/extractor/src/snapshots/extractor__tests__style_variables-6.snap b/libs/extractor/src/snapshots/extractor__tests__style_variables-6.snap new file mode 100644 index 00000000..2f0de7e5 --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__style_variables-6.snap @@ -0,0 +1,8 @@ +--- +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: {}, + code: "
;\n", +} diff --git a/libs/extractor/src/snapshots/extractor__tests__style_variables-7.snap b/libs/extractor/src/snapshots/extractor__tests__style_variables-7.snap new file mode 100644 index 00000000..3417dfb1 --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__style_variables-7.snap @@ -0,0 +1,18 @@ +--- +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: { + Dynamic( + ExtractDynamicStyle { + property: "background", + level: 0, + identifier: "color", + selector: None, + style_order: None, + }, + ), + }, + code: "import \"@devup-ui/core/devup-ui.css\";\n
;\n", +} diff --git a/libs/extractor/src/snapshots/extractor__tests__style_variables-8.snap b/libs/extractor/src/snapshots/extractor__tests__style_variables-8.snap new file mode 100644 index 00000000..c1044f2c --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__style_variables-8.snap @@ -0,0 +1,8 @@ +--- +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: {}, + code: "
;\n", +} diff --git a/libs/extractor/src/snapshots/extractor__tests__style_variables-9.snap b/libs/extractor/src/snapshots/extractor__tests__style_variables-9.snap new file mode 100644 index 00000000..01e7f25d --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__style_variables-9.snap @@ -0,0 +1,8 @@ +--- +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: {}, + code: "
;\n", +} diff --git a/libs/extractor/src/snapshots/extractor__tests__style_variables.snap b/libs/extractor/src/snapshots/extractor__tests__style_variables.snap new file mode 100644 index 00000000..b21d8bfe --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__style_variables.snap @@ -0,0 +1,8 @@ +--- +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: {}, + code: "
;\n", +} diff --git a/libs/extractor/src/style_extractor.rs b/libs/extractor/src/style_extractor.rs index ae75c8d2..9eeb9547 100644 --- a/libs/extractor/src/style_extractor.rs +++ b/libs/extractor/src/style_extractor.rs @@ -95,7 +95,7 @@ pub fn extract_style_from_expression<'a>( match extract_style_from_expression( ast_builder, - Some(&name), + Some(name), &mut prop.value, 0, None, diff --git a/libs/extractor/src/utils.rs b/libs/extractor/src/utils.rs index 6fc42dcb..37e267a0 100644 --- a/libs/extractor/src/utils.rs +++ b/libs/extractor/src/utils.rs @@ -308,7 +308,7 @@ mod tests { let builder = oxc_ast::AstBuilder::new(&allocator); assert_eq!( jsx_expression_to_number( - &builder + builder .alloc_jsx_attribute( SPAN, JSXAttributeName::Identifier( @@ -327,7 +327,7 @@ mod tests { assert_eq!( jsx_expression_to_number( - &builder + builder .alloc_jsx_attribute( SPAN, JSXAttributeName::Identifier( @@ -344,9 +344,9 @@ mod tests { SPAN, Vec::new_in(&allocator) )), - Vec::new_in(&allocator).into(), + Vec::new_in(&allocator), ), - Vec::new_in(&allocator).into(), + Vec::new_in(&allocator), Some(builder.alloc_jsx_closing_element( SPAN, JSXElementName::Identifier( diff --git a/libs/extractor/src/visit.rs b/libs/extractor/src/visit.rs index 5ca6b2b7..babf962b 100644 --- a/libs/extractor/src/visit.rs +++ b/libs/extractor/src/visit.rs @@ -13,8 +13,8 @@ use oxc_ast::ast::JSXAttributeItem::Attribute; use oxc_ast::ast::JSXAttributeName::Identifier; use oxc_ast::ast::{ Argument, BindingPatternKind, CallExpression, Expression, ImportDeclaration, - ImportOrExportKind, JSXElement, JSXElementName, Program, PropertyKey, Statement, - VariableDeclarator, WithClause, + ImportOrExportKind, JSXAttributeValue, JSXElement, JSXElementName, Program, PropertyKey, + Statement, VariableDeclarator, WithClause, }; use oxc_ast_visit::VisitMut; use oxc_ast_visit::walk_mut::{ @@ -96,8 +96,8 @@ impl<'a> VisitMut<'a> for DevupVisitor<'a> { { Some(format!( "{}.{}", - ident.name.to_string(), - member.property.name.to_string() + ident.name, + member.property.name )) } else { None @@ -109,7 +109,7 @@ impl<'a> VisitMut<'a> for DevupVisitor<'a> { if call.arguments.is_empty() { *it = Expression::StringLiteral(self.ast.alloc_string_literal( SPAN, - self.ast.atom(&"".to_string()), + self.ast.atom(""), None, )); } else if call.arguments.len() == 1 { @@ -210,7 +210,7 @@ impl<'a> VisitMut<'a> for DevupVisitor<'a> { { let mut tag = Expression::StringLiteral(self.ast.alloc_string_literal( SPAN, - self.ast.atom(&kind.to_tag().unwrap_or("div")), + self.ast.atom(kind.to_tag().unwrap_or("div")), None, )); let mut props_styles = vec![]; @@ -345,14 +345,14 @@ impl<'a> VisitMut<'a> for DevupVisitor<'a> { self.imports.insert( format!( "{}.{}", - import_default_specifier.local.to_string(), - kind.to_string() + import_default_specifier.local, + kind ), kind, ); } self.css_imports.insert( - format!("{}.{}", import_default_specifier.local.to_string(), "css"), + format!("{}.{}", import_default_specifier.local, "css"), it.source.value.to_string(), ); } @@ -363,14 +363,14 @@ impl<'a> VisitMut<'a> for DevupVisitor<'a> { self.imports.insert( format!( "{}.{}", - import_namespace_specifier.local.to_string(), - kind.to_string() + import_namespace_specifier.local, + kind ), kind, ); } self.css_imports.insert( - format!("{}.{}", import_namespace_specifier.local.to_string(), "css"), + format!("{}.{}", import_namespace_specifier.local, "css"), it.source.value.to_string(), ); } @@ -399,6 +399,7 @@ impl<'a> VisitMut<'a> for DevupVisitor<'a> { // extract ExtractStyleProp and remain style and class name, just extract let mut duplicate_set = HashSet::new(); let mut style_order = None; + let mut style_vars = None; for i in (0..attrs.len()).rev() { let mut attr = attrs.remove(i); if let Attribute(attr) = &mut attr @@ -414,6 +415,15 @@ impl<'a> VisitMut<'a> for DevupVisitor<'a> { jsx_expression_to_number(attr.value.as_ref().unwrap()).map(|n| n as u8); continue; } + if name == "styleVars" { + if let Some(value) = attr.value.as_ref() + && let JSXAttributeValue::ExpressionContainer(expr) = value + { + style_vars = + Some(expr.expression.to_expression().clone_in(self.ast.allocator)); + } + continue; + } if let Some(at) = &mut attr.value { if let ExtractResult::Extract { styles, tag, .. } = @@ -438,7 +448,7 @@ impl<'a> VisitMut<'a> for DevupVisitor<'a> { .rev() .for_each(|ex| props_styles.push(ExtractStyleProp::Static(ex))); - modify_props(&self.ast, attrs, &mut props_styles, style_order); + modify_props(&self.ast, attrs, &mut props_styles, style_order, style_vars); props_styles .iter() diff --git a/libs/sheet/src/lib.rs b/libs/sheet/src/lib.rs index 58934734..bfbfd54f 100644 --- a/libs/sheet/src/lib.rs +++ b/libs/sheet/src/lib.rs @@ -204,7 +204,7 @@ impl StyleSheet { .join(""); css.push_str( if let Some(break_point) = break_point { - format!("\n@media (min-width:{}px){{{}}}", break_point, inner_css) + format!("\n@media (min-width:{break_point}px){{{inner_css}}}") } else { inner_css } @@ -220,8 +220,7 @@ impl StyleSheet { css.push_str( if let Some(break_point) = break_point { format!( - "\n@media (min-width:{}px) and {}{{{}}}", - break_point, media, inner_css + "\n@media (min-width:{break_point}px) and {media}{{{inner_css}}}" ) } else { format!("\n@media {}{{{}}}", media, inner_css.as_str()) diff --git a/libs/sheet/src/theme.rs b/libs/sheet/src/theme.rs index f4eff29f..4d4c59fd 100644 --- a/libs/sheet/src/theme.rs +++ b/libs/sheet/src/theme.rs @@ -185,7 +185,7 @@ impl Theme { ); } for (prop, value) in theme_properties.0.iter() { - theme_declaration.push_str(format!("--{}:{};", prop, value).as_str()); + theme_declaration.push_str(format!("--{prop}:{value};").as_str()); } theme_declaration.push_str("}\n"); } @@ -199,23 +199,23 @@ impl Theme { "{}{}{}{}{}", t.font_family .clone() - .map(|v| format!("font-family:{};", v)) + .map(|v| format!("font-family:{v};")) .unwrap_or("".to_string()), t.font_size .clone() - .map(|v| format!("font-size:{};", v)) + .map(|v| format!("font-size:{v};")) .unwrap_or("".to_string()), t.font_weight .clone() - .map(|v| format!("font-weight:{};", v)) + .map(|v| format!("font-weight:{v};")) .unwrap_or("".to_string()), t.line_height .clone() - .map(|v| format!("line-height:{};", v)) + .map(|v| format!("line-height:{v};")) .unwrap_or("".to_string()), t.letter_spacing .clone() - .map(|v| format!("letter-spacing:{}", v)) + .map(|v| format!("letter-spacing:{v}")) .unwrap_or("".to_string()) ); if css_content.is_empty() { @@ -237,7 +237,7 @@ impl Theme { } else if let Some(media) = self .breakpoints .get(level as usize) - .map(|v| format!("(min-width:{}px)", v)) + .map(|v| format!("(min-width:{v}px)")) { css.push_str(format!("\n@media {}{{{}}}", media, css_vec.join("")).as_str()); } diff --git a/packages/react/src/types/props/index.ts b/packages/react/src/types/props/index.ts index 271b7d43..83e95c7d 100644 --- a/packages/react/src/types/props/index.ts +++ b/packages/react/src/types/props/index.ts @@ -69,4 +69,5 @@ export interface DevupProps DevupSelectorProps, DevupThemeSelectorProps { as?: T + styleVars?: Record } From b4fc65d403a89521c2843ebac8d8430ee2a4aac3 Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Wed, 2 Jul 2025 03:39:00 +0900 Subject: [PATCH 2/2] Refactor --- libs/extractor/src/gen_class_name.rs | 51 +-- libs/extractor/src/gen_style.rs | 51 +-- libs/extractor/src/lib.rs | 68 +++ libs/extractor/src/prop_modify_utils.rs | 401 ++++++++++-------- ...act_variable_style_props_with_style-2.snap | 2 +- ...tract_variable_style_props_with_style.snap | 2 +- .../extractor__tests__style_order-3.snap | 4 +- .../extractor__tests__style_order-4.snap | 4 +- .../extractor__tests__style_order-5.snap | 4 +- .../extractor__tests__style_variables-7.snap | 2 +- ...tractor__tests__style_variables_mjs-2.snap | 8 + ...extractor__tests__style_variables_mjs.snap | 8 + ...actor__tests__support_transpile_mjs-3.snap | 18 + ...actor__tests__support_transpile_mjs-4.snap | 18 + ...actor__tests__support_transpile_mjs-5.snap | 18 + libs/extractor/src/style_extractor.rs | 33 +- libs/extractor/src/visit.rs | 22 +- 17 files changed, 410 insertions(+), 304 deletions(-) create mode 100644 libs/extractor/src/snapshots/extractor__tests__style_variables_mjs-2.snap create mode 100644 libs/extractor/src/snapshots/extractor__tests__style_variables_mjs.snap create mode 100644 libs/extractor/src/snapshots/extractor__tests__support_transpile_mjs-3.snap create mode 100644 libs/extractor/src/snapshots/extractor__tests__support_transpile_mjs-4.snap create mode 100644 libs/extractor/src/snapshots/extractor__tests__support_transpile_mjs-5.snap diff --git a/libs/extractor/src/gen_class_name.rs b/libs/extractor/src/gen_class_name.rs index 1746df76..0c3183a1 100644 --- a/libs/extractor/src/gen_class_name.rs +++ b/libs/extractor/src/gen_class_name.rs @@ -2,7 +2,7 @@ use crate::{ExtractStyleProp, StyleProperty}; use oxc_allocator::CloneIn; use oxc_ast::AstBuilder; use oxc_ast::ast::{ - Expression, JSXAttribute, JSXAttributeValue, JSXExpression, ObjectPropertyKind, PropertyKey, + Expression, ObjectPropertyKind, PropertyKey, PropertyKind, TemplateElement, TemplateElementValue, }; use oxc_span::SPAN; @@ -214,52 +214,3 @@ pub fn merge_expression_for_class_name<'a>( ))) } } - -pub fn apply_class_name_attribute<'a>( - ast_builder: &AstBuilder<'a>, - class_prop: &mut JSXAttribute<'a>, - expression: Expression<'a>, -) { - if let Some(ref value) = class_prop.value { - if let Some(ret) = match value { - JSXAttributeValue::StringLiteral(str) => merge_expression_for_class_name( - ast_builder, - vec![ - Expression::StringLiteral(str.clone_in(ast_builder.allocator)), - expression, - ], - ), - JSXAttributeValue::ExpressionContainer(container) => match container.expression { - JSXExpression::EmptyExpression(_) => Some(expression), - _ => merge_expression_for_class_name( - ast_builder, - vec![ - container - .expression - .clone_in(ast_builder.allocator) - .into_expression(), - expression, - ], - ), - }, - _ => None, - } { - class_prop.value = match ret { - Expression::StringLiteral(literal) => Some(JSXAttributeValue::StringLiteral( - literal.clone_in(ast_builder.allocator), - )), - _ => Some(JSXAttributeValue::ExpressionContainer( - ast_builder.alloc_jsx_expression_container(SPAN, JSXExpression::from(ret)), - )), - } - } - } else { - class_prop.value = Some(if let Expression::StringLiteral(literal) = expression { - JSXAttributeValue::StringLiteral(literal.clone_in(ast_builder.allocator)) - } else { - JSXAttributeValue::ExpressionContainer( - ast_builder.alloc_jsx_expression_container(SPAN, JSXExpression::from(expression)), - ) - }); - }; -} diff --git a/libs/extractor/src/gen_style.rs b/libs/extractor/src/gen_style.rs index 88b0d376..90d4ad15 100644 --- a/libs/extractor/src/gen_style.rs +++ b/libs/extractor/src/gen_style.rs @@ -1,16 +1,13 @@ use crate::{ExtractStyleProp, StyleProperty}; use oxc_allocator::CloneIn; use oxc_ast::AstBuilder; -use oxc_ast::ast::{ - Expression, JSXAttribute, JSXAttributeValue, JSXExpression, ObjectExpression, - ObjectPropertyKind, PropertyKey, PropertyKind, -}; +use oxc_ast::ast::{Expression, ObjectPropertyKind, PropertyKey, PropertyKind}; use oxc_span::SPAN; use std::collections::BTreeMap; pub fn gen_styles<'a>( ast_builder: &AstBuilder<'a>, style_props: &[ExtractStyleProp<'a>], -) -> Option> { +) -> Option> { if style_props.is_empty() { return None; } @@ -22,9 +19,11 @@ pub fn gen_styles<'a>( if properties.is_empty() { return None; } - Some(ast_builder.object_expression( - SPAN, - oxc_allocator::Vec::from_iter_in(properties, ast_builder.allocator), + Some(Expression::ObjectExpression( + ast_builder.alloc_object_expression( + SPAN, + oxc_allocator::Vec::from_iter_in(properties, ast_builder.allocator), + ), )) } fn gen_style<'a>( @@ -345,39 +344,3 @@ fn gen_style<'a>( properties.reverse(); properties } - -pub fn apply_style_attribute<'a>( - ast_builder: &AstBuilder<'a>, - style_prop: &mut JSXAttribute<'a>, - // must be an object expression - mut expression: ObjectExpression<'a>, -) { - if let Some(ref mut value) = style_prop.value { - if let JSXAttributeValue::ExpressionContainer(container) = value { - if let Some(spread_property) = match &container.expression { - JSXExpression::ObjectExpression(obj) => Some(Expression::ObjectExpression( - obj.clone_in(ast_builder.allocator), - )), - JSXExpression::Identifier(ident) => Some(Expression::Identifier( - ident.clone_in(ast_builder.allocator), - )), - _ => None, - } { - expression.properties.insert( - 0, - ObjectPropertyKind::SpreadProperty( - ast_builder.alloc_spread_element(SPAN, spread_property), - ), - ); - } - container.expression = JSXExpression::ObjectExpression(ast_builder.alloc(expression)); - } - } else { - style_prop.value = Some(JSXAttributeValue::ExpressionContainer( - ast_builder.alloc_jsx_expression_container( - SPAN, - JSXExpression::ObjectExpression(ast_builder.alloc(expression)), - ), - )); - }; -} diff --git a/libs/extractor/src/lib.rs b/libs/extractor/src/lib.rs index 6fb7818d..2a2eae77 100644 --- a/libs/extractor/src/lib.rs +++ b/libs/extractor/src/lib.rs @@ -1818,6 +1818,54 @@ export { ) .unwrap() )); + + reset_class_map(); + assert_debug_snapshot!(ToBTreeSet::from( + extract( + "test.js", + r#"import { jsx as e } from "react/jsx-runtime"; +import { Box as o } from "@devup-ui/core"; +e(o, { className: "a", bg: "red" }) +"#, + ExtractOption { + package: "@devup-ui/core".to_string(), + css_file: None + } + ) + .unwrap() + )); + + reset_class_map(); + assert_debug_snapshot!(ToBTreeSet::from( + extract( + "test.js", + r#"import { jsx as e } from "react/jsx-runtime"; +import { Box as o } from "@devup-ui/core"; +e(o, { className: "a", bg: variable, style: { color: "blue" } }) +"#, + ExtractOption { + package: "@devup-ui/core".to_string(), + css_file: None + } + ) + .unwrap() + )); + + reset_class_map(); + assert_debug_snapshot!(ToBTreeSet::from( + extract( + "test.js", + r#"import { jsx as e } from "react/jsx-runtime"; +import { Box as o } from "@devup-ui/core"; +e(o, { className: "a", bg: variable, style: { color: "blue" }, ...props }) +"#, + ExtractOption { + package: "@devup-ui/core".to_string(), + css_file: None + } + ) + .unwrap() + )); } #[test] @@ -3260,4 +3308,24 @@ export { .unwrap() )); } + + #[test] + #[serial] + fn style_variables_mjs() { + reset_class_map(); + assert_debug_snapshot!(ToBTreeSet::from( + extract( + "test.js", + r#"import { jsx as e } from "react/jsx-runtime"; +import { Box as o } from "@devup-ui/core"; +e(o, { styleVars: { c: "yellow" } }) +"#, + ExtractOption { + package: "@devup-ui/core".to_string(), + css_file: None + } + ) + .unwrap() + )); + } } diff --git a/libs/extractor/src/prop_modify_utils.rs b/libs/extractor/src/prop_modify_utils.rs index 1669590d..94a060b7 100644 --- a/libs/extractor/src/prop_modify_utils.rs +++ b/libs/extractor/src/prop_modify_utils.rs @@ -1,15 +1,13 @@ use crate::ExtractStyleProp; -use crate::gen_class_name::{ - apply_class_name_attribute, gen_class_names, merge_expression_for_class_name, -}; -use crate::gen_style::{apply_style_attribute, gen_styles}; -use oxc_allocator::{CloneIn, Vec}; +use crate::gen_class_name::gen_class_names; +use crate::gen_style::gen_styles; +use oxc_allocator::CloneIn; use oxc_ast::AstBuilder; use oxc_ast::ast::JSXAttributeItem::Attribute; use oxc_ast::ast::JSXAttributeName::Identifier; use oxc_ast::ast::{ - Expression, JSXAttribute, JSXAttributeItem, JSXAttributeValue, JSXExpression, - ObjectPropertyKind, PropertyKey, PropertyKind, TemplateElementValue, + Expression, JSXAttributeItem, JSXAttributeValue, JSXExpression, ObjectPropertyKind, + PropertyKey, PropertyKind, TemplateElementValue, }; use oxc_span::SPAN; @@ -19,50 +17,39 @@ pub fn modify_prop_object<'a>( props: &mut oxc_allocator::Vec>, styles: &mut [ExtractStyleProp<'a>], style_order: Option, + style_vars: Option>, ) { let mut class_name_prop = None; let mut style_prop = None; - for idx in (0..props.len()).rev() { let prop = props.remove(idx); - if let ObjectPropertyKind::ObjectProperty(attr) = prop { - if let PropertyKey::StaticIdentifier(ident) = &attr.key { - if ident.name == "className" { - class_name_prop = Some(attr); - continue; - } else if ident.name == "style" { - style_prop = Some(attr); - continue; + match prop { + ObjectPropertyKind::ObjectProperty(attr) => { + if let PropertyKey::StaticIdentifier(ident) = &attr.key { + match ident.name.as_str() { + "className" => { + class_name_prop = Some(attr.value.clone_in(ast_builder.allocator)); + continue; + } + "style" => { + style_prop = Some(attr.value.clone_in(ast_builder.allocator)); + continue; + } + _ => {} + } } + props.insert(idx, ObjectPropertyKind::ObjectProperty(attr)); + } + _ => { + props.insert(idx, prop); } - props.insert(idx, ObjectPropertyKind::ObjectProperty(attr)); - } else { - props.insert(idx, prop); } } - // should modify class name prop - if let Some(ex) = gen_class_names(ast_builder, styles, style_order) { - if let Some(pr) = if let Some(class_name_prop) = class_name_prop { - merge_expression_for_class_name( - ast_builder, - vec![class_name_prop.value.clone_in(ast_builder.allocator), ex], - ) - .map(|res| { - ast_builder.alloc_object_property( - SPAN, - PropertyKind::Init, - PropertyKey::StaticIdentifier( - ast_builder.alloc_identifier_name(SPAN, "className"), - ), - res, - false, - false, - false, - ) - }) - } else { - Some(ast_builder.alloc_object_property( + if let Some(ex) = get_class_name_expression(ast_builder, &class_name_prop, styles, style_order) + { + props.push(ObjectPropertyKind::ObjectProperty( + ast_builder.alloc_object_property( SPAN, PropertyKind::Init, PropertyKey::StaticIdentifier(ast_builder.alloc_identifier_name(SPAN, "className")), @@ -70,51 +57,21 @@ pub fn modify_prop_object<'a>( false, false, false, - )) - } { - props.push(ObjectPropertyKind::ObjectProperty(pr)); - } - } else if let Some(class_name_prop) = class_name_prop { - // re add class name prop if not modified - props.push(ObjectPropertyKind::ObjectProperty(class_name_prop)) + ), + )); } - - // should modify style prop - if let Some(mut ex) = gen_styles(ast_builder, styles) { - props.push(if let Some(style_prop) = style_prop { - ObjectPropertyKind::ObjectProperty(ast_builder.alloc_object_property( + if let Some(ex) = get_style_expression(ast_builder, &style_prop, styles, &style_vars) { + props.push(ObjectPropertyKind::ObjectProperty( + ast_builder.alloc_object_property( SPAN, PropertyKind::Init, PropertyKey::StaticIdentifier(ast_builder.alloc_identifier_name(SPAN, "style")), - if ex.properties.is_empty() { - Expression::ObjectExpression(ast_builder.alloc(ex)) - } else { - ex.properties.push(ObjectPropertyKind::SpreadProperty( - ast_builder.alloc_spread_element( - SPAN, - style_prop.value.clone_in(ast_builder.allocator), - ), - )); - Expression::ObjectExpression(ast_builder.alloc(ex)) - }, - false, - false, - false, - )) - } else { - ObjectPropertyKind::ObjectProperty(ast_builder.alloc_object_property( - SPAN, - PropertyKind::Init, - PropertyKey::StaticIdentifier(ast_builder.alloc_identifier_name(SPAN, "style")), - Expression::ObjectExpression(ast_builder.alloc(ex)), + ex, false, false, false, - )) - }); - } else if let Some(style_prop) = style_prop { - // re add class name prop if not modified - props.push(ObjectPropertyKind::ObjectProperty(style_prop)) + ), + )); } } /// modify JSX props @@ -127,130 +84,203 @@ pub fn modify_props<'a>( ) { let mut class_name_prop = None; let mut style_prop = None; - for idx in (0..props.len()).rev() { let prop = props.remove(idx); - if let Attribute(attr) = prop { - if let Identifier(ident) = &attr.name { - if ident.name == "className" { - class_name_prop = Some(attr); - continue; - } else if ident.name == "style" { - style_prop = Some(attr); + match prop { + Attribute(attr) => { + if let Identifier(ident) = &attr.name + && let Some(value) = &attr.value + && (ident.name == "className" || ident.name == "style") + { + let value = match &value { + JSXAttributeValue::ExpressionContainer(container) => { + if matches!(container.expression, JSXExpression::EmptyExpression(_)) { + None + } else { + Some( + container + .expression + .to_expression() + .clone_in(ast_builder.allocator), + ) + } + } + JSXAttributeValue::StringLiteral(literal) => { + Some(Expression::StringLiteral(ast_builder.alloc_string_literal( + SPAN, + literal.value, + None, + ))) + } + _ => None, + }; + + match ident.name.as_str() { + "className" => class_name_prop = value, + "style" => style_prop = value, + _ => unreachable!(), + } + continue; } + props.insert(idx, Attribute(attr)); + } + _ => { + props.insert(idx, prop); } - props.insert(idx, Attribute(attr)); - } else { - props.insert(idx, prop); } } + if let Some(ex) = get_class_name_expression(ast_builder, &class_name_prop, styles, style_order) + { + props.push(Attribute(ast_builder.alloc_jsx_attribute( + SPAN, + Identifier(ast_builder.alloc_jsx_identifier(SPAN, "className")), + Some(if let Expression::StringLiteral(literal) = ex { + JSXAttributeValue::StringLiteral(literal) + } else { + JSXAttributeValue::ExpressionContainer( + ast_builder.alloc_jsx_expression_container(SPAN, ex.into()), + ) + }), + ))); + } + if let Some(ex) = get_style_expression(ast_builder, &style_prop, styles, &style_vars) { + props.push(Attribute(ast_builder.alloc_jsx_attribute( + SPAN, + Identifier(ast_builder.alloc_jsx_identifier(SPAN, "style")), + Some(JSXAttributeValue::ExpressionContainer( + ast_builder.alloc_jsx_expression_container(SPAN, ex.into()), + )), + ))); + } +} +pub fn get_class_name_expression<'a>( + ast_builder: &AstBuilder<'a>, + class_name_prop: &Option>, + styles: &mut [ExtractStyleProp<'a>], + style_order: Option, +) -> Option> { // should modify class name prop - if let Some(ex) = gen_class_names(ast_builder, styles, style_order) { - let mut attr = if let Some(class_name_prop) = class_name_prop { - class_name_prop - } else { - ast_builder.alloc_jsx_attribute( - SPAN, - Identifier(ast_builder.alloc_jsx_identifier(SPAN, "className")), - None, - ) - }; + merge_string_expressions( + ast_builder, + [ + class_name_prop.clone_in(ast_builder.allocator), + gen_class_names(ast_builder, styles, style_order), + ] + .into_iter() + .flatten() + .collect::>() + .as_slice(), + ) +} - apply_class_name_attribute(ast_builder, &mut attr, ex); - props.push(Attribute(attr)); - } else if let Some(class_name_prop) = class_name_prop { - // re add class name prop if not modified - props.push(Attribute(class_name_prop)) - } +pub fn get_style_expression<'a>( + ast_builder: &AstBuilder<'a>, + style_prop: &Option>, + styles: &[ExtractStyleProp<'a>], + style_vars: &Option>, +) -> Option> { + merge_object_expressions( + ast_builder, + [ + gen_styles(ast_builder, styles), + style_vars + .as_ref() + .map(|style_vars| convert_style_vars(ast_builder, style_vars)), + style_prop.clone_in(ast_builder.allocator), + ] + .into_iter() + .flatten() + .collect::>() + .as_slice(), + ) +} - // should modify style prop - if let Some(ex) = gen_styles(ast_builder, styles) { - let mut attr = style_prop.unwrap_or_else(|| { - ast_builder.alloc_jsx_attribute( - SPAN, - Identifier(ast_builder.alloc_jsx_identifier(SPAN, "style")), - None, - ) - }); - apply_style_attribute(ast_builder, &mut attr, ex); - if let Some(mut style_vars) = style_vars { - merge_style_vars(ast_builder, &mut attr, &mut style_vars); - } - props.push(Attribute(attr)); - } else if let Some(mut style_prop) = style_prop { - // re add class name prop if not modified +fn merge_string_expressions<'a>( + ast_builder: &AstBuilder<'a>, + expressions: &[Expression<'a>], +) -> Option> { + if expressions.is_empty() { + return None; + } + if expressions.len() == 1 { + return Some(expressions[0].clone_in(ast_builder.allocator)); + } - if let Some(mut style_vars) = style_vars { - merge_style_vars(ast_builder, &mut style_prop, &mut style_vars); + let mut string_literals = vec![]; + let mut other_expressions = vec![]; + for ex in expressions { + if let Expression::StringLiteral(literal) = ex { + string_literals.push(literal.value.trim()); + } else { + other_expressions.push(ex); } - props.push(Attribute(style_prop)) - } else if let Some(mut style_vars) = style_vars { - let mut attr = style_prop.unwrap_or_else(|| { - ast_builder.alloc_jsx_attribute( - SPAN, - Identifier(ast_builder.alloc_jsx_identifier(SPAN, "style")), - None, - ) - }); - merge_style_vars(ast_builder, &mut attr, &mut style_vars); - props.push(Attribute(attr)); } -} + if other_expressions.is_empty() { + return Some(Expression::StringLiteral(ast_builder.alloc_string_literal( + SPAN, + ast_builder.atom(&string_literals.join(" ")), + None, + ))); + } -/// Priority: dynamic style, style, styleVars -fn merge_style_vars<'a>( - ast_builder: &AstBuilder<'a>, - style_prop: &mut JSXAttribute<'a>, - style_vars: &mut Expression<'a>, -) { - convert_style_vars(ast_builder, style_vars); - if let Some(ref mut value) = style_prop.value { - if let JSXAttributeValue::ExpressionContainer(container) = value { - style_prop.value = Some(JSXAttributeValue::ExpressionContainer( - ast_builder.alloc_jsx_expression_container( + Some(Expression::TemplateLiteral( + ast_builder.alloc_template_literal( + SPAN, + oxc_allocator::Vec::from_iter_in( + [ast_builder.template_element( SPAN, - JSXExpression::ObjectExpression( - ast_builder.alloc_object_expression( - SPAN, - Vec::from_array_in( - [ - ObjectPropertyKind::SpreadProperty( - ast_builder.alloc_spread_element( - SPAN, - style_vars.clone_in(ast_builder.allocator), - ), - ), - ObjectPropertyKind::SpreadProperty( - ast_builder.alloc_spread_element( - SPAN, - container - .expression - .clone_in(ast_builder.allocator) - .into_expression(), - ), - ), - ], - ast_builder.allocator, - ), - ), - ), - ), - )); - } - } else { - style_prop.value = Some(JSXAttributeValue::ExpressionContainer( - ast_builder.alloc_jsx_expression_container( - SPAN, - style_vars.clone_in(ast_builder.allocator).into(), + TemplateElementValue { + raw: ast_builder.atom(&format!("{} ", string_literals.join(" "))), + cooked: None, + }, + false, + )], + ast_builder.allocator, ), - )); + oxc_allocator::Vec::from_iter_in( + other_expressions + .into_iter() + .map(|ex| ex.clone_in(ast_builder.allocator)), + ast_builder.allocator, + ), + ), + )) +} + +/// merge expressions to object expression +fn merge_object_expressions<'a>( + ast_builder: &AstBuilder<'a>, + expressions: &[Expression<'a>], +) -> Option> { + if expressions.is_empty() { + return None; + } + if expressions.len() == 1 { + return Some(expressions[0].clone_in(ast_builder.allocator)); } + Some(Expression::ObjectExpression( + ast_builder.alloc_object_expression( + SPAN, + oxc_allocator::Vec::from_iter_in( + expressions.into_iter().map(|ex| { + ObjectPropertyKind::SpreadProperty( + ast_builder.alloc_spread_element(SPAN, ex.clone_in(ast_builder.allocator)), + ) + }), + ast_builder.allocator, + ), + ), + )) } -pub fn convert_style_vars<'a>(ast_builder: &AstBuilder<'a>, style_vars: &mut Expression<'a>) { - if let Expression::ObjectExpression(obj) = style_vars { +pub fn convert_style_vars<'a>( + ast_builder: &AstBuilder<'a>, + style_vars: &Expression<'a>, +) -> Expression<'a> { + let mut style_vars = style_vars.clone_in(ast_builder.allocator); + if let Expression::ObjectExpression(obj) = &mut style_vars { for idx in (0..obj.properties.len()).rev() { let mut prop = obj.properties.remove(idx); @@ -266,7 +296,7 @@ pub fn convert_style_vars<'a>(ast_builder: &AstBuilder<'a>, style_vars: &mut Exp PropertyKind::Init, PropertyKey::TemplateLiteral(ast_builder.alloc_template_literal( SPAN, - Vec::from_array_in( + oxc_allocator::Vec::from_array_in( [ast_builder.template_element( SPAN, TemplateElementValue { @@ -277,7 +307,7 @@ pub fn convert_style_vars<'a>(ast_builder: &AstBuilder<'a>, style_vars: &mut Exp )], ast_builder.allocator, ), - Vec::from_array_in( + oxc_allocator::Vec::from_array_in( [etc.to_expression().clone_in(ast_builder.allocator)], ast_builder.allocator, ), @@ -303,4 +333,5 @@ pub fn convert_style_vars<'a>(ast_builder: &AstBuilder<'a>, style_vars: &mut Exp obj.properties.insert(idx, prop); } } + style_vars } diff --git a/libs/extractor/src/snapshots/extractor__tests__extract_variable_style_props_with_style-2.snap b/libs/extractor/src/snapshots/extractor__tests__extract_variable_style_props_with_style-2.snap index 6af03632..b385fefa 100644 --- a/libs/extractor/src/snapshots/extractor__tests__extract_variable_style_props_with_style-2.snap +++ b/libs/extractor/src/snapshots/extractor__tests__extract_variable_style_props_with_style-2.snap @@ -14,5 +14,5 @@ ToBTreeSet { }, ), }, - code: "import \"@devup-ui/core/devup-ui.css\";\n
;\n", + code: "import \"@devup-ui/core/devup-ui.css\";\n
;\n", } diff --git a/libs/extractor/src/snapshots/extractor__tests__extract_variable_style_props_with_style.snap b/libs/extractor/src/snapshots/extractor__tests__extract_variable_style_props_with_style.snap index 4aed7925..57c3eeea 100644 --- a/libs/extractor/src/snapshots/extractor__tests__extract_variable_style_props_with_style.snap +++ b/libs/extractor/src/snapshots/extractor__tests__extract_variable_style_props_with_style.snap @@ -14,5 +14,5 @@ ToBTreeSet { }, ), }, - code: "import \"@devup-ui/core/devup-ui.css\";\n
;\n", + code: "import \"@devup-ui/core/devup-ui.css\";\n
;\n", } diff --git a/libs/extractor/src/snapshots/extractor__tests__style_order-3.snap b/libs/extractor/src/snapshots/extractor__tests__style_order-3.snap index e1bde500..6fad54aa 100644 --- a/libs/extractor/src/snapshots/extractor__tests__style_order-3.snap +++ b/libs/extractor/src/snapshots/extractor__tests__style_order-3.snap @@ -1,6 +1,6 @@ --- source: libs/extractor/src/lib.rs -expression: "ToBTreeSet::from(extract(\"test.js\",\nr#\"import {Box, css} from '@devup-ui/core'\n \n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())" +expression: "ToBTreeSet::from(extract(\"test.jsx\",\nr#\"import {Box, css} from '@devup-ui/core'\n \n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())" --- ToBTreeSet { styles: { @@ -16,5 +16,5 @@ ToBTreeSet { }, ), }, - code: "import \"@devup-ui/core/devup-ui.css\";\n
;\n", + code: "import \"@devup-ui/core/devup-ui.css\";\n
;\n", } diff --git a/libs/extractor/src/snapshots/extractor__tests__style_order-4.snap b/libs/extractor/src/snapshots/extractor__tests__style_order-4.snap index bb5de1f2..f2c6874f 100644 --- a/libs/extractor/src/snapshots/extractor__tests__style_order-4.snap +++ b/libs/extractor/src/snapshots/extractor__tests__style_order-4.snap @@ -1,6 +1,6 @@ --- source: libs/extractor/src/lib.rs -expression: "ToBTreeSet::from(extract(\"test.js\",\nr#\"import {Box, css} from '@devup-ui/core'\n \n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())" +expression: "ToBTreeSet::from(extract(\"test.jsx\",\nr#\"import {Box, css} from '@devup-ui/core'\n \n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())" --- ToBTreeSet { styles: { @@ -14,5 +14,5 @@ ToBTreeSet { }, ), }, - code: "import \"@devup-ui/core/devup-ui.css\";\n
;\n", + code: "import \"@devup-ui/core/devup-ui.css\";\n
;\n", } diff --git a/libs/extractor/src/snapshots/extractor__tests__style_order-5.snap b/libs/extractor/src/snapshots/extractor__tests__style_order-5.snap index 1e1e3319..c35e28a9 100644 --- a/libs/extractor/src/snapshots/extractor__tests__style_order-5.snap +++ b/libs/extractor/src/snapshots/extractor__tests__style_order-5.snap @@ -1,6 +1,6 @@ --- source: libs/extractor/src/lib.rs -expression: "ToBTreeSet::from(extract(\"test.js\",\nr#\"import {Box, css} from '@devup-ui/core'\n \n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())" +expression: "ToBTreeSet::from(extract(\"test.jsx\",\nr#\"import {Box, css} from '@devup-ui/core'\n \n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())" --- ToBTreeSet { styles: { @@ -16,5 +16,5 @@ ToBTreeSet { }, ), }, - code: "import \"@devup-ui/core/devup-ui.css\";\n
;\n", + code: "import \"@devup-ui/core/devup-ui.css\";\n
;\n", } diff --git a/libs/extractor/src/snapshots/extractor__tests__style_variables-7.snap b/libs/extractor/src/snapshots/extractor__tests__style_variables-7.snap index 3417dfb1..b0d6d937 100644 --- a/libs/extractor/src/snapshots/extractor__tests__style_variables-7.snap +++ b/libs/extractor/src/snapshots/extractor__tests__style_variables-7.snap @@ -14,5 +14,5 @@ ToBTreeSet { }, ), }, - code: "import \"@devup-ui/core/devup-ui.css\";\n
;\n", + code: "import \"@devup-ui/core/devup-ui.css\";\n
;\n", } diff --git a/libs/extractor/src/snapshots/extractor__tests__style_variables_mjs-2.snap b/libs/extractor/src/snapshots/extractor__tests__style_variables_mjs-2.snap new file mode 100644 index 00000000..dc4c9b76 --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__style_variables_mjs-2.snap @@ -0,0 +1,8 @@ +--- +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: {}, + code: "
;\n", +} diff --git a/libs/extractor/src/snapshots/extractor__tests__style_variables_mjs.snap b/libs/extractor/src/snapshots/extractor__tests__style_variables_mjs.snap new file mode 100644 index 00000000..da593509 --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__style_variables_mjs.snap @@ -0,0 +1,8 @@ +--- +source: libs/extractor/src/lib.rs +expression: "ToBTreeSet::from(extract(\"test.js\",\nr#\"import { jsx as e } from \"react/jsx-runtime\";\nimport { Box as o } from \"@devup-ui/core\";\ne(o, { styleVars: { c: \"yellow\" } })\n\"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())" +--- +ToBTreeSet { + styles: {}, + code: "import { jsx as e } from \"react/jsx-runtime\";\ne(\"div\", { style: { \"--c\": \"yellow\" } });\n", +} diff --git a/libs/extractor/src/snapshots/extractor__tests__support_transpile_mjs-3.snap b/libs/extractor/src/snapshots/extractor__tests__support_transpile_mjs-3.snap new file mode 100644 index 00000000..3b0a3fb4 --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__support_transpile_mjs-3.snap @@ -0,0 +1,18 @@ +--- +source: libs/extractor/src/lib.rs +expression: "ToBTreeSet::from(extract(\"test.js\",\nr#\"import { jsx as e } from \"react/jsx-runtime\";\nimport { Box as o } from \"@devup-ui/core\";\ne(o, { className: \"a\", bg: \"red\" })\n\"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())" +--- +ToBTreeSet { + styles: { + Static( + ExtractStaticStyle { + property: "background", + value: "red", + level: 0, + selector: None, + style_order: None, + }, + ), + }, + code: "import \"@devup-ui/core/devup-ui.css\";\nimport { jsx as e } from \"react/jsx-runtime\";\ne(\"div\", { className: \"a d0\" });\n", +} diff --git a/libs/extractor/src/snapshots/extractor__tests__support_transpile_mjs-4.snap b/libs/extractor/src/snapshots/extractor__tests__support_transpile_mjs-4.snap new file mode 100644 index 00000000..d00c70fe --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__support_transpile_mjs-4.snap @@ -0,0 +1,18 @@ +--- +source: libs/extractor/src/lib.rs +expression: "ToBTreeSet::from(extract(\"test.js\",\nr#\"import { jsx as e } from \"react/jsx-runtime\";\nimport { Box as o } from \"@devup-ui/core\";\ne(o, { className: \"a\", bg: variable, style: { color: \"blue\" } })\n\"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())" +--- +ToBTreeSet { + styles: { + Dynamic( + ExtractDynamicStyle { + property: "background", + level: 0, + identifier: "variable", + selector: None, + style_order: None, + }, + ), + }, + code: "import \"@devup-ui/core/devup-ui.css\";\nimport { jsx as e } from \"react/jsx-runtime\";\ne(\"div\", {\n\tclassName: \"a d0\",\n\tstyle: {\n\t\t...{ \"--d1\": variable },\n\t\t...{ color: \"blue\" }\n\t}\n});\n", +} diff --git a/libs/extractor/src/snapshots/extractor__tests__support_transpile_mjs-5.snap b/libs/extractor/src/snapshots/extractor__tests__support_transpile_mjs-5.snap new file mode 100644 index 00000000..dfd2cf8b --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__support_transpile_mjs-5.snap @@ -0,0 +1,18 @@ +--- +source: libs/extractor/src/lib.rs +expression: "ToBTreeSet::from(extract(\"test.js\",\nr#\"import { jsx as e } from \"react/jsx-runtime\";\nimport { Box as o } from \"@devup-ui/core\";\ne(o, { className: \"a\", bg: variable, style: { color: \"blue\" }, ...props })\n\"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())" +--- +ToBTreeSet { + styles: { + Dynamic( + ExtractDynamicStyle { + property: "background", + level: 0, + identifier: "variable", + selector: None, + style_order: None, + }, + ), + }, + code: "import \"@devup-ui/core/devup-ui.css\";\nimport { jsx as e } from \"react/jsx-runtime\";\ne(\"div\", {\n\t...props,\n\tclassName: \"a d0\",\n\tstyle: {\n\t\t...{ \"--d1\": variable },\n\t\t...{ color: \"blue\" }\n\t}\n});\n", +} diff --git a/libs/extractor/src/style_extractor.rs b/libs/extractor/src/style_extractor.rs index 9eeb9547..e3964609 100644 --- a/libs/extractor/src/style_extractor.rs +++ b/libs/extractor/src/style_extractor.rs @@ -31,6 +31,7 @@ pub enum ExtractResult<'a> { styles: Option>>, tag: Option>, style_order: Option, + style_vars: Option>, }, } @@ -76,6 +77,7 @@ pub fn extract_style_from_expression<'a>( if name.is_none() && selector.is_none() { let mut style_order = None; + let mut style_vars = None; return match expression { Expression::ObjectExpression(obj) => { let mut props_styles: Vec> = vec![]; @@ -92,6 +94,10 @@ pub fn extract_style_from_expression<'a>( .map(|v| v as u8); continue; } + if name == "styleVars" { + style_vars = Some(prop.value.clone_in(ast_builder.allocator)); + continue; + } match extract_style_from_expression( ast_builder, @@ -139,13 +145,14 @@ pub fn extract_style_from_expression<'a>( obj.properties.insert(idx, prop); } } - if props_styles.is_empty() { + if props_styles.is_empty() && style_vars.is_none() { ExtractResult::Maintain } else { ExtractResult::Extract { styles: Some(props_styles), tag, style_order, + style_vars, } } } @@ -183,6 +190,7 @@ pub fn extract_style_from_expression<'a>( }]), tag: None, style_order, + style_vars, }, Expression::ParenthesizedExpression(parenthesized) => extract_style_from_expression( ast_builder, @@ -205,6 +213,7 @@ pub fn extract_style_from_expression<'a>( styles: None, tag: Some(expression.clone_in(ast_builder.allocator)), style_order: None, + style_vars: None, }; // return match expression { @@ -284,6 +293,7 @@ pub fn extract_style_from_expression<'a>( styles: Some(props), tag: None, style_order: None, + style_vars: None, }; } @@ -305,6 +315,7 @@ pub fn extract_style_from_expression<'a>( if let Some(value) = utils::get_string_by_literal_expression(expression) { name.map(|name| ExtractResult::Extract { style_order: None, + style_vars: None, tag: None, styles: Some(vec![ExtractStyleProp::Static(if typo { Typography(value.to_string()) @@ -334,6 +345,7 @@ pub fn extract_style_from_expression<'a>( ))]), tag: None, style_order: None, + style_vars: None, }, Expression::TSAsExpression(exp) => extract_style_from_expression( ast_builder, @@ -361,6 +373,7 @@ pub fn extract_style_from_expression<'a>( })]), tag: None, style_order: None, + style_vars: None, } } else if typo { ExtractResult::Extract { @@ -393,6 +406,7 @@ pub fn extract_style_from_expression<'a>( }]), tag: None, style_order: None, + style_vars: None, } } else { ExtractResult::Extract { @@ -406,6 +420,7 @@ pub fn extract_style_from_expression<'a>( ))]), tag: None, style_order: None, + style_vars: None, } } } else { @@ -447,6 +462,7 @@ pub fn extract_style_from_expression<'a>( }]), tag: None, style_order: None, + style_vars: None, } } else { ExtractResult::Extract { @@ -460,6 +476,7 @@ pub fn extract_style_from_expression<'a>( ))]), tag: None, style_order: None, + style_vars: None, } } } else { @@ -489,6 +506,7 @@ pub fn extract_style_from_expression<'a>( }]), tag: None, style_order: None, + style_vars: None, }, LogicalOperator::And => ExtractResult::Extract { styles: Some(vec![ExtractStyleProp::Conditional { @@ -498,6 +516,7 @@ pub fn extract_style_from_expression<'a>( }]), tag: None, style_order: None, + style_vars: None, }, LogicalOperator::Coalesce => ExtractResult::Extract { styles: Some(vec![ExtractStyleProp::Conditional { @@ -533,6 +552,7 @@ pub fn extract_style_from_expression<'a>( }]), tag: None, style_order: None, + style_vars: None, }, } } @@ -568,6 +588,7 @@ pub fn extract_style_from_expression<'a>( styles: Some(vec![ExtractStyleProp::StaticArray(props)]), tag: None, style_order: None, + style_vars: None, } } } @@ -605,6 +626,7 @@ pub fn extract_style_from_expression<'a>( }]), tag: None, style_order: None, + style_vars: None, }, Expression::ObjectExpression(obj) => { let mut props = vec![]; @@ -628,6 +650,7 @@ pub fn extract_style_from_expression<'a>( styles: Some(props), tag: None, style_order: None, + style_vars: None, } } // val if let Some(value) = get_number_by_literal_expression(val) => {} @@ -653,6 +676,7 @@ fn extract_style_from_member_expression<'a>( styles: None, tag: None, style_order: None, + style_vars: None, }; } @@ -662,6 +686,7 @@ fn extract_style_from_member_expression<'a>( styles: None, tag: None, style_order: None, + style_vars: None, }; } let mut etc = None; @@ -685,6 +710,7 @@ fn extract_style_from_member_expression<'a>( styles: Some(styles), tag: None, style_order: None, + style_vars: None, }; } } @@ -707,6 +733,7 @@ fn extract_style_from_member_expression<'a>( }), tag: None, style_order: None, + style_vars: None, }; } @@ -757,6 +784,7 @@ fn extract_style_from_member_expression<'a>( styles: None, tag: None, style_order: None, + style_vars: None, }; } @@ -781,6 +809,7 @@ fn extract_style_from_member_expression<'a>( styles: Some(styles), tag: None, style_order: None, + style_vars: None, }; } } @@ -796,6 +825,7 @@ fn extract_style_from_member_expression<'a>( styles: None, tag: None, style_order: None, + style_vars: None, }; } Some(etc) => { @@ -869,5 +899,6 @@ fn extract_style_from_member_expression<'a>( styles: Some(ret), tag: None, style_order: None, + style_vars: None, } } diff --git a/libs/extractor/src/visit.rs b/libs/extractor/src/visit.rs index babf962b..bf44ddd1 100644 --- a/libs/extractor/src/visit.rs +++ b/libs/extractor/src/visit.rs @@ -94,11 +94,7 @@ impl<'a> VisitMut<'a> for DevupVisitor<'a> { } else if let Expression::StaticMemberExpression(member) = &call.callee && let Expression::Identifier(ident) = &member.object { - Some(format!( - "{}.{}", - ident.name, - member.property.name - )) + Some(format!("{}.{}", ident.name, member.property.name)) } else { None }; @@ -215,10 +211,12 @@ impl<'a> VisitMut<'a> for DevupVisitor<'a> { )); let mut props_styles = vec![]; let mut style_order = None; + let mut style_vars = None; if let ExtractResult::Extract { styles, tag: _tag, style_order: _style_order, + style_vars: _style_vars, } = extract_style_from_expression( &self.ast, None, @@ -233,6 +231,7 @@ impl<'a> VisitMut<'a> for DevupVisitor<'a> { if let Some(t) = _tag { tag = t; } + style_vars = _style_vars; } for ex in kind.extract().into_iter().rev() { @@ -253,6 +252,7 @@ impl<'a> VisitMut<'a> for DevupVisitor<'a> { &mut obj.properties, &mut props_styles, style_order, + style_vars, ); } @@ -343,11 +343,7 @@ impl<'a> VisitMut<'a> for DevupVisitor<'a> { ) => { for kind in ExportVariableKind::iter() { self.imports.insert( - format!( - "{}.{}", - import_default_specifier.local, - kind - ), + format!("{}.{}", import_default_specifier.local, kind), kind, ); } @@ -361,11 +357,7 @@ impl<'a> VisitMut<'a> for DevupVisitor<'a> { ) => { for kind in ExportVariableKind::iter() { self.imports.insert( - format!( - "{}.{}", - import_namespace_specifier.local, - kind - ), + format!("{}.{}", import_namespace_specifier.local, kind), kind, ); }