swc_css_lints/rules/
at_rule_no_unknown.rs

1use serde::{Deserialize, Serialize};
2use swc_css_ast::*;
3use swc_css_visit::{Visit, VisitWith};
4
5use crate::{
6    pattern::NamePattern,
7    rule::{visitor_rule, LintRule, LintRuleContext},
8    ConfigError,
9};
10
11#[derive(Debug, Clone, Default, Serialize, Deserialize)]
12#[serde(rename_all = "camelCase")]
13pub struct AtRuleNoUnknownConfig {
14    ignore_at_rules: Option<Vec<String>>,
15}
16
17pub fn at_rule_no_unknown(
18    ctx: LintRuleContext<AtRuleNoUnknownConfig>,
19) -> Result<Box<dyn LintRule>, ConfigError> {
20    let ignored = ctx
21        .config()
22        .ignore_at_rules
23        .clone()
24        .unwrap_or_default()
25        .into_iter()
26        .map(NamePattern::try_from)
27        .collect::<Result<_, _>>()?;
28    Ok(visitor_rule(
29        ctx.reaction(),
30        AtRuleNoUnknown { ctx, ignored },
31    ))
32}
33
34#[derive(Debug, Default)]
35struct AtRuleNoUnknown {
36    ctx: LintRuleContext<AtRuleNoUnknownConfig>,
37    ignored: Vec<NamePattern>,
38}
39
40impl Visit for AtRuleNoUnknown {
41    fn visit_at_rule(&mut self, at_rule: &AtRule) {
42        if let AtRuleName::Ident(Ident { value, .. }) = &at_rule.name {
43            if let Some(AtRulePrelude::ListOfComponentValues(_)) = at_rule.prelude.as_deref() {
44                if self.ignored.iter().all(|item| !item.is_match(value)) {
45                    let message = format!("Unexpected unknown at-rule \"@{}\".", &value);
46
47                    self.ctx.report(&at_rule.name, message);
48                }
49            }
50        }
51
52        at_rule.visit_children_with(self);
53    }
54}