csskit_transform/
css_minifier.rs1use crate::{ReduceColors, ReduceLengths, ReduceTimeUnits, ReduceUrls, transformer};
2use bitmask_enum::bitmask;
3use css_ast::{CssMetadata, Visitable};
4
5transformer!(
6 pub enum CssMinifierFeature[CssMetadata, Visitable] {
8 ReduceColors,
10 ReduceLengths,
12 ReduceTimeUnits,
14 ReduceUrls,
16 }
17);
18
19impl Default for CssMinifierFeature {
20 fn default() -> Self {
21 Self::none()
22 }
23}
24
25#[cfg(test)]
26mod tests {
27 use super::*;
28 use crate::Transformer;
29 use bumpalo::Bump;
30 use css_ast::{CssAtomSet, StyleSheet};
31 use css_lexer::Lexer;
32 use css_parse::{CursorCompactWriteSink, CursorOverlaySink, Parser, ToCursors};
33
34 fn minify(source_text: &str, features: CssMinifierFeature) -> (String, bool) {
35 let bump = Bump::default();
36 let mut transformer = Transformer::new_in(&bump, features, &CssAtomSet::ATOMS, source_text);
37 let lexer = Lexer::new(&CssAtomSet::ATOMS, source_text);
38 let mut parser = Parser::new(&bump, source_text, lexer);
39 let mut result = parser.parse_entirely::<StyleSheet>();
40 let mut output = String::new();
41 if let Some(ref mut node) = result.output {
42 transformer.transform(node);
43 let overlays = transformer.overlays();
44 let changed = transformer.has_changed();
45 {
46 let mut overlay_stream = CursorOverlaySink::new(
47 source_text,
48 &*overlays,
49 CursorCompactWriteSink::new(source_text, &mut output),
50 );
51 result.output.to_cursors(&mut overlay_stream);
52 }
53 (output, changed)
54 } else {
55 panic!("Could not transform output");
56 }
57 }
58
59 #[test]
60 fn test_reduce_lengths_feature() {
61 let input = "body { width: 0px; }";
62 let (output, changed) = minify(input, CssMinifierFeature::ReduceLengths);
63 assert!(changed);
64 assert!(output.contains("width:0"), "Should apply length reduction, got: {}", output);
65 assert!(!output.contains("0px"), "Should not contain 0px, got: {}", output);
66 }
67
68 #[test]
69 fn test_no_features() {
70 let input = "body { width: 0px; }";
71 let (output, changed) = minify(input, CssMinifierFeature::none());
72 assert!(!changed, "Should not make changes with no features enabled");
73 assert!(output.contains("width:0px"));
74 }
75
76 #[test]
77 fn test_changed_flag_accuracy() {
78 let input = "body { width: 10px; }";
79 let (_, changed) = minify(input, CssMinifierFeature::all_bits());
80 assert!(!changed, "Should report no changes when no optimizations apply");
81 }
82}