Skip to content

Commit

Permalink
Serialize animation name as string when it conflicts with keyword
Browse files Browse the repository at this point in the history
  • Loading branch information
devongovett committed Sep 17, 2022
1 parent 0732197 commit df280dc
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 18 deletions.
10 changes: 9 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8332,6 +8332,14 @@ mod tests {
".foo{animation:\"unset\" 0s 3s infinite,none}",
);

minify_test(
".foo { animation: \"infinite\" 2s 1 }",
".foo{animation:\"infinite\" 2s}",
);
minify_test(".foo { animation: \"paused\" 2s }", ".foo{animation:\"paused\" 2s}");
minify_test(".foo { animation: \"forwards\" 2s }", ".foo{animation:\"forwards\" 2s}");
minify_test(".foo { animation: \"reverse\" 2s }", ".foo{animation:\"reverse\" 2s}");

minify_test(
".foo { animation: 3s ease-in 1s infinite reverse both running slidein }",
".foo{animation:slidein 3s ease-in 1s infinite reverse both}",
Expand All @@ -8340,7 +8348,7 @@ mod tests {
".foo { animation: 3s slidein paused ease 1s 1 reverse both }",
".foo{animation:slidein 3s 1s reverse both paused}",
);
minify_test(".foo { animation: 3s ease ease }", ".foo{animation:ease 3s ease}");
minify_test(".foo { animation: 3s ease ease }", ".foo{animation:\"ease\" 3s}");
minify_test(
".foo { animation: 3s cubic-bezier(0.25, 0.1, 0.25, 1) foo }",
".foo{animation:foo 3s}",
Expand Down
108 changes: 91 additions & 17 deletions src/properties/animation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,25 @@ impl<'i> ToCss for AnimationName<'i> {
}
}

impl<'i> AnimationName<'i> {
fn write_as_string<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
where
W: std::fmt::Write,
{
match self {
AnimationName::None => dest.write_str("none"),
AnimationName::Ident(CustomIdent(s)) | AnimationName::String(s) => {
if let Some(css_module) = &mut dest.css_module {
css_module.reference(&s, dest.loc.source_index)
}

serialize_string(&s, dest)?;
Ok(())
}
}
}
}

/// A list of animation names.
pub type AnimationNameList<'i> = SmallVec<[AnimationName<'i>; 1]>;

Expand All @@ -99,6 +118,12 @@ pub enum AnimationIterationCount {
Infinite,
}

impl Default for AnimationIterationCount {
fn default() -> Self {
AnimationIterationCount::Number(1.0)
}
}

impl<'i> Parse<'i> for AnimationIterationCount {
fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
if input.try_parse(|input| input.expect_ident_matching("infinite")).is_ok() {
Expand Down Expand Up @@ -136,6 +161,12 @@ enum_property! {
}
}

impl Default for AnimationDirection {
fn default() -> Self {
AnimationDirection::Normal
}
}

enum_property! {
/// A value for the [animation-play-state](https://drafts.csswg.org/css-animations/#animation-play-state) property.
pub enum AnimationPlayState {
Expand All @@ -146,6 +177,12 @@ enum_property! {
}
}

impl Default for AnimationPlayState {
fn default() -> Self {
AnimationPlayState::Running
}
}

enum_property! {
/// A value for the [animation-fill-mode](https://drafts.csswg.org/css-animations/#animation-fill-mode) property.
pub enum AnimationFillMode {
Expand All @@ -160,6 +197,12 @@ enum_property! {
}
}

impl Default for AnimationFillMode {
fn default() -> Self {
AnimationFillMode::None
}
}

define_list_shorthand! {
/// A value for the [animation](https://drafts.csswg.org/css-animations/#animation) shorthand property.
pub struct Animation<'i>(VendorPrefix) {
Expand Down Expand Up @@ -235,19 +278,21 @@ impl<'i> ToCss for Animation<'i> {
where
W: std::fmt::Write,
{
self.name.to_css(dest)?;
if self.name_conflicts_with_keyword() {
self.name.write_as_string(dest)?;
} else {
self.name.to_css(dest)?;
};

match &self.name {
AnimationName::None => return Ok(()),
AnimationName::Ident(CustomIdent(name)) | AnimationName::String(name) => {
_ => {
if !self.duration.is_zero() || !self.delay.is_zero() {
dest.write_char(' ')?;
self.duration.to_css(dest)?;
}

if (self.timing_function != EasingFunction::Ease
&& self.timing_function != EasingFunction::CubicBezier(0.25, 0.1, 0.25, 1.0))
|| EasingFunction::is_ident(&name)
{
if !self.is_default_easing() {
dest.write_char(' ')?;
self.timing_function.to_css(dest)?;
}
Expand All @@ -257,29 +302,22 @@ impl<'i> ToCss for Animation<'i> {
self.delay.to_css(dest)?;
}

if self.iteration_count != AnimationIterationCount::Number(1.0) || name.as_ref() == "infinite" {
if self.iteration_count != AnimationIterationCount::default() {
dest.write_char(' ')?;
self.iteration_count.to_css(dest)?;
}

if self.direction != AnimationDirection::Normal || AnimationDirection::parse_string(&name).is_ok() {
if self.direction != AnimationDirection::default() {
dest.write_char(' ')?;
self.direction.to_css(dest)?;
}

// Avoid parsing `animation: "none"` to `animation: "none" none`.
let animation_name_is_none = AnimationName::parse_string(&name)
.as_ref()
.map(|n| n == &AnimationName::None)
.unwrap_or(true);
if self.fill_mode == AnimationFillMode::None && animation_name_is_none {
return Ok(());
} else if self.fill_mode != AnimationFillMode::None || AnimationFillMode::parse_string(&name).is_ok() {
if self.fill_mode != AnimationFillMode::default() {
dest.write_char(' ')?;
self.fill_mode.to_css(dest)?;
}

if self.play_state != AnimationPlayState::Running || AnimationPlayState::parse_string(&name).is_ok() {
if self.play_state != AnimationPlayState::default() {
dest.write_char(' ')?;
self.play_state.to_css(dest)?;
}
Expand All @@ -290,6 +328,42 @@ impl<'i> ToCss for Animation<'i> {
}
}

impl<'i> Animation<'i> {
fn is_default_easing(&self) -> bool {
self.timing_function == EasingFunction::Ease
|| self.timing_function == EasingFunction::CubicBezier(0.25, 0.1, 0.25, 1.0)
}

fn name_conflicts_with_keyword(&self) -> bool {
match &self.name {
AnimationName::Ident(CustomIdent(name)) | AnimationName::String(name) => {
if self.is_default_easing() && EasingFunction::is_ident(&name) {
return true;
}

if self.iteration_count == AnimationIterationCount::default() && name.as_ref() == "infinite" {
return true;
}

if self.direction == AnimationDirection::default() && AnimationDirection::parse_string(&name).is_ok() {
return true;
}

if self.fill_mode == AnimationFillMode::default() && AnimationFillMode::parse_string(&name).is_ok() {
return true;
}

if self.play_state == AnimationPlayState::default() && AnimationPlayState::parse_string(&name).is_ok() {
return true;
}
}
_ => {}
}

false
}
}

/// A list of animations.
pub type AnimationList<'i> = SmallVec<[Animation<'i>; 1]>;

Expand Down

0 comments on commit df280dc

Please sign in to comment.