diff --git a/crates/swc_css_compat/src/compiler/color_alpha_parameter.rs b/crates/swc_css_compat/src/compiler/color_alpha_parameter.rs new file mode 100644 index 000000000000..0bd68e11f1e3 --- /dev/null +++ b/crates/swc_css_compat/src/compiler/color_alpha_parameter.rs @@ -0,0 +1,47 @@ +use swc_atoms::js_word; +use swc_css_ast::{AbsoluteColorBase, ComponentValue}; + +use crate::compiler::Compiler; + +impl Compiler { + pub(crate) fn process_color_alpha_parameter(&mut self, n: &mut AbsoluteColorBase) { + if let AbsoluteColorBase::Function(function) = n { + let name = function.name.value.to_ascii_lowercase(); + + if let Some(ComponentValue::AlphaValue(_) | ComponentValue::Function(_)) = + function.value.last() + { + if !matches!( + name, + js_word!("rgb") | js_word!("rgba") | js_word!("hsl") | js_word!("hsla") + ) { + return; + } + + match name { + js_word!("rgb") => { + function.name.value = js_word!("rgba"); + function.name.raw = None; + } + js_word!("hsl") => { + function.name.value = js_word!("hsla"); + function.name.raw = None; + } + _ => {} + } + } else { + match name { + js_word!("rgba") => { + function.name.value = js_word!("rgb"); + function.name.raw = None; + } + js_word!("hsla") => { + function.name.value = js_word!("hsl"); + function.name.raw = None; + } + _ => {} + } + } + } + } +} diff --git a/crates/swc_css_compat/src/compiler/color_hex_alpha.rs b/crates/swc_css_compat/src/compiler/color_hex_alpha.rs index aa313e3eee25..244afb4c9a3f 100644 --- a/crates/swc_css_compat/src/compiler/color_hex_alpha.rs +++ b/crates/swc_css_compat/src/compiler/color_hex_alpha.rs @@ -5,7 +5,7 @@ use swc_css_ast::{ Ident, Number, }; -use crate::compiler::Compiler; +use crate::compiler::{utils::round_alpha, Compiler}; #[inline] fn from_hex(c: u8) -> u8 { @@ -19,11 +19,6 @@ fn from_hex(c: u8) -> u8 { } } -#[inline] -fn clamp_unit_f32(val: f64) -> u8 { - (val * 255.).round().max(0.).min(255.) as u8 -} - fn shorten_hex_color(value: &str) -> Option<&str> { let length = value.len(); let chars = value.as_bytes(); @@ -83,17 +78,6 @@ impl Compiler { let rgba = hex_to_rgba(&hex_color.value); - let r = rgba.0 as f64; - let g = rgba.1 as f64; - let b = rgba.2 as f64; - let a = rgba.3; - - let mut rounded_alpha = (a * 100.).round() / 100.; - - if clamp_unit_f32(rounded_alpha) != clamp_unit_f32(a) { - rounded_alpha = (a * 1000.).round() / 1000.; - } - *n = ComponentValue::Color(Box::new(Color::AbsoluteColorBase( AbsoluteColorBase::Function(Function { span: hex_color.span, @@ -105,7 +89,7 @@ impl Compiler { value: vec![ ComponentValue::Number(Box::new(Number { span: DUMMY_SP, - value: r, + value: rgba.0 as f64, raw: None, })), ComponentValue::Delimiter(Box::new(Delimiter { @@ -114,7 +98,7 @@ impl Compiler { })), ComponentValue::Number(Box::new(Number { span: DUMMY_SP, - value: g, + value: rgba.1 as f64, raw: None, })), ComponentValue::Delimiter(Box::new(Delimiter { @@ -123,7 +107,7 @@ impl Compiler { })), ComponentValue::Number(Box::new(Number { span: DUMMY_SP, - value: b, + value: rgba.2 as f64, raw: None, })), ComponentValue::Delimiter(Box::new(Delimiter { @@ -132,7 +116,7 @@ impl Compiler { })), ComponentValue::AlphaValue(Box::new(AlphaValue::Number(Number { span: DUMMY_SP, - value: rounded_alpha, + value: round_alpha(rgba.3), raw: None, }))), ], diff --git a/crates/swc_css_compat/src/compiler/color_space_separated_parameters.rs b/crates/swc_css_compat/src/compiler/color_space_separated_parameters.rs new file mode 100644 index 000000000000..2398050edf8a --- /dev/null +++ b/crates/swc_css_compat/src/compiler/color_space_separated_parameters.rs @@ -0,0 +1,72 @@ +use std::mem::take; + +use swc_atoms::js_word; +use swc_common::DUMMY_SP; +use swc_css_ast::{AbsoluteColorBase, ComponentValue, Delimiter, DelimiterValue}; + +use crate::compiler::Compiler; + +impl Compiler { + pub(crate) fn process_color_space_separated_function_notation( + &mut self, + n: &mut AbsoluteColorBase, + ) { + if let AbsoluteColorBase::Function(function) = n { + let name = function.name.value.to_ascii_lowercase(); + + if !matches!( + name, + js_word!("rgb") | js_word!("rgba") | js_word!("hsl") | js_word!("hsla") + ) { + return; + } + + if function.value.len() != 3 && function.value.len() != 5 { + return; + } + + if function.value.iter().any(|n| { + matches!( + n, + ComponentValue::Delimiter(box Delimiter { + value: DelimiterValue::Comma, + .. + }) + ) + }) { + return; + } + + let new_value: Vec = take(&mut function.value) + .into_iter() + .enumerate() + .flat_map(|(idx, node)| { + if matches!(idx, 0 | 1) { + vec![ + node, + ComponentValue::Delimiter(Box::new(Delimiter { + value: DelimiterValue::Comma, + span: DUMMY_SP, + })), + ] + } else if matches!( + node, + ComponentValue::Delimiter(box Delimiter { + value: DelimiterValue::Solidus, + .. + }) + ) { + vec![ComponentValue::Delimiter(Box::new(Delimiter { + value: DelimiterValue::Comma, + span: DUMMY_SP, + }))] + } else { + vec![node] + } + }) + .collect::>(); + + function.value = new_value; + } + } +} diff --git a/crates/swc_css_compat/src/compiler/legacy_rgb_and_hsl.rs b/crates/swc_css_compat/src/compiler/legacy_rgb_and_hsl.rs new file mode 100644 index 000000000000..19771d84b943 --- /dev/null +++ b/crates/swc_css_compat/src/compiler/legacy_rgb_and_hsl.rs @@ -0,0 +1,89 @@ +use std::f64::consts::PI; + +use swc_atoms::js_word; +use swc_css_ast::{AbsoluteColorBase, AlphaValue, Angle, ComponentValue, Hue, Number, Percentage}; + +use crate::compiler::{ + utils::{clamp_unit_f32, round_alpha}, + Compiler, +}; + +impl Compiler { + pub(crate) fn process_rgb_and_hsl(&mut self, n: &mut AbsoluteColorBase) { + if let AbsoluteColorBase::Function(function) = n { + let name = function.name.value.to_ascii_lowercase(); + + let is_rgb = matches!(name, js_word!("rgb") | js_word!("rgba")); + let is_hsl = matches!(name, js_word!("hsl") | js_word!("hsla")); + + if is_rgb { + function.value = function + .value + .drain(..) + .into_iter() + .map(|n| match n { + ComponentValue::Percentage(box Percentage { + span, + value: Number { value, .. }, + .. + }) => ComponentValue::Number(Box::new(Number { + span, + value: clamp_unit_f32(value / 100.0) as f64, + raw: None, + })), + _ => n, + }) + .collect(); + } else if is_hsl { + function.value = function + .value + .drain(..) + .into_iter() + .map(|n| match n { + ComponentValue::Hue(box Hue::Angle(Angle { + span, + value: Number { value, .. }, + unit, + .. + })) => { + let value = match unit.value.to_ascii_lowercase() { + js_word!("deg") => value, + js_word!("grad") => value * 180.0 / 200.0, + js_word!("rad") => value * 180.0 / PI, + js_word!("turn") => value * 360.0, + _ => { + unreachable!(); + } + }; + + ComponentValue::Number(Box::new(Number { + span, + value: value.round(), + raw: None, + })) + } + _ => n, + }) + .collect(); + } + + if is_rgb || is_hsl { + if let Some(ComponentValue::AlphaValue(box alpha_value)) = function.value.last_mut() + { + if let AlphaValue::Percentage(Percentage { + span, + value: Number { value: a, .. }, + .. + }) = alpha_value + { + *alpha_value = AlphaValue::Number(Number { + span: *span, + value: round_alpha(*a / 100.0), + raw: None, + }); + } + } + } + } + } +} diff --git a/crates/swc_css_compat/src/compiler/mod.rs b/crates/swc_css_compat/src/compiler/mod.rs index f1b1e352ebfd..c8904c857a55 100644 --- a/crates/swc_css_compat/src/compiler/mod.rs +++ b/crates/swc_css_compat/src/compiler/mod.rs @@ -1,6 +1,6 @@ use swc_common::{Spanned, DUMMY_SP}; use swc_css_ast::{ - AtRule, ComponentValue, MediaAnd, MediaCondition, MediaConditionAllType, + AbsoluteColorBase, AtRule, ComponentValue, MediaAnd, MediaCondition, MediaConditionAllType, MediaConditionWithoutOr, MediaInParens, MediaQuery, Rule, SupportsCondition, }; use swc_css_visit::{VisitMut, VisitMutWith}; @@ -8,9 +8,13 @@ use swc_css_visit::{VisitMut, VisitMutWith}; use self::custom_media::CustomMediaHandler; use crate::feature::Features; +mod color_alpha_parameter; mod color_hex_alpha; +mod color_space_separated_parameters; mod custom_media; +mod legacy_rgb_and_hsl; mod media_query_ranges; +mod utils; /// Compiles a modern CSS file to a CSS file which works with old browsers. #[derive(Debug)] @@ -130,4 +134,29 @@ impl VisitMut for Compiler { self.process_color_hex_alpha(n); } } + + fn visit_mut_absolute_color_base(&mut self, n: &mut AbsoluteColorBase) { + n.visit_mut_children_with(self); + + if self.in_supports_condition { + return; + } + + // TODO handle color functions in custom variables under the option + // TODO implement the `preserve` option to preserve the original color + + let process = self.c.process; + + if process.contains(Features::COLOR_SPACE_SEPARATED_PARAMETERS) { + self.process_color_space_separated_function_notation(n); + } + + if process.contains(Features::COLOR_ALPHA_PARAMETER) { + self.process_color_alpha_parameter(n); + } + + if process.contains(Features::COLOR_LEGACY_RGB_AND_HSL) { + self.process_rgb_and_hsl(n); + } + } } diff --git a/crates/swc_css_compat/src/compiler/utils.rs b/crates/swc_css_compat/src/compiler/utils.rs new file mode 100644 index 000000000000..355163f9bb5c --- /dev/null +++ b/crates/swc_css_compat/src/compiler/utils.rs @@ -0,0 +1,15 @@ +#[inline] +pub(crate) fn clamp_unit_f32(val: f64) -> u8 { + (val * 255.).round().max(0.).min(255.) as u8 +} + +#[inline] +pub(crate) fn round_alpha(alpha: f64) -> f64 { + let mut rounded_alpha = (alpha * 100.).round() / 100.; + + if clamp_unit_f32(rounded_alpha) != clamp_unit_f32(alpha) { + rounded_alpha = (alpha * 1000.).round() / 1000.; + } + + rounded_alpha +} diff --git a/crates/swc_css_compat/src/feature.rs b/crates/swc_css_compat/src/feature.rs index b94df38e91dd..05984fc04f9e 100644 --- a/crates/swc_css_compat/src/feature.rs +++ b/crates/swc_css_compat/src/feature.rs @@ -6,5 +6,8 @@ bitflags! { const CUSTOM_MEDIA = 1 << 1; const MEDIA_QUERY_RANGES = 1 << 2; const COLOR_HEX_ALPHA = 1 << 3; + const COLOR_ALPHA_PARAMETER = 1 << 4; + const COLOR_SPACE_SEPARATED_PARAMETERS = 1 << 5; + const COLOR_LEGACY_RGB_AND_HSL = 1 << 6; } } diff --git a/crates/swc_css_compat/tests/color-legacy/input.css b/crates/swc_css_compat/tests/color-legacy/input.css new file mode 100644 index 000000000000..3f66a1746238 --- /dev/null +++ b/crates/swc_css_compat/tests/color-legacy/input.css @@ -0,0 +1,233 @@ +.test-rgb { + color: rgb(178 34 34); + color: rgb(178 34 34 / 1); + color: rGB(178 34 34 / .5); + color: rgb(178 34 34 / 100%); + color: rgb(178 34 34 / 50%); + color: rgb(178 34 34 / var(--alpha-50)); + color: rgb(178 34 34 / calc(1 / 2)); +} + +.test-rgba { + color: rgba(178 34 34); + color: rgbA(178 34 34 / 1); + color: rgba(178 34 34 / .5); + color: rgba(178 34 34 / VaR(--alpha-50)); + color: rgba(178 34 34 / CaLC(1 / 2)); +} + +.test-rgb-percentages { + color: rgba(70% 13.5% 13.5%); + color: rgba(70% 13.5% 13.5% / 100%); + color: rgba(70% 13.5% 13.5% / 50%); +} + +.test-hsl { + color: hsl(0 0% 100%); + color: hsl(120deg 100% 50%); + color: hsl(120 100% 50%); + color: HSL(120 100% 50% / 1); + color: hsl(120 100% 50% / .5); + color: hsl(120 100% 50% / 100%); + color: hsl(120 100% 50% / 50%); + color: hslA(120deg 100% 50%); + + color: hsl(0.5turn 100% 50%); + color: hsl(200grad 100% 50%); + color: hsl(3.14159rad 100% 50%); + color: hsl(3.14159rad 100% 50% / var(--alpha-50)); + color: hsl(3.14159rad 100% 50% / calc(1 / 2)); +} + +.test-hsla { + color: hsla(120 100% 50%); + color: hsla(120 100% 50% / 1); + color: hsla(120 100% 50% / .5); + color: hsla(120 100% 50% / 100%); + color: hsla(120 100% 50% / 50%); + color: hsla(120 100% 50% / var(--alpha-50)); + color: hsla(120 100% 50% / calc(1 / 2)); +} + +.test-hwb { + color: hwb(0deg 0% 0%); + color: hwb(0 0% 0%); + color: hwb(0 0% 0% / 1); + color: hwb(0 0% 0% / .5); + color: hwb(0 0% 0% / 100%); + color: hwb(0 0% 0% / 50%); +} + +.test-unparseable-color-function { + color: rgb(; ); +} + +.rgb-with-alpha { + color: rgb(70% 13.5% 13.5% / 50%); + color: rgb(0, 0, 0, 0); +} + +.hsl-with-alpha { + color: hsl(120 100% 50% / 50%); + color: hsl(120, 100%, 50%, 0); +} + +.test-legacy-notation-with-modern-components--rgb { + color: rgb(50%, 34, 34); + color: rgb(178, 10%, 34); + color: rgb(178, 34, 10%); +} + +.test-legacy-notation-with-modern-components--rgba { + color: rgba(50%, 34, 34, 1); + color: rgba(178, 10%, 34, 1); + color: rgba(178, 34, 10%, 1); + color: rgba(178, 34, 34, 100%); +} + +.test-legacy-notation-with-modern-components--hsla { + color: hsla(120, 100%, 50%, 100%); +} + +.test-ignore { + /* this plugin shouldn't format */ + color: rgba(0,0,0,0.1); /* spaceless */ + color: rgba(0, 0, 0, 0.2); /* with spaces */ + color: rGB(178, 34, 34); /* legacy format, but with capitals */ +} + +.order-0 { + order: rgba(178 34 34 / 1) !important; +} + +.order-1 { + order: var(1, rgba(178 34 34 / 1)); +} + +.order-2 { + order: var(rgba(178 34 34 / 1), 1); +} + +.order-3 { + order: var(rgba(178 34 34 / 1), rgba(178 34 34 / 1)); +} + +.order-4 { + order: prergba(178 34 34 / 1); +} + +.order-5 { + order: pre rgba(178 34 34 / 1); +} + +.order-6 { + order: pre,rgba(178 34 34 / 1); +} + +.order-7 { + order: rgba(178 34 34 / 1)post; +} + +.order-8 { + order: rgba(178 34 34 / 1) post; +} + +.order-9 { + order: rgba(178 34 34 / 1),post; +} + +/* rgba(178 34 34 / 1) */ + +:root { + --some-var: rgba(178 34 34 / 1); +} + +.content { + content: 'rgba(178 34 34 / 1)'; +} + +@supports (order: rgba(178 34 34 / 1)) { + .support { + order: rgba(178 34 34 / 1); + } +} + +.order-0 { + order: hsl(120 100% 50% / 1) !important; +} + +.order-1 { + order: var(1, hsl(120 100% 50% / 1)); +} + +.order-2 { + order: var(hsl(120 100% 50% / 1), 1); +} + +.order-3 { + order: var(hsl(120 100% 50% / 1), hsl(120 100% 50% / 1)); +} + +.order-4 { + order: prehsl(120 100% 50% / 1); +} + +.order-5 { + order: pre hsl(120 100% 50% / 1); +} + +.order-6 { + order: pre,hsl(120 100% 50% / 1); +} + +.order-7 { + order: hsl(120 100% 50% / 1)post; +} + +.order-8 { + order: hsl(120 100% 50% / 1) post; +} + +.order-9 { + order: hsl(120 100% 50% / 1),post; +} + +/* hsl(120 100% 50% / 1) */ + +:root { + --some-var: hsl(120 100% 50% / 1); +} + +.content { + content: 'hsl(120 100% 50% / 1)'; +} + +@supports (order: hsl(120 100% 50% / 1)) { + .support { + order: hsl(120 100% 50% / 1); + } +} + +:root { + --firebrick: rgb(40% 56.6 39); + --firebrick-a50: rgb(40% 68.8 34.5 / 50%); + + --opacity-50: 0.5; + --firebrick-a50-var: hsl(40 68.8% 34.5% / var(--opacity-50)); + --firebrick-rgb-a50-var: rgb(40% 68.8 34.5 / var(--opacity-50)); + + --fifty: 50%; + --firebrick-var: hsl(40 var(--fifty) 34.5% / var(--opacity-50)); +} + +:root { + /* Not expected to be handled here at the moment, but might be needed in the future */ + --rgba-functional-notation-percentage-alpha: rgba(0, 0, 0, 100%); + + --hsla-functional-notation-percentage-alpha: hsla(0, 0%, 0%, 100%); +} + +.foo { + color: hsl(720deg 100% 50%); + color: hsl(240grad 100% 50%); +} diff --git a/crates/swc_css_compat/tests/color-legacy/input.expect.css b/crates/swc_css_compat/tests/color-legacy/input.expect.css new file mode 100644 index 000000000000..49cdb8cf1b8d --- /dev/null +++ b/crates/swc_css_compat/tests/color-legacy/input.expect.css @@ -0,0 +1,182 @@ +.test-rgb { + color: rgb(178, 34, 34); + color: rgba(178, 34, 34, 1); + color: rgba(178, 34, 34, .5); + color: rgba(178, 34, 34, 1); + color: rgba(178, 34, 34, 0.5); + color: rgba(178, 34, 34, var(--alpha-50)); + color: rgba(178, 34, 34, calc(1 / 2)); +} +.test-rgba { + color: rgb(178, 34, 34); + color: rgbA(178, 34, 34, 1); + color: rgba(178, 34, 34, .5); + color: rgba(178, 34, 34, VaR(--alpha-50)); + color: rgba(178, 34, 34, CaLC(1 / 2)); +} +.test-rgb-percentages { + color: rgb(179, 34, 34); + color: rgba(179, 34, 34, 1); + color: rgba(179, 34, 34, 0.5); +} +.test-hsl { + color: hsl(0, 0%, 100%); + color: hsl(120, 100%, 50%); + color: hsl(120, 100%, 50%); + color: hsla(120, 100%, 50%, 1); + color: hsla(120, 100%, 50%, .5); + color: hsla(120, 100%, 50%, 1); + color: hsla(120, 100%, 50%, 0.5); + color: hsl(120, 100%, 50%); + color: hsl(180, 100%, 50%); + color: hsl(180, 100%, 50%); + color: hsl(180, 100%, 50%); + color: hsla(180, 100%, 50%, var(--alpha-50)); + color: hsla(180, 100%, 50%, calc(1 / 2)); +} +.test-hsla { + color: hsl(120, 100%, 50%); + color: hsla(120, 100%, 50%, 1); + color: hsla(120, 100%, 50%, .5); + color: hsla(120, 100%, 50%, 1); + color: hsla(120, 100%, 50%, 0.5); + color: hsla(120, 100%, 50%, var(--alpha-50)); + color: hsla(120, 100%, 50%, calc(1 / 2)); +} +.test-hwb { + color: hwb(0deg 0% 0%); + color: hwb(0 0% 0%); + color: hwb(0 0% 0% / 1); + color: hwb(0 0% 0% / .5); + color: hwb(0 0% 0% / 100%); + color: hwb(0 0% 0% / 50%); +} +.test-unparseable-color-function { + color: rgb(; ); +} +.rgb-with-alpha { + color: rgba(179, 34, 34, 0.5); + color: rgba(0, 0, 0, 0); +} +.hsl-with-alpha { + color: hsla(120, 100%, 50%, 0.5); + color: hsla(120, 100%, 50%, 0); +} +.test-legacy-notation-with-modern-components--rgb { + color: rgb(128, 34, 34); + color: rgb(178, 26, 34); + color: rgb(178, 34, 26); +} +.test-legacy-notation-with-modern-components--rgba { + color: rgba(128, 34, 34, 1); + color: rgba(178, 26, 34, 1); + color: rgba(178, 34, 26, 1); + color: rgba(178, 34, 34, 1); +} +.test-legacy-notation-with-modern-components--hsla { + color: hsla(120, 100%, 50%, 1); +} +.test-ignore { + color: rgba(0, 0, 0, 0.1); + color: rgba(0, 0, 0, 0.2); + color: rGB(178, 34, 34); +} +.order-0 { + order: rgba(178, 34, 34, 1) !important; +} +.order-1 { + order: var(1, rgba(178, 34, 34, 1)); +} +.order-2 { + order: var(rgba(178, 34, 34, 1), 1); +} +.order-3 { + order: var(rgba(178, 34, 34, 1), rgba(178, 34, 34, 1)); +} +.order-4 { + order: prergba(178 34 34/ 1); +} +.order-5 { + order: pre rgba(178, 34, 34, 1); +} +.order-6 { + order: pre, rgba(178, 34, 34, 1); +} +.order-7 { + order: rgba(178, 34, 34, 1) post; +} +.order-8 { + order: rgba(178, 34, 34, 1) post; +} +.order-9 { + order: rgba(178, 34, 34, 1), post; +} +:root { + --some-var: rgba(178 34 34 / 1); +} +.content { + content: 'rgba(178 34 34 / 1)'; +} +@supports (order: rgba(178 34 34/ 1)) { + .support { + order: rgba(178, 34, 34, 1); + } +} +.order-0 { + order: hsla(120, 100%, 50%, 1) !important; +} +.order-1 { + order: var(1, hsla(120, 100%, 50%, 1)); +} +.order-2 { + order: var(hsla(120, 100%, 50%, 1), 1); +} +.order-3 { + order: var(hsla(120, 100%, 50%, 1), hsla(120, 100%, 50%, 1)); +} +.order-4 { + order: prehsl(120 100% 50% / 1); +} +.order-5 { + order: pre hsla(120, 100%, 50%, 1); +} +.order-6 { + order: pre, hsla(120, 100%, 50%, 1); +} +.order-7 { + order: hsla(120, 100%, 50%, 1) post; +} +.order-8 { + order: hsla(120, 100%, 50%, 1) post; +} +.order-9 { + order: hsla(120, 100%, 50%, 1), post; +} +:root { + --some-var: hsl(120 100% 50% / 1); +} +.content { + content: 'hsl(120 100% 50% / 1)'; +} +@supports (order: hsl(120 100% 50% / 1)) { + .support { + order: hsla(120, 100%, 50%, 1); + } +} +:root { + --firebrick: rgb(40% 56.6 39); + --firebrick-a50: rgb(40% 68.8 34.5 / 50%); + --opacity-50: 0.5; + --firebrick-a50-var: hsl(40 68.8% 34.5% / var(--opacity-50)); + --firebrick-rgb-a50-var: rgb(40% 68.8 34.5 / var(--opacity-50)); + --fifty: 50%; + --firebrick-var: hsl(40 var(--fifty) 34.5% / var(--opacity-50)); +} +:root { + --rgba-functional-notation-percentage-alpha: rgba(0, 0, 0, 100%); + --hsla-functional-notation-percentage-alpha: hsla(0, 0%, 0%, 100%); +} +.foo { + color: hsl(720, 100%, 50%); + color: hsl(216, 100%, 50%); +} diff --git a/crates/swc_css_compat/tests/fixture.rs b/crates/swc_css_compat/tests/fixture.rs index 83b60c8e46a0..6197c8fa571e 100644 --- a/crates/swc_css_compat/tests/fixture.rs +++ b/crates/swc_css_compat/tests/fixture.rs @@ -147,3 +147,27 @@ fn test_color_hex_alpha(input: PathBuf) { }) .unwrap(); } + +#[testing::fixture("tests/color-legacy/**/*.css", exclude("expect.css"))] +fn test_color_space_separated_function_notation(input: PathBuf) { + let output = input.with_extension("expect.css"); + + testing::run_test(false, |cm, _| { + // + let fm = cm.load_file(&input).unwrap(); + let mut ss = parse_stylesheet(&fm); + + ss.visit_mut_with(&mut Compiler::new(Config { + process: Features::COLOR_SPACE_SEPARATED_PARAMETERS + | Features::COLOR_ALPHA_PARAMETER + | Features::COLOR_LEGACY_RGB_AND_HSL, + })); + + let s = print_stylesheet(&ss); + + NormalizedOutput::from(s).compare_to_file(&output).unwrap(); + + Ok(()) + }) + .unwrap(); +}