@@ -81,6 +81,9 @@ pub fn css_to_style_literal<'a>(
8181 // Parse CSS to extract static styles
8282 let static_styles = css_to_style ( & combined_css, level, selector) ;
8383
84+ // Shared allocator for AST builder used in dynamic expression processing
85+ let shared_allocator = Allocator :: default ( ) ;
86+
8487 // Process each static style and check if it contains expression placeholders
8588 for style in static_styles {
8689 let value = style. value ( ) ;
@@ -146,8 +149,7 @@ pub fn css_to_style_literal<'a>(
146149 | oxc_ast:: ast:: Expression :: FunctionExpression ( _)
147150 ) ;
148151
149- let allocator = Allocator :: default ( ) ;
150- let ast_builder = oxc_ast:: AstBuilder :: new ( & allocator) ;
152+ let ast_builder = oxc_ast:: AstBuilder :: new ( & shared_allocator) ;
151153 let identifier = if is_function {
152154 expression_to_code ( & wrap_direct_call (
153155 & ast_builder,
@@ -191,8 +193,7 @@ pub fn css_to_style_literal<'a>(
191193 | oxc_ast:: ast:: Expression :: FunctionExpression ( _)
192194 ) ;
193195
194- let allocator = Allocator :: default ( ) ;
195- let ast_builder = oxc_ast:: AstBuilder :: new ( & allocator) ;
196+ let ast_builder = oxc_ast:: AstBuilder :: new ( & shared_allocator) ;
196197 let expr_code = if is_function {
197198 expression_to_code ( & wrap_direct_call (
198199 & ast_builder,
@@ -460,44 +461,74 @@ pub fn keyframes_to_keyframes_style(keyframes: &str) -> BTreeMap<String, Vec<Ext
460461}
461462
462463pub fn optimize_css_block ( css : & str ) -> String {
463- rm_css_comment ( css)
464- . split ( "{" )
465- . map ( |s| s. trim ( ) . to_string ( ) )
466- . collect :: < Vec < String > > ( )
467- . join ( "{" )
468- . split ( "}" )
469- . map ( |s| s. trim ( ) . to_string ( ) )
470- . collect :: < Vec < String > > ( )
471- . join ( "}" )
472- . split ( ";" )
473- . map ( |s| {
474- let parts = s. split ( "{" ) . collect :: < Vec < & str > > ( ) ;
475- let first_part = if parts. len ( ) == 1 {
476- "" . to_string ( )
477- } else {
478- format ! ( "{}{{" , parts. first( ) . unwrap( ) . trim( ) )
479- } ;
480- let last_part = parts. last ( ) . unwrap ( ) . trim ( ) ;
481- if !last_part. contains ( ":" ) {
482- format ! ( "{first_part}{last_part}" )
483- } else {
484- let mut iter = last_part. split ( ":" ) ;
485- let property = iter. next ( ) . unwrap ( ) . trim ( ) ;
486- let value = iter. next ( ) . unwrap ( ) . trim ( ) ;
464+ // First pass: remove comments and normalize whitespace around structural chars
465+ let cleaned = rm_css_comment ( css) ;
466+
467+ // Second pass: trim around {, }, ; and optimize declarations in one go
468+ let mut result = String :: with_capacity ( cleaned. len ( ) ) ;
469+ // Split by ; then process, preserving { and }
470+ let trimmed = {
471+ let mut s = String :: with_capacity ( cleaned. len ( ) ) ;
472+ for part in cleaned. split ( '{' ) {
473+ if !s. is_empty ( ) {
474+ s. push ( '{' ) ;
475+ }
476+ s. push_str ( part. trim ( ) ) ;
477+ }
478+ let mut s2 = String :: with_capacity ( s. len ( ) ) ;
479+ for part in s. split ( '}' ) {
480+ if !s2. is_empty ( ) {
481+ s2. push ( '}' ) ;
482+ }
483+ s2. push_str ( part. trim ( ) ) ;
484+ }
485+ s2
486+ } ;
487487
488- let value = if check_multi_css_optimize ( property. split ( "{" ) . last ( ) . unwrap ( ) ) {
488+ let segments: Vec < & str > = trimmed. split ( ';' ) . collect ( ) ;
489+ for ( i, s) in segments. iter ( ) . enumerate ( ) {
490+ if i > 0 {
491+ result. push ( ';' ) ;
492+ }
493+ let parts: Vec < & str > = s. split ( '{' ) . collect ( ) ;
494+ let first_part_str = if parts. len ( ) > 1 {
495+ parts[ ..parts. len ( ) - 1 ]
496+ . iter ( )
497+ . map ( |p| p. trim ( ) )
498+ . collect :: < Vec < _ > > ( )
499+ . join ( "{" )
500+ + "{"
501+ } else {
502+ String :: new ( )
503+ } ;
504+ let last_part = parts. last ( ) . unwrap ( ) . trim ( ) ;
505+ if !last_part. contains ( ':' ) {
506+ result. push_str ( & first_part_str) ;
507+ result. push_str ( last_part) ;
508+ } else {
509+ let mut iter = last_part. split ( ':' ) ;
510+ let property = iter. next ( ) . unwrap ( ) . trim ( ) ;
511+ let value = iter. next ( ) . unwrap ( ) . trim ( ) ;
512+
513+ let optimized_value =
514+ if check_multi_css_optimize ( property. split ( '{' ) . next_back ( ) . unwrap ( ) ) {
489515 optimize_mutli_css_value ( value)
490516 } else {
491517 value. to_string ( )
492518 } ;
493- format ! ( "{first_part}{property}:{value}" )
494- }
495- } )
496- . collect :: < Vec < String > > ( )
497- . join ( ";" )
498- . trim ( )
499- . replace ( ";}" , "}" )
500- . to_string ( )
519+ result. push_str ( & first_part_str) ;
520+ result. push_str ( property) ;
521+ result. push ( ':' ) ;
522+ result. push_str ( & optimized_value) ;
523+ }
524+ }
525+
526+ // Remove trailing ";}" -> "}"
527+ let trimmed_result = result. trim ( ) ;
528+ if trimmed_result. is_empty ( ) {
529+ return String :: new ( ) ;
530+ }
531+ trimmed_result. replace ( ";}" , "}" ) . to_string ( )
501532}
502533
503534#[ cfg( test) ]
0 commit comments