diff --git a/crates/swc_css_prefixer/data/prefixes_and_browsers.json b/crates/swc_css_prefixer/data/prefixes_and_browsers.json index 7277a6cf394d..ce79784b901a 100644 --- a/crates/swc_css_prefixer/data/prefixes_and_browsers.json +++ b/crates/swc_css_prefixer/data/prefixes_and_browsers.json @@ -3275,6 +3275,27 @@ "firefox": "15" } ], + "calc-nested": [ + { + "chrome": "4", + "edge": "12", + "safari": "3.1", + "firefox": "2", + "opera": "10", + "ie": "6", + "ios": "3.2", + "samsung": "4" + }, + { + "chrome": "50", + "edge": "15", + "safari": "10.1", + "firefox": "47", + "opera": "37", + "ios": "10.3", + "samsung": "4" + } + ], "-webkit-image-set()": [ { diff --git a/crates/swc_css_prefixer/src/prefixer.rs b/crates/swc_css_prefixer/src/prefixer.rs index 9d554450406b..9f157bcae12f 100644 --- a/crates/swc_css_prefixer/src/prefixer.rs +++ b/crates/swc_css_prefixer/src/prefixer.rs @@ -5,7 +5,7 @@ use std::mem::take; use once_cell::sync::Lazy; use preset_env_base::{query::targets_to_versions, version::Version, BrowserData, Versions}; -use swc_atoms::js_word; +use swc_atoms::{js_word, JsWord}; use swc_common::{ collections::{AHashMap, AHashSet}, EqIgnoreSpan, DUMMY_SP, @@ -515,6 +515,82 @@ where node.visit_mut_with(&mut MediaFeatureResolutionReplacerOnLegacyVariant { from, to }); } +struct CalcReplacer<'a> { + inside_calc: bool, + to: Option<&'a JsWord>, +} + +impl VisitMut for CalcReplacer<'_> { + fn visit_mut_function(&mut self, n: &mut Function) { + let old_inside_calc = self.inside_calc; + + let name = n.name.value.to_ascii_lowercase(); + + let is_webkit_calc = matches!(name, js_word!("-webkit-calc")); + let is_moz_calc = matches!(name, js_word!("-moz-calc")); + + if self.to.is_none() && (is_webkit_calc || is_moz_calc) { + return; + } + + if (is_webkit_calc && self.to == Some(&js_word!("-moz-calc"))) + || (is_moz_calc && self.to == Some(&js_word!("-webkit-calc"))) + { + return; + } + + self.inside_calc = matches!(name, js_word!("calc")) || is_webkit_calc || is_moz_calc; + + n.visit_mut_children_with(self); + + if matches!(name, js_word!("calc")) { + if let Some(to) = self.to { + n.name.value = to.clone(); + n.name.raw = None; + } + } + + self.inside_calc = old_inside_calc; + } + + fn visit_mut_calc_value(&mut self, n: &mut CalcValue) { + n.visit_mut_children_with(self); + + if !self.inside_calc { + return; + } + + if let CalcValue::Function(function) = n { + let name = function.name.value.to_ascii_lowercase(); + + if matches!( + name, + js_word!("calc") | js_word!("-webkit-calc") | js_word!("-moz-calc") + ) { + let calc_sum = match function.value.get(0) { + Some(ComponentValue::CalcSum(calc_sum)) => *calc_sum.clone(), + _ => return, + }; + + *n = CalcValue::Sum(CalcSum { + span: function.span, + expressions: calc_sum.expressions, + }); + } + } + } +} + +fn replace_calc(node: &mut N, to: Option<&JsWord>) +where + N: for<'aa> VisitMutWith>, +{ + node.visit_mut_with(&mut CalcReplacer { + inside_calc: false, + to, + }); +} + macro_rules! to_ident { ($val:expr) => {{ ComponentValue::Ident(Box::new(Ident { @@ -1243,7 +1319,7 @@ impl VisitMut for Prefixer { } if should_prefix("-webkit-calc()", self.env, false) { - replace_function_name(&mut webkit_value, "calc", "-webkit-calc"); + replace_calc(&mut webkit_value, Some(&js_word!("-webkit-calc"))); } if should_prefix("-webkit-cross-fade()", self.env, false) { @@ -1295,7 +1371,7 @@ impl VisitMut for Prefixer { } if should_prefix("-moz-calc()", self.env, false) { - replace_function_name(&mut moz_value, "calc", "-moz-calc"); + replace_calc(&mut moz_value, Some(&js_word!("-moz-calc"))); } if should_prefix("-moz-linear-gradient()", self.env, false) { @@ -3258,5 +3334,20 @@ impl VisitMut for Prefixer { important: n.important.clone(), })); } + + if should_prefix("calc-nested", self.env, true) { + let mut value = n.value.clone(); + + replace_calc(&mut value, None); + + if !n.value.eq_ignore_span(&value) { + self.added_declarations.push(Box::new(Declaration { + span: n.span, + name: n.name.clone(), + value, + important: n.important.clone(), + })); + } + } } } diff --git a/crates/swc_css_prefixer/tests/fixture/calc/input.css b/crates/swc_css_prefixer/tests/fixture/calc/input.css index 07feee8ce98d..f4e7cff2d2e8 100644 --- a/crates/swc_css_prefixer/tests/fixture/calc/input.css +++ b/crates/swc_css_prefixer/tests/fixture/calc/input.css @@ -9,3 +9,69 @@ div { background-size: calc(20px); } + +.basic { + opacity: calc(1 + calc(2 * 2)); +} + +.mixed-casing { + width: calc(1 + cAlC(2 * 2)); +} + +.multi-line { + width: calc(1 + + calc(2 * 2) + ); +} + +.multiple-nested { + width: calc(2 + calc(2 * 2) + calc(2 * 2)); +} + +.triple-nested { + width: calc(3 + calc(2 * calc(2 * 2))); +} + +.triple-nested-in-other-func { + width: var(--foo, calc(4 + calc(2 * calc(2 * 2)))); +} + +.complex { + width: calc(5 + calc(2 * calc( + 2 * var( + --foo, + calc(6 + calc(2 * calc(2 * 2))))) + ) + ); +} + +.ignore { + width: calc(1 + var(--foo, calc(2 * 2))); +} + +.custom-props { + --order: calc(1 + calc(2 * 2)); +} + +.foo { + width: calc((1 + calc(2 * 2)) * 10px); + width: CALC((1 + CALC(2 * 2)) * 10px); + padding: calc((1 + calc(2 * 2)) * 10px) calc((1 + calc(2 * 2)) * 10px); + padding: calc((1 + -webkit-calc(2 * 2)) * 10px); +} + +.bar { + padding: -webkit-calc((1 + calc(2 * 2)) * 10px); +} + +.baz { + padding: -webkit-calc((1 + -webkit-calc(2 * 2)) * 10px); +} + +.foo1 { + padding: -webkit-calc((1 + (2 * 2)) * 10px); +} + +.foo2 { + padding: var(--foo, calc((1 + calc(2 * 2)) * 10px)); +} \ No newline at end of file diff --git a/crates/swc_css_prefixer/tests/fixture/calc/output.css b/crates/swc_css_prefixer/tests/fixture/calc/output.css index c5c4de4ad675..d73abc2b3c39 100644 --- a/crates/swc_css_prefixer/tests/fixture/calc/output.css +++ b/crates/swc_css_prefixer/tests/fixture/calc/output.css @@ -16,3 +16,88 @@ div { background-size: -moz-calc(20px); background-size: calc(20px); } +.basic { + opacity: -webkit-calc(1 + (2 * 2)); + opacity: -moz-calc(1 + (2 * 2)); + opacity: calc(1 + (2 * 2)); + opacity: calc(1 + calc(2 * 2)); +} +.mixed-casing { + width: -webkit-calc(1 + (2 * 2)); + width: -moz-calc(1 + (2 * 2)); + width: calc(1 + (2 * 2)); + width: calc(1 + cAlC(2 * 2)); +} +.multi-line { + width: -webkit-calc(1 + (2 * 2)); + width: -moz-calc(1 + (2 * 2)); + width: calc(1 + (2 * 2)); + width: calc(1 + calc(2 * 2)); +} +.multiple-nested { + width: -webkit-calc(2 + (2 * 2) + (2 * 2)); + width: -moz-calc(2 + (2 * 2) + (2 * 2)); + width: calc(2 + (2 * 2) + (2 * 2)); + width: calc(2 + calc(2 * 2) + calc(2 * 2)); +} +.triple-nested { + width: -webkit-calc(3 + (2 * (2 * 2))); + width: -moz-calc(3 + (2 * (2 * 2))); + width: calc(3 + (2 * (2 * 2))); + width: calc(3 + calc(2 * calc(2 * 2))); +} +.triple-nested-in-other-func { + width: var(--foo, -webkit-calc(4 + (2 * (2 * 2)))); + width: var(--foo, -moz-calc(4 + (2 * (2 * 2)))); + width: var(--foo, calc(4 + (2 * (2 * 2)))); + width: var(--foo, calc(4 + calc(2 * calc(2 * 2)))); +} +.complex { + width: -webkit-calc(5 + (2 * (2 * var(--foo, -webkit-calc(6 + (2 * (2 * 2))))))); + width: -moz-calc(5 + (2 * (2 * var(--foo, -moz-calc(6 + (2 * (2 * 2))))))); + width: calc(5 + (2 * (2 * var(--foo, calc(6 + (2 * (2 * 2))))))); + width: calc(5 + calc(2 * calc(2 * var(--foo, calc(6 + calc(2 * calc(2 * 2))))))); +} +.ignore { + width: -webkit-calc(1 + var(--foo, -webkit-calc(2 * 2))); + width: -moz-calc(1 + var(--foo, -moz-calc(2 * 2))); + width: calc(1 + var(--foo, calc(2 * 2))); +} +.custom-props { + --order: calc(1 + calc(2 * 2)); +} +.foo { + width: -webkit-calc((1 + (2 * 2)) * 10px); + width: -moz-calc((1 + (2 * 2)) * 10px); + width: calc((1 + (2 * 2)) * 10px); + width: calc((1 + calc(2 * 2)) * 10px); + width: -webkit-calc((1 + (2 * 2)) * 10px); + width: -moz-calc((1 + (2 * 2)) * 10px); + width: CALC((1 + (2 * 2)) * 10px); + width: CALC((1 + CALC(2 * 2)) * 10px); + padding: -webkit-calc((1 + (2 * 2)) * 10px) -webkit-calc((1 + (2 * 2)) * 10px); + padding: -moz-calc((1 + (2 * 2)) * 10px) -moz-calc((1 + (2 * 2)) * 10px); + padding: calc((1 + (2 * 2)) * 10px) calc((1 + (2 * 2)) * 10px); + padding: calc((1 + calc(2 * 2)) * 10px) calc((1 + calc(2 * 2)) * 10px); + padding: -webkit-calc((1 + (2 * 2)) * 10px); + padding: -moz-calc((1 + (2 * 2)) * 10px); + padding: calc((1 + (2 * 2)) * 10px); + padding: calc((1 + -webkit-calc(2 * 2)) * 10px); +} +.bar { + padding: -webkit-calc((1 + (2 * 2)) * 10px); + padding: -webkit-calc((1 + calc(2 * 2)) * 10px); +} +.baz { + padding: -webkit-calc((1 + (2 * 2)) * 10px); + padding: -webkit-calc((1 + -webkit-calc(2 * 2)) * 10px); +} +.foo1 { + padding: -webkit-calc((1 + (2 * 2)) * 10px); +} +.foo2 { + padding: var(--foo, -webkit-calc((1 + (2 * 2)) * 10px)); + padding: var(--foo, -moz-calc((1 + (2 * 2)) * 10px)); + padding: var(--foo, calc((1 + (2 * 2)) * 10px)); + padding: var(--foo, calc((1 + calc(2 * 2)) * 10px)); +} diff --git a/crates/swc_css_prefixer/tests/fixture/calc/output.defaults-not-ie-11.css b/crates/swc_css_prefixer/tests/fixture/calc/output.defaults-not-ie-11.css index 4cb8d9544370..05736e7ac512 100644 --- a/crates/swc_css_prefixer/tests/fixture/calc/output.defaults-not-ie-11.css +++ b/crates/swc_css_prefixer/tests/fixture/calc/output.defaults-not-ie-11.css @@ -7,3 +7,48 @@ div { background-size: calc(20px); } +.basic { + opacity: calc(1 + calc(2 * 2)); +} +.mixed-casing { + width: calc(1 + cAlC(2 * 2)); +} +.multi-line { + width: calc(1 + calc(2 * 2)); +} +.multiple-nested { + width: calc(2 + calc(2 * 2) + calc(2 * 2)); +} +.triple-nested { + width: calc(3 + calc(2 * calc(2 * 2))); +} +.triple-nested-in-other-func { + width: var(--foo, calc(4 + calc(2 * calc(2 * 2)))); +} +.complex { + width: calc(5 + calc(2 * calc(2 * var(--foo, calc(6 + calc(2 * calc(2 * 2))))))); +} +.ignore { + width: calc(1 + var(--foo, calc(2 * 2))); +} +.custom-props { + --order: calc(1 + calc(2 * 2)); +} +.foo { + width: calc((1 + calc(2 * 2)) * 10px); + width: CALC((1 + CALC(2 * 2)) * 10px); + padding: calc((1 + calc(2 * 2)) * 10px) calc((1 + calc(2 * 2)) * 10px); + padding: calc((1 + -webkit-calc(2 * 2)) * 10px); +} +.bar { + padding: -webkit-calc((1 + calc(2 * 2)) * 10px); +} +.baz { + padding: -webkit-calc((1 + -webkit-calc(2 * 2)) * 10px); +} +.foo1 { + padding: -webkit-calc((1 + (2 * 2)) * 10px); +} +.foo2 { + padding: var(--foo, calc((1 + calc(2 * 2)) * 10px)); +}