diff --git a/.changeset/chilly-mice-leave.md b/.changeset/chilly-mice-leave.md new file mode 100644 index 00000000..56a833f0 --- /dev/null +++ b/.changeset/chilly-mice-leave.md @@ -0,0 +1,5 @@ +--- +"@devup-ui/wasm": patch +--- + +Refactor className logic diff --git a/libs/extractor/src/lib.rs b/libs/extractor/src/lib.rs index 02764c56..99971d7b 100644 --- a/libs/extractor/src/lib.rs +++ b/libs/extractor/src/lib.rs @@ -2285,6 +2285,40 @@ e(o, { className: "a", bg: variable, style: { color: "blue" }, ...props }) ) .unwrap() )); + + reset_class_map(); + assert_debug_snapshot!(ToBTreeSet::from( + extract( + "test.jsx", + r#"import { VStack } from '@devup-ui/core' + +export default function Card({ + children, + className, + ...props +}) { + return ( + + {children} + + ) +} + + "#, + ExtractOption { + package: "@devup-ui/core".to_string(), + css_file: None + } + ) + .unwrap() + )); } #[test] diff --git a/libs/extractor/src/prop_modify_utils.rs b/libs/extractor/src/prop_modify_utils.rs index 16290ea8..3660b6ad 100644 --- a/libs/extractor/src/prop_modify_utils.rs +++ b/libs/extractor/src/prop_modify_utils.rs @@ -133,6 +133,8 @@ pub fn modify_props<'a>( } } } + println!("class_name_prop: {:?}", class_name_prop); + println!("style_prop: {:?}", style_prop); if let Some(ex) = get_class_name_expression( ast_builder, &class_name_prop, @@ -179,17 +181,26 @@ pub fn get_class_name_expression<'a>( ] .into_iter() .flatten() - .chain(spread_props.iter().map(|ex| { - convert_class_name( - ast_builder, - &Expression::StaticMemberExpression(ast_builder.alloc_static_member_expression( - SPAN, - ex.clone_in(ast_builder.allocator), - ast_builder.identifier_name(SPAN, ast_builder.atom("className")), - true, - )), - ) - })) + .chain(if class_name_prop.is_some() { + vec![] + } else { + spread_props + .iter() + .map(|ex| { + convert_class_name( + ast_builder, + &Expression::StaticMemberExpression( + ast_builder.alloc_static_member_expression( + SPAN, + ex.clone_in(ast_builder.allocator), + ast_builder.identifier_name(SPAN, ast_builder.atom("className")), + true, + ), + ), + ) + }) + .collect::>() + }) .collect::>() .as_slice(), ) @@ -245,76 +256,67 @@ fn merge_string_expressions<'a>( let mut string_literals: std::vec::Vec = vec![]; let mut other_expressions = vec![]; let mut prev_str = String::new(); - for (idx, ex) in expressions.iter().enumerate() { + for ex in expressions.iter() { match ex { Expression::StringLiteral(literal) => { - prev_str.push_str( - format!( - "{}{}", - if prev_str.trim().is_empty() && other_expressions.is_empty() { - "" - } else { - " " - }, - literal.value.trim() - ) - .as_str(), + let target_prev = prev_str.trim(); + let target = literal.value.trim(); + prev_str = format!( + "{}{}{}", + target_prev, + if target_prev.is_empty() { "" } else { " " }, + target ); } Expression::TemplateLiteral(template) => { for (idx, q) in template.quasis.iter().enumerate() { - if !prev_str.is_empty() { + let target_prev = prev_str.trim(); + let target = q.value.raw.trim(); + if idx < template.quasis.len() - 1 { string_literals.push(format!( - "{}{}{}{}", - prev_str.trim(), - if !prev_str.trim().is_empty() { " " } else { "" }, - q.value.raw.trim(), - if idx == template.quasis.len() - 1 { - "" - } else { + "{}{}{}{}{}", + if !other_expressions.is_empty() || idx > 0 { " " - } - )); - prev_str = String::new(); - } else if q.tail { - prev_str = q.value.raw.trim().to_string(); - } else { - string_literals.push(format!( - "{}{}{}", - if idx == 0 - && other_expressions.is_empty() - && string_literals.is_empty() - { - "" } else { - " " - }, - q.value.raw.trim(), - if q.value.raw.trim().is_empty() || !q.value.raw.ends_with(' ') { "" - } else { + }, + target_prev, + if !target_prev.is_empty() { " " } else { "" }, + target, + if !target.is_empty() && !target.ends_with("typo-") { " " + } else { + "" } )); - prev_str = String::new(); + } else { + prev_str = q.value.raw.trim().to_string(); } } other_expressions.extend(template.expressions.clone_in(ast_builder.allocator)); } ex => { + let target_prev = prev_str.trim(); string_literals.push(format!( - "{}{}", - prev_str.trim(), - if idx > 0 { " " } else { "" } + "{}{}{}", + if !other_expressions.is_empty() { + " " + } else { + "" + }, + target_prev, + if !target_prev.is_empty() { " " } else { "" } )); other_expressions.push(ex.clone_in(ast_builder.allocator)); prev_str = String::new(); } } } - if !prev_str.is_empty() { - string_literals.push(prev_str.trim_end().to_string()); - } + string_literals.push(format!( + "{}{}", + if !prev_str.trim().is_empty() { " " } else { "" }, + prev_str.trim(), + )); if other_expressions.is_empty() { return Some(ast_builder.expression_string_literal( SPAN, diff --git a/libs/extractor/src/snapshots/extractor__tests__extract_style_props_with_class_name-9.snap b/libs/extractor/src/snapshots/extractor__tests__extract_style_props_with_class_name-9.snap index 8c827d5d..35ac0d70 100644 --- a/libs/extractor/src/snapshots/extractor__tests__extract_style_props_with_class_name-9.snap +++ b/libs/extractor/src/snapshots/extractor__tests__extract_style_props_with_class_name-9.snap @@ -20,5 +20,5 @@ ToBTreeSet { "buttonS", ), }, - code: "import \"@devup-ui/core/devup-ui.css\";\nimport clsx from \"clsx\";\n;\n", + code: "import \"@devup-ui/core/devup-ui.css\";\nimport clsx from \"clsx\";\n;\n", } diff --git a/libs/extractor/src/snapshots/extractor__tests__ignore_special_props2.snap.new b/libs/extractor/src/snapshots/extractor__tests__ignore_special_props2.snap.new deleted file mode 100644 index ce1976b1..00000000 --- a/libs/extractor/src/snapshots/extractor__tests__ignore_special_props2.snap.new +++ /dev/null @@ -1,575 +0,0 @@ ---- -source: libs/extractor/src/lib.rs -assertion_line: 236 -expression: "ToBTreeSet::from(extract(\"test.tsx\",\nr#\"import {Box as DevupButton} from '@devup-ui/core'\n \n \n {icon && (\n svg': {\n color: 'inherit',\n },\n }}\n top=\"50%\"\n transform=\"translate(-100%, -50%)\"\n >\n {icon}\n \n )}\n \n {children}\n \n \n \n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())" ---- -ToBTreeSet { - styles: { - Static( - ExtractStaticStyle { - property: "background", - value: "color-mix(in srgb,var(--primary,#674DC7) 10%,var(--inputBackground,#2E2E2E) 90%)", - level: 0, - selector: Some( - Selector( - ":root[data-theme=dark] &:hover", - ), - ), - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "background", - value: "color-mix(in srgb,var(--primary,#674DC7) 100%,#000 15%)", - level: 0, - selector: Some( - Selector( - "&:hover", - ), - ), - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "background", - value: "color-mix(in srgb,var(--primary,#674DC7) 100%,#000 30%)", - level: 0, - selector: Some( - Selector( - "&:active", - ), - ), - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "background", - value: "color-mix(in srgb,var(--primary,#8163E1) 10%,#FFF 90%)", - level: 0, - selector: Some( - Selector( - "&:hover", - ), - ), - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "background", - value: "color-mix(in srgb,var(--primary,#8163E1) 100%,#FFF 15%)", - level: 0, - selector: Some( - Selector( - ":root[data-theme=dark] &:hover", - ), - ), - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "background", - value: "color-mix(in srgb,var(--primary,#8163E1) 100%,#FFF 30%)", - level: 0, - selector: Some( - Selector( - ":root[data-theme=dark] &:active", - ), - ), - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "background", - value: "color-mix(in srgb,var(--primary,#8163E1) 20%,#FFF 80%)", - level: 0, - selector: Some( - Selector( - "&:active", - ), - ), - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "background", - value: "var(--inputBackground,#FFF)", - level: 0, - selector: None, - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "background", - value: "var(--primary,#8163E1)", - level: 0, - selector: None, - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "background", - value: "var(--primary,#8163E1)", - level: 0, - selector: Some( - Selector( - ":root[data-theme=dark] &:active", - ), - ), - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "backgroundColor", - value: "#47474A", - level: 0, - selector: Some( - Selector( - ":root[data-theme=dark] &:disabled", - ), - ), - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "backgroundColor", - value: "#F0F0F3", - level: 0, - selector: Some( - Selector( - "&:disabled", - ), - ), - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "border", - value: "1px solid var(--border,#E4E4E4)", - level: 0, - selector: None, - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "border", - value: "1px solid var(--primary,#8163E1)", - level: 0, - selector: Some( - Selector( - "&:active", - ), - ), - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "border", - value: "none", - level: 0, - selector: None, - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "borderColor", - value: "transparent", - level: 0, - selector: Some( - Selector( - ":root[data-theme=dark] &:disabled", - ), - ), - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "borderColor", - value: "var(--border,#E4E4E4)", - level: 0, - selector: Some( - Selector( - "&:disabled", - ), - ), - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "borderColor", - value: "var(--primary,#8163E1)", - level: 0, - selector: Some( - Selector( - ":root[data-theme=dark] &:hover", - ), - ), - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "borderRadius", - value: "10px", - level: 0, - selector: None, - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "borderRadius", - value: "8px", - level: 0, - selector: None, - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "boxSizing", - value: "border-box", - level: 0, - selector: None, - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "color", - value: "#000", - level: 0, - selector: Some( - Selector( - "&:active", - ), - ), - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "color", - value: "#373737", - level: 0, - selector: Some( - Selector( - ":root[data-theme=dark] &:disabled", - ), - ), - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "color", - value: "#D6D7DE", - level: 0, - selector: Some( - Selector( - "&:disabled", - ), - ), - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "color", - value: "#FFF", - level: 0, - selector: None, - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "color", - value: "var(--text,#272727)", - level: 0, - selector: None, - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "color", - value: "var(--text,#F6F6F6)", - level: 0, - selector: Some( - Selector( - ":root[data-theme=dark] &:active", - ), - ), - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "cursor", - value: "not-allowed", - level: 0, - selector: Some( - Selector( - "&:disabled", - ), - ), - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "cursor", - value: "pointer", - level: 0, - selector: None, - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "fontSize", - value: "14px", - level: 0, - selector: None, - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "fontSize", - value: "15px", - level: 4, - selector: None, - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "fontWeight", - value: "700", - level: 0, - selector: None, - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "letterSpacing", - value: "-0.02em", - level: 0, - selector: None, - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "letterSpacing", - value: "-0.03em", - level: 4, - selector: None, - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "outline", - value: "2px solid", - level: 0, - selector: Some( - Selector( - "&:focus-visible", - ), - ), - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "outlineColor", - value: "var(--primary,#674DC7)", - level: 0, - selector: Some( - Selector( - ":root[data-theme=dark] &:hover", - ), - ), - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "outlineColor", - value: "var(--primaryFocus,#927CE4)", - level: 0, - selector: Some( - Selector( - ":root[data-theme=dark] &:focus-visible", - ), - ), - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "outlineColor", - value: "var(--primaryFocus,#9385D3)", - level: 0, - selector: Some( - Selector( - "&:focus-visible", - ), - ), - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "outlineOffset", - value: "2px", - level: 0, - selector: None, - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "position", - value: "relative", - level: 0, - selector: None, - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "px", - value: "40px", - level: 0, - selector: None, - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "py", - value: "12px", - level: 0, - selector: None, - style_order: Some( - 1, - ), - }, - ), - Static( - ExtractStaticStyle { - property: "transition", - value: ".25s", - level: 0, - selector: None, - style_order: Some( - 1, - ), - }, - ), - }, - code: "import \"@devup-ui/core/devup-ui.css\";\n\n \n {icon && svg\": { color: \"inherit\" } }} top=\"50%\" transform=\"translate(-100%, -50%)\">\n {icon}\n }\n \n {children}\n \n \n ;\n", -} diff --git a/libs/extractor/src/snapshots/extractor__tests__rest_props-2.snap b/libs/extractor/src/snapshots/extractor__tests__rest_props-2.snap new file mode 100644 index 00000000..46512836 --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__rest_props-2.snap @@ -0,0 +1,57 @@ +--- +source: libs/extractor/src/lib.rs +expression: "ToBTreeSet::from(extract(\"test.jsx\",\nr#\"import { VStack } from '@devup-ui/core'\n\nexport default function Card({\n children,\n className,\n ...props\n}) {\n return (\n \n {children}\n \n )\n}\n\n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())" +--- +ToBTreeSet { + styles: { + Static( + ExtractStaticStyle { + property: "boxShadow", + value: "none", + level: 0, + selector: Some( + Selector( + "&:active", + ), + ), + style_order: None, + }, + ), + Static( + ExtractStaticStyle { + property: "display", + value: "flex", + level: 0, + selector: None, + style_order: Some( + 0, + ), + }, + ), + Static( + ExtractStaticStyle { + property: "flexDirection", + value: "column", + level: 0, + selector: None, + style_order: Some( + 0, + ), + }, + ), + Static( + ExtractStaticStyle { + property: "transform", + value: "scale(0.95)", + level: 0, + selector: Some( + Selector( + "&:active", + ), + ), + style_order: None, + }, + ), + }, + code: "import \"@devup-ui/core/devup-ui.css\";\nexport default function Card({ children, className,...props }) {\n\treturn \n {children}\n ;\n}\n", +} diff --git a/libs/extractor/src/snapshots/extractor__tests__support_transpile_cjs_2.snap.new b/libs/extractor/src/snapshots/extractor__tests__support_transpile_cjs_2.snap.new deleted file mode 100644 index f18461f6..00000000 --- a/libs/extractor/src/snapshots/extractor__tests__support_transpile_cjs_2.snap.new +++ /dev/null @@ -1,9 +0,0 @@ ---- -source: libs/extractor/src/lib.rs -assertion_line: 2140 -expression: "ToBTreeSet::from(extract(\"test.cjs\",\nr#\"'use client'\n\nimport type { Conditional } from 'src/types/utils'\n\nimport { DevupTheme } from '../types/theme'\n\n/**\n * Initialize the theme, if you can't use the `ThemeScript` component\n * e.g. in vite\n * @param auto - Whether to use the system theme\n * @param theme - The theme to use\n */\nexport function initTheme(\n auto?: boolean,\n theme?: Conditional,\n): void {\n if (theme) {\n document.documentElement.setAttribute('data-theme', theme)\n } else {\n console.log(\n 'hello',\n typeof process.env.DEVUP_UI_DEFAULT_THEME,\n process.env.DEVUP_UI_DEFAULT_THEME,\n localStorage.getItem('__DF_THEME_SELECTED__') ||\n (auto && window.matchMedia('(prefers-color-scheme:dark)').matches\n ? 'dark'\n : (process.env.DEVUP_UI_DEFAULT_THEME ?? 'default')),\n )\n document.documentElement.setAttribute(\n 'data-theme',\n localStorage.getItem('__DF_THEME_SELECTED__') ||\n (auto && window.matchMedia('(prefers-color-scheme:dark)').matches\n ? 'dark'\n : (process.env.DEVUP_UI_DEFAULT_THEME ?? 'default')),\n )\n }\n}\n\"#,\nExtractOption\n{ package: \"@devup-ui/react\".to_string(), css_file: None }).unwrap())" ---- -ToBTreeSet { - styles: {}, - code: "'use client'\n\nimport type { Conditional } from 'src/types/utils'\n\nimport { DevupTheme } from '../types/theme'\n\n/**\n * Initialize the theme, if you can't use the `ThemeScript` component\n * e.g. in vite\n * @param auto - Whether to use the system theme\n * @param theme - The theme to use\n */\nexport function initTheme(\n auto?: boolean,\n theme?: Conditional,\n): void {\n if (theme) {\n document.documentElement.setAttribute('data-theme', theme)\n } else {\n console.log(\n 'hello',\n typeof process.env.DEVUP_UI_DEFAULT_THEME,\n process.env.DEVUP_UI_DEFAULT_THEME,\n localStorage.getItem('__DF_THEME_SELECTED__') ||\n (auto && window.matchMedia('(prefers-color-scheme:dark)').matches\n ? 'dark'\n : (process.env.DEVUP_UI_DEFAULT_THEME ?? 'default')),\n )\n document.documentElement.setAttribute(\n 'data-theme',\n localStorage.getItem('__DF_THEME_SELECTED__') ||\n (auto && window.matchMedia('(prefers-color-scheme:dark)').matches\n ? 'dark'\n : (process.env.DEVUP_UI_DEFAULT_THEME ?? 'default')),\n )\n }\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 index 35519da9..c72dd920 100644 --- a/libs/extractor/src/snapshots/extractor__tests__support_transpile_mjs-5.snap +++ b/libs/extractor/src/snapshots/extractor__tests__support_transpile_mjs-5.snap @@ -14,5 +14,5 @@ ToBTreeSet { }, ), }, - 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 ${props?.className || \"\"}`,\n\tstyle: {\n\t\t...{ \"--d1\": variable },\n\t\t...{ color: \"blue\" },\n\t\t...props?.style\n\t}\n});\n", + 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\t...props?.style\n\t}\n});\n", } diff --git a/packages/components/src/components/Button/__tests__/__snapshots__/index.browser.test.tsx.snap b/packages/components/src/components/Button/__tests__/__snapshots__/index.browser.test.tsx.snap index fdc1a277..b488fc57 100644 --- a/packages/components/src/components/Button/__tests__/__snapshots__/index.browser.test.tsx.snap +++ b/packages/components/src/components/Button/__tests__/__snapshots__/index.browser.test.tsx.snap @@ -4,7 +4,7 @@ exports[`Button > color should be white 1`] = ` @@ -26,7 +26,7 @@ exports[`Button > should disable 1`] = ` @@ -47,7 +47,7 @@ exports[`Button > should have class name when className is provided 1`] = ` should have font size 14px when size is sm and variant is defa @@ -88,7 +88,7 @@ exports[`Button > should have font size 15px when size is md and variant is prim @@ -109,7 +109,7 @@ exports[`Button > should have font size 15px when size is sm and variant is prim @@ -130,7 +130,7 @@ exports[`Button > should have text overflow ellipsis when ellipsis is true 1`] = @@ -151,7 +151,7 @@ exports[`Button > should have typography when typography is provided 1`] = ` should not have bg when a wrong size variable is provided 1`] should not have px when a wrong size variable is provided 1`] should not have px when a wrong size variable is provided 1`] exports[`Button > should not render error color when danger is false and variant is default 1`] = ` @@ -230,7 +230,7 @@ exports[`Button > should render 1`] = ` should render default style when variant is default 1`] = ` should render error style when danger is true and variant is d should render icon when icon is provided 1`] = ` @@ -329,7 +329,7 @@ exports[`Button > should render primary background color when danger is true and