swc_css_lints/rules/
selector_max_class.rs1use swc_css_ast::*;
2use swc_css_visit::{Visit, VisitWith};
3
4use crate::rule::{visitor_rule, LintRule, LintRuleContext};
5
6pub(crate) type SelectorMaxClassConfig = Option<usize>;
7
8pub fn selector_max_class(ctx: LintRuleContext<SelectorMaxClassConfig>) -> Box<dyn LintRule> {
9 let max = ctx.config().unwrap_or(3);
10 visitor_rule(ctx.reaction(), SelectorMaxClass { ctx, max })
11}
12
13#[derive(Debug, Default)]
14struct SelectorMaxClass {
15 ctx: LintRuleContext<SelectorMaxClassConfig>,
16 max: usize,
17}
18
19impl SelectorMaxClass {
20 fn build_message(&self, count: usize) -> String {
21 let class = if self.max == 1 { "class" } else { "classes" };
22 format!(
23 "Expected selector to have no more than {} {}, but {} actually.",
24 self.max, class, count
25 )
26 }
27}
28
29impl Visit for SelectorMaxClass {
30 fn visit_complex_selector(&mut self, complex_selector: &ComplexSelector) {
31 let count = complex_selector
32 .children
33 .iter()
34 .filter_map(|selector| match selector {
35 ComplexSelectorChildren::CompoundSelector(compound_selector) => {
36 Some(compound_selector)
37 }
38 _ => None,
39 })
40 .flat_map(|selector| {
41 selector
42 .subclass_selectors
43 .iter()
44 .filter(|selector| matches!(selector, SubclassSelector::Class(..)))
45 })
46 .count();
47
48 if count > self.max {
49 let message = self.build_message(count);
50 self.ctx.report(complex_selector, message);
51 }
52
53 complex_selector.visit_children_with(self);
54 }
55}