swc_css_compat/compiler/
nesting.rs

1use std::iter::once;
2
3use swc_common::{util::take::Take, DUMMY_SP};
4use swc_css_ast::*;
5
6use crate::compiler::Compiler;
7
8impl Compiler {
9    fn process_subclass_selectors(
10        &mut self,
11        prelude: &SelectorList,
12        subclass: &mut Vec<SubclassSelector>,
13    ) {
14        for sel in subclass {
15            if let SubclassSelector::PseudoClass(PseudoClassSelector {
16                children: Some(children),
17                ..
18            }) = sel
19            {
20                for c in children {
21                    if let PseudoClassSelectorChildren::ForgivingSelectorList(c) = c {
22                        let mut selectors = Vec::new();
23
24                        for sel in &mut c.children {
25                            match sel {
26                                ForgivingComplexSelector::ComplexSelector(sel) => {
27                                    selectors.push(sel.clone());
28                                }
29                                ForgivingComplexSelector::ListOfComponentValues(_) => {
30                                    // Abort
31                                    return;
32                                }
33                            }
34                        }
35
36                        self.process_complex_selectors(prelude, &mut selectors);
37
38                        *c = ForgivingSelectorList {
39                            children: selectors
40                                .into_iter()
41                                .map(ForgivingComplexSelector::ComplexSelector)
42                                .collect(),
43                            ..*c
44                        };
45                    }
46                }
47            }
48        }
49    }
50
51    fn process_complex_selectors(
52        &mut self,
53        prelude: &SelectorList,
54        selectors: &mut Vec<ComplexSelector>,
55    ) {
56        let mut new_selectors = Vec::new();
57
58        'complex: for complex in selectors.take() {
59            for compound in &complex.children {
60                match compound {
61                    ComplexSelectorChildren::CompoundSelector(compound) => {
62                        if compound.nesting_selector.is_some() {
63                            for prelude_children in &prelude.children {
64                                let mut new = ComplexSelector {
65                                    span: Default::default(),
66                                    children: Default::default(),
67                                };
68                                for compound in &complex.children {
69                                    match compound {
70                                        ComplexSelectorChildren::CompoundSelector(compound) => {
71                                            self.append_compound(
72                                                prelude,
73                                                &mut new,
74                                                prelude_children,
75                                                compound,
76                                            );
77                                        }
78                                        ComplexSelectorChildren::Combinator(_) => {
79                                            new.children.push(compound.clone());
80                                        }
81                                    }
82                                }
83                                new_selectors.push(new);
84                            }
85                            continue 'complex;
86                        }
87                    }
88                    ComplexSelectorChildren::Combinator(_) => {}
89                }
90            }
91
92            new_selectors.push(complex);
93        }
94
95        *selectors = new_selectors;
96    }
97
98    fn append_compound(
99        &mut self,
100        prelude: &SelectorList,
101        to: &mut ComplexSelector,
102        base: &ComplexSelector,
103        c: &CompoundSelector,
104    ) {
105        if c.nesting_selector.is_some() {
106            let len = base.children.len();
107
108            to.children
109                .extend(
110                    base.children
111                        .iter()
112                        .cloned()
113                        .enumerate()
114                        .map(|(idx, mut children)| {
115                            if idx == len - 1 {
116                                if let ComplexSelectorChildren::CompoundSelector(compound) =
117                                    &mut children
118                                {
119                                    if c.type_selector.is_some() {
120                                        compound.type_selector.clone_from(&c.type_selector);
121                                    }
122
123                                    let mut subclass = c.subclass_selectors.clone();
124
125                                    self.process_subclass_selectors(prelude, &mut subclass);
126
127                                    compound.subclass_selectors.extend(subclass);
128                                }
129                            }
130
131                            children
132                        }),
133                );
134        } else {
135            to.children
136                .push(ComplexSelectorChildren::CompoundSelector(c.clone()));
137        }
138    }
139
140    fn relative_selector_list_to_selector_list(
141        &mut self,
142        base: &SelectorList,
143        relative_selector_list: &RelativeSelectorList,
144    ) -> SelectorList {
145        let mut children = Vec::new();
146
147        for base_complex in &base.children {
148            for relative_selector in &relative_selector_list.children {
149                let mut complex_selector = ComplexSelector {
150                    span: relative_selector.span,
151                    children: Default::default(),
152                };
153
154                let is_non_relative = relative_selector.combinator.is_none()
155                    && relative_selector.selector.children.iter().any(|s| match s {
156                        ComplexSelectorChildren::CompoundSelector(s) => {
157                            s.nesting_selector.is_some()
158                        }
159                        _ => false,
160                    });
161
162                if let Some(combinator) = &relative_selector.combinator {
163                    complex_selector
164                        .children
165                        .extend(base_complex.children.clone());
166                    complex_selector
167                        .children
168                        .push(ComplexSelectorChildren::Combinator(combinator.clone()))
169                } else if !is_non_relative {
170                    complex_selector
171                        .children
172                        .extend(base_complex.children.clone());
173                    complex_selector
174                        .children
175                        .push(ComplexSelectorChildren::Combinator(Combinator {
176                            span: DUMMY_SP,
177                            value: CombinatorValue::Descendant,
178                        }))
179                }
180
181                for relative_complex_selector_children in &relative_selector.selector.children {
182                    match relative_complex_selector_children {
183                        ComplexSelectorChildren::CompoundSelector(compound) => {
184                            self.append_compound(
185                                base,
186                                &mut complex_selector,
187                                base_complex,
188                                compound,
189                            );
190                        }
191                        ComplexSelectorChildren::Combinator(combinator) => {
192                            complex_selector
193                                .children
194                                .push(ComplexSelectorChildren::Combinator(combinator.clone()));
195                        }
196                    }
197                }
198
199                children.push(complex_selector);
200            }
201        }
202
203        SelectorList {
204            span: relative_selector_list.span,
205            children,
206        }
207    }
208
209    /// Prepend current selector
210    fn process_prelude(&mut self, base: &QualifiedRulePrelude, to: &mut QualifiedRulePrelude) {
211        if let (
212            QualifiedRulePrelude::SelectorList(base),
213            QualifiedRulePrelude::RelativeSelectorList(relative_selector_list),
214        ) = (base, &to)
215        {
216            let selector_list =
217                self.relative_selector_list_to_selector_list(base, relative_selector_list);
218
219            *to = QualifiedRulePrelude::SelectorList(selector_list);
220        }
221    }
222
223    pub(crate) fn extract_nested_rules(&mut self, rule: &mut QualifiedRule) -> Vec<Rule> {
224        let mut nested_rules = Vec::new();
225        let mut block_values = Vec::new();
226
227        for value in rule.block.value.take() {
228            match value {
229                ComponentValue::QualifiedRule(mut nested) => {
230                    self.process_prelude(&rule.prelude, &mut nested.prelude);
231
232                    nested_rules.push(Rule::QualifiedRule(nested));
233
234                    continue;
235                }
236                ComponentValue::AtRule(ref at_rule) => {
237                    if let Some(
238                        AtRulePrelude::MediaPrelude(..)
239                        | AtRulePrelude::SupportsPrelude(..)
240                        | AtRulePrelude::ContainerPrelude(..)
241                        | AtRulePrelude::DocumentPrelude(..),
242                    ) = at_rule.prelude.as_deref()
243                    {
244                        if let Some(block) = &at_rule.block {
245                            let mut decls_of_media = Vec::new();
246                            let mut nested_of_media = Vec::new();
247
248                            for n in &block.value {
249                                match n {
250                                    ComponentValue::QualifiedRule(n) => {
251                                        let mut q = n.clone();
252
253                                        self.process_prelude(&rule.prelude, &mut q.prelude);
254
255                                        let rules = self.extract_nested_rules(&mut q);
256
257                                        nested_of_media.extend(
258                                            once(Rule::QualifiedRule(q))
259                                                .chain(rules)
260                                                .map(From::from),
261                                        );
262                                    }
263
264                                    _ => {
265                                        decls_of_media.push(n.clone());
266                                    }
267                                }
268                            }
269
270                            if !decls_of_media.is_empty() {
271                                let rule = Box::new(QualifiedRule {
272                                    span: DUMMY_SP,
273                                    prelude: rule.prelude.clone(),
274                                    block: SimpleBlock {
275                                        value: decls_of_media,
276                                        ..block.clone()
277                                    },
278                                });
279
280                                nested_of_media.insert(0, ComponentValue::QualifiedRule(rule));
281                            }
282
283                            nested_rules.push(Rule::AtRule(Box::new(AtRule {
284                                block: Some(SimpleBlock {
285                                    value: nested_of_media,
286                                    ..block.clone()
287                                }),
288                                ..*at_rule.clone()
289                            })));
290
291                            continue;
292                        }
293                    }
294                }
295                _ => {}
296            }
297
298            block_values.push(value);
299        }
300
301        rule.block.value = block_values;
302
303        nested_rules
304    }
305}