From ee1bb3da03c42144080c2b44317aa5835eef2c22 Mon Sep 17 00:00:00 2001 From: qsliu Date: Sat, 9 Mar 2024 22:21:25 +0800 Subject: [PATCH] minify selectors --- selectors/parser.rs | 23 +++++++++++++++++++++++ src/lib.rs | 12 ++++++++++++ src/rules/style.rs | 2 ++ 3 files changed, 37 insertions(+) diff --git a/selectors/parser.rs b/selectors/parser.rs index 25b1445b..2ed1627a 100644 --- a/selectors/parser.rs +++ b/selectors/parser.rs @@ -17,6 +17,7 @@ use cssparser::{Parser as CssParser, ToCss, Token}; use precomputed_hash::PrecomputedHash; use smallvec::{smallvec, SmallVec}; use std::borrow::Borrow; +use std::collections::HashSet; use std::fmt::{self, Debug}; use std::iter::Rev; use std::slice; @@ -540,6 +541,28 @@ impl<'i, Impl: SelectorImpl<'i>> SelectorList<'i, Impl> { } } + pub fn minify(&mut self) { + self + .0 + .iter() + .enumerate() + .fold( + (HashSet::with_capacity(self.0.len()), Vec::with_capacity(self.0.len())), + |(mut seen, mut to_remove), (i, selector)| { + if !seen.insert(selector.to_css_string()) { + to_remove.push(i); + } + (seen, to_remove) + }, + ) + .1 + .into_iter() + .rev() + .for_each(|i| { + self.0.remove(i); + }); + } + /// Creates a new SelectorList. pub fn new(v: SmallVec<[Selector<'i, Impl>; 1]>) -> Self { SelectorList(v) diff --git a/src/lib.rs b/src/lib.rs index 8c70f8ad..1c61205c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8872,6 +8872,18 @@ mod tests { #[test] fn test_merge_rules() { + test( + r#" + .foo, .bar, .foo { + color: red; + } + "#, + indoc! {r#" + .foo, .bar { + color: red; + } + "#}, + ); test( r#" .foo { diff --git a/src/rules/style.rs b/src/rules/style.rs index 63d8ed7d..e10397e5 100644 --- a/src/rules/style.rs +++ b/src/rules/style.rs @@ -56,6 +56,8 @@ impl<'i, T: Clone> StyleRule<'i, T> { context: &mut MinifyContext<'_, 'i>, parent_is_unused: bool, ) -> Result { + self.selectors.minify(); + let mut unused = false; if !context.unused_symbols.is_empty() { if is_unused(&mut self.selectors.0.iter(), &context.unused_symbols, parent_is_unused) {