Skip to content

Commit

Permalink
fix(css/parser): Improve handling of math functions in at-rules (#6140)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-akait committed Oct 14, 2022
1 parent a50b5ae commit 26aeb18
Show file tree
Hide file tree
Showing 23 changed files with 3,133 additions and 722 deletions.
85 changes: 7 additions & 78 deletions crates/swc_css_minifier/src/compressor/calc_sum.rs
@@ -1,15 +1,10 @@
use std::collections::HashMap;

use swc_atoms::{js_word, JsWord};
use swc_atoms::JsWord;
use swc_css_ast::*;

use super::{unit::*, Compressor};

fn is_calc_function_name(ident: &Ident) -> bool {
ident.value.to_ascii_lowercase() == js_word!("calc")
|| ident.value.to_ascii_lowercase() == js_word!("-webkit-calc")
|| ident.value.to_ascii_lowercase() == js_word!("-moz-calc")
}
use crate::compressor::math::{is_calc_function_name, transform_calc_value_into_component_value};

// transform "(simple calc-value)" into "simple calc-value"
fn remove_unnecessary_nesting_from_calc_sum(calc_sum: &mut CalcSum) {
Expand Down Expand Up @@ -50,76 +45,6 @@ fn try_to_extract_into_calc_value(calc_sum: &CalcSum) -> Option<CalcValue> {
None
}

fn transform_calc_value_into_component_value(calc_value: &CalcValue) -> ComponentValue {
match &calc_value {
CalcValue::Number(n) => ComponentValue::Number(n.clone()),
CalcValue::Dimension(Dimension::Length(l)) => {
ComponentValue::Dimension(Dimension::Length(Length {
span: l.span,
value: l.value.clone(),
unit: l.unit.clone(),
}))
}
CalcValue::Dimension(Dimension::Angle(a)) => {
ComponentValue::Dimension(Dimension::Angle(Angle {
span: a.span,
value: a.value.clone(),
unit: a.unit.clone(),
}))
}
CalcValue::Dimension(Dimension::Time(t)) => {
ComponentValue::Dimension(Dimension::Time(Time {
span: t.span,
value: t.value.clone(),
unit: t.unit.clone(),
}))
}
CalcValue::Dimension(Dimension::Frequency(f)) => {
ComponentValue::Dimension(Dimension::Frequency(Frequency {
span: f.span,
value: f.value.clone(),
unit: f.unit.clone(),
}))
}
CalcValue::Dimension(Dimension::Resolution(r)) => {
ComponentValue::Dimension(Dimension::Resolution(Resolution {
span: r.span,
value: r.value.clone(),
unit: r.unit.clone(),
}))
}
CalcValue::Dimension(Dimension::Flex(f)) => {
ComponentValue::Dimension(Dimension::Flex(Flex {
span: f.span,
value: f.value.clone(),
unit: f.unit.clone(),
}))
}
CalcValue::Dimension(Dimension::UnknownDimension(u)) => {
ComponentValue::Dimension(Dimension::UnknownDimension(UnknownDimension {
span: u.span,
value: u.value.clone(),
unit: u.unit.clone(),
}))
}
CalcValue::Percentage(p) => ComponentValue::Percentage(Percentage {
span: p.span,
value: p.value.clone(),
}),
CalcValue::Function(f) => ComponentValue::Function(Function {
span: f.span,
name: f.name.clone(),
value: f.value.to_vec(),
}),
CalcValue::Constant(_) => {
unreachable!("CalcValue::Constant cannot be transformed into a ComponentValue per spec")
}
CalcValue::Sum(_) => {
unreachable!("CalcValue::Sum cannot be transformed into a ComponentValue")
}
}
}

// We want to track the position of data (dimension, percentage, operator...) in
// a Vec<CalcProductOrOperator>
#[derive(Debug, Clone)]
Expand Down Expand Up @@ -1064,7 +989,11 @@ impl Compressor {
// they’re treated like any other
// keyword"
}
CalcValueOrOperator::Value(calc_value) => {
// `calc` and other math functions can be used in `@supports` to
// check availability, we should leave them as is
CalcValueOrOperator::Value(calc_value)
if !self.in_supports_conidition =>
{
*component_value =
transform_calc_value_into_component_value(calc_value);
}
Expand Down
62 changes: 62 additions & 0 deletions crates/swc_css_minifier/src/compressor/container.rs
@@ -0,0 +1,62 @@
use swc_css_ast::*;

use super::Compressor;
use crate::compressor::math::{is_calc_function_name, transform_calc_value_into_component_value};

impl Compressor {
pub(super) fn compress_calc_sum_in_size_feature_value(&mut self, n: &mut SizeFeatureValue) {
match n {
SizeFeatureValue::Function(Function { name, value, .. })
if is_calc_function_name(name) && value.len() == 1 =>
{
match &value[0] {
ComponentValue::CalcSum(CalcSum {
expressions: calc_sum_expressions,
..
}) if calc_sum_expressions.len() == 1 => {
match &calc_sum_expressions[0] {
CalcProductOrOperator::Product(CalcProduct {
expressions: calc_product_expressions,
..
}) if calc_product_expressions.len() == 1 => {
match &calc_product_expressions[0] {
CalcValueOrOperator::Value(CalcValue::Sum(_)) => {
// Do nothing, we cannot transform a
// CalcSum into a ComponentValue
}
CalcValueOrOperator::Value(CalcValue::Constant(_)) => {
// https://www.w3.org/TR/css-values-4/#calc-constants
// "These keywords are only usable
// within a calculation"
// "If used outside of a calculation,
// they’re treated like any other
// keyword"
}
CalcValueOrOperator::Value(calc_value) => {
match transform_calc_value_into_component_value(calc_value)
{
ComponentValue::Function(function) => {
*n = SizeFeatureValue::Function(function);
}
ComponentValue::Dimension(dimension) => {
*n = SizeFeatureValue::Dimension(dimension);
}
ComponentValue::Number(number) => {
*n = SizeFeatureValue::Number(number);
}
_ => {}
}
}
_ => {}
}
}
_ => {}
}
}
_ => {}
}
}
_ => {}
}
}
}
78 changes: 78 additions & 0 deletions crates/swc_css_minifier/src/compressor/math/mod.rs
@@ -0,0 +1,78 @@
use swc_atoms::js_word;
use swc_css_ast::*;

pub fn is_calc_function_name(ident: &Ident) -> bool {
ident.value.to_ascii_lowercase() == js_word!("calc")
|| ident.value.to_ascii_lowercase() == js_word!("-webkit-calc")
|| ident.value.to_ascii_lowercase() == js_word!("-moz-calc")
}

pub fn transform_calc_value_into_component_value(calc_value: &CalcValue) -> ComponentValue {
match &calc_value {
CalcValue::Number(n) => ComponentValue::Number(n.clone()),
CalcValue::Dimension(Dimension::Length(l)) => {
ComponentValue::Dimension(Dimension::Length(Length {
span: l.span,
value: l.value.clone(),
unit: l.unit.clone(),
}))
}
CalcValue::Dimension(Dimension::Angle(a)) => {
ComponentValue::Dimension(Dimension::Angle(Angle {
span: a.span,
value: a.value.clone(),
unit: a.unit.clone(),
}))
}
CalcValue::Dimension(Dimension::Time(t)) => {
ComponentValue::Dimension(Dimension::Time(Time {
span: t.span,
value: t.value.clone(),
unit: t.unit.clone(),
}))
}
CalcValue::Dimension(Dimension::Frequency(f)) => {
ComponentValue::Dimension(Dimension::Frequency(Frequency {
span: f.span,
value: f.value.clone(),
unit: f.unit.clone(),
}))
}
CalcValue::Dimension(Dimension::Resolution(r)) => {
ComponentValue::Dimension(Dimension::Resolution(Resolution {
span: r.span,
value: r.value.clone(),
unit: r.unit.clone(),
}))
}
CalcValue::Dimension(Dimension::Flex(f)) => {
ComponentValue::Dimension(Dimension::Flex(Flex {
span: f.span,
value: f.value.clone(),
unit: f.unit.clone(),
}))
}
CalcValue::Dimension(Dimension::UnknownDimension(u)) => {
ComponentValue::Dimension(Dimension::UnknownDimension(UnknownDimension {
span: u.span,
value: u.value.clone(),
unit: u.unit.clone(),
}))
}
CalcValue::Percentage(p) => ComponentValue::Percentage(Percentage {
span: p.span,
value: p.value.clone(),
}),
CalcValue::Function(f) => ComponentValue::Function(Function {
span: f.span,
name: f.name.clone(),
value: f.value.to_vec(),
}),
CalcValue::Constant(_) => {
unreachable!("CalcValue::Constant cannot be transformed into a ComponentValue per spec")
}
CalcValue::Sum(_) => {
unreachable!("CalcValue::Sum cannot be transformed into a ComponentValue")
}
}
}
61 changes: 60 additions & 1 deletion crates/swc_css_minifier/src/compressor/media.rs
Expand Up @@ -4,7 +4,10 @@ use swc_common::DUMMY_SP;
use swc_css_ast::*;

use super::Compressor;
use crate::util::dedup;
use crate::{
compressor::math::{is_calc_function_name, transform_calc_value_into_component_value},
util::dedup,
};

impl Compressor {
fn is_first_media_in_parens(&self, media_condition: &MediaCondition) -> bool {
Expand Down Expand Up @@ -326,4 +329,60 @@ impl Compressor {
_ => {}
}
}

pub(super) fn compress_calc_sum_in_media_feature_value(&mut self, n: &mut MediaFeatureValue) {
match n {
MediaFeatureValue::Function(Function { name, value, .. })
if is_calc_function_name(name) && value.len() == 1 =>
{
match &value[0] {
ComponentValue::CalcSum(CalcSum {
expressions: calc_sum_expressions,
..
}) if calc_sum_expressions.len() == 1 => {
match &calc_sum_expressions[0] {
CalcProductOrOperator::Product(CalcProduct {
expressions: calc_product_expressions,
..
}) if calc_product_expressions.len() == 1 => {
match &calc_product_expressions[0] {
CalcValueOrOperator::Value(CalcValue::Sum(_)) => {
// Do nothing, we cannot transform a
// CalcSum into a ComponentValue
}
CalcValueOrOperator::Value(CalcValue::Constant(_)) => {
// https://www.w3.org/TR/css-values-4/#calc-constants
// "These keywords are only usable
// within a calculation"
// "If used outside of a calculation,
// they’re treated like any other
// keyword"
}
CalcValueOrOperator::Value(calc_value) => {
match transform_calc_value_into_component_value(calc_value)
{
ComponentValue::Function(function) => {
*n = MediaFeatureValue::Function(function);
}
ComponentValue::Dimension(dimension) => {
*n = MediaFeatureValue::Dimension(dimension);
}
ComponentValue::Number(number) => {
*n = MediaFeatureValue::Number(number);
}
_ => {}
}
}
_ => {}
}
}
_ => {}
}
}
_ => {}
}
}
_ => {}
}
}
}
21 changes: 21 additions & 0 deletions crates/swc_css_minifier/src/compressor/mod.rs
Expand Up @@ -9,6 +9,7 @@ mod alpha_value;
mod angle;
mod calc_sum;
mod color;
mod container;
mod ctx;
mod declaration;
mod easing_function;
Expand All @@ -17,6 +18,7 @@ mod frequency;
mod import;
mod keyframes;
mod length;
mod math;
mod media;
mod selector;
mod supports;
Expand All @@ -34,6 +36,7 @@ pub fn compressor() -> impl VisitMut {
struct Compressor {
ctx: Ctx,
need_utf8_at_rule: bool,
in_supports_conidition: bool,
}

impl Compressor {
Expand Down Expand Up @@ -166,9 +169,21 @@ impl VisitMut for Compressor {
self.compress_media_in_parens(n);
}

fn visit_mut_media_feature_value(&mut self, n: &mut MediaFeatureValue) {
n.visit_mut_children_with(self);

self.compress_calc_sum_in_media_feature_value(n);
}

fn visit_mut_supports_condition(&mut self, n: &mut SupportsCondition) {
let old_in_support_condition = self.in_supports_conidition;

self.in_supports_conidition = true;

n.visit_mut_children_with(self);

self.in_supports_conidition = old_in_support_condition;

self.compress_supports_condition(n);
}

Expand All @@ -178,6 +193,12 @@ impl VisitMut for Compressor {
self.compress_supports_in_parens(n);
}

fn visit_mut_size_feature_value(&mut self, n: &mut SizeFeatureValue) {
n.visit_mut_children_with(self);

self.compress_calc_sum_in_size_feature_value(n);
}

fn visit_mut_keyframe_selector(&mut self, n: &mut KeyframeSelector) {
n.visit_mut_children_with(self);

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

@@ -0,0 +1,4 @@
@container (inline-size >= calc(100px + 100px)) {
/* only applies when an inline-size container is available */
h2 { font-size: calc(1.2em + 1cqi); }
}
@@ -0,0 +1 @@
@container(inline-size>=200px){h2{font-size:calc(1.2em + 1cqi)}}

1 comment on commit 26aeb18

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark

Benchmark suite Current: 26aeb18 Previous: 6a42e51 Ratio
es/full/minify/libraries/antd 1883417163 ns/iter (± 70227705) 1818474063 ns/iter (± 54683097) 1.04
es/full/minify/libraries/d3 413477879 ns/iter (± 17954635) 403154483 ns/iter (± 12195953) 1.03
es/full/minify/libraries/echarts 1553336481 ns/iter (± 40758635) 1515230221 ns/iter (± 31949425) 1.03
es/full/minify/libraries/jquery 104123686 ns/iter (± 1877743) 101581124 ns/iter (± 3790336) 1.03
es/full/minify/libraries/lodash 127738828 ns/iter (± 11951394) 112775252 ns/iter (± 6262579) 1.13
es/full/minify/libraries/moment 63923001 ns/iter (± 5716788) 61265579 ns/iter (± 3384854) 1.04
es/full/minify/libraries/react 21827423 ns/iter (± 890752) 19755434 ns/iter (± 775000) 1.10
es/full/minify/libraries/terser 341501988 ns/iter (± 14701761) 330706170 ns/iter (± 14540902) 1.03
es/full/minify/libraries/three 588641358 ns/iter (± 27204126) 568677827 ns/iter (± 15631562) 1.04
es/full/minify/libraries/typescript 3761632296 ns/iter (± 134742160) 3571262979 ns/iter (± 54199331) 1.05
es/full/minify/libraries/victory 874456803 ns/iter (± 34180847) 848184579 ns/iter (± 7899803) 1.03
es/full/minify/libraries/vue 172225674 ns/iter (± 10108269) 161248690 ns/iter (± 9928654) 1.07
es/full/codegen/es3 34951 ns/iter (± 2959) 35228 ns/iter (± 1552) 0.99
es/full/codegen/es5 34667 ns/iter (± 1035) 34403 ns/iter (± 901) 1.01
es/full/codegen/es2015 34714 ns/iter (± 1147) 41040 ns/iter (± 26796) 0.85
es/full/codegen/es2016 35082 ns/iter (± 3015) 34942 ns/iter (± 2809) 1.00
es/full/codegen/es2017 34475 ns/iter (± 1488) 35466 ns/iter (± 1849) 0.97
es/full/codegen/es2018 34828 ns/iter (± 1771) 34561 ns/iter (± 2159) 1.01
es/full/codegen/es2019 34682 ns/iter (± 1731) 34885 ns/iter (± 2613) 0.99
es/full/codegen/es2020 34618 ns/iter (± 2483) 34686 ns/iter (± 1304) 1.00
es/full/all/es3 238684332 ns/iter (± 19574509) 214882117 ns/iter (± 21815009) 1.11
es/full/all/es5 221692044 ns/iter (± 20860323) 229608894 ns/iter (± 17621877) 0.97
es/full/all/es2015 180016675 ns/iter (± 13960301) 180936280 ns/iter (± 13191942) 0.99
es/full/all/es2016 175417949 ns/iter (± 15000386) 181773418 ns/iter (± 16581486) 0.97
es/full/all/es2017 179145815 ns/iter (± 23349275) 156415965 ns/iter (± 9641108) 1.15
es/full/all/es2018 173284639 ns/iter (± 23235883) 151956238 ns/iter (± 10149521) 1.14
es/full/all/es2019 174238264 ns/iter (± 19869230) 148134805 ns/iter (± 7973197) 1.18
es/full/all/es2020 172837548 ns/iter (± 19166129) 142802591 ns/iter (± 8309813) 1.21
es/full/parser 758769 ns/iter (± 118814) 734944 ns/iter (± 26318) 1.03
es/full/base/fixer 27445 ns/iter (± 1452) 26962 ns/iter (± 1754) 1.02
es/full/base/resolver_and_hygiene 98112 ns/iter (± 7369) 96131 ns/iter (± 4447) 1.02
serialization of ast node 211 ns/iter (± 12) 214 ns/iter (± 9) 0.99
serialization of serde 214 ns/iter (± 7) 211 ns/iter (± 5) 1.01

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.