diff --git a/crates/swc_css_parser/src/lexer/mod.rs b/crates/swc_css_parser/src/lexer/mod.rs index 0be1d3ec369..104aaaf7884 100644 --- a/crates/swc_css_parser/src/lexer/mod.rs +++ b/crates/swc_css_parser/src/lexer/mod.rs @@ -1,4 +1,4 @@ -use std::{cell::RefCell, char::REPLACEMENT_CHARACTER, mem::take, rc::Rc}; +use std::{cell::RefCell, char::REPLACEMENT_CHARACTER, rc::Rc}; use swc_atoms::{js_word, JsWord}; use swc_common::{input::Input, BytePos, Span}; @@ -11,7 +11,7 @@ use crate::{ pub(crate) type LexResult = Result; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Lexer where I: Input, @@ -27,7 +27,7 @@ where raw_buf: Rc>, sub_buf: Rc>, sub_raw_buf: Rc>, - errors: Vec, + errors: Rc>>, } impl Lexer @@ -48,7 +48,7 @@ where raw_buf: Rc::new(RefCell::new(String::with_capacity(256))), sub_buf: Rc::new(RefCell::new(String::with_capacity(32))), sub_raw_buf: Rc::new(RefCell::new(String::with_capacity(32))), - errors: vec![], + errors: Default::default(), } } @@ -155,7 +155,7 @@ where } fn take_errors(&mut self) -> Vec { - take(&mut self.errors) + self.errors.take() } fn skip_ws(&mut self) -> Option { @@ -220,7 +220,7 @@ where #[cold] fn emit_error(&mut self, kind: ErrorKind) { - self.errors.push(Error::new( + self.errors.borrow_mut().push(Error::new( Span::new(self.cur_pos, self.input.cur_pos(), Default::default()), kind, )); @@ -510,6 +510,7 @@ where let span = Span::new(self.start_pos, end, Default::default()); self.errors + .borrow_mut() .push(Error::new(span, ErrorKind::UnterminatedBlockComment)); return; diff --git a/crates/swc_css_parser/src/parser/input.rs b/crates/swc_css_parser/src/parser/input.rs index 68173aa6c9a..8b8c47702dc 100644 --- a/crates/swc_css_parser/src/parser/input.rs +++ b/crates/swc_css_parser/src/parser/input.rs @@ -7,7 +7,7 @@ use swc_css_ast::{ComponentValue, ListOfComponentValues, Token, TokenAndSpan}; use super::PResult; use crate::error::{Error, ErrorKind}; -pub trait ParserInput: Iterator { +pub trait ParserInput: Clone + Iterator { type State: Debug; fn start_pos(&mut self) -> BytePos; @@ -22,7 +22,7 @@ pub trait ParserInput: Iterator { fn skip_ws(&mut self) -> Option; } -#[derive(Debug)] +#[derive(Debug, Clone)] pub(super) struct Buffer where I: ParserInput, @@ -184,7 +184,7 @@ pub struct TokensState { idx: usize, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct TokensInput<'a> { tokens: &'a Tokens, idx: usize, @@ -272,7 +272,7 @@ pub struct ListOfComponentValuesState { balance_stack: Vec, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ListOfComponentValuesInput<'a> { list: &'a ListOfComponentValues, idx: Vec, diff --git a/crates/swc_css_parser/src/parser/mod.rs b/crates/swc_css_parser/src/parser/mod.rs index edef347ecf1..5c5a44173ad 100644 --- a/crates/swc_css_parser/src/parser/mod.rs +++ b/crates/swc_css_parser/src/parser/mod.rs @@ -78,7 +78,7 @@ struct Ctx { is_trying_legacy_nesting: bool, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Parser where I: ParserInput, diff --git a/crates/swc_css_parser/src/parser/util.rs b/crates/swc_css_parser/src/parser/util.rs index 26677edcad7..2dc672c8f2f 100644 --- a/crates/swc_css_parser/src/parser/util.rs +++ b/crates/swc_css_parser/src/parser/util.rs @@ -31,6 +31,21 @@ where self.parse() } + pub(super) fn try_parse( + &mut self, + op: impl FnOnce(&mut Parser) -> PResult, + ) -> Option { + let mut parser = self.clone(); + + match op(&mut parser) { + Ok(v) => { + *self = parser; + Some(v) + } + Err(..) => None, + } + } + pub(super) fn create_locv(&self, children: Vec) -> ListOfComponentValues { let span = match (children.first(), children.last()) { (Some(first), Some(last)) => { 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 9a59daf050a..484e3267e67 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 @@ -1273,12 +1273,29 @@ where break; } - let ctx = Ctx { - block_contents_grammar: BlockContentsGrammar::DeclarationValue, - ..self.ctx + let value = match self.try_parse(|p| { + let ctx = Ctx { + block_contents_grammar: BlockContentsGrammar::DeclarationValue, + ..p.ctx + }; + + p.with_ctx(ctx).parse_as::() + }) { + Some(v) => v, + None => { + if is_one_of!(self, ";", ":") { + let tok = self.input.bump().unwrap(); + ComponentValue::PreservedToken(tok) + } else { + return Err(Error::new( + self.input.cur_span(), + ErrorKind::Expected("Declaration value"), + )); + } + } }; - values.push(self.with_ctx(ctx).parse_as::()?); + values.push(value); }, }; diff --git a/crates/swc_css_parser/tests/fixture/only/1/input.css b/crates/swc_css_parser/tests/fixture/only/1/input.css new file mode 100644 index 00000000000..da42033d867 --- /dev/null +++ b/crates/swc_css_parser/tests/fixture/only/1/input.css @@ -0,0 +1,5 @@ +@media screen and(-webkit-min-device-pixel-ratio:0) { + .gradientWrapper { + -chrome-: only(; z-index: 10;); + } +} \ No newline at end of file diff --git a/crates/swc_css_parser/tests/fixture/only/1/output.json b/crates/swc_css_parser/tests/fixture/only/1/output.json new file mode 100644 index 00000000000..c6aad501f19 --- /dev/null +++ b/crates/swc_css_parser/tests/fixture/only/1/output.json @@ -0,0 +1,301 @@ +{ + "type": "Stylesheet", + "span": { + "start": 1, + "end": 123, + "ctxt": 0 + }, + "rules": [ + { + "type": "AtRule", + "span": { + "start": 1, + "end": 123, + "ctxt": 0 + }, + "name": { + "type": "Ident", + "span": { + "start": 2, + "end": 7, + "ctxt": 0 + }, + "value": "media", + "raw": "media" + }, + "prelude": { + "type": "MediaQueryList", + "span": { + "start": 15, + "end": 52, + "ctxt": 0 + }, + "queries": [ + { + "type": "MediaQuery", + "span": { + "start": 15, + "end": 52, + "ctxt": 0 + }, + "modifier": null, + "mediaType": null, + "keyword": null, + "condition": { + "type": "MediaCondition", + "span": { + "start": 15, + "end": 52, + "ctxt": 0 + }, + "conditions": [ + { + "type": "Function", + "span": { + "start": 15, + "end": 52, + "ctxt": 0 + }, + "name": { + "type": "Ident", + "span": { + "start": 15, + "end": 18, + "ctxt": 0 + }, + "value": "and", + "raw": "and" + }, + "value": [ + { + "type": "PreservedToken", + "span": { + "start": 19, + "end": 49, + "ctxt": 0 + }, + "token": { + "Ident": { + "value": "-webkit-min-device-pixel-ratio", + "raw": "-webkit-min-device-pixel-ratio" + } + } + }, + { + "type": "PreservedToken", + "span": { + "start": 49, + "end": 50, + "ctxt": 0 + }, + "token": "Colon" + }, + { + "type": "PreservedToken", + "span": { + "start": 50, + "end": 51, + "ctxt": 0 + }, + "token": { + "Number": { + "value": 0.0, + "raw": "0", + "type": "integer" + } + } + } + ] + } + ] + } + } + ] + }, + "block": { + "type": "SimpleBlock", + "span": { + "start": 53, + "end": 123, + "ctxt": 0 + }, + "name": { + "type": "PreservedToken", + "span": { + "start": 53, + "end": 54, + "ctxt": 0 + }, + "token": "LBrace" + }, + "value": [ + { + "type": "QualifiedRule", + "span": { + "start": 59, + "end": 121, + "ctxt": 0 + }, + "prelude": { + "type": "SelectorList", + "span": { + "start": 59, + "end": 75, + "ctxt": 0 + }, + "children": [ + { + "type": "ComplexSelector", + "span": { + "start": 59, + "end": 75, + "ctxt": 0 + }, + "children": [ + { + "type": "CompoundSelector", + "span": { + "start": 59, + "end": 75, + "ctxt": 0 + }, + "nestingSelector": null, + "typeSelector": null, + "subclassSelectors": [ + { + "type": "ClassSelector", + "span": { + "start": 59, + "end": 75, + "ctxt": 0 + }, + "text": { + "type": "Ident", + "span": { + "start": 60, + "end": 75, + "ctxt": 0 + }, + "value": "gradientWrapper", + "raw": "gradientWrapper" + } + } + ] + } + ] + } + ] + }, + "block": { + "type": "SimpleBlock", + "span": { + "start": 76, + "end": 121, + "ctxt": 0 + }, + "name": { + "type": "PreservedToken", + "span": { + "start": 76, + "end": 77, + "ctxt": 0 + }, + "token": "LBrace" + }, + "value": [ + { + "type": "Declaration", + "span": { + "start": 84, + "end": 114, + "ctxt": 0 + }, + "name": { + "type": "Ident", + "span": { + "start": 84, + "end": 92, + "ctxt": 0 + }, + "value": "-chrome-", + "raw": "-chrome-" + }, + "value": [ + { + "type": "Function", + "span": { + "start": 94, + "end": 114, + "ctxt": 0 + }, + "name": { + "type": "Ident", + "span": { + "start": 94, + "end": 98, + "ctxt": 0 + }, + "value": "only", + "raw": "only" + }, + "value": [ + { + "type": "Delimiter", + "span": { + "start": 99, + "end": 100, + "ctxt": 0 + }, + "value": ";" + }, + { + "type": "Ident", + "span": { + "start": 101, + "end": 108, + "ctxt": 0 + }, + "value": "z-index", + "raw": "z-index" + }, + { + "type": "PreservedToken", + "span": { + "start": 108, + "end": 109, + "ctxt": 0 + }, + "token": "Colon" + }, + { + "type": "Integer", + "span": { + "start": 110, + "end": 112, + "ctxt": 0 + }, + "value": 10, + "raw": "10" + }, + { + "type": "Delimiter", + "span": { + "start": 112, + "end": 113, + "ctxt": 0 + }, + "value": ";" + } + ] + } + ], + "important": null + } + ] + } + } + ] + } + } + ] +} diff --git a/crates/swc_css_parser/tests/fixture/only/1/span.rust-debug b/crates/swc_css_parser/tests/fixture/only/1/span.rust-debug new file mode 100644 index 00000000000..0d5193eab57 --- /dev/null +++ b/crates/swc_css_parser/tests/fixture/only/1/span.rust-debug @@ -0,0 +1,310 @@ + + x Stylesheet + ,-[$DIR/tests/fixture/only/1/input.css:1:1] + 1 | ,-> @media screen and(-webkit-min-device-pixel-ratio:0) { + 2 | | .gradientWrapper { + 3 | | -chrome-: only(; z-index: 10;); + 4 | | } + 5 | `-> } + `---- + + x Rule + ,-[$DIR/tests/fixture/only/1/input.css:1:1] + 1 | ,-> @media screen and(-webkit-min-device-pixel-ratio:0) { + 2 | | .gradientWrapper { + 3 | | -chrome-: only(; z-index: 10;); + 4 | | } + 5 | `-> } + `---- + + x AtRule + ,-[$DIR/tests/fixture/only/1/input.css:1:1] + 1 | ,-> @media screen and(-webkit-min-device-pixel-ratio:0) { + 2 | | .gradientWrapper { + 3 | | -chrome-: only(; z-index: 10;); + 4 | | } + 5 | `-> } + `---- + + x AtRuleName + ,-[$DIR/tests/fixture/only/1/input.css:1:1] + 1 | @media screen and(-webkit-min-device-pixel-ratio:0) { + : ^^^^^ + `---- + + x Ident + ,-[$DIR/tests/fixture/only/1/input.css:1:1] + 1 | @media screen and(-webkit-min-device-pixel-ratio:0) { + : ^^^^^ + `---- + + x MediaQueryList + ,-[$DIR/tests/fixture/only/1/input.css:1:1] + 1 | @media screen and(-webkit-min-device-pixel-ratio:0) { + : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + `---- + + x MediaQuery + ,-[$DIR/tests/fixture/only/1/input.css:1:1] + 1 | @media screen and(-webkit-min-device-pixel-ratio:0) { + : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + `---- + + x MediaCondition + ,-[$DIR/tests/fixture/only/1/input.css:1:1] + 1 | @media screen and(-webkit-min-device-pixel-ratio:0) { + : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + `---- + + x MediaConditionAllType + ,-[$DIR/tests/fixture/only/1/input.css:1:1] + 1 | @media screen and(-webkit-min-device-pixel-ratio:0) { + : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + `---- + + x MediaInParens + ,-[$DIR/tests/fixture/only/1/input.css:1:1] + 1 | @media screen and(-webkit-min-device-pixel-ratio:0) { + : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + `---- + + x Function + ,-[$DIR/tests/fixture/only/1/input.css:1:1] + 1 | @media screen and(-webkit-min-device-pixel-ratio:0) { + : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + `---- + + x Ident + ,-[$DIR/tests/fixture/only/1/input.css:1:1] + 1 | @media screen and(-webkit-min-device-pixel-ratio:0) { + : ^^^ + `---- + + x ComponentValue + ,-[$DIR/tests/fixture/only/1/input.css:1:1] + 1 | @media screen and(-webkit-min-device-pixel-ratio:0) { + : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + `---- + + x Ident { value: Atom('-webkit-min-device-pixel-ratio' type=dynamic), raw: Atom('-webkit-min-device-pixel-ratio' type=dynamic) } + ,-[$DIR/tests/fixture/only/1/input.css:1:1] + 1 | @media screen and(-webkit-min-device-pixel-ratio:0) { + : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + `---- + + x ComponentValue + ,-[$DIR/tests/fixture/only/1/input.css:1:1] + 1 | @media screen and(-webkit-min-device-pixel-ratio:0) { + : ^ + `---- + + x Colon + ,-[$DIR/tests/fixture/only/1/input.css:1:1] + 1 | @media screen and(-webkit-min-device-pixel-ratio:0) { + : ^ + `---- + + x ComponentValue + ,-[$DIR/tests/fixture/only/1/input.css:1:1] + 1 | @media screen and(-webkit-min-device-pixel-ratio:0) { + : ^ + `---- + + x Number { value: 0.0, raw: Atom('0' type=inline), type_flag: Integer } + ,-[$DIR/tests/fixture/only/1/input.css:1:1] + 1 | @media screen and(-webkit-min-device-pixel-ratio:0) { + : ^ + `---- + + x SimpleBlock + ,-[$DIR/tests/fixture/only/1/input.css:1:1] + 1 | ,-> @media screen and(-webkit-min-device-pixel-ratio:0) { + 2 | | .gradientWrapper { + 3 | | -chrome-: only(; z-index: 10;); + 4 | | } + 5 | `-> } + `---- + + x LBrace + ,-[$DIR/tests/fixture/only/1/input.css:1:1] + 1 | @media screen and(-webkit-min-device-pixel-ratio:0) { + : ^ + `---- + + x ComponentValue + ,-[$DIR/tests/fixture/only/1/input.css:2:5] + 2 | ,-> .gradientWrapper { + 3 | | -chrome-: only(; z-index: 10;); + 4 | `-> } + `---- + + x Rule + ,-[$DIR/tests/fixture/only/1/input.css:2:5] + 2 | ,-> .gradientWrapper { + 3 | | -chrome-: only(; z-index: 10;); + 4 | `-> } + `---- + + x QualifiedRule + ,-[$DIR/tests/fixture/only/1/input.css:2:5] + 2 | ,-> .gradientWrapper { + 3 | | -chrome-: only(; z-index: 10;); + 4 | `-> } + `---- + + x SelectorList + ,-[$DIR/tests/fixture/only/1/input.css:2:5] + 2 | .gradientWrapper { + : ^^^^^^^^^^^^^^^^ + `---- + + x ComplexSelector + ,-[$DIR/tests/fixture/only/1/input.css:2:5] + 2 | .gradientWrapper { + : ^^^^^^^^^^^^^^^^ + `---- + + x CompoundSelector + ,-[$DIR/tests/fixture/only/1/input.css:2:5] + 2 | .gradientWrapper { + : ^^^^^^^^^^^^^^^^ + `---- + + x SubclassSelector + ,-[$DIR/tests/fixture/only/1/input.css:2:5] + 2 | .gradientWrapper { + : ^^^^^^^^^^^^^^^^ + `---- + + x ClassSelector + ,-[$DIR/tests/fixture/only/1/input.css:2:5] + 2 | .gradientWrapper { + : ^^^^^^^^^^^^^^^^ + `---- + + x Ident + ,-[$DIR/tests/fixture/only/1/input.css:2:5] + 2 | .gradientWrapper { + : ^^^^^^^^^^^^^^^ + `---- + + x SimpleBlock + ,-[$DIR/tests/fixture/only/1/input.css:2:5] + 2 | ,-> .gradientWrapper { + 3 | | -chrome-: only(; z-index: 10;); + 4 | `-> } + `---- + + x LBrace + ,-[$DIR/tests/fixture/only/1/input.css:2:5] + 2 | .gradientWrapper { + : ^ + `---- + + x ComponentValue + ,-[$DIR/tests/fixture/only/1/input.css:3:7] + 3 | -chrome-: only(; z-index: 10;); + : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + `---- + + x StyleBlock + ,-[$DIR/tests/fixture/only/1/input.css:3:7] + 3 | -chrome-: only(; z-index: 10;); + : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + `---- + + x Declaration + ,-[$DIR/tests/fixture/only/1/input.css:3:7] + 3 | -chrome-: only(; z-index: 10;); + : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + `---- + + x DeclarationName + ,-[$DIR/tests/fixture/only/1/input.css:3:7] + 3 | -chrome-: only(; z-index: 10;); + : ^^^^^^^^ + `---- + + x Ident + ,-[$DIR/tests/fixture/only/1/input.css:3:7] + 3 | -chrome-: only(; z-index: 10;); + : ^^^^^^^^ + `---- + + x ComponentValue + ,-[$DIR/tests/fixture/only/1/input.css:3:7] + 3 | -chrome-: only(; z-index: 10;); + : ^^^^^^^^^^^^^^^^^^^^ + `---- + + x Function + ,-[$DIR/tests/fixture/only/1/input.css:3:7] + 3 | -chrome-: only(; z-index: 10;); + : ^^^^^^^^^^^^^^^^^^^^ + `---- + + x Ident + ,-[$DIR/tests/fixture/only/1/input.css:3:7] + 3 | -chrome-: only(; z-index: 10;); + : ^^^^ + `---- + + x ComponentValue + ,-[$DIR/tests/fixture/only/1/input.css:3:7] + 3 | -chrome-: only(; z-index: 10;); + : ^ + `---- + + x Delimiter + ,-[$DIR/tests/fixture/only/1/input.css:3:7] + 3 | -chrome-: only(; z-index: 10;); + : ^ + `---- + + x ComponentValue + ,-[$DIR/tests/fixture/only/1/input.css:3:7] + 3 | -chrome-: only(; z-index: 10;); + : ^^^^^^^ + `---- + + x Ident + ,-[$DIR/tests/fixture/only/1/input.css:3:7] + 3 | -chrome-: only(; z-index: 10;); + : ^^^^^^^ + `---- + + x ComponentValue + ,-[$DIR/tests/fixture/only/1/input.css:3:7] + 3 | -chrome-: only(; z-index: 10;); + : ^ + `---- + + x Colon + ,-[$DIR/tests/fixture/only/1/input.css:3:7] + 3 | -chrome-: only(; z-index: 10;); + : ^ + `---- + + x ComponentValue + ,-[$DIR/tests/fixture/only/1/input.css:3:7] + 3 | -chrome-: only(; z-index: 10;); + : ^^ + `---- + + x Integer + ,-[$DIR/tests/fixture/only/1/input.css:3:7] + 3 | -chrome-: only(; z-index: 10;); + : ^^ + `---- + + x ComponentValue + ,-[$DIR/tests/fixture/only/1/input.css:3:7] + 3 | -chrome-: only(; z-index: 10;); + : ^ + `---- + + x Delimiter + ,-[$DIR/tests/fixture/only/1/input.css:3:7] + 3 | -chrome-: only(; z-index: 10;); + : ^ + `---- diff --git a/crates/swc_css_parser/tests/recovery/at-rule/document/output.swc-stderr b/crates/swc_css_parser/tests/recovery/at-rule/document/output.swc-stderr index c46668b312a..cc593509091 100644 --- a/crates/swc_css_parser/tests/recovery/at-rule/document/output.swc-stderr +++ b/crates/swc_css_parser/tests/recovery/at-rule/document/output.swc-stderr @@ -1,4 +1,10 @@ + x Expected Declaration value + ,-[$DIR/tests/recovery/at-rule/document/input.css:1:1] + 1 | @document domain(http://www.example.com/) {} + : ^ + `---- + x Expected Declaration value ,-[$DIR/tests/recovery/at-rule/document/input.css:1:1] 1 | @document domain(http://www.example.com/) {}