swc_css_minifier/compressor/
media.rs1use 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}