1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
use super::{CssStyleProcessor, FragmentVisitor, SelectorFragment};
use cssparser::ToCss;
use lightningcss::{
    selector::Selector,
    stylesheet::{ParserOptions, PrinterOptions},
    visit_types,
    visitor::{Visit, VisitTypes},
};
use parcel_selectors::parser::Component;

pub struct Preprocessor<'i> {
    style: lightningcss::stylesheet::StyleSheet<'i, 'i>,
}
impl SelectorFragment for Selector<'_> {
    fn append_new_class(&mut self, class: &str) {
        let class = class.to_string();
        self.append(Component::Class(class.into()))
    }
}

impl<'i> CssStyleProcessor<'i> for Preprocessor<'i> {
    type Fragment = Selector<'i>;

    fn load_style(style: &'i str) -> Self {
        let style =
            lightningcss::stylesheet::StyleSheet::parse(style, ParserOptions::default()).unwrap();
        Self { style }
    }

    fn visit_modify<F>(&mut self, visitor: F)
    where
        F: FragmentVisitor<Fragment = Self::Fragment>,
    {
        struct LightningVisitor<F> {
            visitor: F,
        }
        impl<'i, F> lightningcss::visitor::Visitor<'i> for LightningVisitor<F>
        where
            F: FragmentVisitor<Fragment = Selector<'i>>,
        {
            type Error = ();
            fn visit_types(&self) -> VisitTypes {
                visit_types!(SELECTORS)
            }
            fn visit_selector(&mut self, fragment: &mut Selector<'i>) -> Result<(), Self::Error> {
                self.visitor.visit_selector_fragment(fragment);

                for component in fragment.iter_mut_raw_match_order() {
                    let Component::Class(ref mut class) = component else {
                        continue;
                    };
                    let source: String = class.to_css_string();
                    let mut cloned = source.clone();
                    self.visitor.visit_each_class(&mut cloned);
                    if cloned != source {
                        let result: cssparser::CowRcStr = cloned.into();
                        *class = result.into();
                    }
                }
                Ok(())
            }
        }
        self.style.visit(&mut LightningVisitor { visitor }).unwrap();
    }

    fn to_string(&self) -> String {
        let options = PrinterOptions {
            minify: true,
            ..Default::default()
        };

        self.style.to_css(options).unwrap().code
    }
}