Skip to content

Commit 8f49928

Browse files
committed
Handle unused rules with nesting
1 parent 8b28eed commit 8f49928

File tree

9 files changed

+108
-31
lines changed

9 files changed

+108
-31
lines changed

src/lib.rs

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8308,15 +8308,15 @@ mod tests {
83088308
}
83098309
83108310
&* {
8311-
color: red;
8311+
color: green;
83128312
}
83138313
83148314
&|x {
83158315
color: red;
83168316
}
83178317
83188318
&*|x {
8319-
color: red;
8319+
color: green;
83208320
}
83218321
83228322
&toto|x {
@@ -8333,15 +8333,15 @@ mod tests {
83338333
}
83348334
83358335
*.foo {
8336-
color: red;
8336+
color: green;
83378337
}
83388338
83398339
|x.foo {
83408340
color: red;
83418341
}
83428342
83438343
*|x.foo {
8344-
color: red;
8344+
color: green;
83458345
}
83468346
83478347
toto|x.foo {
@@ -9057,5 +9057,52 @@ mod tests {
90579057
});
90589058
let res = stylesheet.to_css(PrinterOptions::default()).unwrap();
90599059
assert_eq!(res.code, expected);
9060+
9061+
let source = r#"
9062+
.foo {
9063+
color: red;
9064+
9065+
&.bar {
9066+
color: purple;
9067+
}
9068+
9069+
@nest &.bar {
9070+
color: orange;
9071+
}
9072+
9073+
@nest :not(&) {
9074+
color: green;
9075+
}
9076+
9077+
@media (orientation: portrait) {
9078+
color: brown;
9079+
}
9080+
}
9081+
9082+
.x {
9083+
color: purple;
9084+
9085+
&.y {
9086+
color: green;
9087+
}
9088+
}
9089+
"#;
9090+
9091+
let expected = indoc!{r#"
9092+
:not(.foo) {
9093+
color: green;
9094+
}
9095+
"#};
9096+
9097+
let mut stylesheet = StyleSheet::parse("test.css".into(), source, ParserOptions { nesting: true, ..ParserOptions::default() }).unwrap();
9098+
stylesheet.minify(MinifyOptions {
9099+
unused_symbols: vec!["foo", "x"].iter().map(|s| String::from(*s)).collect(),
9100+
..MinifyOptions::default()
9101+
});
9102+
let res = stylesheet.to_css(PrinterOptions {
9103+
targets: Some(Browsers { chrome: Some(95 << 16), ..Browsers::default() }),
9104+
..PrinterOptions::default()
9105+
}).unwrap();
9106+
assert_eq!(res.code, expected);
90609107
}
90619108
}

src/rules/document.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ pub struct MozDocumentRule {
1212

1313
impl MozDocumentRule {
1414
pub(crate) fn minify(&mut self, context: &mut MinifyContext) {
15-
self.rules.minify(context)
15+
self.rules.minify(context, false)
1616
}
1717
}
1818

src/rules/media.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ pub struct MediaRule {
1414
}
1515

1616
impl MediaRule {
17-
pub(crate) fn minify(&mut self, context: &mut MinifyContext) {
18-
self.rules.minify(context)
17+
pub(crate) fn minify(&mut self, context: &mut MinifyContext, parent_is_unused: bool) {
18+
self.rules.minify(context, parent_is_unused);
1919
}
2020
}
2121

src/rules/mod.rs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ pub(crate) struct MinifyContext<'a> {
9494
}
9595

9696
impl CssRuleList {
97-
pub(crate) fn minify(&mut self, context: &mut MinifyContext) {
97+
pub(crate) fn minify(&mut self, context: &mut MinifyContext, parent_is_unused: bool) {
9898
let mut keyframe_rules = HashMap::new();
9999
let mut rules = Vec::new();
100100
for mut rule in self.0.drain(..) {
@@ -130,16 +130,24 @@ impl CssRuleList {
130130
set_prefix!(keyframes);
131131
keyframe_rules.insert(keyframes.name.clone(), rules.len());
132132
},
133-
CssRule::Media(media) => media.minify(context),
134-
CssRule::Supports(supports) => supports.minify(context),
133+
CssRule::Media(media) => {
134+
media.minify(context, parent_is_unused);
135+
if media.rules.0.is_empty() {
136+
continue
137+
}
138+
},
139+
CssRule::Supports(supports) => {
140+
supports.minify(context, parent_is_unused);
141+
if supports.rules.0.is_empty() {
142+
continue
143+
}
144+
},
135145
CssRule::MozDocument(document) => document.minify(context),
136146
CssRule::Style(style) => {
137-
if is_unused(&mut style.selectors.0.iter(), &context.unused_symbols) {
147+
if parent_is_unused || style.minify(context, parent_is_unused) {
138148
continue
139149
}
140150

141-
style.minify(context);
142-
143151
if let Some(targets) = context.targets {
144152
style.vendor_prefix = get_prefix(&style.selectors);
145153
if style.vendor_prefix.contains(VendorPrefix::None) {
@@ -183,7 +191,11 @@ impl CssRuleList {
183191
continue
184192
}
185193
}
186-
CssRule::Nesting(nesting) => nesting.minify(context),
194+
CssRule::Nesting(nesting) => {
195+
if nesting.minify(context, parent_is_unused) {
196+
continue
197+
}
198+
}
187199
_ => {}
188200
}
189201

src/rules/nesting.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ pub struct NestingRule {
1212
}
1313

1414
impl NestingRule {
15-
pub(crate) fn minify(&mut self, context: &mut MinifyContext) {
16-
self.style.minify(context)
15+
pub(crate) fn minify(&mut self, context: &mut MinifyContext, parent_is_unused: bool) -> bool {
16+
self.style.minify(context, parent_is_unused)
1717
}
1818
}
1919

src/rules/style.rs

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::printer::Printer;
66
use crate::declaration::DeclarationBlock;
77
use crate::vendor_prefix::VendorPrefix;
88
use crate::targets::Browsers;
9-
use crate::rules::{CssRuleList, CssRule, ToCssWithContext, StyleContext};
9+
use crate::rules::{CssRuleList, ToCssWithContext, StyleContext};
1010
use crate::compat::Feature;
1111
use crate::error::PrinterError;
1212
use super::MinifyContext;
@@ -21,17 +21,30 @@ pub struct StyleRule {
2121
}
2222

2323
impl StyleRule {
24-
pub(crate) fn minify(&mut self, context: &mut MinifyContext) {
25-
self.declarations.minify(context.handler, context.important_handler, context.logical_properties);
26-
24+
pub(crate) fn minify(&mut self, context: &mut MinifyContext, parent_is_unused: bool) -> bool {
25+
let mut unused = false;
2726
if !context.unused_symbols.is_empty() {
28-
self.rules.0.retain(|rule| {
29-
match rule {
30-
CssRule::Style(style) => !is_unused(&mut style.selectors.0.iter(), &context.unused_symbols),
31-
_ => true
27+
if is_unused(&mut self.selectors.0.iter(), &context.unused_symbols, parent_is_unused) {
28+
if self.rules.0.is_empty() {
29+
return true
3230
}
33-
});
31+
32+
self.declarations.declarations.clear();
33+
self.declarations.important_declarations.clear();
34+
unused = true;
35+
}
36+
}
37+
38+
self.declarations.minify(context.handler, context.important_handler, context.logical_properties);
39+
40+
if !self.rules.0.is_empty() {
41+
self.rules.minify(context, unused);
42+
if unused && self.rules.0.is_empty() {
43+
return true
44+
}
3445
}
46+
47+
false
3548
}
3649

3750
pub fn is_compatible(&self, targets: Option<Browsers>) -> bool {

src/rules/supports.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ pub struct SupportsRule {
1313
}
1414

1515
impl SupportsRule {
16-
pub(crate) fn minify(&mut self, context: &mut MinifyContext) {
17-
self.rules.minify(context)
16+
pub(crate) fn minify(&mut self, context: &mut MinifyContext, parent_is_unused: bool) {
17+
self.rules.minify(context, parent_is_unused)
1818
}
1919
}
2020

src/selector.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1212,11 +1212,11 @@ pub fn get_necessary_prefixes(selectors: &SelectorList<Selectors>, targets: Brow
12121212

12131213
/// Determines whether a selector list contains only unused selectors.
12141214
/// A selector is considered unused if it contains a class or id component that exists in the set of unsed symbols.
1215-
pub fn is_unused(selectors: &mut std::slice::Iter<Selector<Selectors>>, unused_symbols: &HashSet<String>) -> bool {
1215+
pub fn is_unused(selectors: &mut std::slice::Iter<Selector<Selectors>>, unused_symbols: &HashSet<String>, parent_is_unused: bool) -> bool {
12161216
if unused_symbols.is_empty() {
12171217
return false
12181218
}
1219-
1219+
12201220
selectors.all(|selector| {
12211221
for component in selector.iter_raw_match_order() {
12221222
match component {
@@ -1226,10 +1226,15 @@ pub fn is_unused(selectors: &mut std::slice::Iter<Selector<Selectors>>, unused_s
12261226
}
12271227
}
12281228
Component::Is(is) | Component::Where(is) => {
1229-
if is_unused(&mut is.iter(), unused_symbols) {
1229+
if is_unused(&mut is.iter(), unused_symbols, parent_is_unused) {
12301230
return true
12311231
}
12321232
}
1233+
Component::Nesting => {
1234+
if parent_is_unused {
1235+
return true
1236+
}
1237+
},
12331238
_ => {}
12341239
}
12351240
}

src/stylesheet.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ impl StyleSheet {
7777
important_handler: &mut important_handler,
7878
logical_properties: &mut logical_properties,
7979
unused_symbols: &options.unused_symbols
80-
});
80+
}, false);
8181
logical_properties.to_rules(&mut self.rules);
8282
}
8383

0 commit comments

Comments
 (0)