From d7aeff3db67ee9d15e0fefce9251cf41c0b8ec44 Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Mon, 13 May 2024 19:19:35 -0700 Subject: [PATCH 001/152] optimize "all" property --- Cargo.lock | 7 ++-- Cargo.toml | 1 + node/ast.d.ts | 34 +++++++++++++++- node/test/visitor.test.mjs | 4 +- src/declaration.rs | 66 +++++++++++++++++++++++++------- src/lib.rs | 41 ++++++++++++++++---- src/properties/mod.rs | 48 ++++++++++++++++++++++- src/properties/prefix_handler.rs | 6 --- src/properties/text.rs | 28 ++++++++++++++ 9 files changed, 200 insertions(+), 35 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index db6c59ae..7358e44c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -153,7 +153,7 @@ dependencies = [ "anyhow", "chrono", "either", - "indexmap 2.2.2", + "indexmap 2.2.6", "itertools 0.12.1", "nom", "once_cell", @@ -658,9 +658,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.2" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown 0.14.2", @@ -775,6 +775,7 @@ dependencies = [ "dashmap", "data-encoding", "getrandom", + "indexmap 2.2.6", "indoc", "itertools 0.10.5", "jemallocator", diff --git a/Cargo.toml b/Cargo.toml index 4e27dbf5..795b25ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,6 +62,7 @@ const-str = "0.3.1" pathdiff = "0.2.1" ahash = "0.8.7" paste = "1.0.12" +indexmap = "2.2.6" # CLI deps atty = { version = "0.2", optional = true } clap = { version = "3.0.6", features = ["derive"], optional = true } diff --git a/node/ast.d.ts b/node/ast.d.ts index 25c9dd7f..bd6e0e98 100644 --- a/node/ast.d.ts +++ b/node/ast.d.ts @@ -2066,6 +2066,12 @@ export type PropertyId = property: "text-size-adjust"; vendorPrefix: VendorPrefix; } + | { + property: "direction"; + } + | { + property: "unicode-bidi"; + } | { property: "box-decoration-break"; vendorPrefix: VendorPrefix; @@ -3445,6 +3451,14 @@ export type Declaration = value: TextSizeAdjust; vendorPrefix: VendorPrefix; } + | { + property: "direction"; + value: Direction2; + } + | { + property: "unicode-bidi"; + value: UnicodeBidi; + } | { property: "box-decoration-break"; value: BoxDecorationBreak; @@ -3757,6 +3771,10 @@ export type Declaration = property: "color-scheme"; value: ColorScheme; } + | { + property: "all"; + value: CSSWideKeyword; + } | { property: "unparsed"; value: UnparsedProperty; @@ -5729,6 +5747,14 @@ export type TextSizeAdjust = type: "percentage"; value: number; }; +/** + * A value for the [direction](https://drafts.csswg.org/css-writing-modes-3/#direction) property. + */ +export type Direction2 = "ltr" | "rtl"; +/** + * A value for the [unicode-bidi](https://drafts.csswg.org/css-writing-modes-3/#unicode-bidi) property. + */ +export type UnicodeBidi = "normal" | "embed" | "isolate" | "bidi-override" | "isolate-override" | "plaintext"; /** * A value for the [box-decoration-break](https://www.w3.org/TR/css-break-3/#break-decoration) property. */ @@ -6234,6 +6260,10 @@ export type ContainerNameList = type: "names"; value: String[]; }; +/** + * A [CSS-wide keyword](https://drafts.csswg.org/css-cascade-5/#defaulting-keywords). + */ +export type CSSWideKeyword = "initial" | "inherit" | "unset" | "revert" | "revert-layer"; /** * A CSS custom property name. */ @@ -7040,8 +7070,8 @@ export type ParsedComponent = }; } | { - type: "token"; - value: Token; + type: "token-list"; + value: TokenOrValue[]; }; /** * A [multiplier](https://drafts.css-houdini.org/css-properties-values-api/#multipliers) for a [SyntaxComponent](SyntaxComponent). Indicates whether and how the component may be repeated. diff --git a/node/test/visitor.test.mjs b/node/test/visitor.test.mjs index 3a42a696..87ee208d 100644 --- a/node/test/visitor.test.mjs +++ b/node/test/visitor.test.mjs @@ -116,7 +116,7 @@ test('custom units', () => { } }); - assert.equal(res.code.toString(), '.foo{--step:.25rem;font-size:calc(3*var(--step))}'); + assert.equal(res.code.toString(), '.foo{font-size:calc(3*var(--step));--step:.25rem}'); }); test('design tokens', () => { @@ -822,7 +822,7 @@ test('dashed idents', () => { } }); - assert.equal(res.code.toString(), '.foo{--prefix-foo:#ff0;color:var(--prefix-foo)}'); + assert.equal(res.code.toString(), '.foo{color:var(--prefix-foo);--prefix-foo:#ff0}'); }); test('custom idents', () => { diff --git a/src/declaration.rs b/src/declaration.rs index 556fd152..501e2f8c 100644 --- a/src/declaration.rs +++ b/src/declaration.rs @@ -1,7 +1,6 @@ //! CSS declarations. use std::borrow::Cow; -use std::collections::HashMap; use std::ops::Range; use crate::context::{DeclarationContext, PropertyHandlerContext}; @@ -11,6 +10,7 @@ use crate::printer::Printer; use crate::properties::box_shadow::BoxShadowHandler; use crate::properties::custom::{CustomProperty, CustomPropertyName}; use crate::properties::masking::MaskHandler; +use crate::properties::text::{Direction, UnicodeBidi}; use crate::properties::{ align::AlignHandler, animation::AnimationHandler, @@ -33,13 +33,14 @@ use crate::properties::{ transition::TransitionHandler, ui::ColorSchemeHandler, }; -use crate::properties::{Property, PropertyId}; +use crate::properties::{CSSWideKeyword, Property, PropertyId}; use crate::traits::{PropertyHandler, ToCss}; use crate::values::ident::DashedIdent; use crate::values::string::CowArcStr; #[cfg(feature = "visitor")] use crate::visitor::Visit; use cssparser::*; +use indexmap::IndexMap; /// A CSS declaration block. /// @@ -515,7 +516,10 @@ pub(crate) struct DeclarationHandler<'i> { color_scheme: ColorSchemeHandler, fallback: FallbackHandler, prefix: PrefixHandler, - custom_properties: HashMap<'i>, usize>, + all: Option, + direction: Option, + unicode_bidi: Option, + custom_properties: IndexMap<'i>, CustomProperty<'i>>, decls: DeclarationList<'i>, } @@ -552,6 +556,7 @@ impl<'i> DeclarationHandler<'i> { || self.color_scheme.handle_property(property, &mut self.decls, context) || self.fallback.handle_property(property, &mut self.decls, context) || self.prefix.handle_property(property, &mut self.decls, context) + || self.handle_all(property) || self.handle_custom_property(property, context) } @@ -566,18 +571,13 @@ impl<'i> DeclarationHandler<'i> { } if let CustomPropertyName::Custom(name) = &custom.name { - if let Some(index) = self.custom_properties.get(name) { - if self.decls[*index] == *property { + if let Some(prev) = self.custom_properties.get_mut(name) { + if prev.value == custom.value { return true; } - let mut custom = custom.clone(); - self.add_conditional_fallbacks(&mut custom, context); - self.decls[*index] = Property::Custom(custom); + *prev = custom.clone(); } else { - self.custom_properties.insert(name.clone(), self.decls.len()); - let mut custom = custom.clone(); - self.add_conditional_fallbacks(&mut custom, context); - self.decls.push(Property::Custom(custom)); + self.custom_properties.insert(name.clone(), custom.clone()); } return true; @@ -587,6 +587,32 @@ impl<'i> DeclarationHandler<'i> { false } + fn handle_all(&mut self, property: &Property<'i>) -> bool { + // The `all` property resets all properies except `unicode-bidi`, `direction`, and custom properties. + // https://drafts.csswg.org/css-cascade-5/#all-shorthand + match property { + Property::UnicodeBidi(bidi) => { + self.unicode_bidi = Some(*bidi); + true + } + Property::Direction(direction) => { + self.direction = Some(*direction); + true + } + Property::All(keyword) => { + *self = DeclarationHandler { + custom_properties: std::mem::take(&mut self.custom_properties), + unicode_bidi: self.unicode_bidi.clone(), + direction: self.direction.clone(), + all: Some(keyword.clone()), + ..Default::default() + }; + true + } + _ => false, + } + } + fn add_conditional_fallbacks( &self, custom: &mut CustomProperty<'i>, @@ -607,6 +633,21 @@ impl<'i> DeclarationHandler<'i> { } pub fn finalize(&mut self, context: &mut PropertyHandlerContext<'i, '_>) { + // Always place the `all` property first. Previous properties will have been omitted. + if let Some(all) = std::mem::take(&mut self.all) { + self.decls.push(Property::All(all)); + } + if let Some(direction) = std::mem::take(&mut self.direction) { + self.decls.push(Property::Direction(direction)); + } + if let Some(unicode_bidi) = std::mem::take(&mut self.unicode_bidi) { + self.decls.push(Property::UnicodeBidi(unicode_bidi)); + } + for (_, mut value) in std::mem::take(&mut self.custom_properties) { + self.add_conditional_fallbacks(&mut value, context); + self.decls.push(Property::Custom(value)); + } + self.background.finalize(&mut self.decls, context); self.border.finalize(&mut self.decls, context); self.outline.finalize(&mut self.decls, context); @@ -634,6 +675,5 @@ impl<'i> DeclarationHandler<'i> { self.color_scheme.finalize(&mut self.decls, context); self.fallback.finalize(&mut self.decls, context); self.prefix.finalize(&mut self.decls, context); - self.custom_properties.clear(); } } diff --git a/src/lib.rs b/src/lib.rs index f13b7c48..dd3b0fe8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21435,26 +21435,26 @@ mod tests { indoc! {r#" @keyframes foo { from { - --custom: #ff0; opacity: 0; + --custom: #ff0; } to { - --custom: #ee00be; opacity: 1; + --custom: #ee00be; } } @supports (color: lab(0% 0 0)) { @keyframes foo { from { - --custom: #ff0; opacity: 0; + --custom: #ff0; } to { - --custom: lab(50.998% 125.506 -50.7078); opacity: 1; + --custom: lab(50.998% 125.506 -50.7078); } } } @@ -23457,8 +23457,8 @@ mod tests { } .EgL3uq_foo { - --foo: red; color: var(--foo); + --foo: red; } "#}, map! { @@ -23507,10 +23507,10 @@ mod tests { } .EgL3uq_foo { - --EgL3uq_foo: red; - --EgL3uq_bar: green; color: var(--EgL3uq_foo); font-palette: --EgL3uq_Cooler; + --EgL3uq_foo: red; + --EgL3uq_bar: green; } .EgL3uq_bar { @@ -27244,4 +27244,31 @@ mod tests { }, ); } + + #[test] + fn test_all() { + minify_test(".foo { all: initial; all: initial }", ".foo{all:initial}"); + minify_test(".foo { all: initial; all: revert }", ".foo{all:revert}"); + minify_test(".foo { background: red; all: revert-layer }", ".foo{all:revert-layer}"); + minify_test( + ".foo { background: red; all: revert-layer; background: green }", + ".foo{all:revert-layer;background:green}", + ); + minify_test( + ".foo { --test: red; all: revert-layer }", + ".foo{all:revert-layer;--test:red}", + ); + minify_test( + ".foo { unicode-bidi: embed; all: revert-layer }", + ".foo{all:revert-layer;unicode-bidi:embed}", + ); + minify_test( + ".foo { direction: rtl; all: revert-layer }", + ".foo{all:revert-layer;direction:rtl}", + ); + minify_test( + ".foo { direction: rtl; all: revert-layer; direction: ltr }", + ".foo{all:revert-layer;direction:ltr}", + ); + } } diff --git a/src/properties/mod.rs b/src/properties/mod.rs index 845140f9..98756755 100644 --- a/src/properties/mod.rs +++ b/src/properties/mod.rs @@ -123,6 +123,7 @@ pub mod ui; use crate::declaration::DeclarationBlock; use crate::error::{ParserError, PrinterError}; use crate::logical::{LogicalGroup, PropertyCategory}; +use crate::macros::enum_property; use crate::parser::starts_with_ignore_ascii_case; use crate::parser::ParserOptions; use crate::prefixes::Feature; @@ -688,6 +689,8 @@ macro_rules! define_properties { $(#[$meta])* $property($type, $($vp)?), )+ + /// The [all](https://drafts.csswg.org/css-cascade-5/#all-shorthand) shorthand property. + All(CSSWideKeyword), /// An unparsed property. Unparsed(UnparsedProperty<'i>), /// A custom or unknown property. @@ -710,6 +713,7 @@ macro_rules! define_properties { } }, )+ + PropertyId::All => return Ok(Property::All(CSSWideKeyword::parse(input)?)), PropertyId::Custom(name) => return Ok(Property::Custom(CustomProperty::parse(name, input, options)?)), _ => {} }; @@ -731,6 +735,7 @@ macro_rules! define_properties { $(#[$meta])* $property(_, $(vp_name!($vp, p))?) => PropertyId::$property$((*vp_name!($vp, p)))?, )+ + All(_) => PropertyId::All, Unparsed(unparsed) => unparsed.property_id.clone(), Custom(custom) => PropertyId::Custom(custom.name.clone()) } @@ -779,6 +784,7 @@ macro_rules! define_properties { val.to_css(dest) } )+ + All(keyword) => keyword.to_css(dest), Unparsed(unparsed) => { unparsed.value.to_css(dest, false) } @@ -838,6 +844,7 @@ macro_rules! define_properties { ($name, get_prefix!($($vp)?)) }, )+ + All(_) => ("all", VendorPrefix::None), Unparsed(unparsed) => { let mut prefix = unparsed.property_id.prefix(); if prefix.is_empty() { @@ -966,7 +973,10 @@ macro_rules! define_properties { s.serialize_field("value", value)?; } )+ - _ => unreachable!() + All(value) => { + s.serialize_field("value", value)?; + } + Unparsed(_) | Custom(_) => unreachable!() } s.end() @@ -1072,7 +1082,10 @@ macro_rules! define_properties { Ok(Property::Custom(value)) } } - PropertyId::All => unreachable!() + PropertyId::All => { + let value = CSSWideKeyword::deserialize(deserializer)?; + Ok(Property::All(value)) + } } } } @@ -1134,6 +1147,17 @@ macro_rules! define_properties { with_prefix!($($vp)?) }, )+ + { + property!("all"); + #[derive(schemars::JsonSchema)] + struct T { + #[schemars(rename = "property", schema_with = "property")] + _property: u8, + #[schemars(rename = "value")] + _value: CSSWideKeyword + } + T::json_schema(gen) + }, { property!("unparsed"); @@ -1513,6 +1537,10 @@ define_properties! { // https://w3c.github.io/csswg-drafts/css-size-adjust/ "text-size-adjust": TextSizeAdjust(TextSizeAdjust, VendorPrefix) / WebKit / Moz / Ms, + // https://drafts.csswg.org/css-writing-modes-3/ + "direction": Direction(Direction), + "unicode-bidi": UnicodeBidi(UnicodeBidi), + // https://www.w3.org/TR/css-break-3/ "box-decoration-break": BoxDecorationBreak(BoxDecorationBreak, VendorPrefix) / WebKit, @@ -1667,3 +1695,19 @@ impl ToCss for Vec { Ok(()) } } + +enum_property! { + /// A [CSS-wide keyword](https://drafts.csswg.org/css-cascade-5/#defaulting-keywords). + pub enum CSSWideKeyword { + /// The property's initial value. + "initial": Initial, + /// The property's computed value on the parent element. + "inherit": Inherit, + /// Either inherit or initial depending on whether the property is inherited. + "unset": Unset, + /// Rolls back the cascade to the cascaded value of the earlier origin. + "revert": Revert, + /// Rolls back the cascade to the value of the previous cascade layer. + "revert-layer": RevertLayer, + } +} diff --git a/src/properties/prefix_handler.rs b/src/properties/prefix_handler.rs index 78dd7d09..7ad2dfe8 100644 --- a/src/properties/prefix_handler.rs +++ b/src/properties/prefix_handler.rs @@ -141,12 +141,6 @@ macro_rules! define_fallbacks { (val, paste::paste! { &mut self.[<$name:snake>] }) } )+ - PropertyId::All => { - let mut unparsed = val.clone(); - context.add_unparsed_fallbacks(&mut unparsed); - dest.push(Property::Unparsed(unparsed)); - return true - }, _ => return false }; diff --git a/src/properties/text.rs b/src/properties/text.rs index 4c7b6fde..e22f6fab 100644 --- a/src/properties/text.rs +++ b/src/properties/text.rs @@ -1599,3 +1599,31 @@ impl FallbackValues for SmallVec<[TextShadow; 1]> { res } } + +enum_property! { + /// A value for the [direction](https://drafts.csswg.org/css-writing-modes-3/#direction) property. + pub enum Direction { + /// This value sets inline base direction (bidi directionality) to line-left-to-line-right. + Ltr, + /// This value sets inline base direction (bidi directionality) to line-right-to-line-left. + Rtl, + } +} + +enum_property! { + /// A value for the [unicode-bidi](https://drafts.csswg.org/css-writing-modes-3/#unicode-bidi) property. + pub enum UnicodeBidi { + /// The box does not open an additional level of embedding. + "normal": Normal, + /// If the box is inline, this value creates a directional embedding by opening an additional level of embedding. + "embed": Embed, + /// On an inline box, this bidi-isolates its contents. + "isolate": Isolate, + /// This value puts the box’s immediate inline content in a directional override. + "bidi-override": BidiOverride, + /// This combines the isolation behavior of isolate with the directional override behavior of bidi-override. + "isolate-override": IsolateOverride, + /// This value behaves as isolate except that the base directionality is determined using a heuristic rather than the direction property. + "plaintext": Plaintext, + } +} From a4cc0246d28c364c0f4e16552685bd911444bdc5 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Tue, 14 May 2024 04:51:41 +0200 Subject: [PATCH 002/152] Prevent simplifying `translate: none;` and `scale: none;` (#703) --- node/ast.d.ts | 78 ++++++++++++----------- src/lib.rs | 4 +- src/properties/transform.rs | 119 +++++++++++++++++++++++------------- 3 files changed, 120 insertions(+), 81 deletions(-) diff --git a/node/ast.d.ts b/node/ast.d.ts index bd6e0e98..11e92001 100644 --- a/node/ast.d.ts +++ b/node/ast.d.ts @@ -1,4 +1,4 @@ -/* tslint:disable */ +/* eslint-disable */ /** * This file was automatically generated by json-schema-to-typescript. * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, @@ -5615,6 +5615,48 @@ export type Perspective = type: "length"; value: Length; }; +/** + * A value for the [translate](https://drafts.csswg.org/css-transforms-2/#propdef-translate) property. + */ +export type Translate = + | "None" + | { + XYZ: { + /** + * The x translation. + */ + x: DimensionPercentageFor_LengthValue; + /** + * The y translation. + */ + y: DimensionPercentageFor_LengthValue; + /** + * The z translation. + */ + z: Length; + }; + }; +/** + * A value for the [scale](https://drafts.csswg.org/css-transforms-2/#propdef-scale) property. + */ +export type Scale = + | "None" + | { + XYZ: { + /** + * Scale on the x axis. + */ + x: NumberOrPercentage; + /** + * Scale on the y axis. + */ + y: NumberOrPercentage; + /** + * Scale on the z axis. + */ + z: NumberOrPercentage; + }; + }; /** * Defines how text case should be transformed in the [text-transform](https://www.w3.org/TR/2021/CRD-css-text-3-20210422/#text-transform-property) property. */ @@ -8519,23 +8561,6 @@ export interface Matrix3DForFloat { m43: number; m44: number; } -/** - * A value for the [translate](https://drafts.csswg.org/css-transforms-2/#propdef-translate) property. - */ -export interface Translate { - /** - * The x translation. - */ - x: DimensionPercentageFor_LengthValue; - /** - * The y translation. - */ - y: DimensionPercentageFor_LengthValue; - /** - * The z translation. - */ - z: Length; -} /** * A value for the [rotate](https://drafts.csswg.org/css-transforms-2/#propdef-rotate) property. */ @@ -8557,23 +8582,6 @@ export interface Rotate { */ z: number; } -/** - * A value for the [scale](https://drafts.csswg.org/css-transforms-2/#propdef-scale) property. - */ -export interface Scale { - /** - * Scale on the x axis. - */ - x: NumberOrPercentage; - /** - * Scale on the y axis. - */ - y: NumberOrPercentage; - /** - * Scale on the z axis. - */ - z: NumberOrPercentage; -} /** * A value for the [text-transform](https://www.w3.org/TR/2021/CRD-css-text-3-20210422/#text-transform-property) property. */ diff --git a/src/lib.rs b/src/lib.rs index dd3b0fe8..b63e3ec8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11645,7 +11645,7 @@ mod tests { minify_test(".foo { translate: 1px 0px 0px }", ".foo{translate:1px}"); minify_test(".foo { translate: 1px 2px 0px }", ".foo{translate:1px 2px}"); minify_test(".foo { translate: 1px 0px 2px }", ".foo{translate:1px 0 2px}"); - minify_test(".foo { translate: none }", ".foo{translate:0}"); + minify_test(".foo { translate: none }", ".foo{translate:none}"); minify_test(".foo { rotate: 10deg }", ".foo{rotate:10deg}"); minify_test(".foo { rotate: z 10deg }", ".foo{rotate:10deg}"); minify_test(".foo { rotate: 0 0 1 10deg }", ".foo{rotate:10deg}"); @@ -11659,7 +11659,7 @@ mod tests { minify_test(".foo { scale: 1 }", ".foo{scale:1}"); minify_test(".foo { scale: 1 1 }", ".foo{scale:1}"); minify_test(".foo { scale: 1 1 1 }", ".foo{scale:1}"); - minify_test(".foo { scale: none }", ".foo{scale:1}"); + minify_test(".foo { scale: none }", ".foo{scale:none}"); minify_test(".foo { scale: 1 0 }", ".foo{scale:1 0}"); minify_test(".foo { scale: 1 0 1 }", ".foo{scale:1 0}"); minify_test(".foo { scale: 1 0 0 }", ".foo{scale:1 0 0}"); diff --git a/src/properties/transform.rs b/src/properties/transform.rs index 75cd909a..ae390ae3 100644 --- a/src/properties/transform.rs +++ b/src/properties/transform.rs @@ -1457,23 +1457,25 @@ impl ToCss for Perspective { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))] #[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))] -pub struct Translate { - /// The x translation. - pub x: LengthPercentage, - /// The y translation. - pub y: LengthPercentage, - /// The z translation. - pub z: Length, +pub enum Translate { + /// The "none" keyword. + None, + + /// The x, y, and z translations. + XYZ { + /// The x translation. + x: LengthPercentage, + /// The y translation. + y: LengthPercentage, + /// The z translation. + z: Length, + }, } impl<'i> Parse<'i> for Translate { fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() { - return Ok(Translate { - x: LengthPercentage::zero(), - y: LengthPercentage::zero(), - z: Length::zero(), - }); + return Ok(Translate::None); } let x = LengthPercentage::parse(input)?; @@ -1484,7 +1486,7 @@ impl<'i> Parse<'i> for Translate { None }; - Ok(Translate { + Ok(Translate::XYZ { x, y: y.unwrap_or(LengthPercentage::zero()), z: z.unwrap_or(Length::zero()), @@ -1497,15 +1499,23 @@ impl ToCss for Translate { where W: std::fmt::Write, { - self.x.to_css(dest)?; - if !self.y.is_zero() || !self.z.is_zero() { - dest.write_char(' ')?; - self.y.to_css(dest)?; - if !self.z.is_zero() { - dest.write_char(' ')?; - self.z.to_css(dest)?; + match self { + Translate::None => { + dest.write_str("none")?; } - } + Translate::XYZ { x, y, z } => { + x.to_css(dest)?; + if !y.is_zero() || !z.is_zero() { + dest.write_char(' ')?; + y.to_css(dest)?; + if !z.is_zero() { + dest.write_char(' ')?; + z.to_css(dest)?; + } + } + } + }; + Ok(()) } } @@ -1513,7 +1523,12 @@ impl ToCss for Translate { impl Translate { /// Converts the translation to a transform function. pub fn to_transform(&self) -> Transform { - Transform::Translate3d(self.x.clone(), self.y.clone(), self.z.clone()) + match self { + Translate::None => { + Transform::Translate3d(LengthPercentage::zero(), LengthPercentage::zero(), Length::zero()) + } + Translate::XYZ { x, y, z } => Transform::Translate3d(x.clone(), y.clone(), z.clone()), + } } } @@ -1610,23 +1625,25 @@ impl Rotate { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))] #[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))] -pub struct Scale { - /// Scale on the x axis. - pub x: NumberOrPercentage, - /// Scale on the y axis. - pub y: NumberOrPercentage, - /// Scale on the z axis. - pub z: NumberOrPercentage, +pub enum Scale { + /// The "none" keyword. + None, + + /// Scale on the x, y, and z axis. + XYZ { + /// Scale on the x axis. + x: NumberOrPercentage, + /// Scale on the y axis. + y: NumberOrPercentage, + /// Scale on the z axis. + z: NumberOrPercentage, + }, } impl<'i> Parse<'i> for Scale { fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() { - return Ok(Scale { - x: NumberOrPercentage::Number(1.0), - y: NumberOrPercentage::Number(1.0), - z: NumberOrPercentage::Number(1.0), - }); + return Ok(Scale::None); } let x = NumberOrPercentage::parse(input)?; @@ -1637,7 +1654,7 @@ impl<'i> Parse<'i> for Scale { None }; - Ok(Scale { + Ok(Scale::XYZ { x: x.clone(), y: y.unwrap_or(x), z: z.unwrap_or(NumberOrPercentage::Number(1.0)), @@ -1650,14 +1667,21 @@ impl ToCss for Scale { where W: std::fmt::Write, { - self.x.to_css(dest)?; - let zv: f32 = (&self.z).into(); - if self.y != self.x || zv != 1.0 { - dest.write_char(' ')?; - self.y.to_css(dest)?; - if zv != 1.0 { - dest.write_char(' ')?; - self.z.to_css(dest)?; + match self { + Scale::None => { + dest.write_str("none")?; + } + Scale::XYZ { x, y, z } => { + x.to_css(dest)?; + let zv: f32 = z.into(); + if y != x || zv != 1.0 { + dest.write_char(' ')?; + y.to_css(dest)?; + if zv != 1.0 { + dest.write_char(' ')?; + z.to_css(dest)?; + } + } } } @@ -1668,7 +1692,14 @@ impl ToCss for Scale { impl Scale { /// Converts the scale to a transform function. pub fn to_transform(&self) -> Transform { - Transform::Scale3d(self.x.clone(), self.y.clone(), self.z.clone()) + match self { + Scale::None => Transform::Scale3d( + NumberOrPercentage::Number(1.0), + NumberOrPercentage::Number(1.0), + NumberOrPercentage::Number(1.0), + ), + Scale::XYZ { x, y, z } => Transform::Scale3d(x.clone(), y.clone(), z.clone()), + } } } From 029c88ee04e1599505488a8e4f4a374d45053236 Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Mon, 13 May 2024 21:18:59 -0700 Subject: [PATCH 003/152] fixup ast --- Cargo.lock | 56 ++++++++++++++++----------------- Cargo.toml | 4 +-- napi/Cargo.toml | 2 +- node/ast.d.ts | 62 +++++++++++++++++-------------------- scripts/build-ast.js | 12 +++++++ selectors/Cargo.toml | 4 +-- src/properties/transform.rs | 7 +++-- 7 files changed, 79 insertions(+), 68 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7358e44c..1aba540b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -403,7 +403,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.39", + "syn 2.0.63", ] [[package]] @@ -413,7 +413,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad291aa74992b9b7a7e88c38acbbf6ad7e107f1d90ee8775b7bc1fc3394f485c" dependencies = [ "quote", - "syn 2.0.39", + "syn 2.0.63", ] [[package]] @@ -921,7 +921,7 @@ dependencies = [ "napi-derive-backend", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.63", ] [[package]] @@ -936,7 +936,7 @@ dependencies = [ "quote", "regex", "semver", - "syn 2.0.39", + "syn 2.0.63", ] [[package]] @@ -1122,7 +1122,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.63", ] [[package]] @@ -1223,9 +1223,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" dependencies = [ "unicode-ident", ] @@ -1252,9 +1252,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.33" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -1420,9 +1420,9 @@ dependencies = [ [[package]] name = "schemars" -version = "0.8.15" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f7b0ce13155372a76ee2e1c5ffba1fe61ede73fbea5630d61eee6fac4929c0c" +checksum = "fc6e7ed6919cb46507fb01ff1654309219f62b4d603822501b0b80d42f6f21ef" dependencies = [ "dyn-clone", "schemars_derive", @@ -1433,14 +1433,14 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.15" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e85e2a16b12bdb763244c69ab79363d71db2b4b918a2def53f80b02e0574b13c" +checksum = "185f2b7aa7e02d418e453790dde16890256bbd2bcd04b7dc5348811052b53f49" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 1.0.109", + "syn 2.0.63", ] [[package]] @@ -1463,9 +1463,9 @@ checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" [[package]] name = "serde" -version = "1.0.192" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" dependencies = [ "serde_derive", ] @@ -1491,24 +1491,24 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.192" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.63", ] [[package]] name = "serde_derive_internals" -version = "0.26.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.63", ] [[package]] @@ -1614,9 +1614,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.39" +version = "2.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" dependencies = [ "proc-macro2", "quote", @@ -1680,7 +1680,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.63", ] [[package]] @@ -1793,7 +1793,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.63", "wasm-bindgen-shared", ] @@ -1815,7 +1815,7 @@ checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.63", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1964,5 +1964,5 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.63", ] diff --git a/Cargo.toml b/Cargo.toml index 795b25ac..73ac94fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ into_owned = ["static-self", "static-self/smallvec", "parcel_selectors/into_owne substitute_variables = ["visitor", "into_owned"] [dependencies] -serde = { version = "1.0.123", features = ["derive"], optional = true } +serde = { version = "1.0.201", features = ["derive"], optional = true } cssparser = "0.33.0" cssparser-color = "0.1.0" parcel_selectors = { version = "0.26.4", path = "./selectors" } @@ -71,7 +71,7 @@ rayon = { version = "1.5.1", optional = true } dashmap = { version = "5.0.0", optional = true } serde_json = { version = "1.0.78", optional = true } lightningcss-derive = { version = "=1.0.0-alpha.42", path = "./derive", optional = true } -schemars = { version = "0.8.11", features = ["smallvec"], optional = true } +schemars = { version = "0.8.19", features = ["smallvec"], optional = true } static-self = { version = "0.1.0", path = "static-self", optional = true } [target.'cfg(target_os = "macos")'.dependencies] diff --git a/napi/Cargo.toml b/napi/Cargo.toml index 3360a9cd..b296c3b6 100644 --- a/napi/Cargo.toml +++ b/napi/Cargo.toml @@ -13,7 +13,7 @@ visitor = ["lightningcss/visitor"] bundler = ["dep:crossbeam-channel", "dep:rayon"] [dependencies] -serde = { version = "1.0.123", features = ["derive"] } +serde = { version = "1.0.201", features = ["derive"] } serde_bytes = "0.11.5" cssparser = "0.33.0" lightningcss = { version = "1.0.0-alpha.54", path = "../", features = ["nodejs", "serde"] } diff --git a/node/ast.d.ts b/node/ast.d.ts index 11e92001..6015ff44 100644 --- a/node/ast.d.ts +++ b/node/ast.d.ts @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* tslint:disable */ /** * This file was automatically generated by json-schema-to-typescript. * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, @@ -5619,44 +5619,40 @@ export type Perspective = * A value for the [translate](https://drafts.csswg.org/css-transforms-2/#propdef-translate) property. */ export type Translate = - | "None" + | "none" | { - XYZ: { - /** - * The x translation. - */ - x: DimensionPercentageFor_LengthValue; - /** - * The y translation. - */ - y: DimensionPercentageFor_LengthValue; - /** - * The z translation. - */ - z: Length; - }; - }; + /** + * The x translation. + */ + x: DimensionPercentageFor_LengthValue; + /** + * The y translation. + */ + y: DimensionPercentageFor_LengthValue; + /** + * The z translation. + */ + z: Length; + }; /** * A value for the [scale](https://drafts.csswg.org/css-transforms-2/#propdef-scale) property. */ export type Scale = - | "None" + | "none" | { - XYZ: { - /** - * Scale on the x axis. - */ - x: NumberOrPercentage; - /** - * Scale on the y axis. - */ - y: NumberOrPercentage; - /** - * Scale on the z axis. - */ - z: NumberOrPercentage; - }; - }; + /** + * Scale on the x axis. + */ + x: NumberOrPercentage; + /** + * Scale on the y axis. + */ + y: NumberOrPercentage; + /** + * Scale on the z axis. + */ + z: NumberOrPercentage; + }; /** * Defines how text case should be transformed in the [text-transform](https://www.w3.org/TR/2021/CRD-css-text-3-20210422/#text-transform-property) property. */ diff --git a/scripts/build-ast.js b/scripts/build-ast.js index 4e6bbebf..7b7fc020 100644 --- a/scripts/build-ast.js +++ b/scripts/build-ast.js @@ -55,6 +55,18 @@ compileFromFile('node/ast.json', { if (path.node.name.startsWith('GenericBorderFor_LineStyleAnd_')) { path.node.name = 'GenericBorderFor_LineStyle'; } + }, + TSTypeAliasDeclaration(path) { + // Workaround for schemars not supporting untagged variants. + // https://github.com/GREsau/schemars/issues/222 + if ( + (path.node.id.name === 'Translate' || path.node.id.name === 'Scale') && + path.node.typeAnnotation.type === 'TSUnionType' && + path.node.typeAnnotation.types[1].type === 'TSTypeLiteral' && + path.node.typeAnnotation.types[1].members[0].key.name === 'xyz' + ) { + path.get('typeAnnotation.types.1').replaceWith(path.node.typeAnnotation.types[1].members[0].typeAnnotation.typeAnnotation); + } } }); diff --git a/selectors/Cargo.toml b/selectors/Cargo.toml index c3dd9402..481c468b 100644 --- a/selectors/Cargo.toml +++ b/selectors/Cargo.toml @@ -29,8 +29,8 @@ log = "0.4" phf = "0.10" precomputed-hash = "0.1" smallvec = "1.0" -serde = { version = "1.0.123", features = ["derive"], optional = true } -schemars = { version = "0.8.11", features = ["smallvec"], optional = true } +serde = { version = "1.0.201", features = ["derive"], optional = true } +schemars = { version = "0.8.19", features = ["smallvec"], optional = true } static-self = { version = "0.1.0", path = "../static-self", optional = true } [build-dependencies] diff --git a/src/properties/transform.rs b/src/properties/transform.rs index ae390ae3..cbf6d60b 100644 --- a/src/properties/transform.rs +++ b/src/properties/transform.rs @@ -19,6 +19,7 @@ use crate::vendor_prefix::VendorPrefix; use crate::visitor::Visit; use cssparser::*; use std::f32::consts::PI; +use crate::serialization::ValueWrapper; /// A value for the [transform](https://www.w3.org/TR/2019/CR-css-transforms-1-20190214/#propdef-transform) property. #[derive(Debug, Clone, PartialEq, Default)] @@ -1454,7 +1455,7 @@ impl ToCss for Perspective { /// A value for the [translate](https://drafts.csswg.org/css-transforms-2/#propdef-translate) property. #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "visitor", derive(Visit))] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(rename_all = "lowercase"))] #[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))] #[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))] pub enum Translate { @@ -1462,6 +1463,7 @@ pub enum Translate { None, /// The x, y, and z translations. + #[cfg_attr(feature = "serde", serde(untagged))] XYZ { /// The x translation. x: LengthPercentage, @@ -1622,7 +1624,7 @@ impl Rotate { /// A value for the [scale](https://drafts.csswg.org/css-transforms-2/#propdef-scale) property. #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "visitor", derive(Visit))] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(rename_all = "lowercase"))] #[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))] #[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))] pub enum Scale { @@ -1630,6 +1632,7 @@ pub enum Scale { None, /// Scale on the x, y, and z axis. + #[cfg_attr(feature = "serde", serde(untagged))] XYZ { /// Scale on the x axis. x: NumberOrPercentage, From 06ba62f6d1af563cd8ad31dba13844f434345720 Mon Sep 17 00:00:00 2001 From: magic-akari Date: Tue, 14 May 2024 12:19:25 +0800 Subject: [PATCH 004/152] Fix box-shadow combination of `oklch` and `currentColor` (#722) --- src/lib.rs | 20 ++++++++++++++++++++ src/properties/box_shadow.rs | 4 ++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b63e3ec8..90b4ca33 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27182,6 +27182,26 @@ mod tests { ..Browsers::default() }, ); + prefix_test( + r#" + .foo { + box-shadow: + oklch(100% 0 0deg / 50%) 0 0.63rem 0.94rem -0.19rem, + currentColor 0 0.44rem 0.8rem -0.58rem; + } + "#, + indoc! { r#" + .foo { + box-shadow: 0 .63rem .94rem -.19rem #ffffff80, 0 .44rem .8rem -.58rem; + box-shadow: 0 .63rem .94rem -.19rem lab(100% 0 0 / .5), 0 .44rem .8rem -.58rem; + } + "#}, + Browsers { + chrome: Some(95 << 16), + ..Browsers::default() + }, + ); + prefix_test( ".foo { color: light-dark(var(--light), var(--dark)); }", indoc! { r#" diff --git a/src/properties/box_shadow.rs b/src/properties/box_shadow.rs index 0d757caf..de60f4d3 100644 --- a/src/properties/box_shadow.rs +++ b/src/properties/box_shadow.rs @@ -208,7 +208,7 @@ impl BoxShadowHandler { let rgb = box_shadows .iter() .map(|shadow| BoxShadow { - color: shadow.color.to_rgb().unwrap(), + color: shadow.color.to_rgb().unwrap_or_else(|_| shadow.color.clone()), ..shadow.clone() }) .collect(); @@ -236,7 +236,7 @@ impl BoxShadowHandler { let lab = box_shadows .iter() .map(|shadow| BoxShadow { - color: shadow.color.to_lab().unwrap(), + color: shadow.color.to_lab().unwrap_or_else(|_| shadow.color.clone()), ..shadow.clone() }) .collect(); From 4a168e94a671492ba834ed7682dc41ecf66205be Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Mon, 13 May 2024 21:18:59 -0700 Subject: [PATCH 005/152] fixup ast --- Cargo.lock | 56 ++++++++++++++++----------------- Cargo.toml | 4 +-- napi/Cargo.toml | 2 +- node/ast.d.ts | 62 +++++++++++++++++-------------------- scripts/build-ast.js | 12 +++++++ selectors/Cargo.toml | 4 +-- src/properties/transform.rs | 6 ++-- 7 files changed, 78 insertions(+), 68 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7358e44c..1aba540b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -403,7 +403,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.39", + "syn 2.0.63", ] [[package]] @@ -413,7 +413,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad291aa74992b9b7a7e88c38acbbf6ad7e107f1d90ee8775b7bc1fc3394f485c" dependencies = [ "quote", - "syn 2.0.39", + "syn 2.0.63", ] [[package]] @@ -921,7 +921,7 @@ dependencies = [ "napi-derive-backend", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.63", ] [[package]] @@ -936,7 +936,7 @@ dependencies = [ "quote", "regex", "semver", - "syn 2.0.39", + "syn 2.0.63", ] [[package]] @@ -1122,7 +1122,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.63", ] [[package]] @@ -1223,9 +1223,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" dependencies = [ "unicode-ident", ] @@ -1252,9 +1252,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.33" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -1420,9 +1420,9 @@ dependencies = [ [[package]] name = "schemars" -version = "0.8.15" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f7b0ce13155372a76ee2e1c5ffba1fe61ede73fbea5630d61eee6fac4929c0c" +checksum = "fc6e7ed6919cb46507fb01ff1654309219f62b4d603822501b0b80d42f6f21ef" dependencies = [ "dyn-clone", "schemars_derive", @@ -1433,14 +1433,14 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.15" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e85e2a16b12bdb763244c69ab79363d71db2b4b918a2def53f80b02e0574b13c" +checksum = "185f2b7aa7e02d418e453790dde16890256bbd2bcd04b7dc5348811052b53f49" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 1.0.109", + "syn 2.0.63", ] [[package]] @@ -1463,9 +1463,9 @@ checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" [[package]] name = "serde" -version = "1.0.192" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" dependencies = [ "serde_derive", ] @@ -1491,24 +1491,24 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.192" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.63", ] [[package]] name = "serde_derive_internals" -version = "0.26.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.63", ] [[package]] @@ -1614,9 +1614,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.39" +version = "2.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" dependencies = [ "proc-macro2", "quote", @@ -1680,7 +1680,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.63", ] [[package]] @@ -1793,7 +1793,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.63", "wasm-bindgen-shared", ] @@ -1815,7 +1815,7 @@ checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.63", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1964,5 +1964,5 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.63", ] diff --git a/Cargo.toml b/Cargo.toml index 795b25ac..73ac94fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ into_owned = ["static-self", "static-self/smallvec", "parcel_selectors/into_owne substitute_variables = ["visitor", "into_owned"] [dependencies] -serde = { version = "1.0.123", features = ["derive"], optional = true } +serde = { version = "1.0.201", features = ["derive"], optional = true } cssparser = "0.33.0" cssparser-color = "0.1.0" parcel_selectors = { version = "0.26.4", path = "./selectors" } @@ -71,7 +71,7 @@ rayon = { version = "1.5.1", optional = true } dashmap = { version = "5.0.0", optional = true } serde_json = { version = "1.0.78", optional = true } lightningcss-derive = { version = "=1.0.0-alpha.42", path = "./derive", optional = true } -schemars = { version = "0.8.11", features = ["smallvec"], optional = true } +schemars = { version = "0.8.19", features = ["smallvec"], optional = true } static-self = { version = "0.1.0", path = "static-self", optional = true } [target.'cfg(target_os = "macos")'.dependencies] diff --git a/napi/Cargo.toml b/napi/Cargo.toml index 3360a9cd..b296c3b6 100644 --- a/napi/Cargo.toml +++ b/napi/Cargo.toml @@ -13,7 +13,7 @@ visitor = ["lightningcss/visitor"] bundler = ["dep:crossbeam-channel", "dep:rayon"] [dependencies] -serde = { version = "1.0.123", features = ["derive"] } +serde = { version = "1.0.201", features = ["derive"] } serde_bytes = "0.11.5" cssparser = "0.33.0" lightningcss = { version = "1.0.0-alpha.54", path = "../", features = ["nodejs", "serde"] } diff --git a/node/ast.d.ts b/node/ast.d.ts index 11e92001..6015ff44 100644 --- a/node/ast.d.ts +++ b/node/ast.d.ts @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* tslint:disable */ /** * This file was automatically generated by json-schema-to-typescript. * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, @@ -5619,44 +5619,40 @@ export type Perspective = * A value for the [translate](https://drafts.csswg.org/css-transforms-2/#propdef-translate) property. */ export type Translate = - | "None" + | "none" | { - XYZ: { - /** - * The x translation. - */ - x: DimensionPercentageFor_LengthValue; - /** - * The y translation. - */ - y: DimensionPercentageFor_LengthValue; - /** - * The z translation. - */ - z: Length; - }; - }; + /** + * The x translation. + */ + x: DimensionPercentageFor_LengthValue; + /** + * The y translation. + */ + y: DimensionPercentageFor_LengthValue; + /** + * The z translation. + */ + z: Length; + }; /** * A value for the [scale](https://drafts.csswg.org/css-transforms-2/#propdef-scale) property. */ export type Scale = - | "None" + | "none" | { - XYZ: { - /** - * Scale on the x axis. - */ - x: NumberOrPercentage; - /** - * Scale on the y axis. - */ - y: NumberOrPercentage; - /** - * Scale on the z axis. - */ - z: NumberOrPercentage; - }; - }; + /** + * Scale on the x axis. + */ + x: NumberOrPercentage; + /** + * Scale on the y axis. + */ + y: NumberOrPercentage; + /** + * Scale on the z axis. + */ + z: NumberOrPercentage; + }; /** * Defines how text case should be transformed in the [text-transform](https://www.w3.org/TR/2021/CRD-css-text-3-20210422/#text-transform-property) property. */ diff --git a/scripts/build-ast.js b/scripts/build-ast.js index 4e6bbebf..7b7fc020 100644 --- a/scripts/build-ast.js +++ b/scripts/build-ast.js @@ -55,6 +55,18 @@ compileFromFile('node/ast.json', { if (path.node.name.startsWith('GenericBorderFor_LineStyleAnd_')) { path.node.name = 'GenericBorderFor_LineStyle'; } + }, + TSTypeAliasDeclaration(path) { + // Workaround for schemars not supporting untagged variants. + // https://github.com/GREsau/schemars/issues/222 + if ( + (path.node.id.name === 'Translate' || path.node.id.name === 'Scale') && + path.node.typeAnnotation.type === 'TSUnionType' && + path.node.typeAnnotation.types[1].type === 'TSTypeLiteral' && + path.node.typeAnnotation.types[1].members[0].key.name === 'xyz' + ) { + path.get('typeAnnotation.types.1').replaceWith(path.node.typeAnnotation.types[1].members[0].typeAnnotation.typeAnnotation); + } } }); diff --git a/selectors/Cargo.toml b/selectors/Cargo.toml index c3dd9402..481c468b 100644 --- a/selectors/Cargo.toml +++ b/selectors/Cargo.toml @@ -29,8 +29,8 @@ log = "0.4" phf = "0.10" precomputed-hash = "0.1" smallvec = "1.0" -serde = { version = "1.0.123", features = ["derive"], optional = true } -schemars = { version = "0.8.11", features = ["smallvec"], optional = true } +serde = { version = "1.0.201", features = ["derive"], optional = true } +schemars = { version = "0.8.19", features = ["smallvec"], optional = true } static-self = { version = "0.1.0", path = "../static-self", optional = true } [build-dependencies] diff --git a/src/properties/transform.rs b/src/properties/transform.rs index ae390ae3..8ff7b968 100644 --- a/src/properties/transform.rs +++ b/src/properties/transform.rs @@ -1454,7 +1454,7 @@ impl ToCss for Perspective { /// A value for the [translate](https://drafts.csswg.org/css-transforms-2/#propdef-translate) property. #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "visitor", derive(Visit))] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(rename_all = "lowercase"))] #[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))] #[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))] pub enum Translate { @@ -1462,6 +1462,7 @@ pub enum Translate { None, /// The x, y, and z translations. + #[cfg_attr(feature = "serde", serde(untagged))] XYZ { /// The x translation. x: LengthPercentage, @@ -1622,7 +1623,7 @@ impl Rotate { /// A value for the [scale](https://drafts.csswg.org/css-transforms-2/#propdef-scale) property. #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "visitor", derive(Visit))] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(rename_all = "lowercase"))] #[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))] #[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))] pub enum Scale { @@ -1630,6 +1631,7 @@ pub enum Scale { None, /// Scale on the x, y, and z axis. + #[cfg_attr(feature = "serde", serde(untagged))] XYZ { /// Scale on the x axis. x: NumberOrPercentage, From 445def9a77f89aa612fecb2f776261fc2c9d8f66 Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Mon, 13 May 2024 22:27:30 -0700 Subject: [PATCH 006/152] fix minifying predefined color spaces fixes #727 --- src/lib.rs | 31 +++++++++++++++++-------------- src/properties/transform.rs | 12 ++++++++++-- src/values/color.rs | 30 +++++++++--------------------- 3 files changed, 36 insertions(+), 37 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 90b4ca33..7d8aff33 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16040,11 +16040,11 @@ mod tests { ); minify_test( ".foo { color: color(display-p3 1 0.5 0); }", - ".foo{color:color(display-p3 1 .5)}", + ".foo{color:color(display-p3 1 .5 0)}", ); minify_test( ".foo { color: color(display-p3 100% 50% 0%); }", - ".foo{color:color(display-p3 1 .5)}", + ".foo{color:color(display-p3 1 .5 0)}", ); minify_test( ".foo { color: color(xyz-d50 0.2005 0.14089 0.4472); }", @@ -16070,24 +16070,27 @@ mod tests { ".foo { color: color(xyz 20.05% 14.089% 44.72%); }", ".foo{color:color(xyz .2005 .14089 .4472)}", ); - minify_test(".foo { color: color(xyz 0.2005 0 0); }", ".foo{color:color(xyz .2005)}"); - minify_test(".foo { color: color(xyz 0 0 0); }", ".foo{color:color(xyz)}"); - minify_test(".foo { color: color(xyz 0 1 0); }", ".foo{color:color(xyz 0 1)}"); - minify_test(".foo { color: color(xyz 0 1); }", ".foo{color:color(xyz 0 1)}"); - minify_test(".foo { color: color(xyz 1); }", ".foo{color:color(xyz 1)}"); - minify_test(".foo { color: color(xyz); }", ".foo{color:color(xyz)}"); + minify_test( + ".foo { color: color(xyz 0.2005 0 0); }", + ".foo{color:color(xyz .2005 0 0)}", + ); + minify_test(".foo { color: color(xyz 0 0 0); }", ".foo{color:color(xyz 0 0 0)}"); + minify_test(".foo { color: color(xyz 0 1 0); }", ".foo{color:color(xyz 0 1 0)}"); minify_test( ".foo { color: color(xyz 0 1 0 / 20%); }", - ".foo{color:color(xyz 0 1/.2)}", + ".foo{color:color(xyz 0 1 0/.2)}", + ); + minify_test( + ".foo { color: color(xyz 0 0 0 / 20%); }", + ".foo{color:color(xyz 0 0 0/.2)}", ); - minify_test(".foo { color: color(xyz / 20%); }", ".foo{color:color(xyz/.2)}"); minify_test( ".foo { color: color(display-p3 100% 50% 0 / 20%); }", - ".foo{color:color(display-p3 1 .5/.2)}", + ".foo{color:color(display-p3 1 .5 0/.2)}", ); minify_test( - ".foo { color: color(display-p3 100% / 20%); }", - ".foo{color:color(display-p3 1/.2)}", + ".foo { color: color(display-p3 100% 0 0 / 20%); }", + ".foo{color:color(display-p3 1 0 0/.2)}", ); minify_test(".foo { color: hsl(none none none) }", ".foo{color:#000}"); minify_test(".foo { color: hwb(none none none) }", ".foo{color:red}"); @@ -20034,7 +20037,7 @@ mod tests { ".foo {{ color: color-mix(in {0}, color({0} -2 -3 -4 / -5), color({0} -4 -6 -8 / -10)) }}", color_space ), - &format!(".foo{{color:color({}/0)}}", result_color_space), + &format!(".foo{{color:color({} 0 0 0/0)}}", result_color_space), ); minify_test( diff --git a/src/properties/transform.rs b/src/properties/transform.rs index 8ff7b968..d58a08fe 100644 --- a/src/properties/transform.rs +++ b/src/properties/transform.rs @@ -1454,7 +1454,11 @@ impl ToCss for Perspective { /// A value for the [translate](https://drafts.csswg.org/css-transforms-2/#propdef-translate) property. #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "visitor", derive(Visit))] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(rename_all = "lowercase"))] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "lowercase") +)] #[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))] #[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))] pub enum Translate { @@ -1623,7 +1627,11 @@ impl Rotate { /// A value for the [scale](https://drafts.csswg.org/css-transforms-2/#propdef-scale) property. #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "visitor", derive(Visit))] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(rename_all = "lowercase"))] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "lowercase") +)] #[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))] #[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))] pub enum Scale { diff --git a/src/values/color.rs b/src/values/color.rs index 524c43c7..efa2bd26 100644 --- a/src/values/color.rs +++ b/src/values/color.rs @@ -1169,15 +1169,9 @@ fn parse_predefined_relative<'i, 't>( // Out of gamut values should not be clamped, i.e. values < 0 or > 1 should be preserved. // The browser will gamut-map the color for the target device that it is rendered on. - let a = input - .try_parse(|input| parse_number_or_percentage(input, parser)) - .unwrap_or(0.0); - let b = input - .try_parse(|input| parse_number_or_percentage(input, parser)) - .unwrap_or(0.0); - let c = input - .try_parse(|input| parse_number_or_percentage(input, parser)) - .unwrap_or(0.0); + let a = input.try_parse(|input| parse_number_or_percentage(input, parser))?; + let b = input.try_parse(|input| parse_number_or_percentage(input, parser))?; + let c = input.try_parse(|input| parse_number_or_percentage(input, parser))?; let alpha = parse_alpha(input, parser)?; let res = match_ignore_ascii_case! { &*&colorspace, @@ -1429,18 +1423,12 @@ where dest.write_str("color(")?; dest.write_str(name)?; - if !dest.minify || a != 0.0 || b != 0.0 || c != 0.0 { - dest.write_char(' ')?; - write_component(a, dest)?; - if !dest.minify || b != 0.0 || c != 0.0 { - dest.write_char(' ')?; - write_component(b, dest)?; - if !dest.minify || c != 0.0 { - dest.write_char(' ')?; - write_component(c, dest)?; - } - } - } + dest.write_char(' ')?; + write_component(a, dest)?; + dest.write_char(' ')?; + write_component(b, dest)?; + dest.write_char(' ')?; + write_component(c, dest)?; if alpha.is_nan() || (alpha - 1.0).abs() > f32::EPSILON { dest.delim('/', true)?; From 83839a98dbe0000acbdf039d968f33e1e8c50277 Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Wed, 15 May 2024 07:25:52 +0200 Subject: [PATCH 007/152] Add granular CSS Modules options (#739) --- Cargo.lock | 23 ++++++ Cargo.toml | 1 + napi/src/lib.rs | 9 +++ src/css_modules.rs | 23 +++++- src/lib.rs | 132 +++++++++++++++++++++++++++++++++++ src/printer.rs | 44 ++++++------ src/properties/animation.rs | 19 +++-- src/properties/grid.rs | 27 +++---- src/rules/keyframes.rs | 7 +- src/selector.rs | 6 +- src/values/ident.rs | 21 +++++- website/pages/css-modules.md | 51 +++++++++----- 12 files changed, 299 insertions(+), 64 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1aba540b..eed17d06 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -444,6 +444,12 @@ dependencies = [ "matches", ] +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "difflib" version = "0.4.0" @@ -786,6 +792,7 @@ dependencies = [ "paste", "pathdiff", "predicates 2.1.5", + "pretty_assertions", "rayon", "schemars", "serde", @@ -1197,6 +1204,16 @@ dependencies = [ "termtree", ] +[[package]] +name = "pretty_assertions" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" +dependencies = [ + "diff", + "yansi", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -1947,6 +1964,12 @@ dependencies = [ "tap", ] +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + [[package]] name = "zerocopy" version = "0.7.32" diff --git a/Cargo.toml b/Cargo.toml index 73ac94fe..a298e0b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -86,6 +86,7 @@ assert_cmd = "2.0" assert_fs = "1.0" predicates = "2.1" serde_json = "1" +pretty_assertions = "1.4.0" [[test]] name = "cli_integration_tests" diff --git a/napi/src/lib.rs b/napi/src/lib.rs index cc3ce78e..9edfb1ce 100644 --- a/napi/src/lib.rs +++ b/napi/src/lib.rs @@ -605,6 +605,9 @@ enum CssModulesOption { struct CssModulesConfig { pattern: Option, dashed_idents: Option, + animation: Option, + grid: Option, + custom_idents: Option, } #[cfg(feature = "bundler")] @@ -713,6 +716,9 @@ fn compile<'i>( Default::default() }, dashed_idents: c.dashed_idents.unwrap_or_default(), + animation: c.animation.unwrap_or(true), + grid: c.grid.unwrap_or(true), + custom_idents: c.custom_idents.unwrap_or(true), }), } } else { @@ -840,6 +846,9 @@ fn compile_bundle< Default::default() }, dashed_idents: c.dashed_idents.unwrap_or_default(), + animation: c.animation.unwrap_or(true), + grid: c.grid.unwrap_or(true), + custom_idents: c.custom_idents.unwrap_or(true), }), } } else { diff --git a/src/css_modules.rs b/src/css_modules.rs index b794bb43..cfe33d71 100644 --- a/src/css_modules.rs +++ b/src/css_modules.rs @@ -25,13 +25,34 @@ use std::hash::{Hash, Hasher}; use std::path::Path; /// Configuration for CSS modules. -#[derive(Default, Clone, Debug)] +#[derive(Clone, Debug)] pub struct Config<'i> { /// The name pattern to use when renaming class names and other identifiers. /// Default is `[hash]_[local]`. pub pattern: Pattern<'i>, /// Whether to rename dashed identifiers, e.g. custom properties. pub dashed_idents: bool, + /// Whether to scope animation names. + /// Default is `true`. + pub animation: bool, + /// Whether to scope grid names. + /// Default is `true`. + pub grid: bool, + /// Whether to scope custom identifiers + /// Default is `true`. + pub custom_idents: bool, +} + +impl<'i> Default for Config<'i> { + fn default() -> Self { + Config { + pattern: Default::default(), + dashed_idents: Default::default(), + animation: true, + grid: true, + custom_idents: true, + } + } } /// A CSS modules class name pattern. diff --git a/src/lib.rs b/src/lib.rs index 7d8aff33..d0ea1982 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -64,6 +64,7 @@ mod tests { use crate::vendor_prefix::VendorPrefix; use cssparser::SourceLocation; use indoc::indoc; + use pretty_assertions::assert_eq; use std::collections::HashMap; fn test(source: &str, expected: &str) { @@ -23053,6 +23054,97 @@ mod tests { Default::default(), ); + css_modules_test( + r#" + .foo { + color: red; + } + + #id { + animation: 2s test; + } + + @keyframes test { + from { color: red } + to { color: yellow } + } + "#, + indoc! {r#" + .EgL3uq_foo { + color: red; + } + + #EgL3uq_id { + animation: 2s test; + } + + @keyframes test { + from { + color: red; + } + + to { + color: #ff0; + } + } + "#}, + map! { + "foo" => "EgL3uq_foo", + "id" => "EgL3uq_id" + }, + HashMap::new(), + crate::css_modules::Config { + animation: false, + // custom_idents: false, + ..Default::default() + }, + ); + + css_modules_test( + r#" + @counter-style circles { + symbols: Ⓐ Ⓑ Ⓒ; + } + + ul { + list-style: circles; + } + + ol { + list-style-type: none; + } + + li { + list-style-type: disc; + } + "#, + indoc! {r#" + @counter-style circles { + symbols: Ⓐ Ⓑ Ⓒ; + } + + ul { + list-style: circles; + } + + ol { + list-style-type: none; + } + + li { + list-style-type: disc; + } + "#}, + map! { + "circles" => "EgL3uq_circles" referenced: true + }, + HashMap::new(), + crate::css_modules::Config { + custom_idents: false, + ..Default::default() + }, + ); + #[cfg(feature = "grid")] css_modules_test( r#" @@ -23135,6 +23227,46 @@ mod tests { Default::default(), ); + #[cfg(feature = "grid")] + css_modules_test( + r#" + .grid { + grid-template-areas: "foo"; + } + + .foo { + grid-area: foo; + } + + .bar { + grid-column-start: foo-start; + } + "#, + indoc! {r#" + .EgL3uq_grid { + grid-template-areas: "foo"; + } + + .EgL3uq_foo { + grid-area: foo; + } + + .EgL3uq_bar { + grid-column-start: foo-start; + } + "#}, + map! { + "foo" => "EgL3uq_foo", + "grid" => "EgL3uq_grid", + "bar" => "EgL3uq_bar" + }, + HashMap::new(), + crate::css_modules::Config { + grid: false, + ..Default::default() + }, + ); + css_modules_test( r#" test { diff --git a/src/printer.rs b/src/printer.rs index 12fe09e3..d8c08e2d 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -267,30 +267,32 @@ impl<'a, 'b, 'c, W: std::fmt::Write + Sized> Printer<'a, 'b, 'c, W> { /// Writes a CSS identifier to the underlying destination, escaping it /// as appropriate. If the `css_modules` option was enabled, then a hash /// is added, and the mapping is added to the CSS module. - pub fn write_ident(&mut self, ident: &str) -> Result<(), PrinterError> { - if let Some(css_module) = &mut self.css_module { - let dest = &mut self.dest; - let mut first = true; - css_module.config.pattern.write( - &css_module.hashes[self.loc.source_index as usize], - &css_module.sources[self.loc.source_index as usize], - ident, - |s| { - self.col += s.len() as u32; - if first { - first = false; - serialize_identifier(s, dest) - } else { - serialize_name(s, dest) - } - }, - )?; + pub fn write_ident(&mut self, ident: &str, handle_css_module: bool) -> Result<(), PrinterError> { + if handle_css_module { + if let Some(css_module) = &mut self.css_module { + let dest = &mut self.dest; + let mut first = true; + css_module.config.pattern.write( + &css_module.hashes[self.loc.source_index as usize], + &css_module.sources[self.loc.source_index as usize], + ident, + |s| { + self.col += s.len() as u32; + if first { + first = false; + serialize_identifier(s, dest) + } else { + serialize_name(s, dest) + } + }, + )?; - css_module.add_local(&ident, &ident, self.loc.source_index); - } else { - serialize_identifier(ident, self)?; + css_module.add_local(&ident, &ident, self.loc.source_index); + return Ok(()); + } } + serialize_identifier(ident, self)?; Ok(()) } diff --git a/src/properties/animation.rs b/src/properties/animation.rs index c995c222..d5d73f83 100644 --- a/src/properties/animation.rs +++ b/src/properties/animation.rs @@ -58,17 +58,24 @@ impl<'i> ToCss for AnimationName<'i> { where W: std::fmt::Write, { + let css_module_animation_enabled = + dest.css_module.as_ref().map_or(false, |css_module| css_module.config.animation); + match self { AnimationName::None => dest.write_str("none"), AnimationName::Ident(s) => { - if let Some(css_module) = &mut dest.css_module { - css_module.reference(&s.0, dest.loc.source_index) + if css_module_animation_enabled { + if let Some(css_module) = &mut dest.css_module { + css_module.reference(&s.0, dest.loc.source_index) + } } - s.to_css(dest) + s.to_css_with_options(dest, css_module_animation_enabled) } AnimationName::String(s) => { - if let Some(css_module) = &mut dest.css_module { - css_module.reference(&s, dest.loc.source_index) + if css_module_animation_enabled { + if let Some(css_module) = &mut dest.css_module { + css_module.reference(&s, dest.loc.source_index) + } } // CSS-wide keywords and `none` cannot remove quotes. @@ -78,7 +85,7 @@ impl<'i> ToCss for AnimationName<'i> { Ok(()) }, _ => { - dest.write_ident(s.as_ref()) + dest.write_ident(s.as_ref(), css_module_animation_enabled) } } } diff --git a/src/properties/grid.rs b/src/properties/grid.rs index fa0e5a64..912f563d 100644 --- a/src/properties/grid.rs +++ b/src/properties/grid.rs @@ -430,21 +430,24 @@ fn write_ident(name: &str, dest: &mut Printer) -> Result<(), PrinterError> where W: std::fmt::Write, { - if let Some(css_module) = &mut dest.css_module { - if let Some(last) = css_module.config.pattern.segments.last() { - if !matches!(last, crate::css_modules::Segment::Local) { - return Err(Error { - kind: PrinterErrorKind::InvalidCssModulesPatternInGrid, - loc: Some(ErrorLocation { - filename: dest.filename().into(), - line: dest.loc.line, - column: dest.loc.column, - }), - }); + let css_module_grid_enabled = dest.css_module.as_ref().map_or(false, |css_module| css_module.config.grid); + if css_module_grid_enabled { + if let Some(css_module) = &mut dest.css_module { + if let Some(last) = css_module.config.pattern.segments.last() { + if !matches!(last, crate::css_modules::Segment::Local) { + return Err(Error { + kind: PrinterErrorKind::InvalidCssModulesPatternInGrid, + loc: Some(ErrorLocation { + filename: dest.filename().into(), + line: dest.loc.line, + column: dest.loc.column, + }), + }); + } } } } - dest.write_ident(name)?; + dest.write_ident(name, css_module_grid_enabled)?; Ok(()) } diff --git a/src/rules/keyframes.rs b/src/rules/keyframes.rs index a9489f30..511a3172 100644 --- a/src/rules/keyframes.rs +++ b/src/rules/keyframes.rs @@ -92,9 +92,12 @@ impl<'i> ToCss for KeyframesName<'i> { where W: std::fmt::Write, { + let css_module_animation_enabled = + dest.css_module.as_ref().map_or(false, |css_module| css_module.config.animation); + match self { KeyframesName::Ident(ident) => { - dest.write_ident(ident.0.as_ref())?; + dest.write_ident(ident.0.as_ref(), css_module_animation_enabled)?; } KeyframesName::Custom(s) => { // CSS-wide keywords and `none` cannot remove quotes. @@ -103,7 +106,7 @@ impl<'i> ToCss for KeyframesName<'i> { serialize_string(&s, dest)?; }, _ => { - dest.write_ident(s.as_ref())?; + dest.write_ident(s.as_ref(), css_module_animation_enabled)?; } } } diff --git a/src/selector.rs b/src/selector.rs index 9cc04848..62550198 100644 --- a/src/selector.rs +++ b/src/selector.rs @@ -672,7 +672,7 @@ where if let Some(class) = class { dest.write_char('.')?; - dest.write_ident(class) + dest.write_ident(class, true) } else { dest.write_str($s) } @@ -1551,11 +1551,11 @@ where Component::Nesting => serialize_nesting(dest, context, false), Component::Class(ref class) => { dest.write_char('.')?; - dest.write_ident(&class.0) + dest.write_ident(&class.0, true) } Component::ID(ref id) => { dest.write_char('#')?; - dest.write_ident(&id.0) + dest.write_ident(&id.0, true) } Component::Host(selector) => { dest.write_str(":host")?; diff --git a/src/values/ident.rs b/src/values/ident.rs index 9d244965..173d3a08 100644 --- a/src/values/ident.rs +++ b/src/values/ident.rs @@ -49,7 +49,26 @@ impl<'i> ToCss for CustomIdent<'i> { where W: std::fmt::Write, { - dest.write_ident(&self.0) + self.to_css_with_options(dest, true) + } +} + +impl<'i> CustomIdent<'i> { + /// Write the custom ident to CSS. + pub(crate) fn to_css_with_options( + &self, + dest: &mut Printer, + enabled_css_modules: bool, + ) -> Result<(), PrinterError> + where + W: std::fmt::Write, + { + let css_module_custom_idents_enabled = enabled_css_modules + && dest + .css_module + .as_mut() + .map_or(false, |css_module| css_module.config.custom_idents); + dest.write_ident(&self.0, css_module_custom_idents_enabled) } } diff --git a/website/pages/css-modules.md b/website/pages/css-modules.md index d66016c3..732f83cf 100644 --- a/website/pages/css-modules.md +++ b/website/pages/css-modules.md @@ -13,16 +13,16 @@ CSS modules treat the classes defined in each file as unique. Each class name or To enable CSS modules, provide the `cssModules` option when calling the Lightning CSS API. When using the CLI, enable the `--css-modules` flag. ```js -import { transform } from 'lightningcss'; +import {transform} from 'lightningcss'; -let { code, map, exports } = transform({ +let {code, map, exports} = transform({ // ... cssModules: true, code: Buffer.from(` .logo { background: skyblue; } - `) + `), }); ``` @@ -89,7 +89,7 @@ You can also reference class names defined in a different CSS file using the `fr ```css .logo { - composes: bg-indigo from "./colors.module.css"; + composes: bg-indigo from './colors.module.css'; } ``` @@ -150,10 +150,10 @@ compiles to: By default, class names, id selectors, and the names of `@keyframes`, `@counter-style`, and CSS grid lines and areas are scoped to the module they are defined in. Scoping for CSS variables and other [``](https://www.w3.org/TR/css-values-4/#dashed-idents) names can also be enabled using the `dashedIdents` option when calling the Lightning CSS API. When using the CLI, enable the `--css-modules-dashed-idents` flag. ```js -let { code, map, exports } = transform({ +let {code, map, exports} = transform({ // ... cssModules: { - dashedIdents: true + dashedIdents: true, }, }); ``` @@ -186,7 +186,7 @@ You can also reference variables defined in other files using the `from` keyword ```css .button { - background: var(--accent-color from "./vars.module.css"); + background: var(--accent-color from './vars.module.css'); } ``` @@ -207,19 +207,19 @@ By default, Lightning CSS prepends the hash of the filename to each class name a A pattern is a string with placeholders that will be filled in by Lightning CSS. This allows you to add custom prefixes or adjust the naming convention for scoped classes. ```js -let { code, map, exports } = transform({ +let {code, map, exports} = transform({ // ... cssModules: { - pattern: 'my-company-[name]-[hash]-[local]' - } + pattern: 'my-company-[name]-[hash]-[local]', + }, }); ``` The following placeholders are currently supported: -* `[name]` - The base name of the file, without the extension. -* `[hash]` - A hash of the full file path. -* `[local]` - The original class name or identifier. +- `[name]` - The base name of the file, without the extension. +- `[hash]` - A hash of the full file path. +- `[local]` - The original class name or identifier.
@@ -231,7 +231,7 @@ The following placeholders are currently supported: let { code, map, exports } = transform({ // ... cssModules: { - // ❌ [local] must be at the end so that + // ❌ [local] must be at the end so that // auto-generated grid line names work pattern: '[local]-[hash]' // ✅ do this instead @@ -242,7 +242,7 @@ let { code, map, exports } = transform({ ```css .grid { - grid-template-areas: "nav main"; + grid-template-areas: 'nav main'; } .nav { @@ -252,10 +252,25 @@ let { code, map, exports } = transform({
+## Turning off feature scoping + +Scoping of grid, animations, and custom identifiers can be turned off. By default all of these are scoped. + +```js +let {code, map, exports} = transform({ + // ... + cssModules: { + animation: true, + grid: true, + customIdents: true, + }, +}); +``` + ## Unsupported features Lightning CSS does not currently implement all CSS modules features available in other implementations. Some of these may be added in the future. -* Non-function syntax for the `:local` and `:global` pseudo classes. -* The `@value` rule – superseded by standard CSS variables. -* The `:import` and `:export` ICSS rules. +- Non-function syntax for the `:local` and `:global` pseudo classes. +- The `@value` rule – superseded by standard CSS variables. +- The `:import` and `:export` ICSS rules. From fb4b33488cbf54ba063de8babb42aec016a96ce8 Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Tue, 14 May 2024 23:14:24 -0700 Subject: [PATCH 008/152] fix CSS module scoping with variables fixes #194 --- src/lib.rs | 93 +++++++++++++++++++++++++++++++++++++ src/properties/animation.rs | 52 +++++++++++++++------ src/properties/custom.rs | 7 +++ 3 files changed, 139 insertions(+), 13 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d0ea1982..559f3197 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23672,6 +23672,99 @@ mod tests { }, ); + css_modules_test( + r#" + .test { + animation: rotate var(--duration) linear infinite; + } + "#, + indoc! {r#" + .EgL3uq_test { + animation: EgL3uq_rotate var(--duration) linear infinite; + } + "#}, + map! { + "test" => "EgL3uq_test", + "rotate" => "EgL3uq_rotate" referenced: true + }, + HashMap::new(), + Default::default(), + ); + css_modules_test( + r#" + .test { + animation: none var(--duration); + } + "#, + indoc! {r#" + .EgL3uq_test { + animation: none var(--duration); + } + "#}, + map! { + "test" => "EgL3uq_test" + }, + HashMap::new(), + Default::default(), + ); + css_modules_test( + r#" + .test { + animation: var(--animation); + } + "#, + indoc! {r#" + .EgL3uq_test { + animation: var(--animation); + } + "#}, + map! { + "test" => "EgL3uq_test" + }, + HashMap::new(), + Default::default(), + ); + css_modules_test( + r#" + .test { + animation: rotate var(--duration); + } + "#, + indoc! {r#" + .EgL3uq_test { + animation: rotate var(--duration); + } + "#}, + map! { + "test" => "EgL3uq_test" + }, + HashMap::new(), + crate::css_modules::Config { + animation: false, + ..Default::default() + } + ); + css_modules_test( + r#" + .test { + animation: "rotate" var(--duration); + } + "#, + indoc! {r#" + .EgL3uq_test { + animation: EgL3uq_rotate var(--duration); + } + "#}, + map! { + "test" => "EgL3uq_test", + "rotate" => "EgL3uq_rotate" referenced: true + }, + HashMap::new(), + crate::css_modules::Config { + ..Default::default() + } + ); + // Stable hashes between project roots. fn test_project_root(project_root: &str, filename: &str, hash: &str) { let stylesheet = StyleSheet::parse( diff --git a/src/properties/animation.rs b/src/properties/animation.rs index d5d73f83..2b2277d7 100644 --- a/src/properties/animation.rs +++ b/src/properties/animation.rs @@ -1,12 +1,14 @@ //! CSS properties related to keyframe animations. +use std::borrow::Cow; + use crate::context::PropertyHandlerContext; use crate::declaration::{DeclarationBlock, DeclarationList}; use crate::error::{ParserError, PrinterError}; use crate::macros::*; use crate::prefixes::Feature; use crate::printer::Printer; -use crate::properties::{Property, PropertyId, VendorPrefix}; +use crate::properties::{Property, PropertyId, TokenOrValue, VendorPrefix}; use crate::traits::{Parse, PropertyHandler, Shorthand, ToCss, Zero}; use crate::values::number::CSSNumber; use crate::values::string::CowArcStr; @@ -346,8 +348,6 @@ impl<'i> PropertyHandler<'i> for AnimationHandler<'i> { dest: &mut DeclarationList<'i>, context: &mut PropertyHandlerContext<'i, '_>, ) -> bool { - use Property::*; - macro_rules! maybe_flush { ($prop: ident, $val: expr, $vp: ident) => {{ // If two vendor prefixes for the same property have different @@ -376,15 +376,15 @@ impl<'i> PropertyHandler<'i> for AnimationHandler<'i> { } match property { - AnimationName(val, vp) => property!(names, val, vp), - AnimationDuration(val, vp) => property!(durations, val, vp), - AnimationTimingFunction(val, vp) => property!(timing_functions, val, vp), - AnimationIterationCount(val, vp) => property!(iteration_counts, val, vp), - AnimationDirection(val, vp) => property!(directions, val, vp), - AnimationPlayState(val, vp) => property!(play_states, val, vp), - AnimationDelay(val, vp) => property!(delays, val, vp), - AnimationFillMode(val, vp) => property!(fill_modes, val, vp), - Animation(val, vp) => { + Property::AnimationName(val, vp) => property!(names, val, vp), + Property::AnimationDuration(val, vp) => property!(durations, val, vp), + Property::AnimationTimingFunction(val, vp) => property!(timing_functions, val, vp), + Property::AnimationIterationCount(val, vp) => property!(iteration_counts, val, vp), + Property::AnimationDirection(val, vp) => property!(directions, val, vp), + Property::AnimationPlayState(val, vp) => property!(play_states, val, vp), + Property::AnimationDelay(val, vp) => property!(delays, val, vp), + Property::AnimationFillMode(val, vp) => property!(fill_modes, val, vp), + Property::Animation(val, vp) => { let names = val.iter().map(|b| b.name.clone()).collect(); maybe_flush!(names, &names, vp); @@ -418,7 +418,33 @@ impl<'i> PropertyHandler<'i> for AnimationHandler<'i> { property!(delays, &delays, vp); property!(fill_modes, &fill_modes, vp); } - Unparsed(val) if is_animation_property(&val.property_id) => { + Property::Unparsed(val) if is_animation_property(&val.property_id) => { + let mut val = Cow::Borrowed(val); + if matches!(val.property_id, PropertyId::Animation(_)) { + use crate::properties::custom::Token; + + // Find an identifier that isn't a keyword and replace it with an + // AnimationName token so it is scoped in CSS modules. + for token in &mut val.to_mut().value.0 { + match token { + TokenOrValue::Token(Token::Ident(id)) => { + if AnimationDirection::parse_string(&id).is_err() + && AnimationPlayState::parse_string(&id).is_err() + && AnimationFillMode::parse_string(&id).is_err() + && !EasingFunction::is_ident(&id) + && id.as_ref() != "infinite" + { + *token = TokenOrValue::AnimationName(AnimationName::Ident(CustomIdent(id.clone()))); + } + } + TokenOrValue::Token(Token::String(s)) => { + *token = TokenOrValue::AnimationName(AnimationName::String(s.clone())); + } + _ => {} + } + } + } + self.flush(dest, context); dest.push(Property::Unparsed( val.get_prefixed(context.targets, Feature::Animation), diff --git a/src/properties/custom.rs b/src/properties/custom.rs index f93b4005..c3621af6 100644 --- a/src/properties/custom.rs +++ b/src/properties/custom.rs @@ -29,6 +29,7 @@ use cssparser::*; #[cfg(feature = "serde")] use crate::serialization::ValueWrapper; +use super::AnimationName; /// A CSS custom property, representing any unknown property. #[derive(Debug, Clone, PartialEq)] @@ -241,6 +242,8 @@ pub enum TokenOrValue<'i> { Resolution(Resolution), /// A dashed ident. DashedIdent(DashedIdent<'i>), + /// An animation name. + AnimationName(AnimationName<'i>), } impl<'i> From<'i>> for TokenOrValue<'i> { @@ -590,6 +593,10 @@ impl<'i> TokenList<'i> { v.to_css(dest)?; false } + TokenOrValue::AnimationName(v) => { + v.to_css(dest)?; + false + } TokenOrValue::Token(token) => match token { Token::Delim(d) => { if *d == '+' || *d == '-' { From ec9da43eeb1a236f5fd8dc472b3fe060c3db9a79 Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Thu, 16 May 2024 19:29:01 -0700 Subject: [PATCH 009/152] update browser compat data --- package.json | 6 +- scripts/build-prefixes.js | 2 +- src/compat.rs | 287 +++++++++++++++++++++++++++++++------- src/prefixes.rs | 14 +- yarn.lock | 48 +++---- 5 files changed, 274 insertions(+), 83 deletions(-) diff --git a/package.json b/package.json index c9fe3d72..740531f3 100644 --- a/package.json +++ b/package.json @@ -48,10 +48,10 @@ "@codemirror/lang-javascript": "^6.1.2", "@codemirror/lint": "^6.1.0", "@codemirror/theme-one-dark": "^6.1.0", - "@mdn/browser-compat-data": "~5.5.0", + "@mdn/browser-compat-data": "~5.5.28", "@napi-rs/cli": "^2.14.0", - "autoprefixer": "^10.4.17", - "caniuse-lite": "^1.0.30001585", + "autoprefixer": "^10.4.19", + "caniuse-lite": "^1.0.30001620", "codemirror": "^6.0.1", "cssnano": "^5.0.8", "esbuild": "^0.19.8", diff --git a/scripts/build-prefixes.js b/scripts/build-prefixes.js index 597e1dbd..1533c8c9 100644 --- a/scripts/build-prefixes.js +++ b/scripts/build-prefixes.js @@ -282,7 +282,7 @@ let mdnFeatures = { logicalPaddingShorthand: mdn.css.properties['padding-inline'].__compat.support, logicalInset: mdn.css.properties['inset-inline-start'].__compat.support, logicalSize: mdn.css.properties['inline-size'].__compat.support, - logicalTextAlign: mdn.css.properties['text-align']['flow_relative_values_start_and_end'].__compat.support, + logicalTextAlign: mdn.css.properties['text-align'].start.__compat.support, labColors: mdn.css.types.color.lab.__compat.support, oklabColors: mdn.css.types.color.oklab.__compat.support, colorFunction: mdn.css.types.color.color.__compat.support, diff --git a/src/compat.rs b/src/compat.rs index 1a4bec65..1d283cb3 100644 --- a/src/compat.rs +++ b/src/compat.rs @@ -10,11 +10,13 @@ pub enum Feature { AfarListStyleType, AmharicAbegedeListStyleType, AmharicListStyleType, + AnchorSizeSize, AnyLink, AnyPseudo, ArabicIndicListStyleType, ArmenianListStyleType, AsterisksListStyleType, + AutoSize, Autofill, BengaliListStyleType, BinaryListStyleType, @@ -142,6 +144,7 @@ pub enum Feature { MyanmarListStyleType, Namespaces, Nesting, + NoneListStyleType, NotSelectorList, NthChildOf, OctalListStyleType, @@ -441,7 +444,7 @@ impl Feature { } } if let Some(version) = browsers.android { - if version < 7929856 { + if version < 8126464 { return false; } } @@ -533,7 +536,7 @@ impl Feature { } } if let Some(version) = browsers.android { - if version < 7929856 { + if version < 8126464 { return false; } } @@ -578,7 +581,7 @@ impl Feature { } } if let Some(version) = browsers.android { - if version < 7929856 { + if version < 8126464 { return false; } } @@ -623,7 +626,7 @@ impl Feature { } } if let Some(version) = browsers.android { - if version < 7929856 { + if version < 8126464 { return false; } } @@ -663,7 +666,7 @@ impl Feature { } } if let Some(version) = browsers.android { - if version < 7929856 { + if version < 8126464 { return false; } } @@ -708,7 +711,7 @@ impl Feature { } } if let Some(version) = browsers.android { - if version < 7929856 { + if version < 8126464 { return false; } } @@ -753,7 +756,7 @@ impl Feature { } } if let Some(version) = browsers.android { - if version < 7929856 { + if version < 8126464 { return false; } } @@ -798,7 +801,7 @@ impl Feature { } } if let Some(version) = browsers.android { - if version < 7929856 { + if version < 8126464 { return false; } } @@ -890,7 +893,7 @@ impl Feature { } } if let Some(version) = browsers.android { - if version < 7929856 { + if version < 8126464 { return false; } } @@ -935,7 +938,7 @@ impl Feature { } } if let Some(version) = browsers.android { - if version < 7929856 { + if version < 8126464 { return false; } } @@ -1015,7 +1018,7 @@ impl Feature { } } if let Some(version) = browsers.android { - if version < 7929856 { + if version < 8126464 { return false; } } @@ -1060,7 +1063,7 @@ impl Feature { } } if let Some(version) = browsers.android { - if version < 7929856 { + if version < 8126464 { return false; } } @@ -1150,7 +1153,7 @@ impl Feature { } } if let Some(version) = browsers.android { - if version < 7929856 { + if version < 8126464 { return false; } } @@ -1195,7 +1198,7 @@ impl Feature { } } if let Some(version) = browsers.android { - if version < 7929856 { + if version < 8126464 { return false; } } @@ -1245,7 +1248,7 @@ impl Feature { } } if let Some(version) = browsers.android { - if version < 7929856 { + if version < 8126464 { return false; } } @@ -1332,7 +1335,7 @@ impl Feature { } } if let Some(version) = browsers.android { - if version < 7929856 { + if version < 8126464 { return false; } } @@ -1377,7 +1380,7 @@ impl Feature { } } if let Some(version) = browsers.android { - if version < 7929856 { + if version < 8126464 { return false; } } @@ -1422,7 +1425,7 @@ impl Feature { } } if let Some(version) = browsers.android { - if version < 7929856 { + if version < 8126464 { return false; } } @@ -1462,7 +1465,7 @@ impl Feature { } } if let Some(version) = browsers.android { - if version < 7929856 { + if version < 8126464 { return false; } } @@ -1507,7 +1510,7 @@ impl Feature { } } if let Some(version) = browsers.android { - if version < 7929856 { + if version < 8126464 { return false; } } @@ -1552,7 +1555,7 @@ impl Feature { } } if let Some(version) = browsers.android { - if version < 7929856 { + if version < 8126464 { return false; } } @@ -1619,7 +1622,7 @@ impl Feature { } } if let Some(version) = browsers.android { - if version < 7929856 { + if version < 8126464 { return false; } } @@ -2868,12 +2871,47 @@ impl Feature { return false; } } - Feature::RoundFunction - | Feature::RemFunction - | Feature::ModFunction - | Feature::AbsFunction - | Feature::SignFunction - | Feature::HypotFunction => { + Feature::RoundFunction | Feature::RemFunction | Feature::ModFunction => { + if let Some(version) = browsers.chrome { + if version < 8192000 { + return false; + } + } + if let Some(version) = browsers.edge { + if version < 8192000 { + return false; + } + } + if let Some(version) = browsers.firefox { + if version < 7733248 { + return false; + } + } + if let Some(version) = browsers.opera { + if version < 7274496 { + return false; + } + } + if let Some(version) = browsers.safari { + if version < 984064 { + return false; + } + } + if let Some(version) = browsers.ios_saf { + if version < 984064 { + return false; + } + } + if let Some(version) = browsers.android { + if version < 8192000 { + return false; + } + } + if browsers.ie.is_some() || browsers.samsung.is_some() { + return false; + } + } + Feature::AbsFunction | Feature::SignFunction => { if let Some(version) = browsers.firefox { if version < 7733248 { return false; @@ -2899,6 +2937,51 @@ impl Feature { return false; } } + Feature::HypotFunction => { + if let Some(version) = browsers.chrome { + if version < 7864320 { + return false; + } + } + if let Some(version) = browsers.edge { + if version < 7864320 { + return false; + } + } + if let Some(version) = browsers.firefox { + if version < 7733248 { + return false; + } + } + if let Some(version) = browsers.opera { + if version < 5242880 { + return false; + } + } + if let Some(version) = browsers.safari { + if version < 984064 { + return false; + } + } + if let Some(version) = browsers.ios_saf { + if version < 984064 { + return false; + } + } + if let Some(version) = browsers.samsung { + if version < 1638400 { + return false; + } + } + if let Some(version) = browsers.android { + if version < 7864320 { + return false; + } + } + if browsers.ie.is_some() { + return false; + } + } Feature::GradientInterpolationHints => { if let Some(version) = browsers.chrome { if version < 2621440 { @@ -2986,7 +3069,7 @@ impl Feature { } } if let Some(version) = browsers.android { - if version < 2424832 { + if version < 263168 { return false; } } @@ -3080,7 +3163,7 @@ impl Feature { } } if let Some(version) = browsers.android { - if version < 262144 { + if version < 2752512 { return false; } } @@ -3271,19 +3354,37 @@ impl Feature { return false; } } + if let Some(version) = browsers.edge { + if version < 8060928 { + return false; + } + } if let Some(version) = browsers.firefox { if version < 7864320 { return false; } } - if browsers.android.is_some() - || browsers.edge.is_some() - || browsers.ie.is_some() - || browsers.ios_saf.is_some() - || browsers.opera.is_some() - || browsers.safari.is_some() - || browsers.samsung.is_some() - { + if let Some(version) = browsers.opera { + if version < 5373952 { + return false; + } + } + if let Some(version) = browsers.safari { + if version < 1115392 { + return false; + } + } + if let Some(version) = browsers.ios_saf { + if version < 1115392 { + return false; + } + } + if let Some(version) = browsers.android { + if version < 8060928 { + return false; + } + } + if browsers.ie.is_some() || browsers.samsung.is_some() { return false; } } @@ -3389,12 +3490,17 @@ impl Feature { return false; } } + if let Some(version) = browsers.samsung { + if version < 1572864 { + return false; + } + } if let Some(version) = browsers.android { if version < 7667712 { return false; } } - if browsers.ie.is_some() || browsers.samsung.is_some() { + if browsers.ie.is_some() { return false; } } @@ -3704,12 +3810,17 @@ impl Feature { return false; } } + if let Some(version) = browsers.samsung { + if version < 1572864 { + return false; + } + } if let Some(version) = browsers.android { if version < 7667712 { return false; } } - if browsers.firefox.is_some() || browsers.ie.is_some() || browsers.samsung.is_some() { + if browsers.firefox.is_some() || browsers.ie.is_some() { return false; } } @@ -4670,14 +4781,15 @@ impl Feature { | Feature::HiraganaListStyleType | Feature::HiraganaIrohaListStyleType | Feature::KatakanaListStyleType - | Feature::KatakanaIrohaListStyleType => { + | Feature::KatakanaIrohaListStyleType + | Feature::AutoSize => { if let Some(version) = browsers.chrome { if version < 1179648 { return false; } } if let Some(version) = browsers.edge { - if version < 5177344 { + if version < 786432 { return false; } } @@ -4686,6 +4798,11 @@ impl Feature { return false; } } + if let Some(version) = browsers.ie { + if version < 720896 { + return false; + } + } if let Some(version) = browsers.opera { if version < 917504 { return false; @@ -4711,9 +4828,6 @@ impl Feature { return false; } } - if browsers.ie.is_some() { - return false; - } } Feature::KoreanHangulFormalListStyleType | Feature::KoreanHanjaFormalListStyleType @@ -4854,6 +4968,53 @@ impl Feature { } } } + Feature::NoneListStyleType => { + if let Some(version) = browsers.chrome { + if version < 1179648 { + return false; + } + } + if let Some(version) = browsers.edge { + if version < 786432 { + return false; + } + } + if let Some(version) = browsers.firefox { + if version < 5177344 { + return false; + } + } + if let Some(version) = browsers.ie { + if version < 720896 { + return false; + } + } + if let Some(version) = browsers.opera { + if version < 917504 { + return false; + } + } + if let Some(version) = browsers.safari { + if version < 65536 { + return false; + } + } + if let Some(version) = browsers.ios_saf { + if version < 65536 { + return false; + } + } + if let Some(version) = browsers.samsung { + if version < 65536 { + return false; + } + } + if let Some(version) = browsers.android { + if version < 263168 { + return false; + } + } + } Feature::SimpChineseFormalListStyleType | Feature::SimpChineseInformalListStyleType | Feature::TradChineseFormalListStyleType @@ -4965,6 +5126,36 @@ impl Feature { return false; } } + Feature::AnchorSizeSize => { + if let Some(version) = browsers.chrome { + if version < 8192000 { + return false; + } + } + if let Some(version) = browsers.edge { + if version < 8192000 { + return false; + } + } + if let Some(version) = browsers.opera { + if version < 7274496 { + return false; + } + } + if let Some(version) = browsers.android { + if version < 8192000 { + return false; + } + } + if browsers.firefox.is_some() + || browsers.ie.is_some() + || browsers.ios_saf.is_some() + || browsers.safari.is_some() + || browsers.samsung.is_some() + { + return false; + } + } Feature::FitContentSize => { if let Some(version) = browsers.chrome { if version < 1638400 { @@ -5059,7 +5250,7 @@ impl Feature { } Feature::MaxContentSize => { if let Some(version) = browsers.chrome { - if version < 3014656 { + if version < 1638400 { return false; } } @@ -5089,12 +5280,12 @@ impl Feature { } } if let Some(version) = browsers.samsung { - if version < 327680 { + if version < 66816 { return false; } } if let Some(version) = browsers.android { - if version < 3014656 { + if version < 263168 { return false; } } diff --git a/src/prefixes.rs b/src/prefixes.rs index fe06123d..56697404 100644 --- a/src/prefixes.rs +++ b/src/prefixes.rs @@ -806,7 +806,7 @@ impl Feature { } } if let Some(version) = browsers.opera { - if version >= 983040 { + if version >= 983040 && version <= 6881280 { prefixes |= VendorPrefix::WebKit; } } @@ -1124,7 +1124,7 @@ impl Feature { } } if let Some(version) = browsers.opera { - if version >= 983040 { + if version >= 983040 && version <= 6225920 { prefixes |= VendorPrefix::WebKit; } } @@ -1468,7 +1468,7 @@ impl Feature { } } if let Some(version) = browsers.opera { - if version >= 983040 { + if version >= 983040 && version <= 6881280 { prefixes |= VendorPrefix::WebKit; } } @@ -1510,7 +1510,7 @@ impl Feature { } } if let Some(version) = browsers.samsung { - if version >= 262144 { + if version >= 262144 && version <= 327680 { prefixes |= VendorPrefix::WebKit; } } @@ -1785,7 +1785,7 @@ impl Feature { } } if let Some(version) = browsers.samsung { - if version >= 262144 { + if version >= 262144 && version <= 851968 { prefixes |= VendorPrefix::WebKit; } } @@ -1865,7 +1865,7 @@ impl Feature { } } if let Some(version) = browsers.opera { - if version >= 983040 { + if version >= 983040 && version <= 6422528 { prefixes |= VendorPrefix::WebKit; } } @@ -1875,7 +1875,7 @@ impl Feature { } } if let Some(version) = browsers.samsung { - if version >= 262144 { + if version >= 262144 && version <= 1441792 { prefixes |= VendorPrefix::WebKit; } } diff --git a/yarn.lock b/yarn.lock index d2d191f4..af6b910d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -454,10 +454,10 @@ resolved "https://registry.yarnpkg.com/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-2.5.2.tgz#28f643fbc0bec30b07fbe95b137879b6b4d1c9c5" integrity sha512-zrBczSbXKxEyK2ijtbRdICDygRqWSRPpZMN5dD1T8VMEW5RIhIbwFWw2phDRXuBQdVDpSjalCIUMWMV2h3JaZA== -"@mdn/browser-compat-data@~5.5.0": - version "5.5.10" - resolved "https://registry.yarnpkg.com/@mdn/browser-compat-data/-/browser-compat-data-5.5.10.tgz#b7c0454a617b268b7a9ab9ca6c886abd1ec69473" - integrity sha512-s2GGND9oLhEuksOFtICYOBZdMWPANBXTMqAXh89q6g1Mi3+OuWEmp9WFzw2v/nmS175vqeewpC1kDJA7taaxyA== +"@mdn/browser-compat-data@~5.5.28": + version "5.5.28" + resolved "https://registry.yarnpkg.com/@mdn/browser-compat-data/-/browser-compat-data-5.5.28.tgz#76cd0651ebf9725916da10b686ac98288403d89f" + integrity sha512-yKS9hfVRsYx/3usEAk+86rq2KnHHyoafyvw2Xob5dXPSOZKIY56Nkas/JK3cmav8nZjjHWiYS/asv9TQy1YLbg== "@mischnic/json-sourcemap@^0.1.0": version "0.1.0" @@ -1228,13 +1228,13 @@ ast-types@0.15.2: dependencies: tslib "^2.0.1" -autoprefixer@^10.4.17: - version "10.4.17" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.17.tgz#35cd5695cbbe82f536a50fa025d561b01fdec8be" - integrity sha512-/cpVNRLSfhOtcGflT13P2794gVSgmPgTR+erw5ifnMLZb0UnSlkK4tquLmkd3BhA+nLo5tX8Cu0upUsGKvKbmg== +autoprefixer@^10.4.19: + version "10.4.19" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.19.tgz#ad25a856e82ee9d7898c59583c1afeb3fa65f89f" + integrity sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew== dependencies: - browserslist "^4.22.2" - caniuse-lite "^1.0.30001578" + browserslist "^4.23.0" + caniuse-lite "^1.0.30001599" fraction.js "^4.3.7" normalize-range "^0.1.2" picocolors "^1.0.0" @@ -1291,13 +1291,13 @@ braces@^3.0.2: dependencies: fill-range "^7.0.1" -browserslist@^4.0.0, browserslist@^4.16.0, browserslist@^4.16.6, browserslist@^4.22.2, browserslist@^4.6.6: - version "4.22.3" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.3.tgz#299d11b7e947a6b843981392721169e27d60c5a6" - integrity sha512-UAp55yfwNv0klWNapjs/ktHoguxuQNGnOzxYmfnXIS+8AsRDZkSDxg7R1AX3GKzn078SBI5dzwzj/Yx0Or0e3A== +browserslist@^4.0.0, browserslist@^4.16.0, browserslist@^4.16.6, browserslist@^4.23.0, browserslist@^4.6.6: + version "4.23.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.0.tgz#8f3acc2bbe73af7213399430890f86c63a5674ab" + integrity sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ== dependencies: - caniuse-lite "^1.0.30001580" - electron-to-chromium "^1.4.648" + caniuse-lite "^1.0.30001587" + electron-to-chromium "^1.4.668" node-releases "^2.0.14" update-browserslist-db "^1.0.13" @@ -1347,10 +1347,10 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001578, caniuse-lite@^1.0.30001580, caniuse-lite@^1.0.30001585: - version "1.0.30001585" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001585.tgz#0b4e848d84919c783b2a41c13f7de8ce96744401" - integrity sha512-yr2BWR1yLXQ8fMpdS/4ZZXpseBgE7o4g41x3a6AJOqZuOi+iE/WdJYAuZ6Y95i4Ohd2Y+9MzIWRR+uGABH4s3Q== +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001587, caniuse-lite@^1.0.30001599, caniuse-lite@^1.0.30001620: + version "1.0.30001620" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001620.tgz#78bb6f35b8fe315b96b8590597094145d0b146b4" + integrity sha512-WJvYsOjd1/BYUY6SNGUosK9DUidBPDTnOARHp3fSmFO1ekdxaY6nKRttEVrfMmYi80ctS0kz1wiWmm14fVc3ew== chalk@^2.0.0: version "2.4.2" @@ -1727,10 +1727,10 @@ dotenv@^7.0.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-7.0.0.tgz#a2be3cd52736673206e8a85fb5210eea29628e7c" integrity sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g== -electron-to-chromium@^1.4.648: - version "1.4.664" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.664.tgz#b00fc67d5d4f124e429b0dcce5a02ae18ef33ede" - integrity sha512-k9VKKSkOSNPvSckZgDDl/IQx45E1quMjX8QfLzUsAs/zve8AyFDK+ByRynSP/OfEfryiKHpQeMf00z0leLCc3A== +electron-to-chromium@^1.4.668: + version "1.4.773" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.773.tgz#49741af9bb4e712ad899e35d8344d8d59cdb7e12" + integrity sha512-87eHF+h3PlCRwbxVEAw9KtK3v7lWfc/sUDr0W76955AdYTG4bV/k0zrl585Qnj/skRMH2qOSiE+kqMeOQ+LOpw== end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" From f4408c7bdbbfa2c4cbdf5731b351811a0323fa8c Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Fri, 17 May 2024 09:58:42 -0700 Subject: [PATCH 010/152] Implement animation-timeline property and add to animation shorthand #572 --- node/ast.d.ts | 121 ++++++++++++-- scripts/build-prefixes.js | 1 + src/compat.rs | 35 ++++ src/lib.rs | 150 ++++++++++++++++- src/properties/animation.rs | 324 +++++++++++++++++++++++++++++++++++- src/properties/custom.rs | 2 +- src/properties/mod.rs | 2 + 7 files changed, 613 insertions(+), 22 deletions(-) diff --git a/node/ast.d.ts b/node/ast.d.ts index 6015ff44..8bc02772 100644 --- a/node/ast.d.ts +++ b/node/ast.d.ts @@ -504,6 +504,10 @@ export type TokenOrValue = | { type: "dashed-ident"; value: String; + } + | { + type: "animation-name"; + value: AnimationName; }; /** * A raw CSS token. @@ -1114,6 +1118,21 @@ export type Time = type: "milliseconds"; value: number; }; +/** + * A value for the [animation-name](https://drafts.csswg.org/css-animations/#animation-name) property. + */ +export type AnimationName = + | { + type: "none"; + } + | { + type: "ident"; + value: String; + } + | { + type: "string"; + value: String; + }; /** * A CSS environment variable name. */ @@ -1935,6 +1954,12 @@ export type PropertyId = property: "animation-fill-mode"; vendorPrefix: VendorPrefix; } + | { + property: "animation-composition"; + } + | { + property: "animation-timeline"; + } | { property: "animation"; vendorPrefix: VendorPrefix; @@ -3283,6 +3308,14 @@ export type Declaration = value: AnimationFillMode[]; vendorPrefix: VendorPrefix; } + | { + property: "animation-composition"; + value: AnimationComposition[]; + } + | { + property: "animation-timeline"; + value: AnimationTimeline[]; + } | { property: "animation"; value: Animation[]; @@ -5442,21 +5475,6 @@ export type StepPosition = | { type: "jump-both"; }; -/** - * A value for the [animation-name](https://drafts.csswg.org/css-animations/#animation-name) property. - */ -export type AnimationName = - | { - type: "none"; - } - | { - type: "ident"; - value: String; - } - | { - type: "string"; - value: String; - }; /** * A value for the [animation-iteration-count](https://drafts.csswg.org/css-animations/#animation-iteration-count) property. */ @@ -5480,6 +5498,49 @@ export type AnimationPlayState = "running" | "paused"; * A value for the [animation-fill-mode](https://drafts.csswg.org/css-animations/#animation-fill-mode) property. */ export type AnimationFillMode = "none" | "forwards" | "backwards" | "both"; +/** + * A value for the [animation-composition](https://drafts.csswg.org/css-animations-2/#animation-composition) property. + */ +export type AnimationComposition = "replace" | "add" | "accumulate"; +/** + * A value for the [animation-timeline](https://drafts.csswg.org/css-animations-2/#animation-timeline) property. + */ +export type AnimationTimeline = + | { + type: "auto"; + } + | { + type: "none"; + } + | { + type: "dashed-ident"; + value: String; + } + | { + type: "scroll"; + value: ScrollTimeline; + } + | { + type: "view"; + value: ViewTimeline; + }; +/** + * A scroll axis, used in the `scroll()` function. + */ +export type ScrollAxis = "block" | "inline" | "x" | "y"; +/** + * A scroller, used in the `scroll()` function. + */ +export type Scroller = "root" | "nearest" | "self"; +/** + * A generic value that represents a value with two components, e.g. a border radius. + * + * When serialized, only a single component will be written if both are equal. + * + * @minItems 2 + * @maxItems 2 + */ +export type Size2DFor_LengthPercentageOrAuto = [LengthPercentageOrAuto, LengthPercentageOrAuto]; /** * An individual [transform function](https://www.w3.org/TR/2019/CR-css-transforms-1-20190214/#two-d-transform-functions). */ @@ -8488,6 +8549,32 @@ export interface Transition { */ timingFunction: EasingFunction; } +/** + * The [scroll()](https://drafts.csswg.org/scroll-animations-1/#scroll-notation) function. + */ +export interface ScrollTimeline { + /** + * Specifies which axis of the scroll container to use as the progress for the timeline. + */ + axis: ScrollAxis; + /** + * Specifies which element to use as the scroll container. + */ + scroller: Scroller; +} +/** + * The [view()](https://drafts.csswg.org/scroll-animations-1/#view-notation) function. + */ +export interface ViewTimeline { + /** + * Specifies which axis of the scroll container to use as the progress for the timeline. + */ + axis: ScrollAxis; + /** + * Provides an adjustment of the view progress visibility range. + */ + inset: Size2DFor_LengthPercentageOrAuto; +} /** * A value for the [animation](https://drafts.csswg.org/css-animations/#animation) shorthand property. */ @@ -8520,6 +8607,10 @@ export interface Animation { * The current play state of the animation. */ playState: AnimationPlayState; + /** + * The animation timeline. + */ + timeline: AnimationTimeline; /** * The easing function for the animation. */ diff --git a/scripts/build-prefixes.js b/scripts/build-prefixes.js index 1533c8c9..8fc83ca2 100644 --- a/scripts/build-prefixes.js +++ b/scripts/build-prefixes.js @@ -329,6 +329,7 @@ let mdnFeatures = { fontStretchPercentage: mdn.css.properties['font-stretch'].percentage.__compat.support, lightDark: mdn.css.types.color['light-dark'].__compat.support, accentSystemColor: mdn.css.types.color['system-color'].accentcolor_accentcolortext.__compat.support, + animationTimelineShorthand: mdn.css.properties.animation['animation-timeline_included'].__compat.support, }; for (let key in mdn.css.types.length) { diff --git a/src/compat.rs b/src/compat.rs index 1d283cb3..b1846f34 100644 --- a/src/compat.rs +++ b/src/compat.rs @@ -11,6 +11,7 @@ pub enum Feature { AmharicAbegedeListStyleType, AmharicListStyleType, AnchorSizeSize, + AnimationTimelineShorthand, AnyLink, AnyPseudo, ArabicIndicListStyleType, @@ -3414,6 +3415,40 @@ impl Feature { return false; } } + Feature::AnimationTimelineShorthand => { + if let Some(version) = browsers.chrome { + if version < 7536640 { + return false; + } + } + if let Some(version) = browsers.edge { + if version < 7536640 { + return false; + } + } + if let Some(version) = browsers.opera { + if version < 5046272 { + return false; + } + } + if let Some(version) = browsers.samsung { + if version < 1507328 { + return false; + } + } + if let Some(version) = browsers.android { + if version < 7536640 { + return false; + } + } + if browsers.firefox.is_some() + || browsers.ie.is_some() + || browsers.ios_saf.is_some() + || browsers.safari.is_some() + { + return false; + } + } Feature::QUnit => { if let Some(version) = browsers.chrome { if version < 4128768 { diff --git a/src/lib.rs b/src/lib.rs index 559f3197..d39c6ac4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11243,6 +11243,46 @@ mod tests { ".foo { animation: foo 0s 3s infinite }", ".foo{animation:0s 3s infinite foo}", ); + minify_test(".foo { animation: foo 3s --test }", ".foo{animation:3s foo --test}"); + minify_test(".foo { animation: foo 3s scroll() }", ".foo{animation:3s foo scroll()}"); + minify_test( + ".foo { animation: foo 3s scroll(block) }", + ".foo{animation:3s foo scroll()}", + ); + minify_test( + ".foo { animation: foo 3s scroll(root inline) }", + ".foo{animation:3s foo scroll(root inline)}", + ); + minify_test( + ".foo { animation: foo 3s scroll(inline root) }", + ".foo{animation:3s foo scroll(root inline)}", + ); + minify_test( + ".foo { animation: foo 3s scroll(inline nearest) }", + ".foo{animation:3s foo scroll(inline)}", + ); + minify_test( + ".foo { animation: foo 3s view(block) }", + ".foo{animation:3s foo view()}", + ); + minify_test( + ".foo { animation: foo 3s view(inline) }", + ".foo{animation:3s foo view(inline)}", + ); + minify_test( + ".foo { animation: foo 3s view(inline 10px 10px) }", + ".foo{animation:3s foo view(inline 10px)}", + ); + minify_test( + ".foo { animation: foo 3s view(inline 10px 12px) }", + ".foo{animation:3s foo view(inline 10px 12px)}", + ); + minify_test( + ".foo { animation: foo 3s view(inline auto auto) }", + ".foo{animation:3s foo view(inline)}", + ); + minify_test(".foo { animation: foo 3s auto }", ".foo{animation:3s foo}"); + minify_test(".foo { animation-composition: add }", ".foo{animation-composition:add}"); test( r#" .foo { @@ -11254,6 +11294,7 @@ mod tests { animation-play-state: running; animation-delay: 100ms; animation-fill-mode: forwards; + animation-timeline: auto; } "#, indoc! {r#" @@ -11273,6 +11314,7 @@ mod tests { animation-play-state: running, paused; animation-delay: 100ms, 0s; animation-fill-mode: forwards, none; + animation-timeline: auto, auto; } "#, indoc! {r#" @@ -11319,6 +11361,7 @@ mod tests { animation-play-state: running; animation-delay: 100ms; animation-fill-mode: forwards; + animation-timeline: auto; } "#, indoc! {r#" @@ -11331,6 +11374,55 @@ mod tests { animation-play-state: running; animation-delay: .1s; animation-fill-mode: forwards; + animation-timeline: auto; + } + "#}, + ); + test( + r#" + .foo { + animation-name: foo; + animation-duration: 0.09s; + animation-timing-function: ease-in-out; + animation-iteration-count: 2; + animation-direction: alternate; + animation-play-state: running; + animation-delay: 100ms; + animation-fill-mode: forwards; + animation-timeline: scroll(); + } + "#, + indoc! {r#" + .foo { + animation: 90ms ease-in-out .1s 2 alternate forwards foo scroll(); + } + "#}, + ); + test( + r#" + .foo { + animation-name: foo; + animation-duration: 0.09s; + animation-timing-function: ease-in-out; + animation-iteration-count: 2; + animation-direction: alternate; + animation-play-state: running; + animation-delay: 100ms; + animation-fill-mode: forwards; + animation-timeline: scroll(), view(); + } + "#, + indoc! {r#" + .foo { + animation-name: foo; + animation-duration: 90ms; + animation-timing-function: ease-in-out; + animation-iteration-count: 2; + animation-direction: alternate; + animation-play-state: running; + animation-delay: .1s; + animation-fill-mode: forwards; + animation-timeline: scroll(), view(); } "#}, ); @@ -11441,6 +11533,58 @@ mod tests { ..Browsers::default() }, ); + + prefix_test( + r#" + .foo { + animation: .2s ease-in-out bar scroll(); + } + "#, + indoc! {r#" + .foo { + animation: .2s ease-in-out bar; + animation-timeline: scroll(); + } + "#}, + Browsers { + safari: Some(16 << 16), + ..Browsers::default() + }, + ); + prefix_test( + r#" + .foo { + animation: .2s ease-in-out bar scroll(); + } + "#, + indoc! {r#" + .foo { + animation: .2s ease-in-out bar scroll(); + } + "#}, + Browsers { + chrome: Some(120 << 16), + ..Browsers::default() + }, + ); + prefix_test( + r#" + .foo { + animation: .2s ease-in-out bar scroll(); + } + "#, + indoc! {r#" + .foo { + -webkit-animation: .2s ease-in-out bar; + animation: .2s ease-in-out bar; + animation-timeline: scroll(); + } + "#}, + Browsers { + safari: Some(6 << 16), + ..Browsers::default() + }, + ); } #[test] @@ -23742,7 +23886,7 @@ mod tests { crate::css_modules::Config { animation: false, ..Default::default() - } + }, ); css_modules_test( r#" @@ -23760,9 +23904,7 @@ mod tests { "rotate" => "EgL3uq_rotate" referenced: true }, HashMap::new(), - crate::css_modules::Config { - ..Default::default() - } + crate::css_modules::Config { ..Default::default() }, ); // Stable hashes between project roots. diff --git a/src/properties/animation.rs b/src/properties/animation.rs index 2b2277d7..03b76166 100644 --- a/src/properties/animation.rs +++ b/src/properties/animation.rs @@ -10,7 +10,9 @@ use crate::prefixes::Feature; use crate::printer::Printer; use crate::properties::{Property, PropertyId, TokenOrValue, VendorPrefix}; use crate::traits::{Parse, PropertyHandler, Shorthand, ToCss, Zero}; +use crate::values::ident::DashedIdent; use crate::values::number::CSSNumber; +use crate::values::size::Size2D; use crate::values::string::CowArcStr; use crate::values::{easing::EasingFunction, ident::CustomIdent, time::Time}; #[cfg(feature = "visitor")] @@ -19,6 +21,8 @@ use cssparser::*; use itertools::izip; use smallvec::SmallVec; +use super::LengthPercentageOrAuto; + /// A value for the [animation-name](https://drafts.csswg.org/css-animations/#animation-name) property. #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "visitor", derive(Visit))] @@ -200,6 +204,255 @@ impl Default for AnimationFillMode { } } +enum_property! { + /// A value for the [animation-composition](https://drafts.csswg.org/css-animations-2/#animation-composition) property. + pub enum AnimationComposition { + /// The result of compositing the effect value with the underlying value is simply the effect value. + Replace, + /// The effect value is added to the underlying value. + Add, + /// The effect value is accumulated onto the underlying value. + Accumulate, + } +} + +/// A value for the [animation-timeline](https://drafts.csswg.org/css-animations-2/#animation-timeline) property. +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "visitor", derive(Visit))] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(tag = "type", content = "value", rename_all = "kebab-case") +)] +#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))] +#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))] +pub enum AnimationTimeline<'i> { + /// The animation’s timeline is a DocumentTimeline, more specifically the default document timeline. + Auto, + /// The animation is not associated with a timeline. + None, + /// A timeline referenced by name. + #[cfg_attr(feature = "serde", serde(borrow))] + DashedIdent(DashedIdent<'i>), + /// The scroll() function. + Scroll(ScrollTimeline), + /// The view() function. + View(ViewTimeline), +} + +impl<'i> Default for AnimationTimeline<'i> { + fn default() -> Self { + AnimationTimeline::Auto + } +} + +/// The [scroll()](https://drafts.csswg.org/scroll-animations-1/#scroll-notation) function. +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "visitor", derive(Visit))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))] +#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))] +pub struct ScrollTimeline { + /// Specifies which element to use as the scroll container. + pub scroller: Scroller, + /// Specifies which axis of the scroll container to use as the progress for the timeline. + pub axis: ScrollAxis, +} + +impl<'i> Parse<'i> for ScrollTimeline { + fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { + let mut scroller = None; + let mut axis = None; + loop { + if scroller.is_none() { + scroller = input.try_parse(Scroller::parse).ok(); + } + + if axis.is_none() { + axis = input.try_parse(ScrollAxis::parse).ok(); + if axis.is_some() { + continue; + } + } + break; + } + + Ok(ScrollTimeline { + scroller: scroller.unwrap_or_default(), + axis: axis.unwrap_or_default(), + }) + } +} + +impl ToCss for ScrollTimeline { + fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> + where + W: std::fmt::Write, + { + let mut needs_space = false; + if self.scroller != Scroller::default() { + self.scroller.to_css(dest)?; + needs_space = true; + } + + if self.axis != ScrollAxis::default() { + if needs_space { + dest.write_char(' ')?; + } + self.axis.to_css(dest)?; + } + + Ok(()) + } +} + +enum_property! { + /// A scroller, used in the `scroll()` function. + pub enum Scroller { + /// Specifies to use the document viewport as the scroll container. + "root": Root, + /// Specifies to use the nearest ancestor scroll container. + "nearest": Nearest, + /// Specifies to use the element’s own principal box as the scroll container. + "self": SelfElement, + } +} + +impl Default for Scroller { + fn default() -> Self { + Scroller::Nearest + } +} + +enum_property! { + /// A scroll axis, used in the `scroll()` function. + pub enum ScrollAxis { + /// Specifies to use the measure of progress along the block axis of the scroll container. + Block, + /// Specifies to use the measure of progress along the inline axis of the scroll container. + Inline, + /// Specifies to use the measure of progress along the horizontal axis of the scroll container. + X, + /// Specifies to use the measure of progress along the vertical axis of the scroll container. + Y, + } +} + +impl Default for ScrollAxis { + fn default() -> Self { + ScrollAxis::Block + } +} + +/// The [view()](https://drafts.csswg.org/scroll-animations-1/#view-notation) function. +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "visitor", derive(Visit))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))] +#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))] +pub struct ViewTimeline { + /// Specifies which axis of the scroll container to use as the progress for the timeline. + pub axis: ScrollAxis, + /// Provides an adjustment of the view progress visibility range. + pub inset: Size2D, +} + +impl<'i> Parse<'i> for ViewTimeline { + fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { + let mut axis = None; + let mut inset = None; + loop { + if axis.is_none() { + axis = input.try_parse(ScrollAxis::parse).ok(); + } + + if inset.is_none() { + inset = input.try_parse(Size2D::parse).ok(); + if inset.is_some() { + continue; + } + } + break; + } + + Ok(ViewTimeline { + axis: axis.unwrap_or_default(), + inset: inset.unwrap_or(Size2D(LengthPercentageOrAuto::Auto, LengthPercentageOrAuto::Auto)), + }) + } +} + +impl ToCss for ViewTimeline { + fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> + where + W: std::fmt::Write, + { + let mut needs_space = false; + if self.axis != ScrollAxis::default() { + self.axis.to_css(dest)?; + needs_space = true; + } + + if self.inset.0 != LengthPercentageOrAuto::Auto || self.inset.1 != LengthPercentageOrAuto::Auto { + if needs_space { + dest.write_char(' ')?; + } + self.inset.to_css(dest)?; + } + + Ok(()) + } +} + +impl<'i> Parse<'i> for AnimationTimeline<'i> { + fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { + if input.try_parse(|i| i.expect_ident_matching("auto")).is_ok() { + return Ok(AnimationTimeline::Auto); + } + + if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() { + return Ok(AnimationTimeline::None); + } + + if let Ok(name) = input.try_parse(DashedIdent::parse) { + return Ok(AnimationTimeline::DashedIdent(name)); + } + + let location = input.current_source_location(); + let f = input.expect_function()?.clone(); + input.parse_nested_block(move |input| { + match_ignore_ascii_case! { &f, + "scroll" => ScrollTimeline::parse(input).map(AnimationTimeline::Scroll), + "view" => ViewTimeline::parse(input).map(AnimationTimeline::View), + _ => Err(location.new_custom_error(ParserError::UnexpectedToken(crate::properties::custom::Token::Function(f.into())))) + } + }) + } +} + +impl<'i> ToCss for AnimationTimeline<'i> { + fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> + where + W: std::fmt::Write, + { + match self { + AnimationTimeline::Auto => dest.write_str("auto"), + AnimationTimeline::None => dest.write_str("none"), + AnimationTimeline::DashedIdent(name) => name.to_css(dest), + AnimationTimeline::Scroll(scroll) => { + dest.write_str("scroll(")?; + scroll.to_css(dest)?; + dest.write_char(')') + } + AnimationTimeline::View(view) => { + dest.write_str("view(")?; + view.to_css(dest)?; + dest.write_char(')') + } + } + } +} + define_list_shorthand! { /// A value for the [animation](https://drafts.csswg.org/css-animations/#animation) shorthand property. pub struct Animation<'i>(VendorPrefix) { @@ -220,6 +473,8 @@ define_list_shorthand! { delay: AnimationDelay(Time, VendorPrefix), /// The animation fill mode. fill_mode: AnimationFillMode(AnimationFillMode, VendorPrefix), + /// The animation timeline. + timeline: AnimationTimeline(AnimationTimeline<'i>), } } @@ -233,6 +488,7 @@ impl<'i> Parse<'i> for Animation<'i> { let mut play_state = None; let mut delay = None; let mut fill_mode = None; + let mut timeline = None; macro_rules! parse_prop { ($var: ident, $type: ident) => { @@ -254,6 +510,7 @@ impl<'i> Parse<'i> for Animation<'i> { parse_prop!(fill_mode, AnimationFillMode); parse_prop!(play_state, AnimationPlayState); parse_prop!(name, AnimationName); + parse_prop!(timeline, AnimationTimeline); break; } @@ -266,6 +523,7 @@ impl<'i> Parse<'i> for Animation<'i> { play_state: play_state.unwrap_or(AnimationPlayState::Running), delay: delay.unwrap_or(Time::Seconds(0.0)), fill_mode: fill_mode.unwrap_or(AnimationFillMode::None), + timeline: timeline.unwrap_or(AnimationTimeline::Auto), }) } } @@ -321,6 +579,11 @@ impl<'i> ToCss for Animation<'i> { // Chrome does not yet support strings, however. self.name.to_css(dest)?; + if self.name != AnimationName::None && self.timeline != AnimationTimeline::default() { + dest.write_char(' ')?; + self.timeline.to_css(dest)?; + } + Ok(()) } } @@ -338,6 +601,7 @@ pub(crate) struct AnimationHandler<'i> { play_states: Option<(SmallVec<[AnimationPlayState; 1]>, VendorPrefix)>, delays: Option<(SmallVec<[Time; 1]>, VendorPrefix)>, fill_modes: Option<(SmallVec<[AnimationFillMode; 1]>, VendorPrefix)>, + timelines: Option<[AnimationTimeline<'i>; 1]>>, has_any: bool, } @@ -384,6 +648,10 @@ impl<'i> PropertyHandler<'i> for AnimationHandler<'i> { Property::AnimationPlayState(val, vp) => property!(play_states, val, vp), Property::AnimationDelay(val, vp) => property!(delays, val, vp), Property::AnimationFillMode(val, vp) => property!(fill_modes, val, vp), + Property::AnimationTimeline(val) => { + self.timelines = Some(val.clone()); + self.has_any = true; + } Property::Animation(val, vp) => { let names = val.iter().map(|b| b.name.clone()).collect(); maybe_flush!(names, &names, vp); @@ -409,6 +677,8 @@ impl<'i> PropertyHandler<'i> for AnimationHandler<'i> { let fill_modes = val.iter().map(|b| b.fill_mode.clone()).collect(); maybe_flush!(fill_modes, &fill_modes, vp); + self.timelines = Some(val.iter().map(|b| b.timeline.clone()).collect()); + property!(names, &names, vp); property!(durations, &durations, vp); property!(timing_functions, &timing_functions, vp); @@ -433,6 +703,7 @@ impl<'i> PropertyHandler<'i> for AnimationHandler<'i> { && AnimationFillMode::parse_string(&id).is_err() && !EasingFunction::is_ident(&id) && id.as_ref() != "infinite" + && id.as_ref() != "auto" { *token = TokenOrValue::AnimationName(AnimationName::Ident(CustomIdent(id.clone()))); } @@ -477,6 +748,7 @@ impl<'i> AnimationHandler<'i> { let mut play_states = std::mem::take(&mut self.play_states); let mut delays = std::mem::take(&mut self.delays); let mut fill_modes = std::mem::take(&mut self.fill_modes); + let mut timelines_value = std::mem::take(&mut self.timelines); if let ( Some((names, names_vp)), @@ -507,6 +779,15 @@ impl<'i> AnimationHandler<'i> { & *play_states_vp & *delays_vp & *fill_modes_vp; + let mut timelines = if let Some(timelines) = &mut timelines_value { + Cow::Borrowed(timelines) + } else if !intersection.contains(VendorPrefix::None) { + // Prefixed animation shorthand does not support animation-timeline + Cow::Owned(std::iter::repeat(AnimationTimeline::Auto).take(len).collect()) + } else { + Cow::Owned(SmallVec::new()) + }; + if !intersection.is_empty() && durations.len() == len && timing_functions.len() == len @@ -515,7 +796,19 @@ impl<'i> AnimationHandler<'i> { && play_states.len() == len && delays.len() == len && fill_modes.len() == len + && timelines.len() == len { + let timeline_property = if timelines.iter().any(|t| *t != AnimationTimeline::Auto) + && (intersection != VendorPrefix::None + || !context + .targets + .is_compatible(crate::compat::Feature::AnimationTimelineShorthand)) + { + Some(Property::AnimationTimeline(timelines.clone().into_owned())) + } else { + None + }; + let animations = izip!( names.drain(..), durations.drain(..), @@ -524,10 +817,21 @@ impl<'i> AnimationHandler<'i> { directions.drain(..), play_states.drain(..), delays.drain(..), - fill_modes.drain(..) + fill_modes.drain(..), + timelines.to_mut().drain(..) ) .map( - |(name, duration, timing_function, iteration_count, direction, play_state, delay, fill_mode)| { + |( + name, + duration, + timing_function, + iteration_count, + direction, + play_state, + delay, + fill_mode, + timeline, + )| { Animation { name, duration, @@ -537,6 +841,11 @@ impl<'i> AnimationHandler<'i> { play_state, delay, fill_mode, + timeline: if timeline_property.is_some() { + AnimationTimeline::Auto + } else { + timeline + }, } }, ) @@ -551,6 +860,11 @@ impl<'i> AnimationHandler<'i> { play_states_vp.remove(intersection); delays_vp.remove(intersection); fill_modes_vp.remove(intersection); + + if let Some(p) = timeline_property { + dest.push(p); + } + timelines_value = None; } } @@ -573,6 +887,10 @@ impl<'i> AnimationHandler<'i> { prop!(play_states, AnimationPlayState); prop!(delays, AnimationDelay); prop!(fill_modes, AnimationFillMode); + + if let Some(val) = timelines_value { + dest.push(Property::AnimationTimeline(val)); + } } } @@ -587,6 +905,8 @@ fn is_animation_property(property_id: &PropertyId) -> bool { | PropertyId::AnimationPlayState(_) | PropertyId::AnimationDelay(_) | PropertyId::AnimationFillMode(_) + | PropertyId::AnimationComposition + | PropertyId::AnimationTimeline | PropertyId::Animation(_) => true, _ => false, } diff --git a/src/properties/custom.rs b/src/properties/custom.rs index c3621af6..3198422b 100644 --- a/src/properties/custom.rs +++ b/src/properties/custom.rs @@ -27,9 +27,9 @@ use crate::visitor::Visit; use cssparser::color::parse_hash_color; use cssparser::*; +use super::AnimationName; #[cfg(feature = "serde")] use crate::serialization::ValueWrapper; -use super::AnimationName; /// A CSS custom property, representing any unknown property. #[derive(Debug, Clone, PartialEq)] diff --git a/src/properties/mod.rs b/src/properties/mod.rs index 98756755..38ff8622 100644 --- a/src/properties/mod.rs +++ b/src/properties/mod.rs @@ -1491,6 +1491,8 @@ define_properties! { "animation-play-state": AnimationPlayState(SmallVec<[AnimationPlayState; 1]>, VendorPrefix) / WebKit / Moz / O, "animation-delay": AnimationDelay(SmallVec<[Time; 1]>, VendorPrefix) / WebKit / Moz / O, "animation-fill-mode": AnimationFillMode(SmallVec<[AnimationFillMode; 1]>, VendorPrefix) / WebKit / Moz / O, + "animation-composition": AnimationComposition(SmallVec<[AnimationComposition; 1]>), + "animation-timeline": AnimationTimeline(SmallVec<[AnimationTimeline<'i>; 1]>), "animation": Animation(AnimationList<'i>, VendorPrefix) / WebKit / Moz / O shorthand: true, // https://drafts.csswg.org/css-transforms-2/ From 81d21b9f5201c6eb8365fbb4fa81cbd947c3474f Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Fri, 17 May 2024 11:57:10 -0700 Subject: [PATCH 011/152] v1.25.0 --- Cargo.lock | 6 +++--- Cargo.toml | 4 ++-- napi/Cargo.toml | 4 ++-- node/Cargo.toml | 2 +- package.json | 2 +- selectors/Cargo.toml | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eed17d06..b1b97132 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -766,7 +766,7 @@ dependencies = [ [[package]] name = "lightningcss" -version = "1.0.0-alpha.55" +version = "1.0.0-alpha.56" dependencies = [ "ahash 0.8.7", "assert_cmd", @@ -812,7 +812,7 @@ dependencies = [ [[package]] name = "lightningcss-napi" -version = "0.1.0" +version = "0.2.0" dependencies = [ "crossbeam-channel", "cssparser", @@ -1006,7 +1006,7 @@ checksum = "7f222829ae9293e33a9f5e9f440c6760a3d450a64affe1846486b140db81c1f4" [[package]] name = "parcel_selectors" -version = "0.26.4" +version = "0.26.5" dependencies = [ "bitflags 2.4.1", "cssparser", diff --git a/Cargo.toml b/Cargo.toml index a298e0b1..e77fa5a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ members = [ [package] authors = ["Devon Govett "] name = "lightningcss" -version = "1.0.0-alpha.55" +version = "1.0.0-alpha.56" description = "A CSS parser, transformer, and minifier" license = "MPL-2.0" edition = "2021" @@ -51,7 +51,7 @@ substitute_variables = ["visitor", "into_owned"] serde = { version = "1.0.201", features = ["derive"], optional = true } cssparser = "0.33.0" cssparser-color = "0.1.0" -parcel_selectors = { version = "0.26.4", path = "./selectors" } +parcel_selectors = { version = "0.26.5", path = "./selectors" } itertools = "0.10.1" smallvec = { version = "1.7.0", features = ["union"] } bitflags = "2.2.1" diff --git a/napi/Cargo.toml b/napi/Cargo.toml index b296c3b6..d8415acd 100644 --- a/napi/Cargo.toml +++ b/napi/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["Devon Govett "] name = "lightningcss-napi" -version = "0.1.0" +version = "0.2.0" description = "Node-API bindings for Lightning CSS" license = "MPL-2.0" repository = "https://github.com/parcel-bundler/lightningcss" @@ -16,7 +16,7 @@ bundler = ["dep:crossbeam-channel", "dep:rayon"] serde = { version = "1.0.201", features = ["derive"] } serde_bytes = "0.11.5" cssparser = "0.33.0" -lightningcss = { version = "1.0.0-alpha.54", path = "../", features = ["nodejs", "serde"] } +lightningcss = { version = "1.0.0-alpha.55", path = "../", features = ["nodejs", "serde"] } parcel_sourcemap = { version = "2.1.1", features = ["json"] } serde-detach = "0.0.1" smallvec = { version = "1.7.0", features = ["union"] } diff --git a/node/Cargo.toml b/node/Cargo.toml index 4f9e8b75..bb472910 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -9,7 +9,7 @@ publish = false crate-type = ["cdylib"] [dependencies] -lightningcss-napi = { version = "0.1.0", path = "../napi", features = ["bundler", "visitor"] } +lightningcss-napi = { version = "0.2.0", path = "../napi", features = ["bundler", "visitor"] } napi = {version = "2.15.4", default-features = false, features = ["compat-mode"]} napi-derive = "2" diff --git a/package.json b/package.json index 740531f3..fa6a3b78 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lightningcss", - "version": "1.24.1", + "version": "1.25.0", "license": "MPL-2.0", "description": "A CSS parser, transformer, and minifier written in Rust", "main": "node/index.js", diff --git a/selectors/Cargo.toml b/selectors/Cargo.toml index 481c468b..8cad46af 100644 --- a/selectors/Cargo.toml +++ b/selectors/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "parcel_selectors" -version = "0.26.4" +version = "0.26.5" authors = ["The Servo Project Developers"] documentation = "https://docs.rs/parcel_selectors/" description = "CSS Selectors matching for Rust - forked for lightningcss" From 60622a1af51c0c6b83582fde60a244bfd9edf963 Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Fri, 24 May 2024 22:44:53 -0700 Subject: [PATCH 012/152] fix order of properties with all shorthand fixes #746 --- Cargo.lock | 1 - Cargo.toml | 1 - node/test/visitor.test.mjs | 4 ++-- src/declaration.rs | 39 +++++++++++++++++++------------------- src/lib.rs | 21 ++++++++++++-------- 5 files changed, 35 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b1b97132..9cb99076 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -781,7 +781,6 @@ dependencies = [ "dashmap", "data-encoding", "getrandom", - "indexmap 2.2.6", "indoc", "itertools 0.10.5", "jemallocator", diff --git a/Cargo.toml b/Cargo.toml index e77fa5a3..a71057a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,7 +62,6 @@ const-str = "0.3.1" pathdiff = "0.2.1" ahash = "0.8.7" paste = "1.0.12" -indexmap = "2.2.6" # CLI deps atty = { version = "0.2", optional = true } clap = { version = "3.0.6", features = ["derive"], optional = true } diff --git a/node/test/visitor.test.mjs b/node/test/visitor.test.mjs index 87ee208d..3a42a696 100644 --- a/node/test/visitor.test.mjs +++ b/node/test/visitor.test.mjs @@ -116,7 +116,7 @@ test('custom units', () => { } }); - assert.equal(res.code.toString(), '.foo{font-size:calc(3*var(--step));--step:.25rem}'); + assert.equal(res.code.toString(), '.foo{--step:.25rem;font-size:calc(3*var(--step))}'); }); test('design tokens', () => { @@ -822,7 +822,7 @@ test('dashed idents', () => { } }); - assert.equal(res.code.toString(), '.foo{color:var(--prefix-foo);--prefix-foo:#ff0}'); + assert.equal(res.code.toString(), '.foo{--prefix-foo:#ff0;color:var(--prefix-foo)}'); }); test('custom idents', () => { diff --git a/src/declaration.rs b/src/declaration.rs index 501e2f8c..da97e37d 100644 --- a/src/declaration.rs +++ b/src/declaration.rs @@ -1,6 +1,7 @@ //! CSS declarations. use std::borrow::Cow; +use std::collections::HashMap; use std::ops::Range; use crate::context::{DeclarationContext, PropertyHandlerContext}; @@ -33,14 +34,13 @@ use crate::properties::{ transition::TransitionHandler, ui::ColorSchemeHandler, }; -use crate::properties::{CSSWideKeyword, Property, PropertyId}; +use crate::properties::{Property, PropertyId}; use crate::traits::{PropertyHandler, ToCss}; use crate::values::ident::DashedIdent; use crate::values::string::CowArcStr; #[cfg(feature = "visitor")] use crate::visitor::Visit; use cssparser::*; -use indexmap::IndexMap; /// A CSS declaration block. /// @@ -516,10 +516,9 @@ pub(crate) struct DeclarationHandler<'i> { color_scheme: ColorSchemeHandler, fallback: FallbackHandler, prefix: PrefixHandler, - all: Option, direction: Option, unicode_bidi: Option, - custom_properties: IndexMap<'i>, CustomProperty<'i>>, + custom_properties: HashMap<'i>, usize>, decls: DeclarationList<'i>, } @@ -571,13 +570,18 @@ impl<'i> DeclarationHandler<'i> { } if let CustomPropertyName::Custom(name) = &custom.name { - if let Some(prev) = self.custom_properties.get_mut(name) { - if prev.value == custom.value { + if let Some(index) = self.custom_properties.get(name) { + if self.decls[*index] == *property { return true; } - *prev = custom.clone(); + let mut custom = custom.clone(); + self.add_conditional_fallbacks(&mut custom, context); + self.decls[*index] = Property::Custom(custom); } else { - self.custom_properties.insert(name.clone(), custom.clone()); + self.custom_properties.insert(name.clone(), self.decls.len()); + let mut custom = custom.clone(); + self.add_conditional_fallbacks(&mut custom, context); + self.decls.push(Property::Custom(custom)); } return true; @@ -600,13 +604,17 @@ impl<'i> DeclarationHandler<'i> { true } Property::All(keyword) => { - *self = DeclarationHandler { - custom_properties: std::mem::take(&mut self.custom_properties), + let mut handler = DeclarationHandler { unicode_bidi: self.unicode_bidi.clone(), direction: self.direction.clone(), - all: Some(keyword.clone()), ..Default::default() }; + for (key, index) in self.custom_properties.drain() { + handler.custom_properties.insert(key, handler.decls.len()); + handler.decls.push(self.decls[index].clone()); + } + handler.decls.push(Property::All(keyword.clone())); + *self = handler; true } _ => false, @@ -633,20 +641,12 @@ impl<'i> DeclarationHandler<'i> { } pub fn finalize(&mut self, context: &mut PropertyHandlerContext<'i, '_>) { - // Always place the `all` property first. Previous properties will have been omitted. - if let Some(all) = std::mem::take(&mut self.all) { - self.decls.push(Property::All(all)); - } if let Some(direction) = std::mem::take(&mut self.direction) { self.decls.push(Property::Direction(direction)); } if let Some(unicode_bidi) = std::mem::take(&mut self.unicode_bidi) { self.decls.push(Property::UnicodeBidi(unicode_bidi)); } - for (_, mut value) in std::mem::take(&mut self.custom_properties) { - self.add_conditional_fallbacks(&mut value, context); - self.decls.push(Property::Custom(value)); - } self.background.finalize(&mut self.decls, context); self.border.finalize(&mut self.decls, context); @@ -675,5 +675,6 @@ impl<'i> DeclarationHandler<'i> { self.color_scheme.finalize(&mut self.decls, context); self.fallback.finalize(&mut self.decls, context); self.prefix.finalize(&mut self.decls, context); + self.custom_properties.clear(); } } diff --git a/src/lib.rs b/src/lib.rs index d39c6ac4..27a7df16 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21583,26 +21583,26 @@ mod tests { indoc! {r#" @keyframes foo { from { - opacity: 0; --custom: #ff0; + opacity: 0; } to { - opacity: 1; --custom: #ee00be; + opacity: 1; } } @supports (color: lab(0% 0 0)) { @keyframes foo { from { - opacity: 0; --custom: #ff0; + opacity: 0; } to { - opacity: 1; --custom: lab(50.998% 125.506 -50.7078); + opacity: 1; } } } @@ -23736,8 +23736,8 @@ mod tests { } .EgL3uq_foo { - color: var(--foo); --foo: red; + color: var(--foo); } "#}, map! { @@ -23786,10 +23786,10 @@ mod tests { } .EgL3uq_foo { - color: var(--EgL3uq_foo); - font-palette: --EgL3uq_Cooler; --EgL3uq_foo: red; --EgL3uq_bar: green; + color: var(--EgL3uq_foo); + font-palette: --EgL3uq_Cooler; } .EgL3uq_bar { @@ -27646,7 +27646,7 @@ mod tests { ); minify_test( ".foo { --test: red; all: revert-layer }", - ".foo{all:revert-layer;--test:red}", + ".foo{--test:red;all:revert-layer}", ); minify_test( ".foo { unicode-bidi: embed; all: revert-layer }", @@ -27660,5 +27660,10 @@ mod tests { ".foo { direction: rtl; all: revert-layer; direction: ltr }", ".foo{all:revert-layer;direction:ltr}", ); + minify_test(".foo { background: var(--foo); all: unset; }", ".foo{all:unset}"); + minify_test( + ".foo { all: unset; background: var(--foo); }", + ".foo{all:unset;background:var(--foo)}", + ); } } From fa6c015317e5cca29fa8a09b12d09ac53dcfbc77 Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Fri, 24 May 2024 22:48:31 -0700 Subject: [PATCH 013/152] v1.25.1 --- Cargo.lock | 2 +- Cargo.toml | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9cb99076..a5d8b853 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -766,7 +766,7 @@ dependencies = [ [[package]] name = "lightningcss" -version = "1.0.0-alpha.56" +version = "1.0.0-alpha.57" dependencies = [ "ahash 0.8.7", "assert_cmd", diff --git a/Cargo.toml b/Cargo.toml index a71057a7..7036f761 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ members = [ [package] authors = ["Devon Govett "] name = "lightningcss" -version = "1.0.0-alpha.56" +version = "1.0.0-alpha.57" description = "A CSS parser, transformer, and minifier" license = "MPL-2.0" edition = "2021" diff --git a/package.json b/package.json index fa6a3b78..91ffd35f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lightningcss", - "version": "1.25.0", + "version": "1.25.1", "license": "MPL-2.0", "description": "A CSS parser, transformer, and minifier written in Rust", "main": "node/index.js", From 76e31cf963cc0ef6ace0415664cc987c6977b07f Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Sun, 2 Jun 2024 21:17:43 -0700 Subject: [PATCH 014/152] Use macros to derive implementations of Parse and ToCss for many enums --- Cargo.lock | 1 + Cargo.toml | 4 +- derive/Cargo.toml | 1 + derive/src/lib.rs | 295 ++------------------------------- derive/src/parse.rs | 213 ++++++++++++++++++++++++ derive/src/to_css.rs | 156 +++++++++++++++++ derive/src/visit.rs | 292 ++++++++++++++++++++++++++++++++ src/macros.rs | 57 +------ src/properties/align.rs | 203 +++-------------------- src/properties/animation.rs | 182 ++++++-------------- src/properties/background.rs | 24 +-- src/properties/border.rs | 33 +--- src/properties/border_image.rs | 34 +--- src/properties/contain.rs | 6 +- src/properties/custom.rs | 20 +-- src/properties/display.rs | 59 ++----- src/properties/flex.rs | 16 +- src/properties/font.rs | 182 +++----------------- src/properties/grid.rs | 58 +------ src/properties/list.rs | 171 +++++++------------ src/properties/masking.rs | 77 +++------ src/properties/outline.rs | 25 +-- src/properties/position.rs | 25 +-- src/properties/size.rs | 4 +- src/properties/svg.rs | 111 ++----------- src/properties/text.rs | 161 +++++------------- src/properties/transform.rs | 38 +---- src/properties/ui.rs | 97 +++++------ src/rules/keyframes.rs | 20 +-- src/rules/page.rs | 32 ++-- src/traits.rs | 37 +++++ src/values/calc.rs | 8 +- src/values/color.rs | 1 + src/values/easing.rs | 16 +- src/values/gradient.rs | 42 +---- src/values/image.rs | 38 +---- src/values/length.rs | 62 +------ src/values/percentage.rs | 32 +--- src/values/shape.rs | 30 +--- 39 files changed, 1100 insertions(+), 1763 deletions(-) create mode 100644 derive/src/parse.rs create mode 100644 derive/src/to_css.rs create mode 100644 derive/src/visit.rs diff --git a/Cargo.lock b/Cargo.lock index a5d8b853..07d5f7e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -804,6 +804,7 @@ dependencies = [ name = "lightningcss-derive" version = "1.0.0-alpha.42" dependencies = [ + "convert_case", "proc-macro2", "quote", "syn 1.0.109", diff --git a/Cargo.toml b/Cargo.toml index 7036f761..75bc397c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,7 +43,7 @@ jsonschema = ["schemars", "serde", "parcel_selectors/jsonschema"] nodejs = ["dep:serde"] serde = ["dep:serde", "smallvec/serde", "cssparser/serde", "parcel_selectors/serde", "into_owned"] sourcemap = ["parcel_sourcemap"] -visitor = ["lightningcss-derive"] +visitor = [] into_owned = ["static-self", "static-self/smallvec", "parcel_selectors/into_owned"] substitute_variables = ["visitor", "into_owned"] @@ -69,7 +69,7 @@ browserslist-rs = { version = "0.15.0", optional = true } rayon = { version = "1.5.1", optional = true } dashmap = { version = "5.0.0", optional = true } serde_json = { version = "1.0.78", optional = true } -lightningcss-derive = { version = "=1.0.0-alpha.42", path = "./derive", optional = true } +lightningcss-derive = { version = "=1.0.0-alpha.42", path = "./derive" } schemars = { version = "0.8.19", features = ["smallvec"], optional = true } static-self = { version = "0.1.0", path = "static-self", optional = true } diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 1864748e..e1f82c70 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -14,3 +14,4 @@ proc-macro = true syn = { version = "1.0", features = ["extra-traits"] } quote = "1.0" proc-macro2 = "1.0" +convert_case = "0.6.0" diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 00b77a26..12241491 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -1,293 +1,20 @@ -use std::collections::HashSet; +use proc_macro::TokenStream; -use proc_macro::{self, TokenStream}; -use proc_macro2::{Span, TokenStream as TokenStream2}; -use quote::quote; -use syn::{ - parse::Parse, parse_macro_input, parse_quote, Attribute, Data, DataEnum, DeriveInput, Field, Fields, - GenericParam, Generics, Ident, Member, Token, Type, Visibility, -}; +mod parse; +mod to_css; +mod visit; #[proc_macro_derive(Visit, attributes(visit, skip_visit, skip_type, visit_types))] pub fn derive_visit_children(input: TokenStream) -> TokenStream { - let DeriveInput { - ident, - data, - generics, - attrs, - .. - } = parse_macro_input!(input); - - let options: Vec = attrs - .iter() - .filter_map(|attr| { - if attr.path.is_ident("visit") { - let opts: VisitOptions = attr.parse_args().unwrap(); - Some(opts) - } else { - None - } - }) - .collect(); - - let visit_types = if let Some(attr) = attrs.iter().find(|attr| attr.path.is_ident("visit_types")) { - let types: VisitTypes = attr.parse_args().unwrap(); - let types = types.types; - Some(quote! { crate::visit_types!(#(#types)|*) }) - } else { - None - }; - - if options.is_empty() { - derive(&ident, &data, &generics, None, visit_types) - } else { - options - .into_iter() - .map(|options| derive(&ident, &data, &generics, Some(options), visit_types.clone())) - .collect() - } -} - -fn derive( - ident: &Ident, - data: &Data, - generics: &Generics, - options: Option, - visit_types: Option, -) -> TokenStream { - let mut impl_generics = generics.clone(); - let mut type_defs = quote! {}; - let generics = if let Some(VisitOptions { - generic: Some(generic), .. - }) = &options - { - let mappings = generics - .type_params() - .zip(generic.type_params()) - .map(|(a, b)| quote! { type #a = #b; }); - type_defs = quote! { #(#mappings)* }; - impl_generics.params.clear(); - generic - } else { - &generics - }; - - if impl_generics.lifetimes().next().is_none() { - impl_generics.params.insert(0, parse_quote! { 'i }) - } - - let lifetime = impl_generics.lifetimes().next().unwrap().clone(); - let t = impl_generics.type_params().find(|g| &g.ident.to_string() == "R"); - let v = quote! { __V }; - let t = if let Some(t) = t { - GenericParam::Type(t.ident.clone().into()) - } else { - let t: GenericParam = parse_quote! { __T }; - impl_generics - .params - .push(parse_quote! { #t: crate::visitor::Visit<#lifetime, __T, #v> }); - t - }; - - impl_generics - .params - .push(parse_quote! { #v: ?Sized + crate::visitor::Visitor<#lifetime, #t> }); - - for ty in generics.type_params() { - let name = &ty.ident; - impl_generics.make_where_clause().predicates.push(parse_quote! { - #name: Visit<#lifetime, #t, #v> - }) - } - - let mut seen_types = HashSet::new(); - let mut child_types = Vec::new(); - let mut visit = Vec::new(); - match data { - Data::Struct(s) => { - for ( - index, - Field { - vis, ty, ident, attrs, .. - }, - ) in s.fields.iter().enumerate() - { - if attrs.iter().any(|attr| attr.path.is_ident("skip_visit")) { - continue; - } - - if matches!(ty, Type::Reference(_)) || !matches!(vis, Visibility::Public(..)) { - continue; - } - - if visit_types.is_none() && !seen_types.contains(ty) && !skip_type(attrs) { - seen_types.insert(ty.clone()); - child_types.push(quote! { - <#ty as Visit<#lifetime, #t, #v>>::CHILD_TYPES.bits() - }); - } - - let name = ident - .as_ref() - .map_or_else(|| Member::Unnamed(index.into()), |ident| Member::Named(ident.clone())); - visit.push(quote! { self.#name.visit(visitor)?; }) - } - } - Data::Enum(DataEnum { variants, .. }) => { - let variants = variants - .iter() - .map(|variant| { - let name = &variant.ident; - let mut field_names = Vec::new(); - let mut visit_fields = Vec::new(); - for (index, Field { ty, ident, attrs, .. }) in variant.fields.iter().enumerate() { - let name = ident.as_ref().map_or_else( - || Ident::new(&format!("_{}", index), Span::call_site()), - |ident| ident.clone(), - ); - field_names.push(name.clone()); - - if matches!(ty, Type::Reference(_)) { - continue; - } - - if visit_types.is_none() && !seen_types.contains(ty) && !skip_type(attrs) && !skip_type(&variant.attrs) - { - seen_types.insert(ty.clone()); - child_types.push(quote! { - <#ty as Visit<#lifetime, #t, #v>>::CHILD_TYPES.bits() - }); - } - - visit_fields.push(quote! { #name.visit(visitor)?; }) - } - - match variant.fields { - Fields::Unnamed(_) => { - quote! { - Self::#name(#(#field_names),*) => { - #(#visit_fields)* - } - } - } - Fields::Named(_) => { - quote! { - Self::#name { #(#field_names),* } => { - #(#visit_fields)* - } - } - } - Fields::Unit => quote! {}, - } - }) - .collect::(); - - visit.push(quote! { - match self { - #variants - _ => {} - } - }) - } - _ => {} - } - - if visit_types.is_none() && child_types.is_empty() { - child_types.push(quote! { crate::visitor::VisitTypes::empty().bits() }); - } - - let (_, ty_generics, _) = generics.split_for_impl(); - let (impl_generics, _, where_clause) = impl_generics.split_for_impl(); - - let self_visit = if let Some(VisitOptions { - visit: Some(visit), - kind: Some(kind), - .. - }) = &options - { - child_types.push(quote! { crate::visitor::VisitTypes::#kind.bits() }); - - quote! { - fn visit(&mut self, visitor: &mut #v) -> Result<(), #v::Error> { - if visitor.visit_types().contains(crate::visitor::VisitTypes::#kind) { - visitor.#visit(self) - } else { - self.visit_children(visitor) - } - } - } - } else { - quote! {} - }; - - let child_types = visit_types.unwrap_or_else(|| { - quote! { - { - #type_defs - crate::visitor::VisitTypes::from_bits_retain(#(#child_types)|*) - } - } - }); - - let output = quote! { - impl #impl_generics Visit<#lifetime, #t, #v> for #ident #ty_generics #where_clause { - const CHILD_TYPES: crate::visitor::VisitTypes = #child_types; - - #self_visit - - fn visit_children(&mut self, visitor: &mut #v) -> Result<(), #v::Error> { - if !<#lifetime, #t, #v>>::CHILD_TYPES.intersects(visitor.visit_types()) { - return Ok(()) - } - - #(#visit)* - - Ok(()) - } - } - }; - - output.into() -} - -fn skip_type(attrs: &Vec) -> bool { - attrs.iter().any(|attr| attr.path.is_ident("skip_type")) -} - -struct VisitOptions { - visit: Option, - kind: Option, - generic: Option, -} - -impl Parse for VisitOptions { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let (visit, kind, comma) = if input.peek(Ident) { - let visit: Ident = input.parse()?; - let _: Token![,] = input.parse()?; - let kind: Ident = input.parse()?; - let comma: Result = input.parse(); - (Some(visit), Some(kind), comma.is_ok()) - } else { - (None, None, true) - }; - let generic: Option = if comma { Some(input.parse()?) } else { None }; - Ok(Self { visit, kind, generic }) - } + visit::derive_visit_children(input) } -struct VisitTypes { - types: Vec, +#[proc_macro_derive(Parse, attributes(css))] +pub fn derive_parse(input: TokenStream) -> TokenStream { + parse::derive_parse(input) } -impl Parse for VisitTypes { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let first: Ident = input.parse()?; - let mut types = vec![first]; - while input.parse::().is_ok() { - let id: Ident = input.parse()?; - types.push(id); - } - Ok(Self { types }) - } +#[proc_macro_derive(ToCss, attributes(css))] +pub fn derive_to_css(input: TokenStream) -> TokenStream { + to_css::derive_to_css(input) } diff --git a/derive/src/parse.rs b/derive/src/parse.rs new file mode 100644 index 00000000..995b344e --- /dev/null +++ b/derive/src/parse.rs @@ -0,0 +1,213 @@ +use convert_case::Casing; +use proc_macro::{self, TokenStream}; +use proc_macro2::{Literal, Span, TokenStream as TokenStream2}; +use quote::quote; +use syn::{ + parse::Parse, parse_macro_input, parse_quote, Attribute, Data, DataEnum, DeriveInput, Fields, Ident, Token, +}; + +pub fn derive_parse(input: TokenStream) -> TokenStream { + let DeriveInput { + ident, + data, + mut generics, + attrs, + .. + } = parse_macro_input!(input); + let opts = CssOptions::parse_attributes(&attrs).unwrap(); + let cloned_generics = generics.clone(); + let (_, ty_generics, _) = cloned_generics.split_for_impl(); + + if generics.lifetimes().next().is_none() { + generics.params.insert(0, parse_quote! { 'i }) + } + + let lifetime = generics.lifetimes().next().unwrap().clone(); + let (impl_generics, _, where_clause) = generics.split_for_impl(); + + let imp = match &data { + Data::Enum(data) => derive_enum(&data, &ident, &opts), + _ => todo!(), + }; + + let output = quote! { + impl #impl_generics Parse<#lifetime> for #ident #ty_generics #where_clause { + fn parse<'t>(input: &mut Parser<#lifetime, 't>) -> Result<#lifetime, ParserError<#lifetime>>> { + #imp + } + } + }; + + output.into() +} + +fn derive_enum(data: &DataEnum, ident: &Ident, opts: &CssOptions) -> TokenStream2 { + let mut idents = Vec::new(); + let mut non_idents = Vec::new(); + for (index, variant) in data.variants.iter().enumerate() { + let name = &variant.ident; + let fields = variant + .fields + .iter() + .enumerate() + .map(|(index, field)| { + field.ident.as_ref().map_or_else( + || Ident::new(&format!("_{}", index), Span::call_site()), + |ident| ident.clone(), + ) + }) + .collect::<_>>(); + + let mut expr = match &variant.fields { + Fields::Unit => { + idents.push(( + Literal::string(&variant.ident.to_string().to_case(opts.case)), + name.clone(), + )); + continue; + } + Fields::Named(_) => { + quote! { + return Ok(#ident::#name { #(#fields),* }) + } + } + Fields::Unnamed(_) => { + quote! { + return Ok(#ident::#name(#(#fields),*)) + } + } + }; + + // Group multiple ident branches together. + if !idents.is_empty() { + if idents.len() == 1 { + let (s, name) = idents.remove(0); + non_idents.push(quote! { + if input.try_parse(|input| input.expect_ident_matching(#s)).is_ok() { + return Ok(#ident::#name) + } + }); + } else { + let matches = idents + .iter() + .map(|(s, name)| { + quote! { + #s => return Ok(#ident::#name), + } + }) + .collect::<_>>(); + non_idents.push(quote! { + { + let state = input.state(); + if let Ok(ident) = input.try_parse(|input| input.expect_ident_cloned()) { + cssparser::match_ignore_ascii_case! { &*ident, + #(#matches)* + _ => {} + } + input.reset(&state); + } + } + }); + idents.clear(); + } + } + + let is_last = index == data.variants.len() - 1; + + for (index, field) in variant.fields.iter().enumerate().rev() { + let ty = &field.ty; + let field_name = field.ident.as_ref().map_or_else( + || Ident::new(&format!("_{}", index), Span::call_site()), + |ident| ident.clone(), + ); + if is_last { + expr = quote! { + let #field_name = <#ty>::parse(input)?; + #expr + }; + } else { + expr = quote! { + if let Ok(#field_name) = input.try_parse(<#ty>::parse) { + #expr + } + }; + } + } + + non_idents.push(expr); + } + + let idents = if idents.is_empty() { + quote! {} + } else if idents.len() == 1 { + let (s, name) = idents.remove(0); + quote! { + input.expect_ident_matching(#s)?; + Ok(#ident::#name) + } + } else { + let idents = idents + .into_iter() + .map(|(s, name)| { + quote! { + #s => Ok(#ident::#name), + } + }) + .collect::<_>>(); + quote! { + let location = input.current_source_location(); + let ident = input.expect_ident()?; + cssparser::match_ignore_ascii_case! { &*ident, + #(#idents)* + _ => Err(location.new_unexpected_token_error( + cssparser::Token::Ident(ident.clone()) + )) + } + } + }; + + let output = quote! { + #(#non_idents)* + #idents + }; + + output.into() +} + +pub struct CssOptions { + pub case: convert_case::Case, +} + +impl CssOptions { + pub fn parse_attributes(attrs: &Vec) -> syn::Result { + for attr in attrs { + if attr.path.is_ident("css") { + let opts: CssOptions = attr.parse_args()?; + return Ok(opts); + } + } + + Ok(CssOptions { + case: convert_case::Case::Kebab, + }) + } +} + +impl Parse for CssOptions { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut case = convert_case::Case::Kebab; + while !input.is_empty() { + let k: Ident = input.parse()?; + let _: Token![=] = input.parse()?; + let v: Ident = input.parse()?; + + if k == "case" { + if v == "lower" { + case = convert_case::Case::Flat; + } + } + } + + Ok(Self { case }) + } +} diff --git a/derive/src/to_css.rs b/derive/src/to_css.rs new file mode 100644 index 00000000..739a16d4 --- /dev/null +++ b/derive/src/to_css.rs @@ -0,0 +1,156 @@ +use convert_case::Casing; +use proc_macro::{self, TokenStream}; +use proc_macro2::{Literal, Span, TokenStream as TokenStream2}; +use quote::quote; +use syn::{parse_macro_input, Data, DataEnum, DeriveInput, Fields, Ident, Type}; + +use crate::parse::CssOptions; + +pub fn derive_to_css(input: TokenStream) -> TokenStream { + let DeriveInput { + ident, + data, + generics, + attrs, + .. + } = parse_macro_input!(input); + + let opts = CssOptions::parse_attributes(&attrs).unwrap(); + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + let imp = match &data { + Data::Enum(data) => derive_enum(&data, &opts), + _ => todo!(), + }; + + let output = quote! { + impl #impl_generics ToCss for #ident #ty_generics #where_clause { + fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> + where + W: std::fmt::Write, + { + #imp + } + } + }; + + output.into() +} + +fn derive_enum(data: &DataEnum, opts: &CssOptions) -> TokenStream2 { + let variants = data + .variants + .iter() + .map(|variant| { + let name = &variant.ident; + let fields = variant + .fields + .iter() + .enumerate() + .map(|(index, field)| { + field.ident.as_ref().map_or_else( + || Ident::new(&format!("_{}", index), Span::call_site()), + |ident| ident.clone(), + ) + }) + .collect::<_>>(); + + #[derive(PartialEq)] + enum NeedsSpace { + Yes, + No, + Maybe, + } + + let mut needs_space = NeedsSpace::No; + let mut fields_iter = variant.fields.iter().zip(fields.iter()).peekable(); + let mut writes = Vec::new(); + let mut has_needs_space = false; + while let Some((field, name)) = fields_iter.next() { + writes.push(if fields.len() > 1 { + let space = match needs_space { + NeedsSpace::Yes => quote! { dest.write_char(' ')?; }, + NeedsSpace::No => quote! {}, + NeedsSpace::Maybe => { + has_needs_space = true; + quote! { + if needs_space { + dest.write_char(' ')?; + } + } + } + }; + + if is_option(&field.ty) { + needs_space = NeedsSpace::Maybe; + let after_space = if matches!(fields_iter.peek(), Some((field, _)) if !is_option(&field.ty)) { + // If the next field is non-optional, just insert the space here. + needs_space = NeedsSpace::No; + quote! { dest.write_char(' ')?; } + } else { + quote! {} + }; + quote! { + if let Some(v) = #name { + #space + v.to_css(dest)?; + #after_space + } + } + } else { + needs_space = NeedsSpace::Yes; + quote! { + #space + #name.to_css(dest)?; + } + } + } else { + quote! { #name.to_css(dest) } + }); + } + + if writes.len() > 1 { + writes.push(quote! { Ok(()) }); + } + + if has_needs_space { + writes.insert(0, quote! { let mut needs_space = false }); + } + + match variant.fields { + Fields::Unit => { + let s = Literal::string(&variant.ident.to_string().to_case(opts.case)); + quote! { + Self::#name => dest.write_str(#s) + } + } + Fields::Named(_) => { + quote! { + Self::#name { #(#fields),* } => { + #(#writes)* + } + } + } + Fields::Unnamed(_) => { + quote! { + Self::#name(#(#fields),*) => { + #(#writes)* + } + } + } + } + }) + .collect::<_>>(); + + let output = quote! { + match self { + #(#variants),* + } + }; + + output.into() +} + +fn is_option(ty: &Type) -> bool { + matches!(&ty, Type::Path(p) if p.qself.is_none() && p.path.segments.iter().next().unwrap().ident == "Option") +} diff --git a/derive/src/visit.rs b/derive/src/visit.rs new file mode 100644 index 00000000..02093364 --- /dev/null +++ b/derive/src/visit.rs @@ -0,0 +1,292 @@ +use std::collections::HashSet; + +use proc_macro::{self, TokenStream}; +use proc_macro2::{Span, TokenStream as TokenStream2}; +use quote::quote; +use syn::{ + parse::Parse, parse_macro_input, parse_quote, Attribute, Data, DataEnum, DeriveInput, Field, Fields, + GenericParam, Generics, Ident, Member, Token, Type, Visibility, +}; + +pub fn derive_visit_children(input: TokenStream) -> TokenStream { + let DeriveInput { + ident, + data, + generics, + attrs, + .. + } = parse_macro_input!(input); + + let options: Vec = attrs + .iter() + .filter_map(|attr| { + if attr.path.is_ident("visit") { + let opts: VisitOptions = attr.parse_args().unwrap(); + Some(opts) + } else { + None + } + }) + .collect(); + + let visit_types = if let Some(attr) = attrs.iter().find(|attr| attr.path.is_ident("visit_types")) { + let types: VisitTypes = attr.parse_args().unwrap(); + let types = types.types; + Some(quote! { crate::visit_types!(#(#types)|*) }) + } else { + None + }; + + if options.is_empty() { + derive(&ident, &data, &generics, None, visit_types) + } else { + options + .into_iter() + .map(|options| derive(&ident, &data, &generics, Some(options), visit_types.clone())) + .collect() + } +} + +fn derive( + ident: &Ident, + data: &Data, + generics: &Generics, + options: Option, + visit_types: Option, +) -> TokenStream { + let mut impl_generics = generics.clone(); + let mut type_defs = quote! {}; + let generics = if let Some(VisitOptions { + generic: Some(generic), .. + }) = &options + { + let mappings = generics + .type_params() + .zip(generic.type_params()) + .map(|(a, b)| quote! { type #a = #b; }); + type_defs = quote! { #(#mappings)* }; + impl_generics.params.clear(); + generic + } else { + &generics + }; + + if impl_generics.lifetimes().next().is_none() { + impl_generics.params.insert(0, parse_quote! { 'i }) + } + + let lifetime = impl_generics.lifetimes().next().unwrap().clone(); + let t = impl_generics.type_params().find(|g| &g.ident.to_string() == "R"); + let v = quote! { __V }; + let t = if let Some(t) = t { + GenericParam::Type(t.ident.clone().into()) + } else { + let t: GenericParam = parse_quote! { __T }; + impl_generics + .params + .push(parse_quote! { #t: crate::visitor::Visit<#lifetime, __T, #v> }); + t + }; + + impl_generics + .params + .push(parse_quote! { #v: ?Sized + crate::visitor::Visitor<#lifetime, #t> }); + + for ty in generics.type_params() { + let name = &ty.ident; + impl_generics.make_where_clause().predicates.push(parse_quote! { + #name: Visit<#lifetime, #t, #v> + }) + } + + let mut seen_types = HashSet::new(); + let mut child_types = Vec::new(); + let mut visit = Vec::new(); + match data { + Data::Struct(s) => { + for ( + index, + Field { + vis, ty, ident, attrs, .. + }, + ) in s.fields.iter().enumerate() + { + if attrs.iter().any(|attr| attr.path.is_ident("skip_visit")) { + continue; + } + + if matches!(ty, Type::Reference(_)) || !matches!(vis, Visibility::Public(..)) { + continue; + } + + if visit_types.is_none() && !seen_types.contains(ty) && !skip_type(attrs) { + seen_types.insert(ty.clone()); + child_types.push(quote! { + <#ty as Visit<#lifetime, #t, #v>>::CHILD_TYPES.bits() + }); + } + + let name = ident + .as_ref() + .map_or_else(|| Member::Unnamed(index.into()), |ident| Member::Named(ident.clone())); + visit.push(quote! { self.#name.visit(visitor)?; }) + } + } + Data::Enum(DataEnum { variants, .. }) => { + let variants = variants + .iter() + .map(|variant| { + let name = &variant.ident; + let mut field_names = Vec::new(); + let mut visit_fields = Vec::new(); + for (index, Field { ty, ident, attrs, .. }) in variant.fields.iter().enumerate() { + let name = ident.as_ref().map_or_else( + || Ident::new(&format!("_{}", index), Span::call_site()), + |ident| ident.clone(), + ); + field_names.push(name.clone()); + + if matches!(ty, Type::Reference(_)) { + continue; + } + + if visit_types.is_none() && !seen_types.contains(ty) && !skip_type(attrs) && !skip_type(&variant.attrs) + { + seen_types.insert(ty.clone()); + child_types.push(quote! { + <#ty as Visit<#lifetime, #t, #v>>::CHILD_TYPES.bits() + }); + } + + visit_fields.push(quote! { #name.visit(visitor)?; }) + } + + match variant.fields { + Fields::Unnamed(_) => { + quote! { + Self::#name(#(#field_names),*) => { + #(#visit_fields)* + } + } + } + Fields::Named(_) => { + quote! { + Self::#name { #(#field_names),* } => { + #(#visit_fields)* + } + } + } + Fields::Unit => quote! {}, + } + }) + .collect::(); + + visit.push(quote! { + match self { + #variants + _ => {} + } + }) + } + _ => {} + } + + if visit_types.is_none() && child_types.is_empty() { + child_types.push(quote! { crate::visitor::VisitTypes::empty().bits() }); + } + + let (_, ty_generics, _) = generics.split_for_impl(); + let (impl_generics, _, where_clause) = impl_generics.split_for_impl(); + + let self_visit = if let Some(VisitOptions { + visit: Some(visit), + kind: Some(kind), + .. + }) = &options + { + child_types.push(quote! { crate::visitor::VisitTypes::#kind.bits() }); + + quote! { + fn visit(&mut self, visitor: &mut #v) -> Result<(), #v::Error> { + if visitor.visit_types().contains(crate::visitor::VisitTypes::#kind) { + visitor.#visit(self) + } else { + self.visit_children(visitor) + } + } + } + } else { + quote! {} + }; + + let child_types = visit_types.unwrap_or_else(|| { + quote! { + { + #type_defs + crate::visitor::VisitTypes::from_bits_retain(#(#child_types)|*) + } + } + }); + + let output = quote! { + impl #impl_generics Visit<#lifetime, #t, #v> for #ident #ty_generics #where_clause { + const CHILD_TYPES: crate::visitor::VisitTypes = #child_types; + + #self_visit + + fn visit_children(&mut self, visitor: &mut #v) -> Result<(), #v::Error> { + if !<#lifetime, #t, #v>>::CHILD_TYPES.intersects(visitor.visit_types()) { + return Ok(()) + } + + #(#visit)* + + Ok(()) + } + } + }; + + output.into() +} + +fn skip_type(attrs: &Vec) -> bool { + attrs.iter().any(|attr| attr.path.is_ident("skip_type")) +} + +struct VisitOptions { + visit: Option, + kind: Option, + generic: Option, +} + +impl Parse for VisitOptions { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let (visit, kind, comma) = if input.peek(Ident) { + let visit: Ident = input.parse()?; + let _: Token![,] = input.parse()?; + let kind: Ident = input.parse()?; + let comma: Result = input.parse(); + (Some(visit), Some(kind), comma.is_ok()) + } else { + (None, None, true) + }; + let generic: Option = if comma { Some(input.parse()?) } else { None }; + Ok(Self { visit, kind, generic }) + } +} + +struct VisitTypes { + types: Vec, +} + +impl Parse for VisitTypes { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let first: Ident = input.parse()?; + let mut types = vec![first]; + while input.parse::().is_ok() { + let id: Ident = input.parse()?; + types.push(id); + } + Ok(Self { types }) + } +} diff --git a/src/macros.rs b/src/macros.rs index e102676f..7e573816 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -8,12 +8,12 @@ macro_rules! enum_property { )+ } ) => { - $(#[$outer])* - #[derive(Debug, Clone, Copy, PartialEq)] + #[derive(Debug, Clone, Copy, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(rename_all = "lowercase"))] #[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))] #[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))] + $(#[$outer])* $vis enum $name { $( $(#[$meta])* @@ -21,50 +21,17 @@ macro_rules! enum_property { )+ } - impl<'i> Parse<'i> for $name { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - let location = input.current_source_location(); - let ident = input.expect_ident()?; - match &ident[..] { - $( - s if s.eq_ignore_ascii_case(stringify!($x)) => Ok($name::$x), - )+ - _ => Err(location.new_unexpected_token_error( - cssparser::Token::Ident(ident.clone()) - )) - } - } - - fn parse_string(input: &'i str) -> Result<'i, ParserError<'i>>> { - match input { - $( - s if s.eq_ignore_ascii_case(stringify!($x)) => Ok($name::$x), - )+ - _ => return Err(ParseError { - kind: ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(cssparser::Token::Ident(input.into()))), - location: cssparser::SourceLocation { line: 0, column: 1 } - }) - } - } - } - impl $name { /// Returns a string representation of the value. pub fn as_str(&self) -> &str { use $name::*; match self { $( - $x => const_str::convert_ascii_case!(lower, stringify!($x)), + $x => const_str::convert_ascii_case!(kebab, stringify!($x)), )+ } } } - - impl ToCss for $name { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> where W: std::fmt::Write { - dest.write_str(self.as_str()) - } - } }; ( $(#[$outer:meta])* @@ -92,25 +59,13 @@ macro_rules! enum_property { fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { let location = input.current_source_location(); let ident = input.expect_ident()?; - match &ident[..] { + cssparser::match_ignore_ascii_case! { &*ident, $( - s if s.eq_ignore_ascii_case($str) => Ok($name::$id), + $str => Ok($name::$id), )+ _ => Err(location.new_unexpected_token_error( cssparser::Token::Ident(ident.clone()) - )) - } - } - - fn parse_string(input: &'i str) -> Result<'i, ParserError<'i>>> { - match input { - $( - s if s.eq_ignore_ascii_case($str) => Ok($name::$id), - )+ - _ => return Err(ParseError { - kind: ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(cssparser::Token::Ident(input.into()))), - location: cssparser::SourceLocation { line: 0, column: 1 } - }) + )), } } } diff --git a/src/properties/align.rs b/src/properties/align.rs index 12917c18..819e9ac4 100644 --- a/src/properties/align.rs +++ b/src/properties/align.rs @@ -73,13 +73,13 @@ enum_property! { /// A [``](https://www.w3.org/TR/css-align-3/#typedef-content-distribution) value. pub enum ContentDistribution { /// Items are spaced evenly, with the first and last items against the edge of the container. - "space-between": SpaceBetween, + SpaceBetween, /// Items are spaced evenly, with half-size spaces at the start and end. - "space-around": SpaceAround, + SpaceAround, /// Items are spaced evenly, with full-size spaces at the start and end. - "space-evenly": SpaceEvenly, + SpaceEvenly, /// Items are stretched evenly to fill free space. - "stretch": Stretch, + Stretch, } } @@ -99,20 +99,20 @@ enum_property! { /// A [``](https://www.w3.org/TR/css-align-3/#typedef-content-position) value. pub enum ContentPosition { /// Content is centered within the container. - "center": Center, + Center, /// Content is aligned to the start of the container. - "start": Start, + Start, /// Content is aligned to the end of the container. - "end": End, + End, /// Same as `start` when within a flexbox container. - "flex-start": FlexStart, + FlexStart, /// Same as `end` when within a flexbox container. - "flex-end": FlexEnd, + FlexEnd, } } /// A value for the [align-content](https://www.w3.org/TR/css-align-3/#propdef-align-content) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr( feature = "serde", @@ -132,54 +132,13 @@ pub enum AlignContent { ContentDistribution(ContentDistribution), /// A content position keyword. ContentPosition { - /// A content position keyword. - value: ContentPosition, /// An overflow alignment mode. overflow: Option, + /// A content position keyword. + value: ContentPosition, }, } -impl<'i> Parse<'i> for AlignContent { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if input.try_parse(|input| input.expect_ident_matching("normal")).is_ok() { - return Ok(AlignContent::Normal); - } - - if let Ok(val) = input.try_parse(BaselinePosition::parse) { - return Ok(AlignContent::BaselinePosition(val)); - } - - if let Ok(val) = input.try_parse(ContentDistribution::parse) { - return Ok(AlignContent::ContentDistribution(val)); - } - - let overflow = input.try_parse(OverflowPosition::parse).ok(); - let value = ContentPosition::parse(input)?; - Ok(AlignContent::ContentPosition { overflow, value }) - } -} - -impl ToCss for AlignContent { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - match self { - AlignContent::Normal => dest.write_str("normal"), - AlignContent::BaselinePosition(val) => val.to_css(dest), - AlignContent::ContentDistribution(val) => val.to_css(dest), - AlignContent::ContentPosition { overflow, value } => { - if let Some(overflow) = overflow { - overflow.to_css(dest)?; - dest.write_str(" ")?; - } - - value.to_css(dest) - } - } - } -} - /// A value for the [justify-content](https://www.w3.org/TR/css-align-3/#propdef-justify-content) property. #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "visitor", derive(Visit))] @@ -349,24 +308,24 @@ enum_property! { /// A [``](https://www.w3.org/TR/css-align-3/#typedef-self-position) value. pub enum SelfPosition { /// Item is centered within the container. - "center": Center, + Center, /// Item is aligned to the start of the container. - "start": Start, + Start, /// Item is aligned to the end of the container. - "end": End, + End, /// Item is aligned to the edge of the container corresponding to the start side of the item. - "self-start": SelfStart, + SelfStart, /// Item is aligned to the edge of the container corresponding to the end side of the item. - "self-end": SelfEnd, + SelfEnd, /// Item is aligned to the start of the container, within flexbox layouts. - "flex-start": FlexStart, + FlexStart, /// Item is aligned to the end of the container, within flexbox layouts. - "flex-end": FlexEnd, + FlexEnd, } } /// A value for the [align-self](https://www.w3.org/TR/css-align-3/#align-self-property) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr( feature = "serde", @@ -387,59 +346,13 @@ pub enum AlignSelf { BaselinePosition(BaselinePosition), /// A self position keyword. SelfPosition { - /// A self position keyword. - value: SelfPosition, /// An overflow alignment mode. overflow: Option, + /// A self position keyword. + value: SelfPosition, }, } -impl<'i> Parse<'i> for AlignSelf { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if input.try_parse(|input| input.expect_ident_matching("auto")).is_ok() { - return Ok(AlignSelf::Auto); - } - - if input.try_parse(|input| input.expect_ident_matching("normal")).is_ok() { - return Ok(AlignSelf::Normal); - } - - if input.try_parse(|input| input.expect_ident_matching("stretch")).is_ok() { - return Ok(AlignSelf::Stretch); - } - - if let Ok(val) = input.try_parse(BaselinePosition::parse) { - return Ok(AlignSelf::BaselinePosition(val)); - } - - let overflow = input.try_parse(OverflowPosition::parse).ok(); - let value = SelfPosition::parse(input)?; - Ok(AlignSelf::SelfPosition { overflow, value }) - } -} - -impl ToCss for AlignSelf { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - match self { - AlignSelf::Auto => dest.write_str("auto"), - AlignSelf::Normal => dest.write_str("normal"), - AlignSelf::Stretch => dest.write_str("stretch"), - AlignSelf::BaselinePosition(val) => val.to_css(dest), - AlignSelf::SelfPosition { overflow, value } => { - if let Some(overflow) = overflow { - overflow.to_css(dest)?; - dest.write_str(" ")?; - } - - value.to_css(dest) - } - } - } -} - /// A value for the [justify-self](https://www.w3.org/TR/css-align-3/#justify-self-property) property. #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "visitor", derive(Visit))] @@ -615,7 +528,7 @@ impl ToCss for PlaceSelf { } /// A value for the [align-items](https://www.w3.org/TR/css-align-3/#align-items-property) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr( feature = "serde", @@ -634,54 +547,13 @@ pub enum AlignItems { BaselinePosition(BaselinePosition), /// A self position keyword. SelfPosition { - /// A self position keyword. - value: SelfPosition, /// An overflow alignment mode. overflow: Option, + /// A self position keyword. + value: SelfPosition, }, } -impl<'i> Parse<'i> for AlignItems { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if input.try_parse(|input| input.expect_ident_matching("normal")).is_ok() { - return Ok(AlignItems::Normal); - } - - if input.try_parse(|input| input.expect_ident_matching("stretch")).is_ok() { - return Ok(AlignItems::Stretch); - } - - if let Ok(val) = input.try_parse(BaselinePosition::parse) { - return Ok(AlignItems::BaselinePosition(val)); - } - - let overflow = input.try_parse(OverflowPosition::parse).ok(); - let value = SelfPosition::parse(input)?; - Ok(AlignItems::SelfPosition { overflow, value }) - } -} - -impl ToCss for AlignItems { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - match self { - AlignItems::Normal => dest.write_str("normal"), - AlignItems::Stretch => dest.write_str("stretch"), - AlignItems::BaselinePosition(val) => val.to_css(dest), - AlignItems::SelfPosition { overflow, value } => { - if let Some(overflow) = overflow { - overflow.to_css(dest)?; - dest.write_str(" ")?; - } - - value.to_css(dest) - } - } - } -} - /// A legacy justification keyword, as used in the `justify-items` property. #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "visitor", derive(Visit))] @@ -925,7 +797,7 @@ impl ToCss for PlaceItems { /// A [gap](https://www.w3.org/TR/css-align-3/#column-row-gap) value, as used in the /// `column-gap` and `row-gap` properties. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr( feature = "serde", @@ -941,29 +813,6 @@ pub enum GapValue { LengthPercentage(LengthPercentage), } -impl<'i> Parse<'i> for GapValue { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if input.try_parse(|input| input.expect_ident_matching("normal")).is_ok() { - return Ok(GapValue::Normal); - } - - let val = LengthPercentage::parse(input)?; - Ok(GapValue::LengthPercentage(val)) - } -} - -impl ToCss for GapValue { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - match self { - GapValue::Normal => dest.write_str("normal"), - GapValue::LengthPercentage(lp) => lp.to_css(dest), - } - } -} - define_shorthand! { /// A value for the [gap](https://www.w3.org/TR/css-align-3/#gap-shorthand) shorthand property. pub struct Gap { diff --git a/src/properties/animation.rs b/src/properties/animation.rs index 03b76166..de83b9fa 100644 --- a/src/properties/animation.rs +++ b/src/properties/animation.rs @@ -13,7 +13,7 @@ use crate::traits::{Parse, PropertyHandler, Shorthand, ToCss, Zero}; use crate::values::ident::DashedIdent; use crate::values::number::CSSNumber; use crate::values::size::Size2D; -use crate::values::string::CowArcStr; +use crate::values::string::CSSString; use crate::values::{easing::EasingFunction, ident::CustomIdent, time::Time}; #[cfg(feature = "visitor")] use crate::visitor::Visit; @@ -24,7 +24,7 @@ use smallvec::SmallVec; use super::LengthPercentageOrAuto; /// A value for the [animation-name](https://drafts.csswg.org/css-animations/#animation-name) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))] #[cfg_attr( @@ -41,22 +41,7 @@ pub enum AnimationName<'i> { Ident(CustomIdent<'i>), /// A `` name of a `@keyframes` rule. #[cfg_attr(feature = "serde", serde(borrow))] - String(CowArcStr<'i>), -} - -impl<'i> Parse<'i> for AnimationName<'i> { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if input.try_parse(|input| input.expect_ident_matching("none")).is_ok() { - return Ok(AnimationName::None); - } - - if let Ok(s) = input.try_parse(|input| input.expect_string_cloned()) { - return Ok(AnimationName::String(s.into())); - } - - let ident = CustomIdent::parse(input)?; - Ok(AnimationName::Ident(ident)) - } + String(CSSString<'i>), } impl<'i> ToCss for AnimationName<'i> { @@ -103,7 +88,7 @@ impl<'i> ToCss for AnimationName<'i> { pub type AnimationNameList<'i> = SmallVec<[AnimationName<'i>; 1]>; /// A value for the [animation-iteration-count](https://drafts.csswg.org/css-animations/#animation-iteration-count) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr( feature = "serde", @@ -125,40 +110,17 @@ impl Default for AnimationIterationCount { } } -impl<'i> Parse<'i> for AnimationIterationCount { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if input.try_parse(|input| input.expect_ident_matching("infinite")).is_ok() { - return Ok(AnimationIterationCount::Infinite); - } - - let number = CSSNumber::parse(input)?; - return Ok(AnimationIterationCount::Number(number)); - } -} - -impl ToCss for AnimationIterationCount { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - match self { - AnimationIterationCount::Number(val) => val.to_css(dest), - AnimationIterationCount::Infinite => dest.write_str("infinite"), - } - } -} - enum_property! { /// A value for the [animation-direction](https://drafts.csswg.org/css-animations/#animation-direction) property. pub enum AnimationDirection { /// The animation is played as specified - "normal": Normal, + Normal, /// The animation is played in reverse. - "reverse": Reverse, + Reverse, /// The animation iterations alternate between forward and reverse. - "alternate": Alternate, + Alternate, /// The animation iterations alternate between forward and reverse, with reverse occurring first. - "alternate-reverse": AlternateReverse, + AlternateReverse, } } @@ -217,7 +179,7 @@ enum_property! { } /// A value for the [animation-timeline](https://drafts.csswg.org/css-animations-2/#animation-timeline) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr( feature = "serde", @@ -261,25 +223,28 @@ pub struct ScrollTimeline { impl<'i> Parse<'i> for ScrollTimeline { fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - let mut scroller = None; - let mut axis = None; - loop { - if scroller.is_none() { - scroller = input.try_parse(Scroller::parse).ok(); - } + input.expect_function_matching("scroll")?; + input.parse_nested_block(|input| { + let mut scroller = None; + let mut axis = None; + loop { + if scroller.is_none() { + scroller = input.try_parse(Scroller::parse).ok(); + } - if axis.is_none() { - axis = input.try_parse(ScrollAxis::parse).ok(); - if axis.is_some() { - continue; + if axis.is_none() { + axis = input.try_parse(ScrollAxis::parse).ok(); + if axis.is_some() { + continue; + } } + break; } - break; - } - Ok(ScrollTimeline { - scroller: scroller.unwrap_or_default(), - axis: axis.unwrap_or_default(), + Ok(ScrollTimeline { + scroller: scroller.unwrap_or_default(), + axis: axis.unwrap_or_default(), + }) }) } } @@ -289,6 +254,8 @@ impl ToCss for ScrollTimeline { where W: std::fmt::Write, { + dest.write_str("scroll(")?; + let mut needs_space = false; if self.scroller != Scroller::default() { self.scroller.to_css(dest)?; @@ -302,7 +269,7 @@ impl ToCss for ScrollTimeline { self.axis.to_css(dest)?; } - Ok(()) + dest.write_char(')') } } @@ -359,25 +326,28 @@ pub struct ViewTimeline { impl<'i> Parse<'i> for ViewTimeline { fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - let mut axis = None; - let mut inset = None; - loop { - if axis.is_none() { - axis = input.try_parse(ScrollAxis::parse).ok(); - } + input.expect_function_matching("view")?; + input.parse_nested_block(|input| { + let mut axis = None; + let mut inset = None; + loop { + if axis.is_none() { + axis = input.try_parse(ScrollAxis::parse).ok(); + } - if inset.is_none() { - inset = input.try_parse(Size2D::parse).ok(); - if inset.is_some() { - continue; + if inset.is_none() { + inset = input.try_parse(Size2D::parse).ok(); + if inset.is_some() { + continue; + } } + break; } - break; - } - Ok(ViewTimeline { - axis: axis.unwrap_or_default(), - inset: inset.unwrap_or(Size2D(LengthPercentageOrAuto::Auto, LengthPercentageOrAuto::Auto)), + Ok(ViewTimeline { + axis: axis.unwrap_or_default(), + inset: inset.unwrap_or(Size2D(LengthPercentageOrAuto::Auto, LengthPercentageOrAuto::Auto)), + }) }) } } @@ -387,6 +357,7 @@ impl ToCss for ViewTimeline { where W: std::fmt::Write, { + dest.write_str("view(")?; let mut needs_space = false; if self.axis != ScrollAxis::default() { self.axis.to_css(dest)?; @@ -400,56 +371,7 @@ impl ToCss for ViewTimeline { self.inset.to_css(dest)?; } - Ok(()) - } -} - -impl<'i> Parse<'i> for AnimationTimeline<'i> { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if input.try_parse(|i| i.expect_ident_matching("auto")).is_ok() { - return Ok(AnimationTimeline::Auto); - } - - if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() { - return Ok(AnimationTimeline::None); - } - - if let Ok(name) = input.try_parse(DashedIdent::parse) { - return Ok(AnimationTimeline::DashedIdent(name)); - } - - let location = input.current_source_location(); - let f = input.expect_function()?.clone(); - input.parse_nested_block(move |input| { - match_ignore_ascii_case! { &f, - "scroll" => ScrollTimeline::parse(input).map(AnimationTimeline::Scroll), - "view" => ViewTimeline::parse(input).map(AnimationTimeline::View), - _ => Err(location.new_custom_error(ParserError::UnexpectedToken(crate::properties::custom::Token::Function(f.into())))) - } - }) - } -} - -impl<'i> ToCss for AnimationTimeline<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - match self { - AnimationTimeline::Auto => dest.write_str("auto"), - AnimationTimeline::None => dest.write_str("none"), - AnimationTimeline::DashedIdent(name) => name.to_css(dest), - AnimationTimeline::Scroll(scroll) => { - dest.write_str("scroll(")?; - scroll.to_css(dest)?; - dest.write_char(')') - } - AnimationTimeline::View(view) => { - dest.write_str("view(")?; - view.to_css(dest)?; - dest.write_char(')') - } - } + dest.write_char(')') } } @@ -535,7 +457,7 @@ impl<'i> ToCss for Animation<'i> { { match &self.name { AnimationName::None => {} - AnimationName::Ident(CustomIdent(name)) | AnimationName::String(name) => { + AnimationName::Ident(CustomIdent(name)) | AnimationName::String(CSSString(name)) => { if !self.duration.is_zero() || !self.delay.is_zero() { self.duration.to_css(dest)?; dest.write_char(' ')?; @@ -709,7 +631,7 @@ impl<'i> PropertyHandler<'i> for AnimationHandler<'i> { } } TokenOrValue::Token(Token::String(s)) => { - *token = TokenOrValue::AnimationName(AnimationName::String(s.clone())); + *token = TokenOrValue::AnimationName(AnimationName::String(CSSString(s.clone()))); } _ => {} } diff --git a/src/properties/background.rs b/src/properties/background.rs index 1dc1aeb3..f47821db 100644 --- a/src/properties/background.rs +++ b/src/properties/background.rs @@ -113,13 +113,13 @@ enum_property! { /// See [BackgroundRepeat](BackgroundRepeat). pub enum BackgroundRepeatKeyword { /// The image is repeated in this direction. - "repeat": Repeat, + Repeat, /// The image is repeated so that it fits, and then spaced apart evenly. - "space": Space, + Space, /// The image is scaled so that it repeats an even number of times. - "round": Round, + Round, /// The image is placed once and not repeated in this direction. - "no-repeat": NoRepeat, + NoRepeat, } } @@ -214,11 +214,11 @@ enum_property! { /// A value for the [background-origin](https://www.w3.org/TR/css-backgrounds-3/#background-origin) property. pub enum BackgroundOrigin { /// The position is relative to the border box. - "border-box": BorderBox, + BorderBox, /// The position is relative to the padding box. - "padding-box": PaddingBox, + PaddingBox, /// The position is relative to the content box. - "content-box": ContentBox, + ContentBox, } } @@ -226,15 +226,15 @@ enum_property! { /// A value for the [background-clip](https://drafts.csswg.org/css-backgrounds-4/#background-clip) property. pub enum BackgroundClip { /// The background is clipped to the border box. - "border-box": BorderBox, + BorderBox, /// The background is clipped to the padding box. - "padding-box": PaddingBox, + PaddingBox, /// The background is clipped to the content box. - "content-box": ContentBox, + ContentBox, /// The background is clipped to the area painted by the border. - "border": Border, + Border, /// The background is clipped to the text content of the element. - "text": Text, + Text, } } diff --git a/src/properties/border.rs b/src/properties/border.rs index ee2c4102..9308eebb 100644 --- a/src/properties/border.rs +++ b/src/properties/border.rs @@ -23,7 +23,7 @@ use crate::visitor::Visit; use cssparser::*; /// A value for the [border-width](https://www.w3.org/TR/css-backgrounds-3/#border-width) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr( feature = "serde", @@ -58,37 +58,6 @@ impl IsCompatible for BorderSideWidth { } } -impl<'i> Parse<'i> for BorderSideWidth { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if let Ok(length) = input.try_parse(|i| Length::parse(i)) { - return Ok(BorderSideWidth::Length(length)); - } - let location = input.current_source_location(); - let ident = input.expect_ident()?; - match_ignore_ascii_case! { &ident, - "thin" => Ok(BorderSideWidth::Thin), - "medium" => Ok(BorderSideWidth::Medium), - "thick" => Ok(BorderSideWidth::Thick), - _ => return Err(location.new_unexpected_token_error(Token::Ident(ident.clone()))) - } - } -} - -impl ToCss for BorderSideWidth { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - use BorderSideWidth::*; - match self { - Thin => dest.write_str("thin"), - Medium => dest.write_str("medium"), - Thick => dest.write_str("thick"), - Length(length) => length.to_css(dest), - } - } -} - enum_property! { /// A [``](https://drafts.csswg.org/css-backgrounds/#typedef-line-style) value, used in the `border-style` property. pub enum LineStyle { diff --git a/src/properties/border_image.rs b/src/properties/border_image.rs index 8f444efe..300d458e 100644 --- a/src/properties/border_image.rs +++ b/src/properties/border_image.rs @@ -102,7 +102,7 @@ impl IsCompatible for BorderImageRepeat { } /// A value for the [border-image-width](https://www.w3.org/TR/css-backgrounds-3/#border-image-width) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr( feature = "serde", @@ -126,38 +126,6 @@ impl Default for BorderImageSideWidth { } } -impl<'i> Parse<'i> for BorderImageSideWidth { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if input.try_parse(|i| i.expect_ident_matching("auto")).is_ok() { - return Ok(BorderImageSideWidth::Auto); - } - - if let Ok(number) = input.try_parse(CSSNumber::parse) { - return Ok(BorderImageSideWidth::Number(number)); - } - - if let Ok(percent) = input.try_parse(|input| LengthPercentage::parse(input)) { - return Ok(BorderImageSideWidth::LengthPercentage(percent)); - } - - Err(input.new_error_for_next_token()) - } -} - -impl ToCss for BorderImageSideWidth { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - use BorderImageSideWidth::*; - match self { - Auto => dest.write_str("auto"), - LengthPercentage(l) => l.to_css(dest), - Number(n) => n.to_css(dest), - } - } -} - impl IsCompatible for BorderImageSideWidth { fn is_compatible(&self, browsers: Browsers) -> bool { match self { diff --git a/src/properties/contain.rs b/src/properties/contain.rs index 761e2d92..1c57bab4 100644 --- a/src/properties/contain.rs +++ b/src/properties/contain.rs @@ -25,11 +25,11 @@ enum_property! { pub enum ContainerType { /// The element is not a query container for any container size queries, /// but remains a query container for container style queries. - "normal": Normal, + Normal, /// Establishes a query container for container size queries on the container’s own inline axis. - "inline-size": InlineSize, + InlineSize, /// Establishes a query container for container size queries on both the inline and block axis. - "size": Size, + Size, } } diff --git a/src/properties/custom.rs b/src/properties/custom.rs index 3198422b..40b522e8 100644 --- a/src/properties/custom.rs +++ b/src/properties/custom.rs @@ -1329,25 +1329,25 @@ enum_property! { /// A UA-defined environment variable name. pub enum UAEnvironmentVariable { /// The safe area inset from the top of the viewport. - "safe-area-inset-top": SafeAreaInsetTop, + SafeAreaInsetTop, /// The safe area inset from the right of the viewport. - "safe-area-inset-right": SafeAreaInsetRight, + SafeAreaInsetRight, /// The safe area inset from the bottom of the viewport. - "safe-area-inset-bottom": SafeAreaInsetBottom, + SafeAreaInsetBottom, /// The safe area inset from the left of the viewport. - "safe-area-inset-left": SafeAreaInsetLeft, + SafeAreaInsetLeft, /// The viewport segment width. - "viewport-segment-width": ViewportSegmentWidth, + ViewportSegmentWidth, /// The viewport segment height. - "viewport-segment-height": ViewportSegmentHeight, + ViewportSegmentHeight, /// The viewport segment top position. - "viewport-segment-top": ViewportSegmentTop, + ViewportSegmentTop, /// The viewport segment left position. - "viewport-segment-left": ViewportSegmentLeft, + ViewportSegmentLeft, /// The viewport segment bottom position. - "viewport-segment-bottom": ViewportSegmentBottom, + ViewportSegmentBottom, /// The viewport segment right position. - "viewport-segment-right": ViewportSegmentRight, + ViewportSegmentRight, } } diff --git a/src/properties/display.rs b/src/properties/display.rs index d5a53d13..3df7c819 100644 --- a/src/properties/display.rs +++ b/src/properties/display.rs @@ -18,9 +18,9 @@ enum_property! { /// A [``](https://drafts.csswg.org/css-display-3/#typedef-display-outside) value. #[allow(missing_docs)] pub enum DisplayOutside { - "block": Block, - "inline": Inline, - "run-in": RunIn, + Block, + Inline, + RunIn, } } @@ -309,25 +309,25 @@ enum_property! { /// See [Display](Display). #[allow(missing_docs)] pub enum DisplayKeyword { - "none": None, - "contents": Contents, - "table-row-group": TableRowGroup, - "table-header-group": TableHeaderGroup, - "table-footer-group": TableFooterGroup, - "table-row": TableRow, - "table-cell": TableCell, - "table-column-group": TableColumnGroup, - "table-column": TableColumn, - "table-caption": TableCaption, - "ruby-base": RubyBase, - "ruby-text": RubyText, - "ruby-base-container": RubyBaseContainer, - "ruby-text-container": RubyTextContainer, + None, + Contents, + TableRowGroup, + TableHeaderGroup, + TableFooterGroup, + TableRow, + TableCell, + TableColumnGroup, + TableColumn, + TableCaption, + RubyBase, + RubyText, + RubyBaseContainer, + RubyTextContainer, } } /// A value for the [display](https://drafts.csswg.org/css-display-3/#the-display-properties) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr( feature = "serde", @@ -347,29 +347,6 @@ pub enum Display { Pair(DisplayPair), } -impl<'i> Parse<'i> for Display { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if let Ok(pair) = input.try_parse(DisplayPair::parse) { - return Ok(Display::Pair(pair)); - } - - let keyword = DisplayKeyword::parse(input)?; - Ok(Display::Keyword(keyword)) - } -} - -impl ToCss for Display { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - match self { - Display::Keyword(keyword) => keyword.to_css(dest), - Display::Pair(pair) => pair.to_css(dest), - } - } -} - enum_property! { /// A value for the [visibility](https://drafts.csswg.org/css-display-3/#visibility) property. pub enum Visibility { diff --git a/src/properties/flex.rs b/src/properties/flex.rs index cf631993..e885c597 100644 --- a/src/properties/flex.rs +++ b/src/properties/flex.rs @@ -25,13 +25,13 @@ enum_property! { /// A value for the [flex-direction](https://www.w3.org/TR/2018/CR-css-flexbox-1-20181119/#propdef-flex-direction) property. pub enum FlexDirection { /// Flex items are laid out in a row. - "row": Row, + Row, /// Flex items are laid out in a row, and reversed. - "row-reverse": RowReverse, + RowReverse, /// Flex items are laid out in a column. - "column": Column, + Column, /// Flex items are laid out in a column, and reversed. - "column-reverse": ColumnReverse, + ColumnReverse, } } @@ -233,13 +233,13 @@ enum_property! { /// Partially equivalent to `flex-direction` in the standard syntax. pub enum BoxOrient { /// Items are laid out horizontally. - "horizontal": Horizontal, + Horizontal, /// Items are laid out vertically. - "vertical": Vertical, + Vertical, /// Items are laid out along the inline axis, according to the writing direction. - "inline-axis": InlineAxis, + InlineAxis, /// Items are laid out along the block axis, according to the writing direction. - "block-axis": BlockAxis, + BlockAxis, } } diff --git a/src/properties/font.rs b/src/properties/font.rs index 84396cdb..377ea77c 100644 --- a/src/properties/font.rs +++ b/src/properties/font.rs @@ -20,7 +20,7 @@ use crate::visitor::Visit; use cssparser::*; /// A value for the [font-weight](https://www.w3.org/TR/css-fonts-4/#font-weight-prop) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr( feature = "serde", @@ -44,38 +44,6 @@ impl Default for FontWeight { } } -impl<'i> Parse<'i> for FontWeight { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if let Ok(val) = input.try_parse(AbsoluteFontWeight::parse) { - return Ok(FontWeight::Absolute(val)); - } - - let location = input.current_source_location(); - let ident = input.expect_ident()?; - match_ignore_ascii_case! { &*ident, - "bolder" => Ok(FontWeight::Bolder), - "lighter" => Ok(FontWeight::Lighter), - _ => Err(location.new_unexpected_token_error( - cssparser::Token::Ident(ident.clone()) - )) - } - } -} - -impl ToCss for FontWeight { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - use FontWeight::*; - match self { - Absolute(val) => val.to_css(dest), - Bolder => dest.write_str("bolder"), - Lighter => dest.write_str("lighter"), - } - } -} - impl IsCompatible for FontWeight { fn is_compatible(&self, browsers: crate::targets::Browsers) -> bool { match self { @@ -89,7 +57,7 @@ impl IsCompatible for FontWeight { /// as used in the `font-weight` property. /// /// See [FontWeight](FontWeight). -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr( feature = "serde", @@ -112,24 +80,6 @@ impl Default for AbsoluteFontWeight { } } -impl<'i> Parse<'i> for AbsoluteFontWeight { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if let Ok(val) = input.try_parse(CSSNumber::parse) { - return Ok(AbsoluteFontWeight::Weight(val)); - } - - let location = input.current_source_location(); - let ident = input.expect_ident()?; - match_ignore_ascii_case! { &*ident, - "normal" => Ok(AbsoluteFontWeight::Normal), - "bold" => Ok(AbsoluteFontWeight::Bold), - _ => Err(location.new_unexpected_token_error( - cssparser::Token::Ident(ident.clone()) - )) - } - } -} - impl ToCss for AbsoluteFontWeight { fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> where @@ -197,7 +147,7 @@ enum_property! { } /// A value for the [font-size](https://www.w3.org/TR/css-fonts-4/#font-size-prop) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr( feature = "serde", @@ -215,35 +165,6 @@ pub enum FontSize { Relative(RelativeFontSize), } -impl<'i> Parse<'i> for FontSize { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if let Ok(val) = input.try_parse(LengthPercentage::parse) { - return Ok(FontSize::Length(val)); - } - - if let Ok(val) = input.try_parse(AbsoluteFontSize::parse) { - return Ok(FontSize::Absolute(val)); - } - - let val = RelativeFontSize::parse(input)?; - Ok(FontSize::Relative(val)) - } -} - -impl ToCss for FontSize { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - use FontSize::*; - match self { - Absolute(val) => val.to_css(dest), - Length(val) => val.to_css(dest), - Relative(val) => val.to_css(dest), - } - } -} - impl IsCompatible for FontSize { fn is_compatible(&self, browsers: crate::targets::Browsers) -> bool { match self { @@ -309,7 +230,7 @@ impl Into for &FontStretchKeyword { } /// A value for the [font-stretch](https://www.w3.org/TR/css-fonts-4/#font-stretch-prop) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr( feature = "serde", @@ -331,17 +252,6 @@ impl Default for FontStretch { } } -impl<'i> Parse<'i> for FontStretch { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if let Ok(val) = input.try_parse(Percentage::parse) { - return Ok(FontStretch::Percentage(val)); - } - - let keyword = FontStretchKeyword::parse(input)?; - Ok(FontStretch::Keyword(keyword)) - } -} - impl Into for &FontStretch { fn into(self) -> Percentage { match self { @@ -601,19 +511,19 @@ enum_property! { /// A value for the [font-variant-caps](https://www.w3.org/TR/css-fonts-4/#font-variant-caps-prop) property. pub enum FontVariantCaps { /// No special capitalization features are applied. - "normal": Normal, + Normal, /// The small capitals feature is used for lower case letters. - "small-caps": SmallCaps, + SmallCaps, /// Small capitals are used for both upper and lower case letters. - "all-small-caps": AllSmallCaps, + AllSmallCaps, /// Petite capitals are used. - "petite-caps": PetiteCaps, + PetiteCaps, /// Petite capitals are used for both upper and lower case letters. - "all-petite-caps": AllPetiteCaps, + AllPetiteCaps, /// Enables display of mixture of small capitals for uppercase letters with normal lowercase letters. - "unicase": Unicase, + Unicase, /// Uses titling capitals. - "titling-caps": TitlingCaps, + TitlingCaps, } } @@ -644,7 +554,7 @@ impl IsCompatible for FontVariantCaps { } /// A value for the [line-height](https://www.w3.org/TR/2020/WD-css-inline-3-20200827/#propdef-line-height) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr( feature = "serde", @@ -668,33 +578,6 @@ impl Default for LineHeight { } } -impl<'i> Parse<'i> for LineHeight { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if input.try_parse(|input| input.expect_ident_matching("normal")).is_ok() { - return Ok(LineHeight::Normal); - } - - if let Ok(val) = input.try_parse(CSSNumber::parse) { - return Ok(LineHeight::Number(val)); - } - - Ok(LineHeight::Length(LengthPercentage::parse(input)?)) - } -} - -impl ToCss for LineHeight { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - match self { - LineHeight::Normal => dest.write_str("normal"), - LineHeight::Number(val) => val.to_css(dest), - LineHeight::Length(val) => val.to_css(dest), - } - } -} - impl IsCompatible for LineHeight { fn is_compatible(&self, browsers: crate::targets::Browsers) -> bool { match self { @@ -708,27 +591,27 @@ enum_property! { /// A keyword for the [vertical align](https://drafts.csswg.org/css2/#propdef-vertical-align) property. pub enum VerticalAlignKeyword { /// Align the baseline of the box with the baseline of the parent box. - "baseline": Baseline, + Baseline, /// Lower the baseline of the box to the proper position for subscripts of the parent’s box. - "sub": Sub, + Sub, /// Raise the baseline of the box to the proper position for superscripts of the parent’s box. - "super": Super, + Super, /// Align the top of the aligned subtree with the top of the line box. - "top": Top, + Top, /// Align the top of the box with the top of the parent’s content area. - "text-top": TextTop, + TextTop, /// Align the vertical midpoint of the box with the baseline of the parent box plus half the x-height of the parent. - "middle": Middle, + Middle, /// Align the bottom of the aligned subtree with the bottom of the line box. - "bottom": Bottom, + Bottom, /// Align the bottom of the box with the bottom of the parent’s content area. - "text-bottom": TextBottom, + TextBottom, } } /// A value for the [vertical align](https://drafts.csswg.org/css2/#propdef-vertical-align) property. // TODO: there is a more extensive spec in CSS3 but it doesn't seem any browser implements it? https://www.w3.org/TR/css-inline-3/#transverse-alignment -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr( feature = "serde", @@ -744,29 +627,6 @@ pub enum VerticalAlign { Length(LengthPercentage), } -impl<'i> Parse<'i> for VerticalAlign { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if let Ok(len) = input.try_parse(LengthPercentage::parse) { - return Ok(VerticalAlign::Length(len)); - } - - let kw = VerticalAlignKeyword::parse(input)?; - Ok(VerticalAlign::Keyword(kw)) - } -} - -impl ToCss for VerticalAlign { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - match self { - VerticalAlign::Keyword(kw) => kw.to_css(dest), - VerticalAlign::Length(len) => len.to_css(dest), - } - } -} - define_shorthand! { /// A value for the [font](https://www.w3.org/TR/css-fonts-4/#font-prop) shorthand property. pub struct Font<'i> { diff --git a/src/properties/grid.rs b/src/properties/grid.rs index 912f563d..6e706319 100644 --- a/src/properties/grid.rs +++ b/src/properties/grid.rs @@ -24,7 +24,7 @@ use crate::serialization::ValueWrapper; /// A [track sizing](https://drafts.csswg.org/css-grid-2/#track-sizing) value /// for the `grid-template-rows` and `grid-template-columns` properties. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))] #[cfg_attr( @@ -178,7 +178,7 @@ pub struct TrackRepeat<'i> { /// used in the `repeat()` function. /// /// See [TrackRepeat](TrackRepeat). -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr( feature = "serde", @@ -365,37 +365,6 @@ impl<'i> ToCss for TrackRepeat<'i> { } } -impl<'i> Parse<'i> for RepeatCount { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if let Ok(num) = input.try_parse(CSSInteger::parse) { - return Ok(RepeatCount::Number(num)); - } - - let location = input.current_source_location(); - let ident = input.expect_ident()?; - match_ignore_ascii_case! { &*ident, - "auto-fill" => Ok(RepeatCount::AutoFill), - "auto-fit" => Ok(RepeatCount::AutoFit), - _ => Err(location.new_unexpected_token_error( - cssparser::Token::Ident(ident.clone()) - )) - } - } -} - -impl ToCss for RepeatCount { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - match self { - RepeatCount::AutoFill => dest.write_str("auto-fill"), - RepeatCount::AutoFit => dest.write_str("auto-fit"), - RepeatCount::Number(num) => num.to_css(dest), - } - } -} - fn parse_line_names<'i, 't>( input: &mut Parser<'i, 't>, ) -> Result<'i>, ParseError<'i, ParserError<'i>>> { @@ -519,29 +488,6 @@ impl<'i> TrackList<'i> { } } -impl<'i> Parse<'i> for TrackSizing<'i> { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if input.try_parse(|input| input.expect_ident_matching("none")).is_ok() { - return Ok(TrackSizing::None); - } - - let track_list = TrackList::parse(input)?; - Ok(TrackSizing::TrackList(track_list)) - } -} - -impl<'i> ToCss for TrackSizing<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - match self { - TrackSizing::None => dest.write_str("none"), - TrackSizing::TrackList(list) => list.to_css(dest), - } - } -} - impl<'i> TrackSizing<'i> { fn is_explicit(&self) -> bool { match self { diff --git a/src/properties/list.rs b/src/properties/list.rs index 4112ddfe..54a0c052 100644 --- a/src/properties/list.rs +++ b/src/properties/list.rs @@ -15,7 +15,7 @@ use crate::visitor::Visit; use cssparser::*; /// A value for the [list-style-type](https://www.w3.org/TR/2020/WD-css-lists-3-20201117/#text-markers) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))] #[cfg_attr( @@ -40,34 +40,6 @@ impl Default for ListStyleType<'_> { } } -impl<'i> Parse<'i> for ListStyleType<'i> { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if input.try_parse(|input| input.expect_ident_matching("none")).is_ok() { - return Ok(ListStyleType::None); - } - - if let Ok(val) = input.try_parse(CounterStyle::parse) { - return Ok(ListStyleType::CounterStyle(val)); - } - - let s = CSSString::parse(input)?; - Ok(ListStyleType::String(s)) - } -} - -impl ToCss for ListStyleType<'_> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - match self { - ListStyleType::None => dest.write_str("none"), - ListStyleType::CounterStyle(style) => style.to_css(dest), - ListStyleType::String(s) => s.to_css(dest), - } - } -} - impl IsCompatible for ListStyleType<'_> { fn is_compatible(&self, browsers: Browsers) -> bool { match self { @@ -117,7 +89,7 @@ macro_rules! counter_styles { $vis:vis enum $name:ident { $( $(#[$meta: meta])* - $str: literal: $id: ident, + $id: ident, )+ } ) => { @@ -127,7 +99,7 @@ macro_rules! counter_styles { pub enum PredefinedCounterStyle { $( $(#[$meta])* - $str: $id, + $id, )+ } } @@ -151,68 +123,68 @@ counter_styles! { #[allow(missing_docs)] pub enum PredefinedCounterStyle { // https://www.w3.org/TR/css-counter-styles-3/#simple-numeric - "decimal": Decimal, - "decimal-leading-zero": DecimalLeadingZero, - "arabic-indic": ArabicIndic, - "armenian": Armenian, - "upper-armenian": UpperArmenian, - "lower-armenian": LowerArmenian, - "bengali": Bengali, - "cambodian": Cambodian, - "khmer": Khmer, - "cjk-decimal": CjkDecimal, - "devanagari": Devanagari, - "georgian": Georgian, - "gujarati": Gujarati, - "gurmukhi": Gurmukhi, - "hebrew": Hebrew, - "kannada": Kannada, - "lao": Lao, - "malayalam": Malayalam, - "mongolian": Mongolian, - "myanmar": Myanmar, - "oriya": Oriya, - "persian": Persian, - "lower-roman": LowerRoman, - "upper-roman": UpperRoman, - "tamil": Tamil, - "telugu": Telugu, - "thai": Thai, - "tibetan": Tibetan, + Decimal, + DecimalLeadingZero, + ArabicIndic, + Armenian, + UpperArmenian, + LowerArmenian, + Bengali, + Cambodian, + Khmer, + CjkDecimal, + Devanagari, + Georgian, + Gujarati, + Gurmukhi, + Hebrew, + Kannada, + Lao, + Malayalam, + Mongolian, + Myanmar, + Oriya, + Persian, + LowerRoman, + UpperRoman, + Tamil, + Telugu, + Thai, + Tibetan, // https://www.w3.org/TR/css-counter-styles-3/#simple-alphabetic - "lower-alpha": LowerAlpha, - "lower-latin": LowerLatin, - "upper-alpha": UpperAlpha, - "upper-latin": UpperLatin, - "lower-greek": LowerGreek, - "hiragana": Hiragana, - "hiragana-iroha": HiraganaIroha, - "katakana": Katakana, - "katakana-iroha": KatakanaIroha, + LowerAlpha, + LowerLatin, + UpperAlpha, + UpperLatin, + LowerGreek, + Hiragana, + HiraganaIroha, + Katakana, + KatakanaIroha, // https://www.w3.org/TR/css-counter-styles-3/#simple-symbolic - "disc": Disc, - "circle": Circle, - "square": Square, - "disclosure-open": DisclosureOpen, - "disclosure-closed": DisclosureClosed, + Disc, + Circle, + Square, + DisclosureOpen, + DisclosureClosed, // https://www.w3.org/TR/css-counter-styles-3/#simple-fixed - "cjk-earthly-branch": CjkEarthlyBranch, - "cjk-heavenly-stem": CjkHeavenlyStem, + CjkEarthlyBranch, + CjkHeavenlyStem, // https://www.w3.org/TR/css-counter-styles-3/#complex-cjk - "japanese-informal": JapaneseInformal, - "japanese-formal": JapaneseFormal, - "korean-hangul-formal": KoreanHangulFormal, - "korean-hanja-informal": KoreanHanjaInformal, - "korean-hanja-formal": KoreanHanjaFormal, - "simp-chinese-informal": SimpChineseInformal, - "simp-chinese-formal": SimpChineseFormal, - "trad-chinese-informal": TradChineseInformal, - "trad-chinese-formal": TradChineseFormal, - "ethiopic-numeric": EthiopicNumeric, + JapaneseInformal, + JapaneseFormal, + KoreanHangulFormal, + KoreanHanjaInformal, + KoreanHanjaFormal, + SimpChineseInformal, + SimpChineseFormal, + TradChineseInformal, + TradChineseFormal, + EthiopicNumeric, } } @@ -309,7 +281,7 @@ impl Default for SymbolsType { /// `symbols()` function. /// /// See [CounterStyle](CounterStyle). -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))] #[cfg_attr( @@ -326,29 +298,6 @@ pub enum Symbol<'i> { Image(Image<'i>), } -impl<'i> Parse<'i> for Symbol<'i> { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if let Ok(img) = input.try_parse(Image::parse) { - return Ok(Symbol::Image(img)); - } - - let s = CSSString::parse(input)?; - Ok(Symbol::String(s.into())) - } -} - -impl<'i> ToCss for Symbol<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - match self { - Symbol::String(s) => s.to_css(dest), - Symbol::Image(img) => img.to_css(dest), - } - } -} - enum_property! { /// A value for the [list-style-position](https://www.w3.org/TR/2020/WD-css-lists-3-20201117/#list-style-position-property) property. pub enum ListStylePosition { @@ -375,8 +324,8 @@ enum_property! { /// A value for the [marker-side](https://www.w3.org/TR/2020/WD-css-lists-3-20201117/#marker-side) property. #[allow(missing_docs)] pub enum MarkerSide { - "match-self": MatchSelf, - "match-parent": MatchParent, + MatchSelf, + MatchParent, } } diff --git a/src/properties/masking.rs b/src/properties/masking.rs index 35560c6e..ff05e1f6 100644 --- a/src/properties/masking.rs +++ b/src/properties/masking.rs @@ -37,11 +37,11 @@ enum_property! { /// A value for the [mask-mode](https://www.w3.org/TR/css-masking-1/#the-mask-mode) property. pub enum MaskMode { /// The luminance values of the mask image is used. - "luminance": Luminance, + Luminance, /// The alpha values of the mask image is used. - "alpha": Alpha, + Alpha, /// If an SVG source is used, the value matches the `mask-type` property. Otherwise, the alpha values are used. - "match-source": MatchSource, + MatchSource, } } @@ -58,11 +58,11 @@ enum_property! { /// See also [MaskMode](MaskMode). pub enum WebKitMaskSourceType { /// Equivalent to `match-source` in the standard `mask-mode` syntax. - "auto": Auto, + Auto, /// The luminance values of the mask image is used. - "luminance": Luminance, + Luminance, /// The alpha values of the mask image is used. - "alpha": Alpha, + Alpha, } } @@ -81,19 +81,19 @@ enum_property! { /// as used in the `mask-clip` and `clip-path` properties. pub enum GeometryBox { /// The painted content is clipped to the content box. - "border-box": BorderBox, + BorderBox, /// The painted content is clipped to the padding box. - "padding-box": PaddingBox, + PaddingBox, /// The painted content is clipped to the border box. - "content-box": ContentBox, + ContentBox, /// The painted content is clipped to the margin box. - "margin-box": MarginBox, + MarginBox, /// The painted content is clipped to the object bounding box. - "fill-box": FillBox, + FillBox, /// The painted content is clipped to the stroke bounding box. - "stroke-box": StrokeBox, + StrokeBox, /// Uses the nearest SVG viewport as reference box. - "view-box": ViewBox, + ViewBox, } } @@ -104,7 +104,7 @@ impl Default for GeometryBox { } /// A value for the [mask-clip](https://www.w3.org/TR/css-masking-1/#the-mask-clip) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr( feature = "serde", @@ -120,29 +120,6 @@ pub enum MaskClip { NoClip, } -impl<'i> Parse<'i> for MaskClip { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if let Ok(b) = input.try_parse(GeometryBox::parse) { - return Ok(MaskClip::GeometryBox(b)); - } - - input.expect_ident_matching("no-clip")?; - Ok(MaskClip::NoClip) - } -} - -impl ToCss for MaskClip { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - match self { - MaskClip::GeometryBox(b) => b.to_css(dest), - MaskClip::NoClip => dest.write_str("no-clip"), - } - } -} - impl IsCompatible for MaskClip { fn is_compatible(&self, browsers: Browsers) -> bool { match self { @@ -191,21 +168,21 @@ enum_property! { /// See also [MaskComposite](MaskComposite). #[allow(missing_docs)] pub enum WebKitMaskComposite { - "clear": Clear, - "copy": Copy, + Clear, + Copy, /// Equivalent to `add` in the standard `mask-composite` syntax. - "source-over": SourceOver, + SourceOver, /// Equivalent to `intersect` in the standard `mask-composite` syntax. - "source-in": SourceIn, + SourceIn, /// Equivalent to `subtract` in the standard `mask-composite` syntax. - "source-out": SourceOut, - "source-atop": SourceAtop, - "destination-over": DestinationOver, - "destination-in": DestinationIn, - "destination-out": DestinationOut, - "destination-atop": DestinationAtop, + SourceOut, + SourceAtop, + DestinationOver, + DestinationIn, + DestinationOut, + DestinationAtop, /// Equivalent to `exclude` in the standard `mask-composite` syntax. - "xor": Xor, + Xor, } } @@ -477,9 +454,9 @@ enum_property! { /// A value for the [mask-border-mode](https://www.w3.org/TR/css-masking-1/#the-mask-border-mode) property. pub enum MaskBorderMode { /// The luminance values of the mask image is used. - "luminance": Luminance, + Luminance, /// The alpha values of the mask image is used. - "alpha": Alpha, + Alpha, } } diff --git a/src/properties/outline.rs b/src/properties/outline.rs index e0a710e2..2ecdb202 100644 --- a/src/properties/outline.rs +++ b/src/properties/outline.rs @@ -15,7 +15,7 @@ use crate::visitor::Visit; use cssparser::*; /// A value for the [outline-style](https://drafts.csswg.org/css-ui/#outline-style) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr( feature = "serde", @@ -31,29 +31,6 @@ pub enum OutlineStyle { LineStyle(LineStyle), } -impl<'i> Parse<'i> for OutlineStyle { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if let Ok(border_style) = input.try_parse(LineStyle::parse) { - return Ok(OutlineStyle::LineStyle(border_style)); - } - - input.expect_ident_matching("auto")?; - Ok(OutlineStyle::Auto) - } -} - -impl ToCss for OutlineStyle { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - match self { - OutlineStyle::Auto => dest.write_str("auto"), - OutlineStyle::LineStyle(border_style) => border_style.to_css(dest), - } - } -} - impl Default for OutlineStyle { fn default() -> OutlineStyle { OutlineStyle::LineStyle(LineStyle::None) diff --git a/src/properties/position.rs b/src/properties/position.rs index ace9a40d..34a0cf3b 100644 --- a/src/properties/position.rs +++ b/src/properties/position.rs @@ -73,7 +73,7 @@ impl ToCss for Position { } /// A value for the [z-index](https://drafts.csswg.org/css2/#z-index) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr( feature = "serde", @@ -89,29 +89,6 @@ pub enum ZIndex { Integer(CSSInteger), } -impl<'i> Parse<'i> for ZIndex { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if let Ok(value) = input.expect_integer() { - return Ok(ZIndex::Integer(value)); - } - - input.expect_ident_matching("auto")?; - Ok(ZIndex::Auto) - } -} - -impl ToCss for ZIndex { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - match self { - ZIndex::Auto => dest.write_str("auto"), - ZIndex::Integer(value) => value.to_css(dest), - } - } -} - #[derive(Default)] pub(crate) struct PositionHandler { position: Option, diff --git a/src/properties/size.rs b/src/properties/size.rs index 54cc134d..2475773b 100644 --- a/src/properties/size.rs +++ b/src/properties/size.rs @@ -300,9 +300,9 @@ enum_property! { /// A value for the [box-sizing](https://drafts.csswg.org/css-sizing-3/#box-sizing) property. pub enum BoxSizing { /// Exclude the margin/border/padding from the width and height. - "content-box": ContentBox, + ContentBox, /// Include the padding and border (but not the margin) in the width and height. - "border-box": BorderBox, + BorderBox, } } diff --git a/src/properties/svg.rs b/src/properties/svg.rs index 26109941..150e5a25 100644 --- a/src/properties/svg.rs +++ b/src/properties/svg.rs @@ -13,7 +13,7 @@ use cssparser::*; /// An SVG [``](https://www.w3.org/TR/SVG2/painting.html#SpecifyingPaint) value /// used in the `fill` and `stroke` properties. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))] #[cfg_attr( @@ -23,8 +23,6 @@ use cssparser::*; )] #[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))] pub enum SVGPaint<'i> { - /// No paint. - None, /// A URL reference to a paint server element, e.g. `linearGradient`, `radialGradient`, and `pattern`. Url { #[cfg_attr(feature = "serde", serde(borrow))] @@ -40,12 +38,14 @@ pub enum SVGPaint<'i> { ContextFill, /// Use the paint value of stroke from a context element. ContextStroke, + /// No paint. + None, } /// A fallback for an SVG paint in case a paint server `url()` cannot be resolved. /// /// See [SVGPaint](SVGPaint). -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr( feature = "serde", @@ -61,74 +61,6 @@ pub enum SVGPaintFallback { Color(CssColor), } -impl<'i> Parse<'i> for SVGPaint<'i> { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if let Ok(url) = input.try_parse(Url::parse) { - let fallback = input.try_parse(SVGPaintFallback::parse).ok(); - return Ok(SVGPaint::Url { url, fallback }); - } - - if let Ok(color) = input.try_parse(CssColor::parse) { - return Ok(SVGPaint::Color(color)); - } - - let location = input.current_source_location(); - let keyword = input.expect_ident()?; - match_ignore_ascii_case! { &keyword, - "none" => Ok(SVGPaint::None), - "context-fill" => Ok(SVGPaint::ContextFill), - "context-stroke" => Ok(SVGPaint::ContextStroke), - _ => Err(location.new_unexpected_token_error( - cssparser::Token::Ident(keyword.clone()) - )) - } - } -} - -impl<'i> ToCss for SVGPaint<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - match self { - SVGPaint::None => dest.write_str("none"), - SVGPaint::Url { url, fallback } => { - url.to_css(dest)?; - if let Some(fallback) = fallback { - dest.write_char(' ')?; - fallback.to_css(dest)?; - } - Ok(()) - } - SVGPaint::Color(color) => color.to_css(dest), - SVGPaint::ContextFill => dest.write_str("context-fill"), - SVGPaint::ContextStroke => dest.write_str("context-stroke"), - } - } -} - -impl<'i> Parse<'i> for SVGPaintFallback { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if input.try_parse(|input| input.expect_ident_matching("none")).is_ok() { - return Ok(SVGPaintFallback::None); - } - - Ok(SVGPaintFallback::Color(CssColor::parse(input)?)) - } -} - -impl ToCss for SVGPaintFallback { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - match self { - SVGPaintFallback::None => dest.write_str("none"), - SVGPaintFallback::Color(color) => color.to_css(dest), - } - } -} - impl<'i> FallbackValues for SVGPaint<'i> { fn get_fallbacks(&mut self, targets: Targets) -> Vec { match self { @@ -182,15 +114,15 @@ enum_property! { /// A value for the [stroke-linejoin](https://www.w3.org/TR/SVG2/painting.html#LineJoin) property. pub enum StrokeLinejoin { /// A sharp corner is to be used to join path segments. - "miter": Miter, + Miter, /// Same as `miter` but clipped beyond `stroke-miterlimit`. - "miter-clip": MiterClip, + MiterClip, /// A round corner is to be used to join path segments. - "round": Round, + Round, /// A bevelled corner is to be used to join path segments. - "bevel": Bevel, + Bevel, /// An arcs corner is to be used to join path segments. - "arcs": Arcs, + Arcs, } } @@ -260,7 +192,7 @@ impl ToCss for StrokeDasharray { } /// A value for the [marker](https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties) properties. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))] #[cfg_attr( @@ -277,29 +209,6 @@ pub enum Marker<'i> { Url(Url<'i>), } -impl<'i> Parse<'i> for Marker<'i> { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if let Ok(url) = input.try_parse(Url::parse) { - return Ok(Marker::Url(url)); - } - - input.expect_ident_matching("none")?; - Ok(Marker::None) - } -} - -impl<'i> ToCss for Marker<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - match self { - Marker::None => dest.write_str("none"), - Marker::Url(url) => url.to_css(dest), - } - } -} - enum_property! { /// A value for the [color-interpolation](https://www.w3.org/TR/SVG2/painting.html#ColorInterpolation) property. pub enum ColorInterpolation { diff --git a/src/properties/text.rs b/src/properties/text.rs index e22f6fab..622b870f 100644 --- a/src/properties/text.rs +++ b/src/properties/text.rs @@ -238,13 +238,13 @@ enum_property! { /// A value for the [word-break](https://www.w3.org/TR/2021/CRD-css-text-3-20210422/#word-break-property) property. pub enum WordBreak { /// Words break according to their customary rules. - "normal": Normal, + Normal, /// Breaking is forbidden within “words”. - "keep-all": KeepAll, + KeepAll, /// Breaking is allowed within “words”. - "break-all": BreakAll, + BreakAll, /// Breaking is allowed if there is no otherwise acceptable break points in a line. - "break-word": BreakWord, + BreakWord, } } @@ -279,12 +279,12 @@ enum_property! { /// A value for the [overflow-wrap](https://www.w3.org/TR/2021/CRD-css-text-3-20210422/#overflow-wrap-property) property. pub enum OverflowWrap { /// Lines may break only at allowed break points. - "normal": Normal, + Normal, /// Breaking is allowed if there is no otherwise acceptable break points in a line. - "anywhere": Anywhere, + Anywhere, /// As for anywhere except that soft wrap opportunities introduced by break-word are /// not considered when calculating min-content intrinsic sizes. - "break-word": BreakWord, + BreakWord, } } @@ -292,21 +292,21 @@ enum_property! { /// A value for the [text-align](https://www.w3.org/TR/2021/CRD-css-text-3-20210422/#text-align-property) property. pub enum TextAlign { /// Inline-level content is aligned to the start edge of the line box. - "start": Start, + Start, /// Inline-level content is aligned to the end edge of the line box. - "end": End, + End, /// Inline-level content is aligned to the line-left edge of the line box. - "left": Left, + Left, /// Inline-level content is aligned to the line-right edge of the line box. - "right": Right, + Right, /// Inline-level content is centered within the line box. - "center": Center, + Center, /// Text is justified according to the method specified by the text-justify property. - "justify": Justify, + Justify, /// Matches the parent element. - "match-parent": MatchParent, + MatchParent, /// Same as justify, but also justifies the last line. - "justify-all": JustifyAll, + JustifyAll, } } @@ -314,21 +314,21 @@ enum_property! { /// A value for the [text-align-last](https://www.w3.org/TR/2021/CRD-css-text-3-20210422/#text-align-last-property) property. pub enum TextAlignLast { /// Content on the affected line is aligned per `text-align-all` unless set to `justify`, in which case it is start-aligned. - "auto": Auto, + Auto, /// Inline-level content is aligned to the start edge of the line box. - "start": Start, + Start, /// Inline-level content is aligned to the end edge of the line box. - "end": End, + End, /// Inline-level content is aligned to the line-left edge of the line box. - "left": Left, + Left, /// Inline-level content is aligned to the line-right edge of the line box. - "right": Right, + Right, /// Inline-level content is centered within the line box. - "center": Center, + Center, /// Text is justified according to the method specified by the text-justify property. - "justify": Justify, + Justify, /// Matches the parent element. - "match-parent": MatchParent, + MatchParent, } } @@ -336,19 +336,19 @@ enum_property! { /// A value for the [text-justify](https://www.w3.org/TR/2021/CRD-css-text-3-20210422/#text-justify-property) property. pub enum TextJustify { /// The UA determines the justification algorithm to follow. - "auto": Auto, + Auto, /// Justification is disabled. - "none": None, + None, /// Justification adjusts spacing at word separators only. - "inter-word": InterWord, + InterWord, /// Justification adjusts spacing between each character. - "inter-character": InterCharacter, + InterCharacter, } } /// A value for the [word-spacing](https://www.w3.org/TR/2021/CRD-css-text-3-20210422/#word-spacing-property) /// and [letter-spacing](https://www.w3.org/TR/2021/CRD-css-text-3-20210422/#letter-spacing-property) properties. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr( feature = "serde", @@ -364,29 +364,6 @@ pub enum Spacing { Length(Length), } -impl<'i> Parse<'i> for Spacing { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if input.try_parse(|input| input.expect_ident_matching("normal")).is_ok() { - return Ok(Spacing::Normal); - } - - let length = Length::parse(input)?; - Ok(Spacing::Length(length)) - } -} - -impl ToCss for Spacing { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - match self { - Spacing::Normal => dest.write_str("normal"), - Spacing::Length(len) => len.to_css(dest), - } - } -} - /// A value for the [text-indent](https://www.w3.org/TR/2021/CRD-css-text-3-20210422/#text-indent-property) property. #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "visitor", derive(Visit))] @@ -466,7 +443,7 @@ impl ToCss for TextIndent { } /// A value for the [text-size-adjust](https://w3c.github.io/csswg-drafts/css-size-adjust/#adjustment-control) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr( feature = "serde", @@ -484,34 +461,6 @@ pub enum TextSizeAdjust { Percentage(Percentage), } -impl<'i> Parse<'i> for TextSizeAdjust { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if let Ok(p) = input.try_parse(Percentage::parse) { - return Ok(TextSizeAdjust::Percentage(p)); - } - - let ident = input.expect_ident_cloned()?; - match_ignore_ascii_case! {&*ident, - "auto" => Ok(TextSizeAdjust::Auto), - "none" => Ok(TextSizeAdjust::None), - _ => Err(input.new_unexpected_token_error(Token::Ident(ident.clone()))) - } - } -} - -impl ToCss for TextSizeAdjust { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - match self { - TextSizeAdjust::Auto => dest.write_str("auto"), - TextSizeAdjust::None => dest.write_str("none"), - TextSizeAdjust::Percentage(p) => p.to_css(dest), - } - } -} - bitflags! { /// A value for the [text-decoration-line](https://www.w3.org/TR/2020/WD-css-text-decor-4-20200506/#text-decoration-line-property) property. /// @@ -749,7 +698,7 @@ impl Default for TextDecorationStyle { } /// A value for the [text-decoration-thickness](https://www.w3.org/TR/2020/WD-css-text-decor-4-20200506/#text-decoration-width-property) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr( feature = "serde", @@ -773,34 +722,6 @@ impl Default for TextDecorationThickness { } } -impl<'i> Parse<'i> for TextDecorationThickness { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if input.try_parse(|input| input.expect_ident_matching("auto")).is_ok() { - return Ok(TextDecorationThickness::Auto); - } - - if input.try_parse(|input| input.expect_ident_matching("from-font")).is_ok() { - return Ok(TextDecorationThickness::FromFont); - } - - let lp = LengthPercentage::parse(input)?; - Ok(TextDecorationThickness::LengthPercentage(lp)) - } -} - -impl ToCss for TextDecorationThickness { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - match self { - TextDecorationThickness::Auto => dest.write_str("auto"), - TextDecorationThickness::FromFont => dest.write_str("from-font"), - TextDecorationThickness::LengthPercentage(lp) => lp.to_css(dest), - } - } -} - define_shorthand! { /// A value for the [text-decoration](https://www.w3.org/TR/2020/WD-css-text-decor-4-20200506/#text-decoration-property) shorthand property. pub struct TextDecoration(VendorPrefix) { @@ -927,15 +848,15 @@ enum_property! { /// See [TextEmphasisStyle](TextEmphasisStyle). pub enum TextEmphasisShape { /// Display small circles as marks. - "dot": Dot, + Dot, /// Display large circles as marks. - "circle": Circle, + Circle, /// Display double circles as marks. - "double-circle": DoubleCircle, + DoubleCircle, /// Display triangles as marks. - "triangle": Triangle, + Triangle, /// Display sesames as marks. - "sesame": Sesame, + Sesame, } } @@ -1614,16 +1535,16 @@ enum_property! { /// A value for the [unicode-bidi](https://drafts.csswg.org/css-writing-modes-3/#unicode-bidi) property. pub enum UnicodeBidi { /// The box does not open an additional level of embedding. - "normal": Normal, + Normal, /// If the box is inline, this value creates a directional embedding by opening an additional level of embedding. - "embed": Embed, + Embed, /// On an inline box, this bidi-isolates its contents. - "isolate": Isolate, + Isolate, /// This value puts the box’s immediate inline content in a directional override. - "bidi-override": BidiOverride, + BidiOverride, /// This combines the isolation behavior of isolate with the directional override behavior of bidi-override. - "isolate-override": IsolateOverride, + IsolateOverride, /// This value behaves as isolate except that the base directionality is determined using a heuristic rather than the direction property. - "plaintext": Plaintext, + Plaintext, } } diff --git a/src/properties/transform.rs b/src/properties/transform.rs index d58a08fe..c8f5a7f7 100644 --- a/src/properties/transform.rs +++ b/src/properties/transform.rs @@ -1382,8 +1382,8 @@ enum_property! { /// A value for the [transform-style](https://drafts.csswg.org/css-transforms-2/#transform-style-property) property. #[allow(missing_docs)] pub enum TransformStyle { - "flat": Flat, - "preserve-3d": Preserve3d, + Flat, + Preserve3d, } } @@ -1391,15 +1391,15 @@ enum_property! { /// A value for the [transform-box](https://drafts.csswg.org/css-transforms-1/#transform-box) property. pub enum TransformBox { /// Uses the content box as reference box. - "content-box": ContentBox, + ContentBox, /// Uses the border box as reference box. - "border-box": BorderBox, + BorderBox, /// Uses the object bounding box as reference box. - "fill-box": FillBox, + FillBox, /// Uses the stroke bounding box as reference box. - "stroke-box": StrokeBox, + StrokeBox, /// Uses the nearest SVG viewport as reference box. - "view-box": ViewBox, + ViewBox, } } @@ -1413,7 +1413,7 @@ enum_property! { } /// A value for the [perspective](https://drafts.csswg.org/css-transforms-2/#perspective-property) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr( feature = "serde", @@ -1429,28 +1429,6 @@ pub enum Perspective { Length(Length), } -impl<'i> Parse<'i> for Perspective { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if input.try_parse(|input| input.expect_ident_matching("none")).is_ok() { - return Ok(Perspective::None); - } - - Ok(Perspective::Length(Length::parse(input)?)) - } -} - -impl ToCss for Perspective { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - match self { - Perspective::None => dest.write_str("none"), - Perspective::Length(len) => len.to_css(dest), - } - } -} - /// A value for the [translate](https://drafts.csswg.org/css-transforms-2/#propdef-translate) property. #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "visitor", derive(Visit))] diff --git a/src/properties/ui.rs b/src/properties/ui.rs index 4952082e..11e1eade 100644 --- a/src/properties/ui.rs +++ b/src/properties/ui.rs @@ -94,42 +94,42 @@ enum_property! { /// See [Cursor](Cursor). #[allow(missing_docs)] pub enum CursorKeyword { - "auto": Auto, - "default": Default, - "none": None, - "context-menu": ContextMenu, - "help": Help, - "pointer": Pointer, - "progress": Progress, - "wait": Wait, - "cell": Cell, - "crosshair": Crosshair, - "text": Text, - "vertical-text": VerticalText, - "alias": Alias, - "copy": Copy, - "move": Move, - "no-drop": NoDrop, - "not-allowed": NotAllowed, - "grab": Grab, - "grabbing": Grabbing, - "e-resize": EResize, - "n-resize": NResize, - "ne-resize": NeResize, - "nw-resize": NwResize, - "s-resize": SResize, - "se-resize": SeResize, - "sw-resize": SwResize, - "w-resize": WResize, - "ew-resize": EwResize, - "ns-resize": NsResize, - "nesw-resize": NeswResize, - "nwse-resize": NwseResize, - "col-resize": ColResize, - "row-resize": RowResize, - "all-scroll": AllScroll, - "zoom-in": ZoomIn, - "zoom-out": ZoomOut, + Auto, + Default, + None, + ContextMenu, + Help, + Pointer, + Progress, + Wait, + Cell, + Crosshair, + Text, + VerticalText, + Alias, + Copy, + Move, + NoDrop, + NotAllowed, + Grab, + Grabbing, + EResize, + NResize, + NeResize, + NwResize, + SResize, + SeResize, + SwResize, + WResize, + EwResize, + NsResize, + NeswResize, + NwseResize, + ColResize, + RowResize, + AllScroll, + ZoomIn, + ZoomOut, } } @@ -179,7 +179,7 @@ impl<'i> ToCss for Cursor<'i> { } /// A value for the [caret-color](https://www.w3.org/TR/2021/WD-css-ui-4-20210316/#caret-color) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr( feature = "serde", @@ -201,29 +201,6 @@ impl Default for ColorOrAuto { } } -impl<'i> Parse<'i> for ColorOrAuto { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if input.try_parse(|input| input.expect_ident_matching("auto")).is_ok() { - return Ok(ColorOrAuto::Auto); - } - - let color = CssColor::parse(input)?; - Ok(ColorOrAuto::Color(color)) - } -} - -impl ToCss for ColorOrAuto { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - match self { - ColorOrAuto::Auto => dest.write_str("auto"), - ColorOrAuto::Color(color) => color.to_css(dest), - } - } -} - impl FallbackValues for ColorOrAuto { fn get_fallbacks(&mut self, targets: Targets) -> Vec { match self { diff --git a/src/rules/keyframes.rs b/src/rules/keyframes.rs index 511a3172..adf2fb5b 100644 --- a/src/rules/keyframes.rs +++ b/src/rules/keyframes.rs @@ -267,7 +267,7 @@ impl<'i> ToCss for KeyframesRule<'i> { /// A [keyframe selector](https://drafts.csswg.org/css-animations/#typedef-keyframe-selector) /// within an `@keyframes` rule. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Parse)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr( feature = "serde", @@ -285,24 +285,6 @@ pub enum KeyframeSelector { To, } -impl<'i> Parse<'i> for KeyframeSelector { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if let Ok(val) = input.try_parse(Percentage::parse) { - return Ok(KeyframeSelector::Percentage(val)); - } - - let location = input.current_source_location(); - let ident = input.expect_ident()?; - match_ignore_ascii_case! { &*ident, - "from" => Ok(KeyframeSelector::From), - "to" => Ok(KeyframeSelector::To), - _ => Err(location.new_unexpected_token_error( - cssparser::Token::Ident(ident.clone()) - )) - } - } -} - impl ToCss for KeyframeSelector { fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> where diff --git a/src/rules/page.rs b/src/rules/page.rs index e4897cd3..d95c94c2 100644 --- a/src/rules/page.rs +++ b/src/rules/page.rs @@ -81,41 +81,41 @@ enum_property! { /// A [page margin box](https://www.w3.org/TR/css-page-3/#margin-boxes). pub enum PageMarginBox { /// A fixed-size box defined by the intersection of the top and left margins of the page box. - "top-left-corner": TopLeftCorner, + TopLeftCorner, /// A variable-width box filling the top page margin between the top-left-corner and top-center page-margin boxes. - "top-left": TopLeft, + TopLeft, /// A variable-width box centered horizontally between the page’s left and right border edges and filling the /// page top margin between the top-left and top-right page-margin boxes. - "top-center": TopCenter, + TopCenter, /// A variable-width box filling the top page margin between the top-center and top-right-corner page-margin boxes. - "top-right": TopRight, + TopRight, /// A fixed-size box defined by the intersection of the top and right margins of the page box. - "top-right-corner": TopRightCorner, + TopRightCorner, /// A variable-height box filling the left page margin between the top-left-corner and left-middle page-margin boxes. - "left-top": LeftTop, + LeftTop, /// A variable-height box centered vertically between the page’s top and bottom border edges and filling the /// left page margin between the left-top and left-bottom page-margin boxes. - "left-middle": LeftMiddle, + LeftMiddle, /// A variable-height box filling the left page margin between the left-middle and bottom-left-corner page-margin boxes. - "left-bottom": LeftBottom, + LeftBottom, /// A variable-height box filling the right page margin between the top-right-corner and right-middle page-margin boxes. - "right-top": RightTop, + RightTop, /// A variable-height box centered vertically between the page’s top and bottom border edges and filling the right /// page margin between the right-top and right-bottom page-margin boxes. - "right-middle": RightMiddle, + RightMiddle, /// A variable-height box filling the right page margin between the right-middle and bottom-right-corner page-margin boxes. - "right-bottom": RightBottom, + RightBottom, /// A fixed-size box defined by the intersection of the bottom and left margins of the page box. - "bottom-left-corner": BottomLeftCorner, + BottomLeftCorner, /// A variable-width box filling the bottom page margin between the bottom-left-corner and bottom-center page-margin boxes. - "bottom-left": BottomLeft, + BottomLeft, /// A variable-width box centered horizontally between the page’s left and right border edges and filling the bottom /// page margin between the bottom-left and bottom-right page-margin boxes. - "bottom-center": BottomCenter, + BottomCenter, /// A variable-width box filling the bottom page margin between the bottom-center and bottom-right-corner page-margin boxes. - "bottom-right": BottomRight, + BottomRight, /// A fixed-size box defined by the intersection of the bottom and right margins of the page box. - "bottom-right-corner": BottomRightCorner, + BottomRightCorner, } } diff --git a/src/traits.rs b/src/traits.rs index 1d520421..4aecaf7c 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -30,6 +30,20 @@ pub trait Parse<'i>: Sized { } } +pub(crate) use lightningcss_derive::Parse; + +impl<'i, T: Parse<'i>> Parse<'i> for Option { + fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { + Ok(input.try_parse(T::parse).ok()) + } +} + +impl<'i, T: Parse<'i>> Parse<'i> for Box { + fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { + Ok(Box::new(T::parse(input)?)) + } +} + /// Trait for things that can be parsed from CSS syntax and require ParserOptions. pub trait ParseWithOptions<'i>: Sized { /// Parse a value of this type with the given options. @@ -78,6 +92,8 @@ pub trait ToCss { } } +pub(crate) use lightningcss_derive::ToCss; + impl<'a, T> ToCss for &'a T where T: ToCss + ?Sized, @@ -90,6 +106,27 @@ where } } +impl ToCss for Box { + fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> + where + W: std::fmt::Write, + { + (**self).to_css(dest) + } +} + +impl ToCss for Option { + fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> + where + W: std::fmt::Write, + { + if let Some(v) = self { + v.to_css(dest)?; + } + Ok(()) + } +} + pub(crate) trait PropertyHandler<'i>: Sized { fn handle_property( &mut self, diff --git a/src/values/calc.rs b/src/values/calc.rs index 95a0bb59..742c5184 100644 --- a/src/values/calc.rs +++ b/src/values/calc.rs @@ -92,13 +92,13 @@ enum_property! { /// as used in the `round()` function. pub enum RoundingStrategy { /// Round to the nearest integer. - "nearest": Nearest, + Nearest, /// Round up (ceil). - "up": Up, + Up, /// Round down (floor). - "down": Down, + Down, /// Round toward zero (truncate). - "to-zero": ToZero, + ToZero, } } diff --git a/src/values/color.rs b/src/values/color.rs index efa2bd26..b91c1426 100644 --- a/src/values/color.rs +++ b/src/values/color.rs @@ -3581,6 +3581,7 @@ impl<'i, V: ?Sized + Visitor<'i, T>, T: Visit<'i, T, V>> Visit<'i, T, V> for RGB } enum_property! { + #[css(case = lower)] /// A CSS [system color](https://drafts.csswg.org/css-color/#css-system-colors) keyword. pub enum SystemColor { /// Background of accented user interface controls. diff --git a/src/values/easing.rs b/src/values/easing.rs index c1e834a0..4db5c063 100644 --- a/src/values/easing.rs +++ b/src/values/easing.rs @@ -192,7 +192,7 @@ impl EasingFunction { } /// A [step position](https://www.w3.org/TR/css-easing-1/#step-position), used within the `steps()` function. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr( feature = "serde", @@ -233,17 +233,3 @@ impl<'i> Parse<'i> for StepPosition { Ok(keyword) } } - -impl ToCss for StepPosition { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - match self { - StepPosition::Start => dest.write_str("start"), - StepPosition::End => dest.write_str("end"), - StepPosition::JumpNone => dest.write_str("jump-none"), - StepPosition::JumpBoth => dest.write_str("jump-both"), - } - } -} diff --git a/src/values/gradient.rs b/src/values/gradient.rs index 01054b44..fec51938 100644 --- a/src/values/gradient.rs +++ b/src/values/gradient.rs @@ -533,7 +533,7 @@ impl LineDirection { /// A `radial-gradient()` [ending shape](https://www.w3.org/TR/css-images-3/#valdef-radial-gradient-ending-shape). /// /// See [RadialGradient](RadialGradient). -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr( feature = "serde", @@ -542,10 +542,11 @@ impl LineDirection { )] #[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))] pub enum EndingShape { - /// A circle. - Circle(Circle), + // Note: Ellipse::parse MUST run before Circle::parse for this to be correct. /// An ellipse. Ellipse(Ellipse), + /// A circle. + Circle(Circle), } impl Default for EndingShape { @@ -554,33 +555,6 @@ impl Default for EndingShape { } } -impl<'i> Parse<'i> for EndingShape { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - // Note: Ellipse::parse MUST run before Circle::parse for this to be correct. - if let Ok(ellipse) = input.try_parse(Ellipse::parse) { - return Ok(EndingShape::Ellipse(ellipse)); - } - - if let Ok(circle) = input.try_parse(Circle::parse) { - return Ok(EndingShape::Circle(circle)); - } - - return Err(input.new_error_for_next_token()); - } -} - -impl ToCss for EndingShape { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - match self { - EndingShape::Circle(circle) => circle.to_css(dest), - EndingShape::Ellipse(ellipse) => ellipse.to_css(dest), - } - } -} - /// A circle ending shape for a `radial-gradient()`. /// /// See [RadialGradient](RadialGradient). @@ -734,13 +708,13 @@ enum_property! { /// See [RadialGradient](RadialGradient). pub enum ShapeExtent { /// The closest side of the box to the gradient's center. - "closest-side": ClosestSide, + ClosestSide, /// The farthest side of the box from the gradient's center. - "farthest-side": FarthestSide, + FarthestSide, /// The closest cornder of the box to the gradient's center. - "closest-corner": ClosestCorner, + ClosestCorner, /// The farthest corner of the box from the gradient's center. - "farthest-corner": FarthestCorner, + FarthestCorner, } } diff --git a/src/values/image.rs b/src/values/image.rs index fa778cc2..e6ca26c5 100644 --- a/src/values/image.rs +++ b/src/values/image.rs @@ -19,7 +19,7 @@ use cssparser::*; use smallvec::SmallVec; /// A CSS [``](https://www.w3.org/TR/css-images-3/#image-values) value. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))] #[cfg_attr(feature = "visitor", visit(visit_image, IMAGES))] @@ -309,42 +309,6 @@ impl<'i, T: ImageFallback<'i>> FallbackValues for SmallVec<[T; 1]> { } } -impl<'i> Parse<'i> for Image<'i> { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() { - return Ok(Image::None); - } - - if let Ok(url) = input.try_parse(Url::parse) { - return Ok(Image::Url(url)); - } - - if let Ok(grad) = input.try_parse(Gradient::parse) { - return Ok(Image::Gradient(Box::new(grad))); - } - - if let Ok(image_set) = input.try_parse(ImageSet::parse) { - return Ok(Image::ImageSet(image_set)); - } - - Err(input.new_error_for_next_token()) - } -} - -impl<'i> ToCss for Image<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - match self { - Image::None => dest.write_str("none"), - Image::Url(url) => url.to_css(dest), - Image::Gradient(grad) => grad.to_css(dest), - Image::ImageSet(image_set) => image_set.to_css(dest), - } - } -} - /// A CSS [`image-set()`](https://drafts.csswg.org/css-images-4/#image-set-notation) value. /// /// `image-set()` allows the user agent to choose between multiple versions of an image to diff --git a/src/values/length.rs b/src/values/length.rs index 58967f08..149f1dd1 100644 --- a/src/values/length.rs +++ b/src/values/length.rs @@ -49,7 +49,7 @@ impl IsCompatible for LengthPercentage { } /// Either a [``](https://www.w3.org/TR/css-values-4/#typedef-length-percentage), or the `auto` keyword. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr( feature = "serde", @@ -65,33 +65,6 @@ pub enum LengthPercentageOrAuto { LengthPercentage(LengthPercentage), } -impl<'i> Parse<'i> for LengthPercentageOrAuto { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if input.try_parse(|i| i.expect_ident_matching("auto")).is_ok() { - return Ok(LengthPercentageOrAuto::Auto); - } - - if let Ok(percent) = input.try_parse(|input| LengthPercentage::parse(input)) { - return Ok(LengthPercentageOrAuto::LengthPercentage(percent)); - } - - Err(input.new_error_for_next_token()) - } -} - -impl ToCss for LengthPercentageOrAuto { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - use LengthPercentageOrAuto::*; - match self { - Auto => dest.write_str("auto"), - LengthPercentage(l) => l.to_css(dest), - } - } -} - impl IsCompatible for LengthPercentageOrAuto { fn is_compatible(&self, browsers: Browsers) -> bool { match self { @@ -830,7 +803,7 @@ impl TrySign for Length { impl_try_from_angle!(Length); /// Either a [``](https://www.w3.org/TR/css-values-4/#lengths) or a [``](https://www.w3.org/TR/css-values-4/#numbers). -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr( feature = "serde", @@ -840,10 +813,10 @@ impl_try_from_angle!(Length); #[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))] #[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))] pub enum LengthOrNumber { - /// A length. - Length(Length), /// A number. Number(CSSNumber), + /// A length. + Length(Length), } impl Default for LengthOrNumber { @@ -865,33 +838,6 @@ impl Zero for LengthOrNumber { } } -impl<'i> Parse<'i> for LengthOrNumber { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - // Parse number first so unitless numbers are not parsed as lengths. - if let Ok(number) = input.try_parse(CSSNumber::parse) { - return Ok(LengthOrNumber::Number(number)); - } - - if let Ok(length) = Length::parse(input) { - return Ok(LengthOrNumber::Length(length)); - } - - Err(input.new_error_for_next_token()) - } -} - -impl ToCss for LengthOrNumber { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - match self { - LengthOrNumber::Length(length) => length.to_css(dest), - LengthOrNumber::Number(number) => number.to_css(dest), - } - } -} - impl IsCompatible for LengthOrNumber { fn is_compatible(&self, browsers: Browsers) -> bool { match self { diff --git a/src/values/percentage.rs b/src/values/percentage.rs index c51be75e..54352750 100644 --- a/src/values/percentage.rs +++ b/src/values/percentage.rs @@ -144,7 +144,7 @@ impl_op!(Percentage, std::ops::Add, add); impl_try_from_angle!(Percentage); /// Either a `` or ``. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr( feature = "serde", @@ -154,36 +154,10 @@ impl_try_from_angle!(Percentage); #[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))] #[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))] pub enum NumberOrPercentage { - /// A percentage. - Percentage(Percentage), /// A number. Number(CSSNumber), -} - -impl<'i> Parse<'i> for NumberOrPercentage { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if let Ok(number) = input.try_parse(CSSNumber::parse) { - return Ok(NumberOrPercentage::Number(number)); - } - - if let Ok(percent) = input.try_parse(|input| Percentage::parse(input)) { - return Ok(NumberOrPercentage::Percentage(percent)); - } - - Err(input.new_error_for_next_token()) - } -} - -impl ToCss for NumberOrPercentage { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - match self { - NumberOrPercentage::Percentage(percent) => percent.to_css(dest), - NumberOrPercentage::Number(number) => number.to_css(dest), - } - } + /// A percentage. + Percentage(Percentage), } impl std::convert::Into for &NumberOrPercentage { diff --git a/src/values/shape.rs b/src/values/shape.rs index c5f4cc31..9cb5f585 100644 --- a/src/values/shape.rs +++ b/src/values/shape.rs @@ -59,7 +59,7 @@ pub struct Circle { /// A [``](https://www.w3.org/TR/css-shapes-1/#typedef-shape-radius) value /// that defines the radius of a `circle()` or `ellipse()` shape. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] #[cfg_attr(feature = "visitor", derive(Visit))] #[cfg_attr( feature = "serde", @@ -182,21 +182,6 @@ impl<'i> Parse<'i> for Circle { } } -impl<'i> Parse<'i> for ShapeRadius { - fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { - if let Ok(len) = input.try_parse(LengthPercentage::parse) { - return Ok(ShapeRadius::LengthPercentage(len)); - } - - if input.try_parse(|input| input.expect_ident_matching("closest-side")).is_ok() { - return Ok(ShapeRadius::ClosestSide); - } - - input.expect_ident_matching("farthest-side")?; - Ok(ShapeRadius::FarthestSide) - } -} - impl Default for ShapeRadius { fn default() -> ShapeRadius { ShapeRadius::ClosestSide @@ -315,19 +300,6 @@ impl ToCss for Circle { } } -impl ToCss for ShapeRadius { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - match self { - ShapeRadius::LengthPercentage(len) => len.to_css(dest), - ShapeRadius::ClosestSide => dest.write_str("closest-side"), - ShapeRadius::FarthestSide => dest.write_str("farthest-side"), - } - } -} - impl ToCss for Ellipse { fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> where From 39964f1d78d237bb90006d1517e05b15c4b10514 Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Sun, 2 Jun 2024 23:36:22 -0700 Subject: [PATCH 015/152] Implement animation-range properties #572 --- node/ast.d.ts | 66 ++++++++++ src/lib.rs | 243 ++++++++++++++++++++++++++++++++++ src/properties/animation.rs | 252 +++++++++++++++++++++++++++++++++++- src/properties/mod.rs | 3 + 4 files changed, 563 insertions(+), 1 deletion(-) diff --git a/node/ast.d.ts b/node/ast.d.ts index 8bc02772..fba3f69e 100644 --- a/node/ast.d.ts +++ b/node/ast.d.ts @@ -1960,6 +1960,15 @@ export type PropertyId = | { property: "animation-timeline"; } + | { + property: "animation-range-start"; + } + | { + property: "animation-range-end"; + } + | { + property: "animation-range"; + } | { property: "animation"; vendorPrefix: VendorPrefix; @@ -3316,6 +3325,18 @@ export type Declaration = property: "animation-timeline"; value: AnimationTimeline[]; } + | { + property: "animation-range-start"; + value: AnimationRangeStart[]; + } + | { + property: "animation-range-end"; + value: AnimationRangeEnd[]; + } + | { + property: "animation-range"; + value: AnimationRange[]; + } | { property: "animation"; value: Animation[]; @@ -5541,6 +5562,38 @@ export type Scroller = "root" | "nearest" | "self"; * @maxItems 2 */ export type Size2DFor_LengthPercentageOrAuto = [LengthPercentageOrAuto, LengthPercentageOrAuto]; +/** + * A value for the [animation-range-start](https://drafts.csswg.org/scroll-animations/#animation-range-start) property. + */ +export type AnimationRangeStart = AnimationAttachmentRange; +/** + * A value for the [animation-range-start](https://drafts.csswg.org/scroll-animations/#animation-range-start) or [animation-range-end](https://drafts.csswg.org/scroll-animations/#animation-range-end) property. + */ +export type AnimationAttachmentRange = + | "Normal" + | { + LengthPercentage: DimensionPercentageFor_LengthValue; + } + | { + TimelineRange: { + /** + * The name of the timeline range. + */ + name: TimelineRangeName; + /** + * The offset from the start of the named timeline range. + */ + offset: DimensionPercentageFor_LengthValue; + }; + }; +/** + * A [view progress timeline range](https://drafts.csswg.org/scroll-animations/#view-timelines-ranges) + */ +export type TimelineRangeName = "cover" | "contain" | "entry" | "exit" | "entry-crossing" | "exit-crossing"; +/** + * A value for the [animation-range-end](https://drafts.csswg.org/scroll-animations/#animation-range-end) property. + */ +export type AnimationRangeEnd = AnimationAttachmentRange; /** * An individual [transform function](https://www.w3.org/TR/2019/CR-css-transforms-1-20190214/#two-d-transform-functions). */ @@ -8575,6 +8628,19 @@ export interface ViewTimeline { */ inset: Size2DFor_LengthPercentageOrAuto; } +/** + * A value for the [animation-range](https://drafts.csswg.org/scroll-animations/#animation-range) shorthand property. + */ +export interface AnimationRange { + /** + * The end of the animation's attachment range. + */ + end: AnimationRangeEnd; + /** + * The start of the animation's attachment range. + */ + start: AnimationRangeStart; +} /** * A value for the [animation](https://drafts.csswg.org/css-animations/#animation) shorthand property. */ diff --git a/src/lib.rs b/src/lib.rs index 27a7df16..8b9535f0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11585,6 +11585,249 @@ mod tests { ..Browsers::default() }, ); + + minify_test( + ".foo { animation-range-start: entry 10% }", + ".foo{animation-range-start:entry 10%}", + ); + minify_test( + ".foo { animation-range-start: entry 0% }", + ".foo{animation-range-start:entry}", + ); + minify_test( + ".foo { animation-range-start: entry }", + ".foo{animation-range-start:entry}", + ); + minify_test(".foo { animation-range-start: 50% }", ".foo{animation-range-start:50%}"); + minify_test( + ".foo { animation-range-end: exit 10% }", + ".foo{animation-range-end:exit 10%}", + ); + minify_test( + ".foo { animation-range-end: exit 100% }", + ".foo{animation-range-end:exit}", + ); + minify_test(".foo { animation-range-end: exit }", ".foo{animation-range-end:exit}"); + minify_test(".foo { animation-range-end: 50% }", ".foo{animation-range-end:50%}"); + minify_test( + ".foo { animation-range: entry 10% exit 90% }", + ".foo{animation-range:entry 10% exit 90%}", + ); + minify_test( + ".foo { animation-range: entry 0% exit 100% }", + ".foo{animation-range:entry exit}", + ); + minify_test(".foo { animation-range: entry }", ".foo{animation-range:entry}"); + minify_test( + ".foo { animation-range: entry 0% entry 100% }", + ".foo{animation-range:entry}", + ); + minify_test(".foo { animation-range: 50% normal }", ".foo{animation-range:50%}"); + minify_test( + ".foo { animation-range: normal normal }", + ".foo{animation-range:normal}", + ); + test( + r#" + .foo { + animation-range-start: entry 10%; + animation-range-end: exit 90%; + } + "#, + indoc! {r#" + .foo { + animation-range: entry 10% exit 90%; + } + "#}, + ); + test( + r#" + .foo { + animation-range-start: entry 0%; + animation-range-end: entry 100%; + } + "#, + indoc! {r#" + .foo { + animation-range: entry; + } + "#}, + ); + test( + r#" + .foo { + animation-range-start: entry 0%; + animation-range-end: exit 100%; + } + "#, + indoc! {r#" + .foo { + animation-range: entry exit; + } + "#}, + ); + test( + r#" + .foo { + animation-range-start: 10%; + animation-range-end: normal; + } + "#, + indoc! {r#" + .foo { + animation-range: 10%; + } + "#}, + ); + test( + r#" + .foo { + animation-range-start: 10%; + animation-range-end: 90%; + } + "#, + indoc! {r#" + .foo { + animation-range: 10% 90%; + } + "#}, + ); + test( + r#" + .foo { + animation-range-start: entry 10%; + animation-range-end: exit 100%; + } + "#, + indoc! {r#" + .foo { + animation-range: entry 10% exit; + } + "#}, + ); + test( + r#" + .foo { + animation-range-start: 10%; + animation-range-end: exit 90%; + } + "#, + indoc! {r#" + .foo { + animation-range: 10% exit 90%; + } + "#}, + ); + test( + r#" + .foo { + animation-range-start: entry 10%; + animation-range-end: 90%; + } + "#, + indoc! {r#" + .foo { + animation-range: entry 10% 90%; + } + "#}, + ); + test( + r#" + .foo { + animation-range: entry; + animation-range-end: 90%; + } + "#, + indoc! {r#" + .foo { + animation-range: entry 90%; + } + "#}, + ); + test( + r#" + .foo { + animation-range: entry; + animation-range-end: var(--end); + } + "#, + indoc! {r#" + .foo { + animation-range: entry; + animation-range-end: var(--end); + } + "#}, + ); + test( + r#" + .foo { + animation-range-start: entry 10%, entry 50%; + animation-range-end: exit 90%; + } + "#, + indoc! {r#" + .foo { + animation-range-start: entry 10%, entry 50%; + animation-range-end: exit 90%; + } + "#}, + ); + test( + r#" + .foo { + animation-range-start: entry 10%, entry 50%; + animation-range-end: exit 90%, exit 100%; + } + "#, + indoc! {r#" + .foo { + animation-range: entry 10% exit 90%, entry 50% exit; + } + "#}, + ); + test( + r#" + .foo { + animation-range: entry; + animation-range-end: 90%; + animation: spin 100ms; + } + "#, + indoc! {r#" + .foo { + animation: .1s spin; + } + "#}, + ); + test( + r#" + .foo { + animation: spin 100ms; + animation-range: entry; + animation-range-end: 90%; + } + "#, + indoc! {r#" + .foo { + animation: .1s spin; + animation-range: entry 90%; + } + "#}, + ); + test( + r#" + .foo { + animation-range: entry; + animation-range-end: 90%; + animation: var(--animation) 100ms; + } + "#, + indoc! {r#" + .foo { + animation: var(--animation) .1s; + } + "#}, + ); } #[test] diff --git a/src/properties/animation.rs b/src/properties/animation.rs index de83b9fa..9855127c 100644 --- a/src/properties/animation.rs +++ b/src/properties/animation.rs @@ -12,6 +12,7 @@ use crate::properties::{Property, PropertyId, TokenOrValue, VendorPrefix}; use crate::traits::{Parse, PropertyHandler, Shorthand, ToCss, Zero}; use crate::values::ident::DashedIdent; use crate::values::number::CSSNumber; +use crate::values::percentage::Percentage; use crate::values::size::Size2D; use crate::values::string::CSSString; use crate::values::{easing::EasingFunction, ident::CustomIdent, time::Time}; @@ -21,7 +22,7 @@ use cssparser::*; use itertools::izip; use smallvec::SmallVec; -use super::LengthPercentageOrAuto; +use super::{LengthPercentage, LengthPercentageOrAuto}; /// A value for the [animation-name](https://drafts.csswg.org/css-animations/#animation-name) property. #[derive(Debug, Clone, PartialEq, Parse)] @@ -375,6 +376,201 @@ impl ToCss for ViewTimeline { } } +/// A [view progress timeline range](https://drafts.csswg.org/scroll-animations/#view-timelines-ranges) +#[derive(Debug, Clone, PartialEq, Parse, ToCss)] +#[cfg_attr(feature = "visitor", derive(Visit))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))] +#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))] +pub enum TimelineRangeName { + /// Represents the full range of the view progress timeline. + Cover, + /// Represents the range during which the principal box is either fully contained by, + /// or fully covers, its view progress visibility range within the scrollport. + Contain, + /// Represents the range during which the principal box is entering the view progress visibility range. + Entry, + /// Represents the range during which the principal box is exiting the view progress visibility range. + Exit, + /// Represents the range during which the principal box crosses the end border edge. + EntryCrossing, + /// Represents the range during which the principal box crosses the start border edge. + ExitCrossing, +} + +/// A value for the [animation-range-start](https://drafts.csswg.org/scroll-animations/#animation-range-start) +/// or [animation-range-end](https://drafts.csswg.org/scroll-animations/#animation-range-end) property. +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "visitor", derive(Visit))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))] +#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))] +pub enum AnimationAttachmentRange { + /// The start of the animation’s attachment range is the start of its associated timeline. + Normal, + /// The animation attachment range starts at the specified point on the timeline measuring from the start of the timeline. + LengthPercentage(LengthPercentage), + /// The animation attachment range starts at the specified point on the timeline measuring from the start of the specified named timeline range. + TimelineRange { + /// The name of the timeline range. + name: TimelineRangeName, + /// The offset from the start of the named timeline range. + offset: LengthPercentage, + }, +} + +impl<'i> AnimationAttachmentRange { + fn parse<'t>(input: &mut Parser<'i, 't>, default: f32) -> Result<'i, ParserError<'i>>> { + if input.try_parse(|input| input.expect_ident_matching("normal")).is_ok() { + return Ok(AnimationAttachmentRange::Normal); + } + + if let Ok(val) = input.try_parse(LengthPercentage::parse) { + return Ok(AnimationAttachmentRange::LengthPercentage(val)); + } + + let name = TimelineRangeName::parse(input)?; + let offset = input + .try_parse(LengthPercentage::parse) + .unwrap_or(LengthPercentage::Percentage(Percentage(default))); + Ok(AnimationAttachmentRange::TimelineRange { name, offset }) + } + + fn to_css(&self, dest: &mut Printer, default: f32) -> Result<(), PrinterError> + where + W: std::fmt::Write, + { + match self { + Self::Normal => dest.write_str("normal"), + Self::LengthPercentage(val) => val.to_css(dest), + Self::TimelineRange { name, offset } => { + name.to_css(dest)?; + if *offset != LengthPercentage::Percentage(Percentage(default)) { + dest.write_char(' ')?; + offset.to_css(dest)?; + } + Ok(()) + } + } + } +} + +impl Default for AnimationAttachmentRange { + fn default() -> Self { + AnimationAttachmentRange::Normal + } +} + +/// A value for the [animation-range-start](https://drafts.csswg.org/scroll-animations/#animation-range-start) property. +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "visitor", derive(Visit))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))] +#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))] +pub struct AnimationRangeStart(pub AnimationAttachmentRange); + +impl<'i> Parse<'i> for AnimationRangeStart { + fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { + let range = AnimationAttachmentRange::parse(input, 0.0)?; + Ok(Self(range)) + } +} + +impl ToCss for AnimationRangeStart { + fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> + where + W: std::fmt::Write, + { + self.0.to_css(dest, 0.0) + } +} + +/// A value for the [animation-range-end](https://drafts.csswg.org/scroll-animations/#animation-range-end) property. +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "visitor", derive(Visit))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))] +#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))] +pub struct AnimationRangeEnd(pub AnimationAttachmentRange); + +impl<'i> Parse<'i> for AnimationRangeEnd { + fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { + let range = AnimationAttachmentRange::parse(input, 1.0)?; + Ok(Self(range)) + } +} + +impl ToCss for AnimationRangeEnd { + fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> + where + W: std::fmt::Write, + { + self.0.to_css(dest, 1.0) + } +} + +/// A value for the [animation-range](https://drafts.csswg.org/scroll-animations/#animation-range) shorthand property. +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "visitor", derive(Visit))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))] +#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))] +pub struct AnimationRange { + /// The start of the animation's attachment range. + pub start: AnimationRangeStart, + /// The end of the animation's attachment range. + pub end: AnimationRangeEnd, +} + +impl<'i> Parse<'i> for AnimationRange { + fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> { + let start = AnimationRangeStart::parse(input)?; + let end = input + .try_parse(AnimationRangeStart::parse) + .map(|r| AnimationRangeEnd(r.0)) + .unwrap_or_else(|_| { + // If <'animation-range-end'> is omitted and <'animation-range-start'> includes a component, then + // animation-range-end is set to that same and 100%. Otherwise, any omitted longhand is set to its initial value. + match &start.0 { + AnimationAttachmentRange::TimelineRange { name, .. } => { + AnimationRangeEnd(AnimationAttachmentRange::TimelineRange { + name: name.clone(), + offset: LengthPercentage::Percentage(Percentage(1.0)), + }) + } + _ => AnimationRangeEnd(AnimationAttachmentRange::default()), + } + }); + Ok(AnimationRange { start, end }) + } +} + +impl ToCss for AnimationRange { + fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> + where + W: std::fmt::Write, + { + self.start.to_css(dest)?; + + let omit_end = match (&self.start.0, &self.end.0) { + ( + AnimationAttachmentRange::TimelineRange { name: start_name, .. }, + AnimationAttachmentRange::TimelineRange { + name: end_name, + offset: end_offset, + }, + ) => start_name == end_name && *end_offset == LengthPercentage::Percentage(Percentage(1.0)), + (_, end) => *end == AnimationAttachmentRange::default(), + }; + + if !omit_end { + dest.write_char(' ')?; + self.end.to_css(dest)?; + } + Ok(()) + } +} + define_list_shorthand! { /// A value for the [animation](https://drafts.csswg.org/css-animations/#animation) shorthand property. pub struct Animation<'i>(VendorPrefix) { @@ -524,6 +720,8 @@ pub(crate) struct AnimationHandler<'i> { delays: Option<(SmallVec<[Time; 1]>, VendorPrefix)>, fill_modes: Option<(SmallVec<[AnimationFillMode; 1]>, VendorPrefix)>, timelines: Option<[AnimationTimeline<'i>; 1]>>, + range_starts: Option<[AnimationRangeStart; 1]>>, + range_ends: Option<[AnimationRangeEnd; 1]>>, has_any: bool, } @@ -574,6 +772,19 @@ impl<'i> PropertyHandler<'i> for AnimationHandler<'i> { self.timelines = Some(val.clone()); self.has_any = true; } + Property::AnimationRangeStart(val) => { + self.range_starts = Some(val.clone()); + self.has_any = true; + } + Property::AnimationRangeEnd(val) => { + self.range_ends = Some(val.clone()); + self.has_any = true; + } + Property::AnimationRange(val) => { + self.range_starts = Some(val.iter().map(|v| v.start.clone()).collect()); + self.range_ends = Some(val.iter().map(|v| v.end.clone()).collect()); + self.has_any = true; + } Property::Animation(val, vp) => { let names = val.iter().map(|b| b.name.clone()).collect(); maybe_flush!(names, &names, vp); @@ -609,6 +820,11 @@ impl<'i> PropertyHandler<'i> for AnimationHandler<'i> { property!(play_states, &play_states, vp); property!(delays, &delays, vp); property!(fill_modes, &fill_modes, vp); + + // The animation shorthand resets animation-range + // https://drafts.csswg.org/scroll-animations/#named-range-animation-declaration + self.range_starts = None; + self.range_ends = None; } Property::Unparsed(val) if is_animation_property(&val.property_id) => { let mut val = Cow::Borrowed(val); @@ -636,6 +852,9 @@ impl<'i> PropertyHandler<'i> for AnimationHandler<'i> { _ => {} } } + + self.range_starts = None; + self.range_ends = None; } self.flush(dest, context); @@ -671,6 +890,8 @@ impl<'i> AnimationHandler<'i> { let mut delays = std::mem::take(&mut self.delays); let mut fill_modes = std::mem::take(&mut self.fill_modes); let mut timelines_value = std::mem::take(&mut self.timelines); + let range_starts = std::mem::take(&mut self.range_starts); + let range_ends = std::mem::take(&mut self.range_ends); if let ( Some((names, names_vp)), @@ -813,6 +1034,32 @@ impl<'i> AnimationHandler<'i> { if let Some(val) = timelines_value { dest.push(Property::AnimationTimeline(val)); } + + match (range_starts, range_ends) { + (Some(range_starts), Some(range_ends)) => { + if range_starts.len() == range_ends.len() { + dest.push(Property::AnimationRange( + range_starts + .into_iter() + .zip(range_ends.into_iter()) + .map(|(start, end)| AnimationRange { start, end }) + .collect(), + )); + } else { + dest.push(Property::AnimationRangeStart(range_starts)); + dest.push(Property::AnimationRangeEnd(range_ends)); + } + } + (range_starts, range_ends) => { + if let Some(range_starts) = range_starts { + dest.push(Property::AnimationRangeStart(range_starts)); + } + + if let Some(range_ends) = range_ends { + dest.push(Property::AnimationRangeEnd(range_ends)); + } + } + } } } @@ -829,6 +1076,9 @@ fn is_animation_property(property_id: &PropertyId) -> bool { | PropertyId::AnimationFillMode(_) | PropertyId::AnimationComposition | PropertyId::AnimationTimeline + | PropertyId::AnimationRange + | PropertyId::AnimationRangeStart + | PropertyId::AnimationRangeEnd | PropertyId::Animation(_) => true, _ => false, } diff --git a/src/properties/mod.rs b/src/properties/mod.rs index 38ff8622..7c5f4071 100644 --- a/src/properties/mod.rs +++ b/src/properties/mod.rs @@ -1493,6 +1493,9 @@ define_properties! { "animation-fill-mode": AnimationFillMode(SmallVec<[AnimationFillMode; 1]>, VendorPrefix) / WebKit / Moz / O, "animation-composition": AnimationComposition(SmallVec<[AnimationComposition; 1]>), "animation-timeline": AnimationTimeline(SmallVec<[AnimationTimeline<'i>; 1]>), + "animation-range-start": AnimationRangeStart(SmallVec<[AnimationRangeStart; 1]>), + "animation-range-end": AnimationRangeEnd(SmallVec<[AnimationRangeEnd; 1]>), + "animation-range": AnimationRange(SmallVec<[AnimationRange; 1]>), "animation": Animation(AnimationList<'i>, VendorPrefix) / WebKit / Moz / O shorthand: true, // https://drafts.csswg.org/css-transforms-2/ From f7c883131c3463756ac935496cfe5da710eec601 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Fri, 2 Aug 2024 00:30:56 +0900 Subject: [PATCH 016/152] fix: Fix codegen of `:is(input:checked)` (#783) --- src/selector.rs | 7 ++++++- tests/cli_integration_tests.rs | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/selector.rs b/src/selector.rs index 62550198..56df04c8 100644 --- a/src/selector.rs +++ b/src/selector.rs @@ -1624,8 +1624,13 @@ where #[inline] fn has_type_selector(selector: &Selector) -> bool { - let mut iter = selector.iter_raw_parse_order_from(0); + // For input:checked the component vector is + // [input, :checked] so we have to check it using matching order. + // + // This both happens for input:checked and is(input:checked) + let mut iter = selector.iter_raw_match_order(); let first = iter.next(); + if is_namespace(first) { is_type_selector(iter.next()) } else { diff --git a/tests/cli_integration_tests.rs b/tests/cli_integration_tests.rs index 744ac7ca..c57d26e8 100644 --- a/tests/cli_integration_tests.rs +++ b/tests/cli_integration_tests.rs @@ -759,3 +759,23 @@ fn browserslist_environment_from_browserslist_env() -> Result<(), Box Result<(), Box> { + let infile = assert_fs::NamedTempFile::new("test.css")?; + infile.write_str( + r#" + .cb:is(input:checked) { + margin: 3rem; + } + "#, + )?; + let outfile = assert_fs::NamedTempFile::new("test.out")?; + let mut cmd = Command::cargo_bin("lightningcss")?; + cmd.arg(infile.path()); + cmd.arg("--output-file").arg(outfile.path()); + cmd.assert().success(); + outfile.assert(predicate::str::contains(indoc! {r#".cb:is(input:checked)"#})); + + Ok(()) +} From 3e623473839c7fc76b277f97f88c649c6c94e44e Mon Sep 17 00:00:00 2001 From: neverland Date: Thu, 1 Aug 2024 23:31:42 +0800 Subject: [PATCH 017/152] Bump browserslist-rs 0.16.0 (#772) --- Cargo.lock | 156 ++++++++++++++++++++++++++----------------------- Cargo.toml | 2 +- c/Cargo.toml | 2 +- src/targets.rs | 4 +- 4 files changed, 88 insertions(+), 76 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 07d5f7e1..29c70813 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -57,12 +57,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" -[[package]] -name = "anyhow" -version = "1.0.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" - [[package]] name = "assert_cmd" version = "2.0.12" @@ -145,23 +139,19 @@ dependencies = [ [[package]] name = "browserslist-rs" -version = "0.15.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "405bbd46590a441abe5db3e5c8af005aa42e640803fecb51912703e93e4ce8d3" +checksum = "fdf0ca73de70c3da94e4194e4a01fe732378f55d47cf4c0588caab22a0dbfa14" dependencies = [ "ahash 0.8.7", - "anyhow", "chrono", "either", "indexmap 2.2.6", - "itertools 0.12.1", + "itertools 0.13.0", "nom", "once_cell", - "quote", "serde", "serde_json", - "string_cache", - "string_cache_codegen", "thiserror", ] @@ -246,14 +236,14 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -485,9 +475,9 @@ checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" [[package]] name = "either" -version = "1.9.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "equivalent" @@ -699,9 +689,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] @@ -955,12 +945,6 @@ dependencies = [ "libloading", ] -[[package]] -name = "new_debug_unreachable" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" - [[package]] name = "nom" version = "7.1.3" @@ -1035,16 +1019,6 @@ dependencies = [ "vlq", ] -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - [[package]] name = "parking_lot_core" version = "0.9.9" @@ -1055,7 +1029,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -1586,32 +1560,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "string_cache" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" -dependencies = [ - "new_debug_unreachable", - "once_cell", - "parking_lot", - "phf_shared 0.10.0", - "precomputed-hash", - "serde", -] - -[[package]] -name = "string_cache_codegen" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" -dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", - "proc-macro2", - "quote", -] - [[package]] name = "strsim" version = "0.10.0" @@ -1880,7 +1828,7 @@ version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -1889,7 +1837,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -1898,13 +1846,29 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -1913,42 +1877,90 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + [[package]] name = "wyz" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index 75bc397c..80e64af3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,7 +65,7 @@ paste = "1.0.12" # CLI deps atty = { version = "0.2", optional = true } clap = { version = "3.0.6", features = ["derive"], optional = true } -browserslist-rs = { version = "0.15.0", optional = true } +browserslist-rs = { version = "0.16.0", optional = true } rayon = { version = "1.5.1", optional = true } dashmap = { version = "5.0.0", optional = true } serde_json = { version = "1.0.78", optional = true } diff --git a/c/Cargo.toml b/c/Cargo.toml index 443d34ba..96641dc7 100644 --- a/c/Cargo.toml +++ b/c/Cargo.toml @@ -11,7 +11,7 @@ crate-type = ["cdylib"] [dependencies] lightningcss = { path = "../", features = ["browserslist"] } parcel_sourcemap = { version = "2.1.1", features = ["json"] } -browserslist-rs = { version = "0.15.0" } +browserslist-rs = { version = "0.16.0" } [build-dependencies] cbindgen = "0.24.3" diff --git a/src/targets.rs b/src/targets.rs index 3dd40061..4d44ddae 100644 --- a/src/targets.rs +++ b/src/targets.rs @@ -48,7 +48,7 @@ impl Browsers { ) -> Result