diff --git a/crates/swc_css_parser/src/parser/at_rules/mod.rs b/crates/swc_css_parser/src/parser/at_rules/mod.rs index 84bdce80fe1d..a932854650ab 100644 --- a/crates/swc_css_parser/src/parser/at_rules/mod.rs +++ b/crates/swc_css_parser/src/parser/at_rules/mod.rs @@ -565,7 +565,7 @@ where | js_word!("-o-keyframes") | js_word!("-ms-keyframes") => { let ctx = Ctx { - block_contents_grammar: BlockContentsGrammar::DeclarationList, + block_contents_grammar: BlockContentsGrammar::RuleList, is_top_level: false, in_keyframes_at_rule: true, ..self.ctx diff --git a/crates/swc_css_parser/src/parser/syntax/mod.rs b/crates/swc_css_parser/src/parser/syntax/mod.rs index 639dff8819e9..35b187046b17 100644 --- a/crates/swc_css_parser/src/parser/syntax/mod.rs +++ b/crates/swc_css_parser/src/parser/syntax/mod.rs @@ -134,24 +134,18 @@ where unreachable!() } }; - let (normalized_at_rule_name, name) = if at_keyword_name.0.starts_with("--") { - ( - at_keyword_name.0.to_ascii_lowercase(), - AtRuleName::DashedIdent(DashedIdent { - span: Span::new(span.lo + BytePos(1), span.hi, Default::default()), - value: at_keyword_name.0, - raw: Some(at_keyword_name.1), - }), - ) + let name = if at_keyword_name.0.starts_with("--") { + AtRuleName::DashedIdent(DashedIdent { + span: Span::new(span.lo + BytePos(1), span.hi, Default::default()), + value: at_keyword_name.0, + raw: Some(at_keyword_name.1), + }) } else { - ( - at_keyword_name.0.to_ascii_lowercase(), - AtRuleName::Ident(Ident { - span: Span::new(span.lo + BytePos(1), span.hi, Default::default()), - value: at_keyword_name.0, - raw: Some(at_keyword_name.1), - }), - ) + AtRuleName::Ident(Ident { + span: Span::new(span.lo + BytePos(1), span.hi, Default::default()), + value: at_keyword_name.0, + raw: Some(at_keyword_name.1), + }) }; let mut prelude = vec![]; let mut at_rule = AtRule { @@ -181,103 +175,31 @@ where tok!(";") => { self.input.bump(); - let list_of_component_values = self.create_locv(prelude); - - at_rule.prelude = match self - .parse_according_to_grammar(&list_of_component_values, |parser| { - parser.parse_at_rule_prelude(&normalized_at_rule_name) - }) { - Ok(at_rule_prelude) => match at_rule_prelude { - None if normalized_at_rule_name == js_word!("layer") => { - self.errors.push(Error::new( - span, - ErrorKind::Expected("at least one name"), - )); - - Some(Box::new(AtRulePrelude::ListOfComponentValues( - list_of_component_values, - ))) - } - _ => at_rule_prelude.map(Box::new), - }, - Err(err) => { - if *err.kind() != ErrorKind::Ignore { - self.errors.push(err); - } - - if !list_of_component_values.children.is_empty() { - Some(Box::new(AtRulePrelude::ListOfComponentValues( - list_of_component_values, - ))) - } else { - None - } - } - }; + at_rule.prelude = Some(Box::new(AtRulePrelude::ListOfComponentValues( + self.create_locv(prelude), + ))); at_rule.span = span!(self, span.lo); + // Canonicalization against a grammar + at_rule = self.canonicalize_at_rule_prelude(at_rule)?; + return Ok(at_rule); } // <{-token> // Consume a simple block and assign it to the at-rule’s block. Return the at-rule. tok!("{") => { - let mut block = self.parse_as::()?; - - let list_of_component_values = self.create_locv(prelude); - - at_rule.prelude = match self - .parse_according_to_grammar(&list_of_component_values, |parser| { - parser.parse_at_rule_prelude(&normalized_at_rule_name) - }) { - Ok(at_rule_prelude) => match at_rule_prelude { - Some(AtRulePrelude::LayerPrelude(LayerPrelude::NameList( - name_list, - ))) if name_list.name_list.len() > 1 => { - self.errors.push(Error::new( - name_list.span, - ErrorKind::Expected("only one name"), - )); - - Some(Box::new(AtRulePrelude::ListOfComponentValues( - list_of_component_values, - ))) - } - _ => at_rule_prelude.map(Box::new), - }, - Err(err) => { - if *err.kind() != ErrorKind::Ignore { - self.errors.push(err); - } - - if !list_of_component_values.children.is_empty() { - Some(Box::new(AtRulePrelude::ListOfComponentValues( - list_of_component_values, - ))) - } else { - None - } - } - }; + let block = self.parse_as::()?; - at_rule.block = match self.parse_according_to_grammar( - &self.create_locv(block.value.clone()), - |parser| parser.parse_at_rule_block(&normalized_at_rule_name), - ) { - Ok(block_contents) => { - block.value = block_contents; - - Some(block) - } - Err(err) => { - if *err.kind() != ErrorKind::Ignore { - self.errors.push(err); - } - - Some(block) - } - }; + at_rule.prelude = Some(Box::new(AtRulePrelude::ListOfComponentValues( + self.create_locv(prelude), + ))); + at_rule.block = Some(block); at_rule.span = span!(self, span.lo); + // Canonicalization against a grammar + at_rule = self.canonicalize_at_rule_prelude(at_rule)?; + at_rule = self.canonicalize_at_rule_block(at_rule)?; + return Ok(at_rule); } // anything else @@ -299,50 +221,10 @@ where { fn parse(&mut self) -> PResult { // To consume a qualified rule: - let create_prelude = - |p: &mut Parser, list: Vec| -> PResult { - let list_of_component_values = p.create_locv(list); - - if p.ctx.in_keyframes_at_rule { - Ok(QualifiedRulePrelude::ListOfComponentValues( - list_of_component_values, - )) - } else if p.ctx.mixed_with_declarations { - match p.parse_according_to_grammar::( - &list_of_component_values, - |parser| parser.parse(), - ) { - Ok(relative_selector_list) => Ok( - QualifiedRulePrelude::RelativeSelectorList(relative_selector_list), - ), - Err(err) => { - p.errors.push(err); - - Ok(QualifiedRulePrelude::ListOfComponentValues( - list_of_component_values, - )) - } - } - } else { - match p.parse_according_to_grammar::( - &list_of_component_values, - |parser| parser.parse(), - ) { - Ok(selector_list) => Ok(QualifiedRulePrelude::SelectorList(selector_list)), - Err(err) => { - p.errors.push(err); - Ok(QualifiedRulePrelude::ListOfComponentValues( - list_of_component_values, - )) - } - } - } - }; - - let span = self.input.cur_span(); // Create a new qualified rule with its prelude initially set to an empty list, // and its value initially set to nothing. + let span = self.input.cur_span(); let mut prelude = vec![]; // Repeatedly consume the next input token: @@ -375,30 +257,20 @@ where // Consume a simple block and assign it to the qualified rule’s block. Return the // qualified rule. tok!("{") => { - let mut block = self.parse_as::()?; - - block.value = match self.ctx.block_contents_grammar { - BlockContentsGrammar::DeclarationList => self - .parse_according_to_grammar(&self.create_locv(block.value), |parser| { - parser.parse_as::>() - })? - .into_iter() - .map(ComponentValue::DeclarationOrAtRule) - .collect(), - _ => self - .parse_according_to_grammar(&self.create_locv(block.value), |parser| { - parser.parse_as::>() - })? - .into_iter() - .map(ComponentValue::StyleBlock) - .collect(), - }; - - return Ok(QualifiedRule { + let block = self.parse_as::()?; + let mut qualified_rule = QualifiedRule { span: span!(self, span.lo), - prelude: create_prelude(self, prelude)?, + prelude: QualifiedRulePrelude::ListOfComponentValues( + self.create_locv(prelude), + ), block, - }); + }; + + // Canonicalization against a grammar + qualified_rule = self.canonicalize_qualified_rule_prelude(qualified_rule)?; + qualified_rule = self.canonicalize_qualified_rule_block(qualified_rule)?; + + return Ok(qualified_rule); } // Reconsume the current input token. Consume a component value. Append the returned // value to the qualified rule’s prelude. @@ -458,12 +330,7 @@ where // Reconsume the current input token. Consume an at-rule, and append the result to // rules. tok!("@") => { - let at_rule = self - .with_ctx(Ctx { - block_contents_grammar: BlockContentsGrammar::StyleBlock, - ..self.ctx - }) - .parse_as::()?; + let at_rule = self.parse()?; rules.push(StyleBlock::AtRule(Box::new(at_rule))); } @@ -880,32 +747,8 @@ where return Ok(declaration); } - // Grammar parsing - let list_of_component_values = self.create_locv(declaration.value); - - declaration.value = - match self.parse_according_to_grammar(&list_of_component_values, |parser| { - let mut values = vec![]; - - loop { - if is!(parser, EOF) { - break; - } - - values.push(parser.parse_generic_value()?); - } - - Ok(values) - }) { - Ok(values) => values, - Err(err) => { - if *err.kind() != ErrorKind::Ignore { - self.errors.push(err); - } - - list_of_component_values.children - } - }; + // Canonicalization against a grammar + declaration = self.canonicalize_declaration_value(declaration)?; // 8. Return the declaration. Ok(declaration) @@ -1055,9 +898,8 @@ where unreachable!() } }; - let function_name = &*ident.0.to_ascii_lowercase(); let name = Ident { - span: swc_common::Span::new(span.lo, span.hi - BytePos(1), Default::default()), + span: Span::new(span.lo, span.hi - BytePos(1), Default::default()), value: ident.0, raw: Some(ident.1), }; @@ -1101,26 +943,8 @@ where function.span = span!(self, span.lo); - // Grammar parsing - match self.ctx.block_contents_grammar { - BlockContentsGrammar::DeclarationList => {} - _ => { - let locv = self.create_locv(function.value); - - function.value = match self.parse_according_to_grammar(&locv, |parser| { - parser.parse_function_values(function_name) - }) { - Ok(values) => values, - Err(err) => { - if *err.kind() != ErrorKind::Ignore { - self.errors.push(err); - } - - locv.children - } - }; - } - } + // Canonicalization against a grammar + function = self.canonicalize_function_value(function)?; return Ok(function); } diff --git a/crates/swc_css_parser/src/parser/util.rs b/crates/swc_css_parser/src/parser/util.rs index 56be179d66fe..22b96c1f7fb5 100644 --- a/crates/swc_css_parser/src/parser/util.rs +++ b/crates/swc_css_parser/src/parser/util.rs @@ -1,5 +1,6 @@ use std::ops::{Deref, DerefMut}; +use swc_atoms::js_word; use swc_common::{Span, Spanned, SyntaxContext, DUMMY_SP}; use swc_css_ast::*; @@ -7,7 +8,7 @@ use super::{ input::{Input, InputType, ParserInput}, Ctx, Error, PResult, Parse, Parser, }; -use crate::parser::BlockContentsGrammar; +use crate::{error::ErrorKind, parser::BlockContentsGrammar}; impl Parser where @@ -74,6 +75,255 @@ where res } + pub(super) fn canonicalize_at_rule_prelude(&mut self, mut at_rule: AtRule) -> PResult { + let normalized_at_rule_name = match &at_rule.name { + AtRuleName::Ident(Ident { value, .. }) => value.to_ascii_lowercase(), + AtRuleName::DashedIdent(_) => return Ok(at_rule), + }; + + let list_of_component_values = match at_rule.prelude { + Some(at_rule_prelude) => match *at_rule_prelude { + AtRulePrelude::ListOfComponentValues(list_of_component_values) => { + list_of_component_values + } + _ => { + unreachable!(); + } + }, + _ => { + unreachable!(); + } + }; + + at_rule.prelude = match self + .parse_according_to_grammar(&list_of_component_values, |parser| { + parser.parse_at_rule_prelude(&normalized_at_rule_name) + }) { + Ok(at_rule_prelude) => match at_rule_prelude { + Some(AtRulePrelude::LayerPrelude(LayerPrelude::NameList(name_list))) + if name_list.name_list.len() > 1 && at_rule.block.is_some() => + { + self.errors.push(Error::new( + name_list.span, + ErrorKind::Expected("only one name"), + )); + + Some(Box::new(AtRulePrelude::ListOfComponentValues( + list_of_component_values, + ))) + } + None if *normalized_at_rule_name == js_word!("layer") + && at_rule.block.is_none() => + { + self.errors.push(Error::new( + at_rule.span, + ErrorKind::Expected("at least one name"), + )); + + Some(Box::new(AtRulePrelude::ListOfComponentValues( + list_of_component_values, + ))) + } + _ => at_rule_prelude.map(Box::new), + }, + Err(err) => { + if *err.kind() != ErrorKind::Ignore { + self.errors.push(err); + } + + if !list_of_component_values.children.is_empty() { + Some(Box::new(AtRulePrelude::ListOfComponentValues( + list_of_component_values, + ))) + } else { + None + } + } + }; + + Ok(at_rule) + } + + pub(super) fn canonicalize_at_rule_block(&mut self, mut at_rule: AtRule) -> PResult { + let normalized_at_rule_name = match &at_rule.name { + AtRuleName::Ident(Ident { value, .. }) => value.to_ascii_lowercase(), + AtRuleName::DashedIdent(_) => return Ok(at_rule), + }; + + let mut block = match at_rule.block { + Some(simple_block) => simple_block, + _ => { + unreachable!(); + } + }; + + let list_of_component_values = self.create_locv(block.value); + + block.value = match self.parse_according_to_grammar(&list_of_component_values, |parser| { + parser.parse_at_rule_block(&normalized_at_rule_name) + }) { + Ok(block_contents) => block_contents, + Err(err) => { + if *err.kind() != ErrorKind::Ignore { + self.errors.push(err); + } + + list_of_component_values.children + } + }; + + at_rule.block = Some(block); + + Ok(at_rule) + } + + pub(super) fn canonicalize_qualified_rule_prelude( + &mut self, + mut qualified_rule: QualifiedRule, + ) -> PResult { + let list_of_component_values = match qualified_rule.prelude { + QualifiedRulePrelude::ListOfComponentValues(list_of_component_values) => { + list_of_component_values + } + _ => { + unreachable!(); + } + }; + + qualified_rule.prelude = if self.ctx.in_keyframes_at_rule { + QualifiedRulePrelude::ListOfComponentValues(list_of_component_values) + } else if self.ctx.mixed_with_declarations { + match self.parse_according_to_grammar::( + &list_of_component_values, + |parser| parser.parse(), + ) { + Ok(relative_selector_list) => { + QualifiedRulePrelude::RelativeSelectorList(relative_selector_list) + } + Err(err) => { + self.errors.push(err); + + QualifiedRulePrelude::ListOfComponentValues(list_of_component_values) + } + } + } else { + match self + .parse_according_to_grammar::(&list_of_component_values, |parser| { + parser.parse() + }) { + Ok(selector_list) => QualifiedRulePrelude::SelectorList(selector_list), + Err(err) => { + self.errors.push(err); + + QualifiedRulePrelude::ListOfComponentValues(list_of_component_values) + } + } + }; + + Ok(qualified_rule) + } + + pub(super) fn canonicalize_qualified_rule_block( + &mut self, + mut qualified_rule: QualifiedRule, + ) -> PResult { + qualified_rule.block.value = match self.ctx.block_contents_grammar { + BlockContentsGrammar::RuleList if self.ctx.in_keyframes_at_rule => self + .parse_according_to_grammar( + &self.create_locv(qualified_rule.block.value), + |parser| { + parser + .with_ctx(Ctx { + block_contents_grammar: BlockContentsGrammar::DeclarationList, + ..parser.ctx + }) + .parse_as::>() + }, + )? + .into_iter() + .map(ComponentValue::DeclarationOrAtRule) + .collect(), + _ => self + .parse_according_to_grammar( + &self.create_locv(qualified_rule.block.value), + |parser| { + parser + .with_ctx(Ctx { + block_contents_grammar: BlockContentsGrammar::StyleBlock, + ..parser.ctx + }) + .parse_as::>() + }, + )? + .into_iter() + .map(ComponentValue::StyleBlock) + .collect(), + }; + + Ok(qualified_rule) + } + + pub(super) fn canonicalize_function_value( + &mut self, + mut function: Function, + ) -> PResult { + match self.ctx.block_contents_grammar { + BlockContentsGrammar::DeclarationList => {} + _ => { + let function_name = function.name.value.to_ascii_lowercase(); + + let locv = self.create_locv(function.value); + + function.value = match self.parse_according_to_grammar(&locv, |parser| { + parser.parse_function_values(&function_name) + }) { + Ok(values) => values, + Err(err) => { + if *err.kind() != ErrorKind::Ignore { + self.errors.push(err); + } + + locv.children + } + }; + } + } + + Ok(function) + } + + pub(super) fn canonicalize_declaration_value( + &mut self, + mut declaration: Declaration, + ) -> PResult { + let locv = self.create_locv(declaration.value); + + declaration.value = match self.parse_according_to_grammar(&locv, |parser| { + let mut values = vec![]; + + loop { + if is!(parser, EOF) { + break; + } + + values.push(parser.parse_generic_value()?); + } + + Ok(values) + }) { + Ok(values) => values, + Err(err) => { + if *err.kind() != ErrorKind::Ignore { + self.errors.push(err); + } + + locv.children + } + }; + + Ok(declaration) + } + pub(super) fn try_to_parse_legacy_nesting(&mut self) -> Option { let state = self.input.state(); let qualified_rule = self diff --git a/crates/swc_css_parser/src/parser/values_and_units/mod.rs b/crates/swc_css_parser/src/parser/values_and_units/mod.rs index 8fb13b759cb3..6e1c5e324f96 100644 --- a/crates/swc_css_parser/src/parser/values_and_units/mod.rs +++ b/crates/swc_css_parser/src/parser/values_and_units/mod.rs @@ -266,6 +266,7 @@ where Ok(tokens) } + // TODO use `JsWord` pub fn parse_function_values(&mut self, function_name: &str) -> PResult> { let mut values = vec![]; diff --git a/crates/swc_css_parser/tests/recovery/at-rule/layer/empty/output.swc-stderr b/crates/swc_css_parser/tests/recovery/at-rule/layer/empty/output.swc-stderr index c749e494a9ff..0da5bc1cef6c 100644 --- a/crates/swc_css_parser/tests/recovery/at-rule/layer/empty/output.swc-stderr +++ b/crates/swc_css_parser/tests/recovery/at-rule/layer/empty/output.swc-stderr @@ -2,5 +2,5 @@ x Expected at least one name ,-[$DIR/tests/recovery/at-rule/layer/empty/input.css:1:1] 1 | @layer ; - : ^^^^^^ + : ^^^^^^^^ `----