Skip to content

Commit ce8f39b

Browse files
committed
Optimize
1 parent 12ad819 commit ce8f39b

5 files changed

Lines changed: 104 additions & 56 deletions

File tree

libs/extractor/src/component.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,12 @@ impl TryFrom<String> for ExportVariableKind {
9494
type Error = ();
9595

9696
fn try_from(value: String) -> Result<Self, Self::Error> {
97+
ExportVariableKind::from_str(&value)
98+
}
99+
}
100+
101+
impl ExportVariableKind {
102+
pub fn from_str(value: &str) -> Result<Self, ()> {
97103
for kind in ExportVariableKind::iter() {
98104
if kind.to_string() == value {
99105
return Ok(kind);

libs/extractor/src/css_utils.rs

Lines changed: 68 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -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

462463
pub 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)]

libs/extractor/src/util_type.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,20 @@ impl TryFrom<String> for UtilType {
99
type Error = String;
1010

1111
fn try_from(value: String) -> Result<Self, Self::Error> {
12+
UtilType::from_str(&value).map_err(|()| value)
13+
}
14+
}
15+
16+
impl UtilType {
17+
pub fn from_str(value: &str) -> Result<Self, ()> {
1218
if value == "css" {
1319
Ok(UtilType::Css)
1420
} else if value == "globalCss" {
1521
Ok(UtilType::GlobalCss)
1622
} else if value == "keyframes" {
1723
Ok(UtilType::Keyframes)
1824
} else {
19-
Err(value)
25+
Err(())
2026
}
2127
}
2228
}

libs/extractor/src/visit.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -290,12 +290,13 @@ impl<'a> VisitMut<'a> for DevupVisitor<'a> {
290290
&& let Expression::Identifier(ident) = &tag.tag
291291
&& let Some(css_type) = self.util_imports.get(ident.name.as_str())
292292
{
293-
let css_str = tag
294-
.quasi
295-
.quasis
296-
.iter()
297-
.map(|quasi| quasi.value.raw.to_string())
298-
.collect::<String>();
293+
let css_str = {
294+
let mut s = String::new();
295+
for quasi in tag.quasi.quasis.iter() {
296+
s.push_str(quasi.value.raw.as_str());
297+
}
298+
s
299+
};
299300
let r = css_type.as_ref();
300301
*it = if let UtilType::Css = r {
301302
let styles = css_to_style_literal(&tag.quasi, 0, &None);
@@ -362,7 +363,7 @@ impl<'a> VisitMut<'a> for DevupVisitor<'a> {
362363
self.imports.get(ident.name.as_str()).cloned()
363364
} else if let Expression::StaticMemberExpression(member) = expr
364365
&& let Expression::Identifier(ident) = &member.object
365-
&& self.import_object == Some(ident.name.to_string())
366+
&& self.import_object.as_deref() == Some(ident.name.as_str())
366367
{
367368
ExportVariableKind::try_from(member.property.name.to_string()).ok()
368369
} else {
@@ -570,15 +571,16 @@ impl<'a> VisitMut<'a> for DevupVisitor<'a> {
570571
for i in (0..specifiers.len()).rev() {
571572
match &specifiers[i] {
572573
ImportSpecifier(import) => {
573-
if let Ok(kind) = ExportVariableKind::try_from(import.imported.to_string())
574+
let imported_str = import.imported.to_string();
575+
if let Ok(kind) = ExportVariableKind::from_str(&imported_str)
574576
{
575577
self.imports.insert(import.local.to_string(), kind);
576578
specifiers.remove(i);
577-
} else if let Ok(kind) = UtilType::try_from(import.imported.to_string()) {
579+
} else if let Ok(kind) = UtilType::from_str(&imported_str) {
578580
self.util_imports
579581
.insert(import.local.to_string(), Rc::new(kind));
580582
specifiers.remove(i);
581-
} else if import.imported.to_string() == "styled" {
583+
} else if imported_str == "styled" {
582584
self.styled_import = Some(import.local.to_string());
583585
specifiers.remove(i);
584586
}

libs/sheet/src/lib.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use css::{
88
use extractor::extract_style::ExtractStyleProperty;
99
use extractor::extract_style::extract_style_value::ExtractStyleValue;
1010
use extractor::extract_style::style_property::StyleProperty;
11+
use std::borrow::Cow;
1112
use std::sync::LazyLock;
1213
use regex::Regex;
1314
use serde::de::Error;
@@ -90,15 +91,17 @@ fn convert_interface_key(key: &str) -> String {
9091
}
9192
}
9293

93-
fn convert_theme_variable_value(value: &str) -> String {
94-
if value.contains("$") {
95-
VAR_RE
96-
.replace_all(value, |caps: &regex::Captures| {
97-
format!("var(--{})", &caps[0][1..].replace('.', "-"))
98-
})
99-
.to_string()
94+
fn convert_theme_variable_value(value: &str) -> Cow<'_, str> {
95+
if value.contains('$') {
96+
Cow::Owned(
97+
VAR_RE
98+
.replace_all(value, |caps: &regex::Captures| {
99+
format!("var(--{})", &caps[0][1..].replace('.', "-"))
100+
})
101+
.into_owned(),
102+
)
100103
} else {
101-
value.to_string()
104+
Cow::Borrowed(value)
102105
}
103106
}
104107

0 commit comments

Comments
 (0)