Skip to content

Commit

Permalink
fix(css/parser): nested rule parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-akait committed Dec 1, 2022
1 parent 9287709 commit c50ef5b
Show file tree
Hide file tree
Showing 8 changed files with 557 additions and 31 deletions.
28 changes: 5 additions & 23 deletions crates/swc_css_parser/src/parser/at_rules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,6 @@ where
}
_ => {
let ctx = Ctx {
is_top_level: false,
in_container_at_rule: true,
..self.ctx
};
Expand Down Expand Up @@ -493,11 +492,7 @@ where
style_blocks
}
_ => {
let ctx = Ctx {
is_top_level: false,
..self.ctx
};
let rule_list = self.with_ctx(ctx).parse_as::<Vec<Rule>>()?;
let rule_list = self.parse_as::<Vec<Rule>>()?;
let rule_list: Vec<ComponentValue> =
rule_list.into_iter().map(ComponentValue::Rule).collect();

Expand Down Expand Up @@ -565,7 +560,6 @@ where
| js_word!("-ms-keyframes") => {
let ctx = Ctx {
block_contents_grammar: BlockContentsGrammar::RuleList,
is_top_level: false,
in_keyframes_at_rule: true,
..self.ctx
};
Expand Down Expand Up @@ -636,11 +630,7 @@ where
rule_list
}
js_word!("layer") => {
let ctx = Ctx {
is_top_level: false,
..self.ctx
};
let rule_list = self.with_ctx(ctx).parse_as::<Vec<Rule>>()?;
let rule_list = self.parse_as::<Vec<Rule>>()?;
let rule_list: Vec<ComponentValue> =
rule_list.into_iter().map(ComponentValue::Rule).collect();

Expand All @@ -657,11 +647,7 @@ where
style_blocks
}
_ => {
let ctx = Ctx {
is_top_level: false,
..self.ctx
};
let rule_list = self.with_ctx(ctx).parse_as::<Vec<Rule>>()?;
let rule_list = self.parse_as::<Vec<Rule>>()?;
let rule_list: Vec<ComponentValue> =
rule_list.into_iter().map(ComponentValue::Rule).collect();

Expand Down Expand Up @@ -742,11 +728,7 @@ where
style_blocks
}
_ => {
let ctx = Ctx {
is_top_level: false,
..self.ctx
};
let rule_list = self.with_ctx(ctx).parse_as::<Vec<Rule>>()?;
let rule_list = self.parse_as::<Vec<Rule>>()?;
let rule_list: Vec<ComponentValue> =
rule_list.into_iter().map(ComponentValue::Rule).collect();

Expand Down Expand Up @@ -934,7 +916,7 @@ where

return Err(Error::new(
span,
ErrorKind::Expected("ident or percentage token"),
ErrorKind::Expected("'from', 'to' or percentage"),
));
}
}
Expand Down
22 changes: 18 additions & 4 deletions crates/swc_css_parser/src/parser/syntax/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ where
let mut rules = vec![];

// Repeatedly consume the next input token:

// Reset the `is_top_level` value
let ctx = Ctx {
is_top_level: false,
..self.ctx
};
loop {
// <EOF-token>
// Return the list of rules.
Expand All @@ -70,21 +76,28 @@ where
// Otherwise, reconsume the current input token. Consume a qualified rule. If
// anything is returned, append it to the list of rules.
else {
rules.push(Rule::QualifiedRule(self.parse()?));
let qualified_rule = self.with_ctx(ctx).parse_as::<Box<QualifiedRule>>()?;

rules.push(Rule::QualifiedRule(qualified_rule));
}
}
// <at-keyword-token>
// Reconsume the current input token. Consume an at-rule, and append the returned
// value to the list of rules.
tok!("@") => {
rules.push(Rule::AtRule(self.parse()?));
let at_rule = self.with_ctx(ctx).parse_as::<Box<AtRule>>()?;

rules.push(Rule::AtRule(at_rule));
}
// anything else
// Reconsume the current input token. Consume a qualified rule. If anything is
// returned, append it to the list of rules.
//
// For better recovery we parse broken code into the list of component values and
// append it to the list of rules.
_ => {
let state = self.input.state();
let qualified_rule = self.parse();
let qualified_rule = self.with_ctx(ctx).parse_as::<Box<QualifiedRule>>();

match qualified_rule {
Ok(i) => rules.push(Rule::QualifiedRule(i)),
Expand All @@ -99,7 +112,8 @@ where
};

while !is_one_of!(self, EOF) {
let component_value = self.parse_as::<ComponentValue>()?;
let component_value =
self.with_ctx(ctx).parse_as::<ComponentValue>()?;

list_of_component_values.children.push(component_value);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

x Expected ident or percentage token
x Expected 'from', 'to' or percentage
,-[$DIR/tests/recovery/at-rule/keyframes/keyframe-broke-and-normal/input.css:1:1]
1 | @keyframes foo {
2 | 10 {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

x Expected ident or percentage token
x Expected 'from', 'to' or percentage
,-[$DIR/tests/recovery/at-rule/keyframes/keyframe-number/input.css:1:1]
1 | @keyframes foo {
2 | 10 {
Expand Down
6 changes: 6 additions & 0 deletions crates/swc_css_parser/tests/recovery/cdo-and-cdc/input.css
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,9 @@ a {
color: red;
}
}

@keyframes box {
<!-- -->
50% { left: 0; }
90% { left: 300px; }
}
226 changes: 225 additions & 1 deletion crates/swc_css_parser/tests/recovery/cdo-and-cdc/output.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"type": "Stylesheet",
"span": {
"start": 1,
"end": 227,
"end": 306,
"ctxt": 0
},
"rules": [
Expand Down Expand Up @@ -923,6 +923,230 @@
}
]
}
},
{
"type": "AtRule",
"span": {
"start": 228,
"end": 305,
"ctxt": 0
},
"name": {
"type": "Ident",
"span": {
"start": 229,
"end": 238,
"ctxt": 0
},
"value": "keyframes",
"raw": "keyframes"
},
"prelude": {
"type": "CustomIdent",
"span": {
"start": 239,
"end": 242,
"ctxt": 0
},
"value": "box",
"raw": "box"
},
"block": {
"type": "SimpleBlock",
"span": {
"start": 243,
"end": 305,
"ctxt": 0
},
"name": {
"type": "PreservedToken",
"span": {
"start": 243,
"end": 244,
"ctxt": 0
},
"token": "LBrace"
},
"value": [
{
"type": "ListOfComponentValues",
"span": {
"start": 249,
"end": 266,
"ctxt": 0
},
"children": [
{
"type": "PreservedToken",
"span": {
"start": 249,
"end": 253,
"ctxt": 0
},
"token": "CDO"
},
{
"type": "PreservedToken",
"span": {
"start": 253,
"end": 254,
"ctxt": 0
},
"token": {
"WhiteSpace": {
"value": " "
}
}
},
{
"type": "PreservedToken",
"span": {
"start": 254,
"end": 257,
"ctxt": 0
},
"token": "CDC"
},
{
"type": "PreservedToken",
"span": {
"start": 257,
"end": 262,
"ctxt": 0
},
"token": {
"WhiteSpace": {
"value": "\n "
}
}
},
{
"type": "PreservedToken",
"span": {
"start": 262,
"end": 265,
"ctxt": 0
},
"token": {
"Percentage": {
"value": 50.0,
"raw": "50"
}
}
},
{
"type": "PreservedToken",
"span": {
"start": 265,
"end": 266,
"ctxt": 0
},
"token": {
"WhiteSpace": {
"value": " "
}
}
}
]
},
{
"type": "KeyframeBlock",
"span": {
"start": 283,
"end": 303,
"ctxt": 0
},
"prelude": [
{
"type": "Percentage",
"span": {
"start": 283,
"end": 286,
"ctxt": 0
},
"value": {
"type": "Number",
"span": {
"start": 283,
"end": 285,
"ctxt": 0
},
"value": 90.0,
"raw": "90"
}
}
],
"block": {
"type": "SimpleBlock",
"span": {
"start": 287,
"end": 303,
"ctxt": 0
},
"name": {
"type": "PreservedToken",
"span": {
"start": 287,
"end": 288,
"ctxt": 0
},
"token": "LBrace"
},
"value": [
{
"type": "Declaration",
"span": {
"start": 289,
"end": 300,
"ctxt": 0
},
"name": {
"type": "Ident",
"span": {
"start": 289,
"end": 293,
"ctxt": 0
},
"value": "left",
"raw": "left"
},
"value": [
{
"type": "Length",
"span": {
"start": 295,
"end": 300,
"ctxt": 0
},
"value": {
"type": "Number",
"span": {
"start": 295,
"end": 298,
"ctxt": 0
},
"value": 300.0,
"raw": "300"
},
"unit": {
"type": "Ident",
"span": {
"start": 298,
"end": 300,
"ctxt": 0
},
"value": "px",
"raw": "px"
}
}
],
"important": null
}
]
}
}
]
}
}
]
}

0 comments on commit c50ef5b

Please sign in to comment.