Skip to main content

oxc_css_parser/parser/at_rule/
keyframes.rs

1use super::Parser;
2use crate::{
3    Parse, arena_vec,
4    ast::*,
5    eat,
6    error::{Error, ErrorKind, PResult},
7    parser::state::ParserState,
8    peek,
9    pos::{Span, Spanned},
10    tokenizer::Token,
11    util,
12};
13
14// https://drafts.csswg.org/css-animations/#keyframes
15impl<'a> Parse<'a> for KeyframeBlock<'a> {
16    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
17        let first_selector = input.parse::<KeyframeSelector>()?;
18        let start = first_selector.span().start;
19
20        let mut selectors = input.vec_with_capacity(2);
21        selectors.push(first_selector);
22        let mut comma_spans = arena_vec!(input);
23        while let Some((_, comma_span)) = eat!(input, Comma) {
24            comma_spans.push(comma_span);
25            selectors.push(input.parse()?);
26        }
27        debug_assert_eq!(comma_spans.len() + 1, selectors.len());
28
29        let block = input
30            .with_state(ParserState { in_keyframes_at_rule: false, ..input.state.clone() })
31            .parse::<SimpleBlock>()?;
32
33        let span = Span { start, end: block.span.end };
34        Ok(KeyframeBlock { selectors, comma_spans, block, span })
35    }
36}
37
38impl<'a> Parse<'a> for KeyframeSelector<'a> {
39    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
40        match &peek!(input).token {
41            Token::Percentage(..) => Ok(KeyframeSelector::Percentage(input.parse()?)),
42            _ => {
43                let ident = input.parse()?;
44                match &ident {
45                    InterpolableIdent::Literal(ident)
46                        if !ident.name.eq_ignore_ascii_case("from")
47                            && !ident.name.eq_ignore_ascii_case("to") =>
48                    {
49                        input.recoverable_errors.push(Error {
50                            kind: ErrorKind::UnknownKeyframeSelectorIdent,
51                            span: ident.span.clone(),
52                        });
53                    }
54                    _ => {}
55                }
56                Ok(KeyframeSelector::Ident(ident))
57            }
58        }
59    }
60}
61
62// https://drafts.csswg.org/css-animations/#keyframes
63impl<'a> Parse<'a> for KeyframesName<'a> {
64    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
65        match &peek!(input).token {
66            Token::Ident(..) | Token::HashLBrace(..) | Token::AtLBraceVar(..) => {
67                let ident = input.parse()?;
68                match &ident {
69                    InterpolableIdent::Literal(ident)
70                        if util::is_css_wide_keyword(ident.name)
71                            || ident.name.eq_ignore_ascii_case("default") =>
72                    {
73                        input.recoverable_errors.push(Error {
74                            kind: ErrorKind::CSSWideKeywordDisallowed,
75                            span: ident.span.clone(),
76                        });
77                    }
78                    _ => {}
79                }
80                Ok(KeyframesName::Ident(ident))
81            }
82            Token::AtKeyword(..) => input.parse().map(KeyframesName::LessVariable),
83            Token::Tilde(..) => input.parse().map(KeyframesName::LessEscapedStr),
84            _ => input.parse().map(KeyframesName::Str),
85        }
86    }
87}