Skip to content

Commit a705c01

Browse files
committed
Optimize
1 parent 4227b1e commit a705c01

5 files changed

Lines changed: 104 additions & 140 deletions

File tree

libs/css/src/optimize_value.rs

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -89,14 +89,14 @@ pub fn optimize_value(value: &str) -> String {
8989
ret = s;
9090
}
9191

92+
let mut lower = ret.to_lowercase();
9293
for f in ZERO_PERCENT_FUNCTION.iter() {
93-
let tmp = ret.to_lowercase();
94-
if tmp.contains(f) {
95-
let index = tmp.find(f).unwrap() + f.len();
94+
if lower.contains(f) {
95+
let index = lower.find(f).unwrap() + f.len();
9696
let mut zero_idx = Vec::with_capacity(4);
9797
let mut depth = 0;
98-
let chars: Vec<char> = tmp.chars().collect();
99-
let byte_indices: Vec<usize> = tmp.char_indices().map(|(i, _)| i).collect();
98+
let chars: Vec<char> = lower.chars().collect();
99+
let byte_indices: Vec<usize> = lower.char_indices().map(|(i, _)| i).collect();
100100

101101
for (char_idx, &ch) in chars.iter().enumerate().skip(index) {
102102
if ch == '(' {
@@ -115,6 +115,8 @@ pub fn optimize_value(value: &str) -> String {
115115
for i in zero_idx.iter().rev() {
116116
ret.replace_range(*i..*i + 1, "0%");
117117
}
118+
// Refresh lowercase after modification for subsequent iterations
119+
lower = ret.to_lowercase();
118120
}
119121
}
120122
}
@@ -181,25 +183,35 @@ fn optimize_color(value: &str) -> String {
181183
let mut ret = value.to_uppercase();
182184

183185
if ret.len() == 6 {
184-
let ch = ret.chars().collect::<Vec<char>>();
185-
if ch[0] == ch[1] && ch[2] == ch[3] && ch[4] == ch[5] {
186-
ret = format!("{}{}{}", ch[0], ch[2], ch[4]);
186+
let b = ret.as_bytes();
187+
if b[0] == b[1] && b[2] == b[3] && b[4] == b[5] {
188+
let (c0, c2, c4) = (b[0], b[2], b[4]);
189+
ret.clear();
190+
ret.push(c0 as char);
191+
ret.push(c2 as char);
192+
ret.push(c4 as char);
187193
}
188194
} else if ret.len() == 8 {
189-
let ch = ret.chars().collect::<Vec<char>>();
190-
if ch[0] == ch[1] && ch[2] == ch[3] && ch[4] == ch[5] && ch[6] == ch[7] {
191-
ret = format!("{}{}{}{}", ch[0], ch[2], ch[4], ch[6]);
192-
if ret.ends_with("F") {
193-
ret = ret[..ret.len() - 1].to_string();
195+
let b = ret.as_bytes();
196+
if b[0] == b[1] && b[2] == b[3] && b[4] == b[5] && b[6] == b[7] {
197+
let (c0, c2, c4, c6) = (b[0], b[2], b[4], b[6]);
198+
let has_trailing_f = c6 == b'F';
199+
ret.clear();
200+
ret.push(c0 as char);
201+
ret.push(c2 as char);
202+
ret.push(c4 as char);
203+
if !has_trailing_f {
204+
ret.push(c6 as char);
194205
}
195206
} else if ret.ends_with("FF") {
196-
ret = ret[..ret.len() - 2].to_string();
207+
ret.truncate(ret.len() - 2);
197208
}
198-
} else if ret.len() == 4 && ret.ends_with("F") {
199-
ret = ret[..ret.len() - 1].to_string();
209+
} else if ret.len() == 4 && ret.ends_with('F') {
210+
ret.truncate(ret.len() - 1);
200211
}
201212

202-
format!("#{ret}")
213+
ret.insert(0, '#');
214+
ret
203215
}
204216

205217
#[cfg(test)]

libs/extractor/src/css_utils.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ pub fn css_to_style_literal<'a>(
6262
// Build a combined CSS string with unique placeholders for expressions
6363
// Use a format that won't conflict with actual CSS values
6464
let mut css_parts = Vec::new();
65-
let mut expression_map = std::collections::HashMap::new();
65+
let mut expression_map = std::collections::HashMap::with_capacity(css.expressions.len());
6666

6767
for (i, quasi) in css.quasis.iter().enumerate() {
6868
css_parts.push(quasi.value.raw.to_string());

libs/extractor/src/vanilla_extract.rs

Lines changed: 48 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -545,99 +545,62 @@ fn remap_style_names(
545545
// Track the last processed theme's vars_object_json for ThemeVars handling
546546
let mut last_theme_vars_json: Option<String> = None;
547547

548-
// First pass: collect old entries preserving all fields
549-
let old_styles: HashMap<String, StyleEntry> = collected.styles.drain().collect();
550-
let old_keyframes: HashMap<String, StyleEntry> = collected.keyframes.drain().collect();
551-
let old_style_variants: HashMap<String, HashMap<String, StyleVariant>> = collected
552-
.style_variants
553-
.drain()
554-
.map(|(k, v)| (k, v.0))
555-
.collect();
556-
let old_vars: HashMap<String, String> = collected.vars.drain().map(|(k, v)| (k, v.0)).collect();
557-
let old_containers: HashMap<String, String> = collected
558-
.containers
559-
.drain()
560-
.map(|(k, v)| (k, v.0))
561-
.collect();
562-
let old_layers: HashMap<String, String> =
563-
collected.layers.drain().map(|(k, v)| (k, v.0)).collect();
564-
// font_faces: placeholder_id -> (json, font_family, exported)
565-
let old_font_faces: HashMap<String, (String, String)> = collected
566-
.font_faces
567-
.drain()
568-
.map(|(k, v)| (k, (v.0, v.1)))
569-
.collect();
570-
// global_themes: placeholder_id -> GlobalThemeEntry (without exported flag for remapping)
571-
let old_global_themes: HashMap<String, GlobalThemeEntry> =
572-
collected.global_themes.drain().collect();
573-
// themes: placeholder_id -> ThemeEntry (without exported flag for remapping)
574-
let old_themes: HashMap<String, ThemeEntry> = collected.themes.drain().collect();
548+
// First pass: take ownership of old entries (avoids drain+collect overhead)
549+
let mut old_styles = std::mem::take(&mut collected.styles);
550+
let mut old_keyframes = std::mem::take(&mut collected.keyframes);
551+
let mut old_style_variants = std::mem::take(&mut collected.style_variants);
552+
let mut old_vars = std::mem::take(&mut collected.vars);
553+
let mut old_containers = std::mem::take(&mut collected.containers);
554+
let mut old_layers = std::mem::take(&mut collected.layers);
555+
let mut old_font_faces = std::mem::take(&mut collected.font_faces);
556+
let mut old_global_themes = std::mem::take(&mut collected.global_themes);
557+
let mut old_themes = std::mem::take(&mut collected.themes);
575558

576559
for (name, info) in vars {
577560
match info {
578561
VarInfo::StyleApi { exported } => {
579562
// First check if this is a fontFace (uses __font_N__ placeholder)
580563
let font_placeholder = format!("__font_{}__", font_idx);
581-
if let Some((json, font_family)) = old_font_faces.get(&font_placeholder) {
582-
font_placeholder_to_name.insert(font_placeholder.clone(), name.clone());
583-
new_font_faces
584-
.insert(name.clone(), (json.clone(), font_family.clone(), *exported));
564+
if let Some((json, font_family, _)) = old_font_faces.remove(&font_placeholder) {
565+
font_placeholder_to_name.insert(font_placeholder, name.clone());
566+
new_font_faces.insert(name.clone(), (json, font_family, *exported));
585567
font_idx += 1;
586568
continue;
587569
}
588570

589571
// Check if this is a createGlobalTheme (uses __global_theme_N__ placeholder)
590572
let global_theme_placeholder = format!("__global_theme_{}__", global_theme_idx);
591-
if let Some(entry) = old_global_themes.get(&global_theme_placeholder) {
592-
new_global_themes.insert(
593-
name.clone(),
594-
GlobalThemeEntry {
595-
selector: entry.selector.clone(),
596-
css_vars: entry.css_vars.clone(),
597-
vars_object_json: entry.vars_object_json.clone(),
598-
exported: *exported,
599-
},
600-
);
573+
if let Some(mut entry) = old_global_themes.remove(&global_theme_placeholder) {
574+
entry.exported = *exported;
575+
new_global_themes.insert(name.clone(), entry);
601576
global_theme_idx += 1;
602577
continue;
603578
}
604579

605580
let placeholder = format!("__style_{}__", style_idx);
606581
placeholder_to_name.insert(placeholder.clone(), name.clone());
607582

608-
if let Some(entry) = old_styles.get(&placeholder) {
609-
new_styles.insert(
610-
name.clone(),
611-
StyleEntry {
612-
json: entry.json.clone(),
613-
exported: *exported,
614-
bases: entry.bases.clone(),
615-
},
616-
);
583+
if let Some(mut entry) = old_styles.remove(&placeholder) {
584+
entry.exported = *exported;
585+
new_styles.insert(name.clone(), entry);
617586
style_idx += 1;
618-
} else if let Some(entry) = old_keyframes.get(&placeholder) {
619-
new_keyframes.insert(
620-
name.clone(),
621-
StyleEntry {
622-
json: entry.json.clone(),
623-
exported: *exported,
624-
bases: entry.bases.clone(),
625-
},
626-
);
587+
} else if let Some(mut entry) = old_keyframes.remove(&placeholder) {
588+
entry.exported = *exported;
589+
new_keyframes.insert(name.clone(), entry);
627590
style_idx += 1;
628-
} else if let Some(variants) = old_style_variants.get(&placeholder) {
629-
new_style_variants.insert(name.clone(), (variants.clone(), *exported));
591+
} else if let Some((variants, _)) = old_style_variants.remove(&placeholder) {
592+
new_style_variants.insert(name.clone(), (variants, *exported));
630593
style_idx += 1;
631-
} else if let Some(value) = old_vars.get(&placeholder) {
632-
new_vars.insert(name.clone(), (value.clone(), *exported));
594+
} else if let Some((value, _)) = old_vars.remove(&placeholder) {
595+
new_vars.insert(name.clone(), (value, *exported));
633596
style_idx += 1;
634-
} else if let Some(value) = old_containers.get(&placeholder) {
635-
new_containers.insert(name.clone(), (value.clone(), *exported));
597+
} else if let Some((value, _)) = old_containers.remove(&placeholder) {
598+
new_containers.insert(name.clone(), (value, *exported));
636599
style_idx += 1;
637-
} else if let Some(value) = old_layers.get(&placeholder) {
638-
new_layers.insert(name.clone(), (value.clone(), *exported));
600+
} else if let Some((value, _)) = old_layers.remove(&placeholder) {
601+
new_layers.insert(name.clone(), (value, *exported));
639602
style_idx += 1;
640-
} else if let Some(entry) = old_themes.get(&placeholder) {
603+
} else if let Some(mut entry) = old_themes.remove(&placeholder) {
641604
// Track this theme name for the next ThemeVars entry
642605
if entry.vars_object_json.is_some() {
643606
last_theme_vars_json = Some(name.clone());
@@ -662,16 +625,10 @@ fn remap_style_names(
662625
.push((format!(".{}", class_name), vars_json));
663626
}
664627

665-
new_themes.insert(
666-
name.clone(),
667-
ThemeEntry {
668-
css_vars: entry.css_vars.clone(),
669-
exported: *exported,
670-
vars_object_json: entry.vars_object_json.clone(),
671-
vars_name: None, // Will be set by ThemeVars if present
672-
class_name,
673-
},
674-
);
628+
entry.exported = *exported;
629+
entry.vars_name = None;
630+
entry.class_name = class_name;
631+
new_themes.insert(name.clone(), entry);
675632
style_idx += 1;
676633
}
677634
}
@@ -705,30 +662,27 @@ fn remap_style_names(
705662

706663
// Remap base references in styles (for composition)
707664
for entry in new_styles.values_mut() {
708-
entry.bases = entry
709-
.bases
710-
.iter()
665+
let old_bases = std::mem::take(&mut entry.bases);
666+
entry.bases = old_bases
667+
.into_iter()
711668
.map(|b| {
712-
placeholder_to_name
713-
.get(b)
714-
.cloned()
715-
.unwrap_or_else(|| b.clone())
669+
if let Some(name) = placeholder_to_name.get(&b) {
670+
name.clone()
671+
} else {
672+
b
673+
}
716674
})
717675
.collect();
718676
}
719677

720678
// Replace font placeholders in style JSONs with actual font-family names
721679
// Build a mapping from placeholder to font-family name
722-
let font_family_map: HashMap<String, String> = new_font_faces
680+
let font_family_map: HashMap<&str, &str> = font_placeholder_to_name
723681
.iter()
724-
.map(|(name, (_, font_family, _))| {
725-
// Find the placeholder that maps to this name
726-
let placeholder = font_placeholder_to_name
727-
.iter()
728-
.find(|(_, n)| *n == name)
729-
.map(|(p, _)| p.clone())
730-
.unwrap_or_default();
731-
(placeholder, font_family.clone())
682+
.filter_map(|(placeholder, name)| {
683+
new_font_faces
684+
.get(name)
685+
.map(|(_, font_family, _)| (placeholder.as_str(), font_family.as_str()))
732686
})
733687
.collect();
734688

libs/sheet/src/lib.rs

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -294,24 +294,21 @@ impl StyleSheet {
294294
self.font_faces.remove(file);
295295
let property_key = if single_css { "" } else { file }.to_string();
296296

297-
for map in self
298-
.properties
299-
.entry(property_key.clone())
300-
.or_default()
301-
.values_mut()
302-
{
303-
for props in map.values_mut() {
304-
props.retain(|prop| {
305-
if let Some(StyleSelector::Global(_, f)) = prop.selector.as_ref() {
306-
f != file
307-
} else {
308-
true
309-
}
310-
});
311-
}
312-
// remove empty map
313-
if map.iter().all(|(_, v)| v.is_empty()) {
314-
map.clear();
297+
if let Some(prop_map) = self.properties.get_mut(&property_key) {
298+
for map in prop_map.values_mut() {
299+
for props in map.values_mut() {
300+
props.retain(|prop| {
301+
if let Some(StyleSelector::Global(_, f)) = prop.selector.as_ref() {
302+
f != file
303+
} else {
304+
true
305+
}
306+
});
307+
}
308+
// remove empty map
309+
if map.iter().all(|(_, v)| v.is_empty()) {
310+
map.clear();
311+
}
315312
}
316313
}
317314
if self
@@ -551,14 +548,14 @@ impl StyleSheet {
551548
let mut selector_map: BTreeMap<_, Vec<_>> = BTreeMap::new();
552549
for prop in non_layered_props {
553550
if let Some(StyleSelector::Global(selector, _)) = &prop.selector {
554-
selector_map.entry(selector.clone()).or_default().push(prop);
551+
selector_map.entry(selector).or_default().push(prop);
555552
}
556553
}
557554
if let Some(break_point) = break_point {
558555
write!(current_css, "@media(min-width:{break_point}px){{").unwrap();
559556
}
560557
for (selector, props) in selector_map {
561-
current_css.push_str(&selector);
558+
current_css.push_str(selector);
562559
current_css.push('{');
563560
let mut first = true;
564561
for prop in props {

libs/sheet/src/theme.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use css::optimize_value::optimize_value;
22
use serde::{Deserialize, Deserializer, Serialize};
33
use serde_json::Value;
44
use std::collections::{BTreeMap, HashMap};
5+
use std::fmt::Write;
56

67
/// ColorEntry stores both the original key (for TypeScript interface) and CSS key (for CSS variables)
78
#[derive(Debug, Clone, Serialize)]
@@ -428,10 +429,10 @@ impl Theme {
428429
Some(theme_name)
429430
};
430431
if let Some(theme_key) = theme_key {
431-
theme_declaration.push_str(format!(":root[data-theme={theme_key}]{{").as_str());
432+
write!(theme_declaration, ":root[data-theme={theme_key}]{{").unwrap();
432433
css_contents.push("color-scheme:dark".to_string());
433434
} else {
434-
theme_declaration.push_str(":root{".to_string().as_str());
435+
theme_declaration.push_str(":root{");
435436
if !single_theme {
436437
css_contents.push("color-scheme:light".to_string());
437438
}
@@ -496,23 +497,23 @@ impl Theme {
496497
t.font_family
497498
.as_ref()
498499
.map(|v| format!("font-family:{}", optimize_value(v)))
499-
.unwrap_or("".to_string()),
500+
.unwrap_or_default(),
500501
t.font_size
501502
.as_ref()
502503
.map(|v| format!("font-size:{}", optimize_value(v)))
503-
.unwrap_or("".to_string()),
504+
.unwrap_or_default(),
504505
t.font_weight
505506
.as_ref()
506507
.map(|v| format!("font-weight:{}", optimize_value(v)))
507-
.unwrap_or("".to_string()),
508+
.unwrap_or_default(),
508509
t.line_height
509510
.as_ref()
510511
.map(|v| format!("line-height:{}", optimize_value(v)))
511-
.unwrap_or("".to_string()),
512+
.unwrap_or_default(),
512513
t.letter_spacing
513514
.as_ref()
514515
.map(|v| format!("letter-spacing:{}", optimize_value(v)))
515-
.unwrap_or("".to_string()),
516+
.unwrap_or_default(),
516517
]
517518
.iter()
518519
.filter_map(|v| {
@@ -539,7 +540,7 @@ impl Theme {
539540
.get(level as usize)
540541
.map(|v| format!("(min-width:{v}px)"))
541542
{
542-
css.push_str(format!("@media{media}{{{}}}", css_vec.join("")).as_str());
543+
css.push_str(&format!("@media{media}{{{}}}", css_vec.join("")));
543544
}
544545
}
545546
css

0 commit comments

Comments
 (0)