@@ -29,6 +29,59 @@ impl From<CssToStyleResult> for ExtractStyleValue {
2929 }
3030 }
3131}
32+
33+ /// Normalize whitespace while preserving string literals
34+ fn normalize_whitespace_preserving_strings ( input : & str ) -> String {
35+ let mut result = String :: with_capacity ( input. len ( ) ) ;
36+ let mut chars = input. chars ( ) . peekable ( ) ;
37+ let mut string_placeholders = Vec :: new ( ) ;
38+ let mut placeholder_counter = 0 ;
39+
40+ while let Some ( ch) = chars. next ( ) {
41+ if ch == '"' || ch == '\'' || ch == '`' {
42+ // Found start of string literal
43+ let quote = ch;
44+ let mut string_content = String :: from ( quote) ;
45+ let mut escaped = false ;
46+
47+ while let Some ( & next_ch) = chars. peek ( ) {
48+ chars. next ( ) ;
49+ string_content. push ( next_ch) ;
50+
51+ if escaped {
52+ escaped = false ;
53+ } else if next_ch == '\\' {
54+ escaped = true ;
55+ } else if next_ch == quote {
56+ // End of string literal
57+ break ;
58+ }
59+ }
60+
61+ // Replace string literal with placeholder
62+ let placeholder = format ! ( "__STRING_{}__" , placeholder_counter) ;
63+ placeholder_counter += 1 ;
64+ string_placeholders. push ( ( placeholder. clone ( ) , string_content) ) ;
65+ result. push_str ( & placeholder) ;
66+ } else {
67+ result. push ( ch) ;
68+ }
69+ }
70+
71+ // Normalize whitespace (newlines, tabs, multiple spaces)
72+ result = result. replace ( [ '\n' , '\t' ] , " " ) ;
73+ while result. contains ( " " ) {
74+ result = result. replace ( " " , " " ) ;
75+ }
76+ result = result. trim ( ) . to_string ( ) ;
77+
78+ // Restore string literals
79+ for ( placeholder, original_string) in string_placeholders. iter ( ) . rev ( ) {
80+ result = result. replace ( placeholder, original_string) ;
81+ }
82+
83+ result
84+ }
3285pub fn css_to_style_literal < ' a > (
3386 css : & TemplateLiteral < ' a > ,
3487 level : u8 ,
@@ -142,14 +195,9 @@ pub fn css_to_style_literal<'a>(
142195 let mut identifier = expression_to_code ( expr) ;
143196
144197 // Normalize the code string
145- // 1. Remove newlines and tabs, replace with spaces
146- identifier = identifier. replace ( [ '\n' , '\t' ] , " " ) ;
147- // 2. Normalize multiple spaces to single space
148-
149- while identifier. contains ( " " ) {
150- identifier = identifier. replace ( " " , " " ) ;
151- }
152- // 3. Normalize arrow function whitespace
198+ // 1. Normalize whitespace while preserving string literals
199+ identifier = normalize_whitespace_preserving_strings ( & identifier) ;
200+ // 2. Normalize arrow function whitespace
153201 identifier = identifier
154202 . replace ( " => " , "=>" )
155203 . replace ( " =>" , "=>" )
@@ -253,12 +301,9 @@ pub fn css_to_style_literal<'a>(
253301 if * idx < css. expressions . len ( ) {
254302 let expr = & css. expressions [ * idx] ;
255303 let expr_code = expression_to_code ( expr) ;
256- // Normalize the expression code
257- let mut normalized_code = expr_code. replace ( [ '\n' , '\t' ] , " " ) ;
258- while normalized_code. contains ( " " ) {
259- normalized_code = normalized_code. replace ( " " , " " ) ;
260- }
261- normalized_code = normalized_code. trim ( ) . to_string ( ) ;
304+ // Normalize the expression code while preserving string literals
305+ let normalized_code =
306+ normalize_whitespace_preserving_strings ( & expr_code) ;
262307
263308 // Replace placeholder with ${expr} syntax
264309 let expr_template = format ! ( "${{{}}}" , normalized_code) ;
@@ -281,16 +326,8 @@ pub fn css_to_style_literal<'a>(
281326 } else {
282327 // Check if property name contains a dynamic expression placeholder
283328 let property = style. property ( ) ;
284- let mut prop_is_dynamic = false ;
285-
286- for placeholder in expression_map. keys ( ) {
287- if property. contains ( placeholder) {
288- prop_is_dynamic = true ;
289- break ;
290- }
291- }
292329
293- if !prop_is_dynamic {
330+ if !expression_map . keys ( ) . any ( |p| property . contains ( p ) ) {
294331 // Static style
295332 styles. push ( CssToStyleResult :: Static ( style) ) ;
296333 }
@@ -858,6 +895,8 @@ mod tests {
858895 #[ case( "`width: ${func(1)}px;`" , vec![ ( "width" , "`${func(1)}px`" , None ) ] ) ]
859896 #[ case( "`width: ${func(1)}${2}px;`" , vec![ ( "width" , "`${func(1)}${2}px`" , None ) ] ) ]
860897 #[ case( "`width: ${1}${2}px;`" , vec![ ( "width" , "12px" , None ) ] ) ]
898+ #[ case( "`width: ${func(\n \t 1 , \n \t 2\n )}px;`" , vec![ ( "width" , "`${func(1,2)}px`" , None ) ] ) ]
899+ #[ case( "`width: ${func(\" wow \" )}px;`" , vec![ ( "width" , "`${func(\" wow \" )}px`" , None ) ] ) ]
861900 // wrong cases
862901 #[ case(
863902 "`@media (min-width: 768px) {
@@ -1196,6 +1235,13 @@ mod tests {
11961235 ( "font-family" , "\" Roboto Hello\" ,sans-serif" , Some ( StyleSelector :: Selector ( "ul" . to_string( ) ) ) ) ,
11971236 ]
11981237 ) ]
1238+ #[ case(
1239+ "div { color: red; ; { background: blue; } }" ,
1240+ vec![
1241+ ( "color" , "red" , Some ( StyleSelector :: Selector ( "div" . to_string( ) ) ) ) ,
1242+ ( "background" , "blue" , Some ( StyleSelector :: Selector ( "div" . to_string( ) ) ) ) ,
1243+ ]
1244+ ) ]
11991245 fn test_css_to_style (
12001246 #[ case] input : & str ,
12011247 #[ case] expected : Vec < ( & str , & str , Option < StyleSelector > ) > ,
0 commit comments