diff --git a/.changeset/four-ideas-enjoy.md b/.changeset/four-ideas-enjoy.md new file mode 100644 index 00000000..2c117dc4 --- /dev/null +++ b/.changeset/four-ideas-enjoy.md @@ -0,0 +1,5 @@ +--- +"@devup-ui/wasm": patch +--- + +Support auto wrapping for src in font faces diff --git a/.changeset/hot-moose-count.md b/.changeset/hot-moose-count.md new file mode 100644 index 00000000..e51b2660 --- /dev/null +++ b/.changeset/hot-moose-count.md @@ -0,0 +1,6 @@ +--- +"@devup-ui/react": patch +"@devup-ui/wasm": patch +--- + +Support \_ selector and typing in globalCss diff --git a/libs/css/src/constant.rs b/libs/css/src/constant.rs index 2ef8c1b3..7652e4f0 100644 --- a/libs/css/src/constant.rs +++ b/libs/css/src/constant.rs @@ -125,6 +125,9 @@ pub(super) static ZERO_PERCENT_FUNCTION: phf::Set<&str> = phf_set! { }; pub(super) static F_SPACE_RE: Lazy = Lazy::new(|| Regex::new(r"\s*,\s*").unwrap()); +pub(super) static CSS_FUNCTION_RE: Lazy = + Lazy::new(|| Regex::new(r"^[a-zA-Z-]+(\(.*\))").unwrap()); +pub(super) static CHECK_QUOTES_RE: Lazy = Lazy::new(|| Regex::new(r"[()\s]").unwrap()); pub(super) static CSS_COMMENT_RE: Lazy = Lazy::new(|| Regex::new(r"/\*[\s\S]*?\*/").unwrap()); diff --git a/libs/css/src/optimize_multi_css_value.rs b/libs/css/src/optimize_multi_css_value.rs index d77c6bdd..ff0e1a0f 100644 --- a/libs/css/src/optimize_multi_css_value.rs +++ b/libs/css/src/optimize_multi_css_value.rs @@ -1,4 +1,4 @@ -use crate::constant::OPTIMIZE_MULTI_CSS_VALUE_PROPERTY; +use crate::constant::{CHECK_QUOTES_RE, CSS_FUNCTION_RE, OPTIMIZE_MULTI_CSS_VALUE_PROPERTY}; pub fn optimize_mutli_css_value(value: &str) -> String { value @@ -12,7 +12,7 @@ pub fn optimize_mutli_css_value(value: &str) -> String { } else { s.to_string() }; - if s.contains(" ") { + if CHECK_QUOTES_RE.is_match(&s) && !CSS_FUNCTION_RE.is_match(&s) { format!("\"{s}\"") } else { s @@ -22,6 +22,14 @@ pub fn optimize_mutli_css_value(value: &str) -> String { .join(",") } +pub fn wrap_url(s: &str) -> String { + if CSS_FUNCTION_RE.is_match(s) { + s.to_string() + } else { + format!("url({s})") + } +} + pub fn check_multi_css_optimize(property: &str) -> bool { OPTIMIZE_MULTI_CSS_VALUE_PROPERTY.contains(property) } @@ -49,6 +57,8 @@ mod tests { #[case("'A B', 'C D', E", "\"A B\",\"C D\",E")] #[case("A,B,C", "A,B,C")] #[case("A, B, C", "A,B,C")] + #[case("url(abc)", "url(abc)")] + #[case("url(\"a bc\")", "url(\"a bc\")")] #[case("'A', 'B', 'C'", "A,B,C")] #[case("\"A\", \"B\", \"C\"", "A,B,C")] fn test_optimize_mutli_css_value(#[case] input: &str, #[case] expected: &str) { @@ -66,4 +76,25 @@ mod tests { fn test_check_multi_css_optimize(#[case] property: &str, #[case] expected: bool) { assert_eq!(check_multi_css_optimize(property), expected); } + + #[rstest] + #[case("url('/fonts/Roboto-Regular.ttf')", "url('/fonts/Roboto-Regular.ttf')")] + #[case( + "url(\"/fonts/Roboto-Regular.ttf\")", + "url(\"/fonts/Roboto-Regular.ttf\")" + )] + #[case("//fonts/Roboto-Regular.ttf", "url(//fonts/Roboto-Regular.ttf)")] + #[case("fonts/Roboto-Regular.ttf", "url(fonts/Roboto-Regular.ttf)")] + #[case( + "local('fonts/Roboto Regular.ttf')", + "local('fonts/Roboto Regular.ttf')" + )] + #[case("(hello)", "url(\"(hello)\")")] + #[case("(hello world)", "url(\"(hello world)\")")] + fn test_wrap_url(#[case] input: &str, #[case] expected: &str) { + assert_eq!( + super::wrap_url(&super::optimize_mutli_css_value(input)), + expected + ); + } } diff --git a/libs/extractor/src/extractor/extract_global_style_from_expression.rs b/libs/extractor/src/extractor/extract_global_style_from_expression.rs index b3b5fc15..191b3acd 100644 --- a/libs/extractor/src/extractor/extract_global_style_from_expression.rs +++ b/libs/extractor/src/extractor/extract_global_style_from_expression.rs @@ -14,7 +14,7 @@ use crate::{ }; use css::{ disassemble_property, - optimize_multi_css_value::{check_multi_css_optimize, optimize_mutli_css_value}, + optimize_multi_css_value::{check_multi_css_optimize, optimize_mutli_css_value, wrap_url}, style_selector::StyleSelector, }; use oxc_ast::{ @@ -86,7 +86,15 @@ pub fn extract_global_style_from_expression<'a>( && let PropertyKey::StaticIdentifier(ident) = &o.key && let Some(s) = get_string_by_literal_expression(&o.value) { - Some(disassemble_property(&ident.name).iter().map(|p| (p.to_string(), if check_multi_css_optimize(p) { optimize_mutli_css_value(&s) } else { s.clone() })).collect::>()) + Some(disassemble_property(&ident.name).iter().map(|p| { + let v= if check_multi_css_optimize(p) { optimize_mutli_css_value(&s) } else { s.clone() }; + if *p == "src" { + (p.to_string(), wrap_url(&v)) + } + else { + (p.to_string(), v) + } + }).collect::>()) } else { None } @@ -135,7 +143,14 @@ pub fn extract_global_style_from_expression<'a>( None, &mut o.value, 0, - &Some(StyleSelector::Global(name.clone(), file.to_string())), + &Some(StyleSelector::Global( + if let Some(name) = name.strip_prefix("_") { + StyleSelector::from(name).to_string().replace("&", "*") + } else { + name.to_string() + }, + file.to_string(), + )), ) .styles, ); diff --git a/libs/extractor/src/lib.rs b/libs/extractor/src/lib.rs index e3c0816c..dd611a1e 100644 --- a/libs/extractor/src/lib.rs +++ b/libs/extractor/src/lib.rs @@ -4684,6 +4684,63 @@ globalCss({ bg: "green" } }) +"#, + ExtractOption { + package: "@devup-ui/core".to_string(), + css_file: None + } + ) + .unwrap() + )); + + reset_class_map(); + assert_debug_snapshot!(ToBTreeSet::from( + extract( + "test.tsx", + r#"import { globalCss } from "@devup-ui/core"; +globalCss({ + _hover: { + bg: "red" + } +}) +"#, + ExtractOption { + package: "@devup-ui/core".to_string(), + css_file: None + } + ) + .unwrap() + )); + + reset_class_map(); + assert_debug_snapshot!(ToBTreeSet::from( + extract( + "test.tsx", + r#"import { globalCss } from "@devup-ui/core"; +globalCss({ + _placeholder: { + bg: "red" + } +}) +"#, + ExtractOption { + package: "@devup-ui/core".to_string(), + css_file: None + } + ) + .unwrap() + )); + + reset_class_map(); + assert_debug_snapshot!(ToBTreeSet::from( + extract( + "test.tsx", + r#"import { globalCss } from "@devup-ui/core"; +globalCss({ + _nthLastChild: { + bg: "red" + } +}) "#, ExtractOption { package: "@devup-ui/core".to_string(), @@ -5209,6 +5266,44 @@ globalCss({ } ] }) +"#, + ExtractOption { + package: "@devup-ui/core".to_string(), + css_file: None + } + ) + .unwrap() + )); + + reset_class_map(); + assert_debug_snapshot!(ToBTreeSet::from( + extract( + "test.tsx", + r#"import { globalCss } from "@devup-ui/core"; +globalCss({ + fontFaces: [ + { + fontFamily: "Roboto Regular2", + src: "//fonts/Roboto-Regular.ttf", + fontWeight: 400, + }, + { + fontFamily: "Roboto Regular", + src: "//fonts/Roboto Regular.ttf", + fontWeight: 400, + }, + { + fontFamily: "Roboto Regular3", + src: "fonts/Roboto Regular.ttf", + fontWeight: 400, + }, + { + fontFamily: "Roboto Regular4", + src: "local('fonts/Roboto Regular.ttf')", + fontWeight: 400, + }, + ] +}) "#, ExtractOption { package: "@devup-ui/core".to_string(), diff --git a/libs/extractor/src/snapshots/extractor__tests__extract_global_css-5.snap b/libs/extractor/src/snapshots/extractor__tests__extract_global_css-5.snap new file mode 100644 index 00000000..01af40c5 --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__extract_global_css-5.snap @@ -0,0 +1,25 @@ +--- +source: libs/extractor/src/lib.rs +expression: "ToBTreeSet::from(extract(\"test.tsx\",\nr#\"import { globalCss } from \"@devup-ui/core\";\nglobalCss({\n _hover: {\n bg: \"red\"\n }\n})\n\"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())" +--- +ToBTreeSet { + styles: { + Static( + ExtractStaticStyle { + property: "background", + value: "red", + level: 0, + selector: Some( + Global( + "*:hover", + "test.tsx", + ), + ), + style_order: Some( + 0, + ), + }, + ), + }, + code: "import \"@devup-ui/core/devup-ui.css\";\n;\n", +} diff --git a/libs/extractor/src/snapshots/extractor__tests__extract_global_css-6.snap b/libs/extractor/src/snapshots/extractor__tests__extract_global_css-6.snap new file mode 100644 index 00000000..8941b1bb --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__extract_global_css-6.snap @@ -0,0 +1,25 @@ +--- +source: libs/extractor/src/lib.rs +expression: "ToBTreeSet::from(extract(\"test.tsx\",\nr#\"import { globalCss } from \"@devup-ui/core\";\nglobalCss({\n _placeholder: {\n bg: \"red\"\n }\n})\n\"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())" +--- +ToBTreeSet { + styles: { + Static( + ExtractStaticStyle { + property: "background", + value: "red", + level: 0, + selector: Some( + Global( + "*::placeholder", + "test.tsx", + ), + ), + style_order: Some( + 0, + ), + }, + ), + }, + code: "import \"@devup-ui/core/devup-ui.css\";\n;\n", +} diff --git a/libs/extractor/src/snapshots/extractor__tests__extract_global_css-7.snap b/libs/extractor/src/snapshots/extractor__tests__extract_global_css-7.snap new file mode 100644 index 00000000..c7d7096c --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__extract_global_css-7.snap @@ -0,0 +1,25 @@ +--- +source: libs/extractor/src/lib.rs +expression: "ToBTreeSet::from(extract(\"test.tsx\",\nr#\"import { globalCss } from \"@devup-ui/core\";\nglobalCss({\n _nthLastChild: {\n bg: \"red\"\n }\n})\n\"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())" +--- +ToBTreeSet { + styles: { + Static( + ExtractStaticStyle { + property: "background", + value: "red", + level: 0, + selector: Some( + Global( + "*:nth-last-child", + "test.tsx", + ), + ), + style_order: Some( + 0, + ), + }, + ), + }, + code: "import \"@devup-ui/core/devup-ui.css\";\n;\n", +} diff --git a/libs/extractor/src/snapshots/extractor__tests__extract_global_css_with_font_faces-7.snap b/libs/extractor/src/snapshots/extractor__tests__extract_global_css_with_font_faces-7.snap new file mode 100644 index 00000000..61c1fe44 --- /dev/null +++ b/libs/extractor/src/snapshots/extractor__tests__extract_global_css_with_font_faces-7.snap @@ -0,0 +1,49 @@ +--- +source: libs/extractor/src/lib.rs +expression: "ToBTreeSet::from(extract(\"test.tsx\",\nr#\"import { globalCss } from \"@devup-ui/core\";\nglobalCss({\n fontFaces: [\n {\n fontFamily: \"Roboto Regular2\",\n src: \"//fonts/Roboto-Regular.ttf\",\n fontWeight: 400,\n },\n {\n fontFamily: \"Roboto Regular\",\n src: \"//fonts/Roboto Regular.ttf\",\n fontWeight: 400,\n },\n {\n fontFamily: \"Roboto Regular3\",\n src: \"fonts/Roboto Regular.ttf\",\n fontWeight: 400,\n },\n {\n fontFamily: \"Roboto Regular4\",\n src: \"local('fonts/Roboto Regular.ttf')\",\n fontWeight: 400,\n },\n ]\n})\n\"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap())" +--- +ToBTreeSet { + styles: { + FontFace( + ExtractFontFace { + file: "test.tsx", + properties: { + "font-family": "\"Roboto Regular\"", + "font-weight": "400", + "src": "url(\"//fonts/Roboto Regular.ttf\")", + }, + }, + ), + FontFace( + ExtractFontFace { + file: "test.tsx", + properties: { + "font-family": "\"Roboto Regular2\"", + "font-weight": "400", + "src": "url(//fonts/Roboto-Regular.ttf)", + }, + }, + ), + FontFace( + ExtractFontFace { + file: "test.tsx", + properties: { + "font-family": "\"Roboto Regular3\"", + "font-weight": "400", + "src": "url(\"fonts/Roboto Regular.ttf\")", + }, + }, + ), + FontFace( + ExtractFontFace { + file: "test.tsx", + properties: { + "font-family": "\"Roboto Regular4\"", + "font-weight": "400", + "src": "local('fonts/Roboto Regular.ttf')", + }, + }, + ), + }, + code: "import \"@devup-ui/core/devup-ui.css\";\n;\n", +} diff --git a/packages/react/src/types/props/selector/index.ts b/packages/react/src/types/props/selector/index.ts index 5ec37b66..adf700b1 100644 --- a/packages/react/src/types/props/selector/index.ts +++ b/packages/react/src/types/props/selector/index.ts @@ -18,11 +18,11 @@ export type DevupThemeSelectorProps = keyof DevupTheme extends undefined ? Partial> : Partial}`, SelectorProps>> -type NormalSelector = Exclude< +export type NormalSelector = Exclude< Pseudos, `:-${string}` | `::-${string}` | `${string}()` > -type ExtractSelector = T extends `::${infer R}` +export type ExtractSelector = T extends `::${infer R}` ? R : T extends `:${infer R}` ? R diff --git a/packages/react/src/utils/global-css.ts b/packages/react/src/utils/global-css.ts index 72268340..6d364141 100644 --- a/packages/react/src/utils/global-css.ts +++ b/packages/react/src/utils/global-css.ts @@ -1,12 +1,20 @@ import type { DevupCommonProps } from '../types/props' -import type { DevupThemeSelectorProps } from '../types/props/selector' +import type { + DevupThemeSelectorProps, + ExtractSelector, + NormalSelector, +} from '../types/props/selector' import type { DevupSelectorProps } from '../types/props/selector' +type GlobalCssKeys = + | `*${NormalSelector | ''}` + | `${keyof HTMLElementTagNameMap}${NormalSelector | ''}` + | `${keyof SVGElementTagNameMap}${NormalSelector | ''}` + | `_${ExtractSelector}` + | (string & {}) + type GlobalCssProps = { - [key in - | keyof HTMLElementTagNameMap - | keyof SVGElementTagNameMap - | (string & {})]?: + [K in GlobalCssKeys]?: | DevupCommonProps | DevupSelectorProps | DevupThemeSelectorProps