swc_css_lints/rules/
selector_max_combinators.rs

1use swc_css_ast::*;
2use swc_css_visit::{Visit, VisitWith};
3
4use crate::rule::{visitor_rule, LintRule, LintRuleContext};
5
6pub type SelectorMaxCombinatorsConfig = Option<usize>;
7
8pub fn selector_max_combinators(
9    ctx: LintRuleContext<SelectorMaxCombinatorsConfig>,
10) -> Box<dyn LintRule> {
11    let max = ctx.config().unwrap_or(3);
12    visitor_rule(ctx.reaction(), SelectorMaxCombinators { ctx, max })
13}
14
15#[derive(Debug, Default)]
16struct SelectorMaxCombinators {
17    ctx: LintRuleContext<SelectorMaxCombinatorsConfig>,
18    max: usize,
19}
20
21impl SelectorMaxCombinators {
22    fn build_message(&self, count: usize) -> String {
23        let combinators = if self.max == 1 {
24            "combinator"
25        } else {
26            "combinators"
27        };
28        format!(
29            "Expected selector to have no more than {} {}, but {} actually.",
30            self.max, combinators, count
31        )
32    }
33}
34
35impl Visit for SelectorMaxCombinators {
36    fn visit_complex_selector(&mut self, complex_selector: &ComplexSelector) {
37        let count = complex_selector
38            .children
39            .iter()
40            .filter(|child| matches!(child, ComplexSelectorChildren::Combinator(..)))
41            .count();
42
43        if count > self.max {
44            self.ctx.report(complex_selector, self.build_message(count));
45        }
46
47        complex_selector.visit_children_with(self);
48    }
49}