swc_css_lints/rules/
unit_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 UnitNoUnknownConfig {
14    ignore_units: Option<Vec<String>>,
15}
16
17pub fn unit_no_unknown(
18    ctx: LintRuleContext<UnitNoUnknownConfig>,
19) -> Result<Box<dyn LintRule>, ConfigError> {
20    let ignored_units = ctx
21        .config()
22        .ignore_units
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        UnitNoUnknown { ctx, ignored_units },
31    ))
32}
33
34#[derive(Debug, Default)]
35struct UnitNoUnknown {
36    ctx: LintRuleContext<UnitNoUnknownConfig>,
37    ignored_units: Vec<NamePattern>,
38}
39
40impl Visit for UnitNoUnknown {
41    fn visit_unknown_dimension(&mut self, unknown_dimension: &UnknownDimension) {
42        let unit = &unknown_dimension.unit.value;
43
44        if self.ignored_units.iter().all(|item| !item.is_match(unit)) {
45            let message = format!("Unexpected unknown unit \"{unit}\".");
46            self.ctx.report(&unknown_dimension.unit, message);
47        }
48
49        unknown_dimension.visit_children_with(self);
50    }
51
52    fn visit_component_value(&mut self, component_value: &ComponentValue) {
53        if let Some(token_and_span) = component_value.as_preserved_token() {
54            if let Some(unit) = match &token_and_span.token {
55                Token::Dimension {
56                    dimension: dimension_token,
57                } => Some(dimension_token.unit.as_ref()),
58                _ => None,
59            } {
60                if self.ignored_units.iter().all(|item| !item.is_match(unit)) {
61                    let message = format!("Unexpected unknown unit \"{unit}\".");
62                    self.ctx.report(token_and_span, message);
63                }
64            }
65        }
66
67        component_value.visit_children_with(self);
68    }
69}