swc_css_minifier/compressor/
media.rs

1use std::mem::take;
2
3use swc_common::DUMMY_SP;
4use swc_css_ast::*;
5
6use super::Compressor;
7use crate::{
8    compressor::math::{is_calc_function_name, transform_calc_value_into_component_value},
9    util::dedup,
10};
11
12impl Compressor {
13    fn is_first_media_in_parens(&self, media_condition: &MediaCondition) -> bool {
14        if let Some(MediaConditionAllType::MediaInParens(_)) = media_condition.conditions.first() {
15            true
16        } else {
17            false
18        }
19    }
20
21    fn is_first_or_media_type(&self, media_condition: &MediaCondition) -> bool {
22        matches!(
23            media_condition.conditions.get(1),
24            Some(MediaConditionAllType::Or(_))
25        )
26    }
27
28    fn is_first_and_media_type(&self, media_condition: &MediaCondition) -> bool {
29        matches!(
30            media_condition.conditions.get(1),
31            Some(MediaConditionAllType::And(_))
32        )
33    }
34
35    pub(super) fn compress_media_query_list(&mut self, media_query_list: &mut MediaQueryList) {
36        dedup(&mut media_query_list.queries);
37    }
38
39    pub(super) fn compress_media_condition(&mut self, n: &mut MediaCondition) {
40        match n.conditions.get(1) {
41            Some(MediaConditionAllType::Or(_)) => {
42                let need_compress = n.conditions.iter().any(|item| match item {
43                    MediaConditionAllType::MediaInParens(MediaInParens::MediaCondition(
44                        media_condition,
45                    )) if self.is_first_or_media_type(media_condition)
46                        && self.is_first_media_in_parens(media_condition) =>
47                    {
48                        true
49                    }
50                    MediaConditionAllType::Or(media_or) => match &media_or.condition {
51                        MediaInParens::MediaCondition(media_condition)
52                            if self.is_first_or_media_type(media_condition)
53                                && self.is_first_media_in_parens(media_condition) =>
54                        {
55                            true
56                        }
57                        _ => false,
58                    },
59                    _ => false,
60                });
61
62                if !need_compress {
63                    let mut new_conditions = Vec::with_capacity(n.conditions.len());
64
65                    for item in take(&mut n.conditions) {
66                        match item {
67                            MediaConditionAllType::MediaInParens(
68                                MediaInParens::MediaCondition(media_condition),
69                            ) if self.is_first_or_media_type(&media_condition)
70                                && self.is_first_media_in_parens(&media_condition) =>
71                            {
72                                let mut iter = media_condition.conditions.into_iter();
73
74                                if let Some(MediaConditionAllType::MediaInParens(media_in_parens)) =
75                                    iter.next()
76                                {
77                                    new_conditions.push(MediaConditionAllType::MediaInParens(
78                                        media_in_parens,
79                                    ));
80
81                                    new_conditions.extend(iter);
82                                }
83                            }
84                            MediaConditionAllType::Or(media_or) => match media_or.condition {
85                                MediaInParens::MediaCondition(media_condition)
86                                    if self.is_first_or_media_type(&media_condition)
87                                        && self.is_first_media_in_parens(&media_condition) =>
88                                {
89                                    let mut iter = media_condition.conditions.into_iter();
90
91                                    if let Some(MediaConditionAllType::MediaInParens(
92                                        media_in_parens,
93                                    )) = iter.next()
94                                    {
95                                        new_conditions.push(MediaConditionAllType::Or(MediaOr {
96                                            span: DUMMY_SP,
97                                            keyword: None,
98                                            condition: media_in_parens,
99                                        }));
100
101                                        new_conditions.extend(iter);
102                                    }
103                                }
104                                _ => {
105                                    new_conditions.push(MediaConditionAllType::Or(media_or));
106                                }
107                            },
108                            _ => {
109                                new_conditions.push(item);
110                            }
111                        }
112                    }
113
114                    n.conditions = new_conditions;
115                }
116            }
117            Some(MediaConditionAllType::And(_)) => {
118                let need_compress = n.conditions.iter().any(|item| match item {
119                    MediaConditionAllType::MediaInParens(MediaInParens::MediaCondition(
120                        media_condition,
121                    )) if self.is_first_and_media_type(media_condition)
122                        && self.is_first_media_in_parens(media_condition) =>
123                    {
124                        true
125                    }
126                    MediaConditionAllType::And(media_and) => match &media_and.condition {
127                        MediaInParens::MediaCondition(media_condition)
128                            if self.is_first_and_media_type(media_condition)
129                                && self.is_first_media_in_parens(media_condition) =>
130                        {
131                            true
132                        }
133                        _ => false,
134                    },
135                    _ => false,
136                });
137
138                if !need_compress {
139                    let mut new_conditions = Vec::with_capacity(n.conditions.len());
140
141                    for item in take(&mut n.conditions) {
142                        match item {
143                            MediaConditionAllType::MediaInParens(
144                                MediaInParens::MediaCondition(media_condition),
145                            ) if self.is_first_and_media_type(&media_condition)
146                                && self.is_first_media_in_parens(&media_condition) =>
147                            {
148                                let mut iter = media_condition.conditions.into_iter();
149
150                                if let Some(MediaConditionAllType::MediaInParens(media_in_parens)) =
151                                    iter.next()
152                                {
153                                    new_conditions.push(MediaConditionAllType::MediaInParens(
154                                        media_in_parens,
155                                    ));
156
157                                    new_conditions.extend(iter);
158                                }
159                            }
160                            MediaConditionAllType::And(media_and) => match media_and.condition {
161                                MediaInParens::MediaCondition(media_condition)
162                                    if self.is_first_and_media_type(&media_condition)
163                                        && self.is_first_media_in_parens(&media_condition) =>
164                                {
165                                    let mut iter = media_condition.conditions.into_iter();
166
167                                    if let Some(MediaConditionAllType::MediaInParens(
168                                        media_in_parens,
169                                    )) = iter.next()
170                                    {
171                                        new_conditions.push(MediaConditionAllType::And(MediaAnd {
172                                            span: DUMMY_SP,
173                                            keyword: None,
174                                            condition: media_in_parens,
175                                        }));
176
177                                        new_conditions.extend(iter);
178                                    }
179                                }
180                                _ => {
181                                    new_conditions.push(MediaConditionAllType::And(media_and));
182                                }
183                            },
184                            _ => {
185                                new_conditions.push(item);
186                            }
187                        }
188                    }
189
190                    n.conditions = new_conditions;
191                }
192            }
193            _ => {}
194        }
195
196        dedup(&mut n.conditions);
197    }
198
199    pub(super) fn compress_media_condition_without_or(&mut self, n: &mut MediaConditionWithoutOr) {
200        if let Some(MediaConditionWithoutOrType::And(_)) = n.conditions.get(1) {
201            let need_compress = n.conditions.iter().any(|item| match item {
202                MediaConditionWithoutOrType::MediaInParens(MediaInParens::MediaCondition(
203                    media_condition,
204                )) if self.is_first_and_media_type(media_condition)
205                    && self.is_first_media_in_parens(media_condition) =>
206                {
207                    true
208                }
209                MediaConditionWithoutOrType::And(media_and) => match &media_and.condition {
210                    MediaInParens::MediaCondition(media_condition)
211                        if self.is_first_and_media_type(media_condition)
212                            && self.is_first_media_in_parens(media_condition) =>
213                    {
214                        true
215                    }
216                    _ => false,
217                },
218                _ => false,
219            });
220
221            if !need_compress {
222                let mut new_conditions = Vec::with_capacity(n.conditions.len());
223
224                for item in take(&mut n.conditions) {
225                    match item {
226                        MediaConditionWithoutOrType::MediaInParens(
227                            MediaInParens::MediaCondition(media_condition),
228                        ) if self.is_first_and_media_type(&media_condition)
229                            && self.is_first_media_in_parens(&media_condition) =>
230                        {
231                            let mut iter = media_condition.conditions.into_iter();
232
233                            if let Some(MediaConditionAllType::MediaInParens(media_in_parens)) =
234                                iter.next()
235                            {
236                                new_conditions.push(MediaConditionWithoutOrType::MediaInParens(
237                                    media_in_parens,
238                                ));
239
240                                for new_item in iter {
241                                    match new_item {
242                                        MediaConditionAllType::Not(media_not) => {
243                                            new_conditions
244                                                .push(MediaConditionWithoutOrType::Not(media_not));
245                                        }
246                                        MediaConditionAllType::And(media_and) => {
247                                            new_conditions
248                                                .push(MediaConditionWithoutOrType::And(media_and));
249                                        }
250                                        MediaConditionAllType::MediaInParens(media_in_parens) => {
251                                            new_conditions.push(
252                                                MediaConditionWithoutOrType::MediaInParens(
253                                                    media_in_parens,
254                                                ),
255                                            );
256                                        }
257                                        _ => {
258                                            unreachable!();
259                                        }
260                                    }
261                                }
262                            }
263                        }
264                        MediaConditionWithoutOrType::And(media_and) => match media_and.condition {
265                            MediaInParens::MediaCondition(media_condition)
266                                if self.is_first_and_media_type(&media_condition)
267                                    && self.is_first_media_in_parens(&media_condition) =>
268                            {
269                                let mut iter = media_condition.conditions.into_iter();
270
271                                if let Some(MediaConditionAllType::MediaInParens(media_in_parens)) =
272                                    iter.next()
273                                {
274                                    new_conditions.push(MediaConditionWithoutOrType::And(
275                                        MediaAnd {
276                                            span: DUMMY_SP,
277                                            keyword: None,
278                                            condition: media_in_parens,
279                                        },
280                                    ));
281
282                                    for new_item in iter {
283                                        match new_item {
284                                            MediaConditionAllType::Not(media_not) => {
285                                                new_conditions.push(
286                                                    MediaConditionWithoutOrType::Not(media_not),
287                                                );
288                                            }
289                                            MediaConditionAllType::And(media_and) => {
290                                                new_conditions.push(
291                                                    MediaConditionWithoutOrType::And(media_and),
292                                                );
293                                            }
294                                            MediaConditionAllType::MediaInParens(
295                                                media_in_parens,
296                                            ) => {
297                                                new_conditions.push(
298                                                    MediaConditionWithoutOrType::MediaInParens(
299                                                        media_in_parens,
300                                                    ),
301                                                );
302                                            }
303                                            _ => {
304                                                unreachable!();
305                                            }
306                                        }
307                                    }
308                                }
309                            }
310                            _ => {
311                                new_conditions.push(MediaConditionWithoutOrType::And(media_and));
312                            }
313                        },
314                        _ => {
315                            new_conditions.push(item);
316                        }
317                    }
318                }
319
320                n.conditions = new_conditions;
321            }
322        }
323
324        dedup(&mut n.conditions);
325    }
326
327    pub(super) fn compress_media_in_parens(&mut self, n: &mut MediaInParens) {
328        match n {
329            MediaInParens::MediaCondition(media_condition)
330                if media_condition.conditions.len() == 1 =>
331            {
332                if let Some(MediaConditionAllType::MediaInParens(media_in_parens)) =
333                    media_condition.conditions.first()
334                {
335                    *n = media_in_parens.clone();
336                }
337            }
338            _ => {}
339        }
340    }
341
342    pub(super) fn compress_calc_sum_in_media_feature_value(&mut self, n: &mut MediaFeatureValue) {
343        match n {
344            MediaFeatureValue::Function(Function { name, value, .. })
345                if is_calc_function_name(name) && value.len() == 1 =>
346            {
347                match &value[0] {
348                    ComponentValue::CalcSum(calc_sum) if calc_sum.expressions.len() == 1 => {
349                        match &calc_sum.expressions[0] {
350                            CalcProductOrOperator::Product(CalcProduct {
351                                expressions: calc_product_expressions,
352                                ..
353                            }) if calc_product_expressions.len() == 1 => {
354                                if let CalcValueOrOperator::Value(calc_value) =
355                                    &calc_product_expressions[0]
356                                {
357                                    match transform_calc_value_into_component_value(calc_value) {
358                                        Some(ComponentValue::Function(function)) => {
359                                            *n = MediaFeatureValue::Function(*function);
360                                        }
361                                        Some(ComponentValue::Dimension(dimension)) => {
362                                            *n = MediaFeatureValue::Dimension(*dimension);
363                                        }
364                                        Some(ComponentValue::Number(number)) => {
365                                            *n = MediaFeatureValue::Number(*number);
366                                        }
367                                        _ => {}
368                                    }
369                                }
370                            }
371                            _ => {}
372                        }
373                    }
374                    _ => {}
375                }
376            }
377            _ => {}
378        }
379    }
380
381    pub(super) fn compress_media_feature_value_length(&mut self, n: &mut MediaFeatureValue) {
382        if let MediaFeatureValue::Dimension(Dimension::Length(length)) = n {
383            if let Some(number) = self.length_to_zero(length) {
384                *n = MediaFeatureValue::Number(number)
385            }
386        }
387    }
388
389    pub(super) fn compress_media_feature(&mut self, n: &mut MediaFeature) {
390        match n {
391            MediaFeature::Plain(MediaFeaturePlain {
392                span,
393                name: MediaFeatureName::Ident(name),
394                value,
395            }) => {
396                if matches!(
397                    &*name.value,
398                    "min-color" | "min-color-index" | "min-monochrome"
399                ) && value
400                    .as_number()
401                    .map(|value| value.value == 1.0)
402                    .unwrap_or_default()
403                {
404                    *n = MediaFeature::Boolean(MediaFeatureBoolean {
405                        span: *span,
406                        name: MediaFeatureName::Ident(Ident {
407                            span: name.span,
408                            value: (*name.value).chars().skip(4).collect::<String>().into(),
409                            raw: None,
410                        }),
411                    });
412                }
413            }
414            MediaFeature::Range(range) => {
415                if let MediaFeatureValue::Ident(name) = &*range.left {
416                    if matches!(&*name.value, "color" | "color-index" | "monochrome")
417                        && matches!(&*range.right, MediaFeatureValue::Number(number) if number.value == 1.0)
418                        && range.comparison == MediaFeatureRangeComparison::Ge
419                    {
420                        *n = MediaFeature::Boolean(MediaFeatureBoolean {
421                            span: range.span,
422                            name: MediaFeatureName::Ident(name.clone()),
423                        });
424                    } else if range.comparison == MediaFeatureRangeComparison::Eq {
425                        *n = MediaFeature::Plain(MediaFeaturePlain {
426                            span: range.span,
427                            name: MediaFeatureName::Ident(name.clone()),
428                            value: range.right.clone(),
429                        });
430                    }
431                } else if let MediaFeatureValue::Ident(name) = &*range.right {
432                    if matches!(&*name.value, "color" | "color-index" | "monochrome")
433                        && matches!(&*range.left, MediaFeatureValue::Number(number) if number.value == 1.0)
434                        && range.comparison == MediaFeatureRangeComparison::Le
435                    {
436                        *n = MediaFeature::Boolean(MediaFeatureBoolean {
437                            span: range.span,
438                            name: MediaFeatureName::Ident(name.clone()),
439                        });
440                    } else if range.comparison == MediaFeatureRangeComparison::Eq {
441                        *n = MediaFeature::Plain(MediaFeaturePlain {
442                            span: range.span,
443                            name: MediaFeatureName::Ident(name.clone()),
444                            value: range.left.clone(),
445                        });
446                    }
447                }
448            }
449            _ => {}
450        }
451    }
452}