feat(css/parser): Support new nesting spec (#6337)

This commit is contained in:
Alexander Akait 2022-11-03 18:08:23 +03:00 committed by GitHub
parent d90b0c600d
commit e0967efa6e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 13232 additions and 2264 deletions

View File

@ -4,7 +4,7 @@ use swc_common::{ast_node, EqIgnoreSpan, Span};
use crate::{ use crate::{
AlphaValue, AtRule, CalcSum, CmykComponent, Color, ComplexSelector, DashedIdent, Delimiter, AlphaValue, AtRule, CalcSum, CmykComponent, Color, ComplexSelector, DashedIdent, Delimiter,
Dimension, Hue, Ident, Integer, KeyframeBlock, LayerName, Number, Percentage, Ratio, Dimension, Hue, Ident, Integer, KeyframeBlock, LayerName, Number, Percentage, Ratio,
SelectorList, Str, TokenAndSpan, UnicodeRange, Url, RelativeSelectorList, SelectorList, Str, TokenAndSpan, UnicodeRange, Url,
}; };
#[ast_node("Stylesheet")] #[ast_node("Stylesheet")]
@ -38,10 +38,12 @@ pub struct QualifiedRule {
#[ast_node] #[ast_node]
#[derive(Eq, Hash, Is, EqIgnoreSpan)] #[derive(Eq, Hash, Is, EqIgnoreSpan)]
pub enum QualifiedRulePrelude { pub enum QualifiedRulePrelude {
#[tag("ListOfComponentValues")]
ListOfComponentValues(ListOfComponentValues),
#[tag("SelectorList")] #[tag("SelectorList")]
SelectorList(SelectorList), SelectorList(SelectorList),
#[tag("RelativeSelectorList")]
RelativeSelectorList(RelativeSelectorList),
#[tag("ListOfComponentValues")]
ListOfComponentValues(ListOfComponentValues),
} }
#[ast_node] #[ast_node]

View File

@ -93,6 +93,10 @@ where
emit!(self, n); emit!(self, n);
formatting_space!(self); formatting_space!(self);
} }
QualifiedRulePrelude::RelativeSelectorList(n) => {
emit!(self, n);
formatting_space!(self);
}
QualifiedRulePrelude::ListOfComponentValues(n) => { QualifiedRulePrelude::ListOfComponentValues(n) => {
emit!( emit!(
&mut *self.with_ctx(Ctx { &mut *self.with_ctx(Ctx {

View File

@ -24,8 +24,7 @@
.foo { .foo {
color: red; color: red;
@media (min-width: 480px) { @media (min-width: 480px) {
& h1, & h1, & h2 {
& h2 {
color: blue; color: blue;
} }
} }

View File

@ -83,8 +83,8 @@ body:first-of-type .selector {}
$property: value;color: red; $property: value;color: red;
} }
.selector { .selector {
color: red; &property: value;color: red;
&property: value;} }
.selector { .selector {
*property: value;color: red; *property: value;color: red;
} }

View File

@ -1 +1 @@
.selector:not(*:root){}@media screen and (min-width:0\0){}.selector:not(*:root){}@supports(-webkit-appearance:none){}.selector{(;property: value;);}.selector{[;property: value;];}@media \\0 screen {}@media all and (-webkit-min-device-pixel-ratio:0)and (min-resolution:.001dpcm){.selector{}}body:empty .selector{}body:last-child .selector,x:-moz-any-link{}@media \0 all{}body:last-child .selector,x:-moz-any-link,x:default{}body:not(:-moz-handler-blocked) .selector{}@media screen and (-moz-images-in-menus:0){}@media screen and (min--moz-device-pixel-ratio:0){}_::-moz-progress-bar,body:last-child .selector{}@media all and (min--moz-device-pixel-ratio:0)and (min-resolution:.001dpcm){}@media all and (-moz-images-in-menus:0)and (min-resolution:.001dpcm){}@media all and (min--moz-device-pixel-ratio:0){@media(min-width:0px){}}@media all and (-moz-images-in-menus:0){@media(min-width:0px){}}@supports(-moz-appearance:meterbar){}_::-moz-range-track,body:last-child .selector{}@supports(-moz-appearance:meterbar)and (display:flex){}@supports(-moz-appearance:meterbar)and (cursor:zoom-in){}@supports(-moz-appearance:meterbar)and (background-attachment:local){}@supports(-moz-appearance:meterbar)and (image-orientation:90deg){}@supports(-moz-appearance:meterbar)and (all:initial){}@supports(-moz-appearance:meterbar)and (list-style-type:japanese-formal){}@media all and (min--moz-device-pixel-ratio:0)and (min-resolution:30dpcm){}@supports(-moz-appearance:meterbar)and (background-blend-mode:difference,normal){}_:-moz-tree-row(hover),.selector{}_::selection,.selector:not([attr*=""]){}@supports(-webkit-appearance:none){}* html .selector{}.unused-class.selector{}html>body .selector{}*:first-child+html .selector{}.selector,x:-ie7{}*+html .selector{}body *.selector{}.selector\ {}html>body .selector{}head~body .selector{}_::selection,.selector:not([attr*=""]){}:root .selector{}body:last-child .selector{}body:nth-of-type(1) .selector{}body:first-of-type .selector{}.selector:not([attr*=""]){}.selector{_property:value}.selector{-property:value}.selector{property:value\9}.selector{property:value\9}.selector{!property: value;color:red}.selector{$property: value;color:red}.selector{color:red;&property: value;}.selector{*property: value;color:red}.selector{)property: value;color:red}.selector{=property: value;color:red}.selector{%property: value;color:red}.selector{+property: value;color:red}.selector{color:red;@property: value;}.selector{,property: value;color:red}.selector{.property: value;color:red}.selector{/property: value;color:red}.selector{`property: value;color:red}.selector{]property: value;color:red}.selector{#property: value;color:red}.selector{~property: value;color:red}.selector{?property: value;color:red}.selector{:property: value;color:red}.selector{|property: value;color:red}.selector{property:value !ie}@media screen\9{}@media \0screen\,screen\9 {}@media \0screen{}@media screen and (min-width:0\0){}_:-ms-input-placeholder,:root .selector{}_:-ms-fullscreen,:root .selector{}@media screen and (-ms-high-contrast:active),(-ms-high-contrast:none){}@media screen{@media(min-width:0px){}}.selector:not(*:root){}@supports(-webkit-appearance:none){}.selector{(;property: value;);}.selector{[;property: value;];}html:first-child .selector{}_:-o-prefocus,body:last-child .selector{}@media all and (-webkit-min-device-pixel-ratio:1e4),not all and (-webkit-min-device-pixel-ratio:0){}@media(min-resolution:.001dpcm){_:-o-prefocus,.selector{}}*|html[xmlns*=""] .selector{}@media all and (-webkit-min-device-pixel-ratio:0)and (min-resolution:.001dpcm){.selector{}}.selector:not(*:root){}@supports(-webkit-appearance:none){}.selector{(;property: value;);}.selector{[;property: value;];}@media screen and (min-width:0\0){}@media screen{@media(min-width:0px){}}html:first-child .selector{}html[xmlns*=""] body:last-child .selector{}html[xmlns*=""]:root .selector{}*|html[xmlns*=""] .selector{}_::-moz-svg-foreign-content,:root .selector{}@media \\0 screen {}a{*color : black;_background:white;font-size:big;$(var)-size: 100%;}a{*b:c}div{color:red}selector{property:value;property:normal-value}div{color:red} .selector:not(*:root){}@media screen and (min-width:0\0){}.selector:not(*:root){}@supports(-webkit-appearance:none){}.selector{(;property: value;);}.selector{[;property: value;];}@media \\0 screen {}@media all and (-webkit-min-device-pixel-ratio:0)and (min-resolution:.001dpcm){.selector{}}body:empty .selector{}body:last-child .selector,x:-moz-any-link{}@media \0 all{}body:last-child .selector,x:-moz-any-link,x:default{}body:not(:-moz-handler-blocked) .selector{}@media screen and (-moz-images-in-menus:0){}@media screen and (min--moz-device-pixel-ratio:0){}_::-moz-progress-bar,body:last-child .selector{}@media all and (min--moz-device-pixel-ratio:0)and (min-resolution:.001dpcm){}@media all and (-moz-images-in-menus:0)and (min-resolution:.001dpcm){}@media all and (min--moz-device-pixel-ratio:0){@media(min-width:0px){}}@media all and (-moz-images-in-menus:0){@media(min-width:0px){}}@supports(-moz-appearance:meterbar){}_::-moz-range-track,body:last-child .selector{}@supports(-moz-appearance:meterbar)and (display:flex){}@supports(-moz-appearance:meterbar)and (cursor:zoom-in){}@supports(-moz-appearance:meterbar)and (background-attachment:local){}@supports(-moz-appearance:meterbar)and (image-orientation:90deg){}@supports(-moz-appearance:meterbar)and (all:initial){}@supports(-moz-appearance:meterbar)and (list-style-type:japanese-formal){}@media all and (min--moz-device-pixel-ratio:0)and (min-resolution:30dpcm){}@supports(-moz-appearance:meterbar)and (background-blend-mode:difference,normal){}_:-moz-tree-row(hover),.selector{}_::selection,.selector:not([attr*=""]){}@supports(-webkit-appearance:none){}* html .selector{}.unused-class.selector{}html>body .selector{}*:first-child+html .selector{}.selector,x:-ie7{}*+html .selector{}body *.selector{}.selector\ {}html>body .selector{}head~body .selector{}_::selection,.selector:not([attr*=""]){}:root .selector{}body:last-child .selector{}body:nth-of-type(1) .selector{}body:first-of-type .selector{}.selector:not([attr*=""]){}.selector{_property:value}.selector{-property:value}.selector{property:value\9}.selector{property:value\9}.selector{!property: value;color:red}.selector{$property: value;color:red}.selector{&property: value;color:red}.selector{*property: value;color:red}.selector{)property: value;color:red}.selector{=property: value;color:red}.selector{%property: value;color:red}.selector{+property: value;color:red}.selector{color:red;@property: value;}.selector{,property: value;color:red}.selector{.property: value;color:red}.selector{/property: value;color:red}.selector{`property: value;color:red}.selector{]property: value;color:red}.selector{#property: value;color:red}.selector{~property: value;color:red}.selector{?property: value;color:red}.selector{:property: value;color:red}.selector{|property: value;color:red}.selector{property:value !ie}@media screen\9{}@media \0screen\,screen\9 {}@media \0screen{}@media screen and (min-width:0\0){}_:-ms-input-placeholder,:root .selector{}_:-ms-fullscreen,:root .selector{}@media screen and (-ms-high-contrast:active),(-ms-high-contrast:none){}@media screen{@media(min-width:0px){}}.selector:not(*:root){}@supports(-webkit-appearance:none){}.selector{(;property: value;);}.selector{[;property: value;];}html:first-child .selector{}_:-o-prefocus,body:last-child .selector{}@media all and (-webkit-min-device-pixel-ratio:1e4),not all and (-webkit-min-device-pixel-ratio:0){}@media(min-resolution:.001dpcm){_:-o-prefocus,.selector{}}*|html[xmlns*=""] .selector{}@media all and (-webkit-min-device-pixel-ratio:0)and (min-resolution:.001dpcm){.selector{}}.selector:not(*:root){}@supports(-webkit-appearance:none){}.selector{(;property: value;);}.selector{[;property: value;];}@media screen and (min-width:0\0){}@media screen{@media(min-width:0px){}}html:first-child .selector{}html[xmlns*=""] body:last-child .selector{}html[xmlns*=""]:root .selector{}*|html[xmlns*=""] .selector{}_::-moz-svg-foreign-content,:root .selector{}@media \\0 screen {}a{*color : black;_background:white;font-size:big;$(var)-size: 100%;}a{*b:c}div{color:red}selector{property:value;property:normal-value}div{color:red}

View File

@ -1,12 +1,7 @@
use std::iter::once; use std::iter::once;
use swc_common::{util::take::Take, DUMMY_SP}; use swc_common::{util::take::Take, DUMMY_SP};
use swc_css_ast::{ use swc_css_ast::*;
AtRule, AtRulePrelude, ComplexSelector, ComplexSelectorChildren, ComponentValue,
CompoundSelector, ForgivingComplexSelector, ForgivingSelectorList, PseudoClassSelector,
PseudoClassSelectorChildren, QualifiedRule, QualifiedRulePrelude, Rule, SelectorList,
SimpleBlock, StyleBlock, SubclassSelector,
};
use swc_css_visit::{VisitMut, VisitMutWith}; use swc_css_visit::{VisitMut, VisitMutWith};
pub fn nesting() -> impl VisitMut { pub fn nesting() -> impl VisitMut {
@ -16,48 +11,6 @@ pub fn nesting() -> impl VisitMut {
struct NestingHandler {} struct NestingHandler {}
impl NestingHandler { impl NestingHandler {
fn append_compound(
&mut self,
prelude: &SelectorList,
to: &mut ComplexSelector,
base: &ComplexSelector,
c: &CompoundSelector,
) {
if c.nesting_selector.is_some() {
let len = base.children.len();
to.children
.extend(
base.children
.iter()
.cloned()
.enumerate()
.map(|(idx, mut children)| {
if idx == len - 1 {
if let ComplexSelectorChildren::CompoundSelector(compound) =
&mut children
{
if c.type_selector.is_some() {
compound.type_selector = c.type_selector.clone();
}
let mut subclass = c.subclass_selectors.clone();
self.process_subclass_selectors(prelude, &mut subclass);
compound.subclass_selectors.extend(subclass);
}
}
children
}),
);
} else {
to.children
.push(ComplexSelectorChildren::CompoundSelector(c.clone()));
}
}
fn process_subclass_selectors( fn process_subclass_selectors(
&mut self, &mut self,
prelude: &SelectorList, prelude: &SelectorList,
@ -107,14 +60,11 @@ impl NestingHandler {
) { ) {
let mut new_selectors = vec![]; let mut new_selectors = vec![];
//
'complex: for complex in selectors.take() { 'complex: for complex in selectors.take() {
for compound in &complex.children { for compound in &complex.children {
match compound { match compound {
ComplexSelectorChildren::CompoundSelector(compound) => { ComplexSelectorChildren::CompoundSelector(compound) => {
if compound.nesting_selector.is_some() { if compound.nesting_selector.is_some() {
//
for prelude_children in &prelude.children { for prelude_children in &prelude.children {
let mut new = ComplexSelector { let mut new = ComplexSelector {
span: Default::default(), span: Default::default(),
@ -150,14 +100,128 @@ impl NestingHandler {
*selectors = new_selectors; *selectors = new_selectors;
} }
fn append_compound(
&mut self,
prelude: &SelectorList,
to: &mut ComplexSelector,
base: &ComplexSelector,
c: &CompoundSelector,
) {
if c.nesting_selector.is_some() {
let len = base.children.len();
to.children
.extend(
base.children
.iter()
.cloned()
.enumerate()
.map(|(idx, mut children)| {
if idx == len - 1 {
if let ComplexSelectorChildren::CompoundSelector(compound) =
&mut children
{
if c.type_selector.is_some() {
compound.type_selector = c.type_selector.clone();
}
let mut subclass = c.subclass_selectors.clone();
self.process_subclass_selectors(prelude, &mut subclass);
compound.subclass_selectors.extend(subclass);
}
}
children
}),
);
} else {
to.children
.push(ComplexSelectorChildren::CompoundSelector(c.clone()));
}
}
fn relative_selector_list_to_selector_list(
&mut self,
base: &SelectorList,
relative_selector_list: &RelativeSelectorList,
) -> SelectorList {
let mut children = vec![];
for base_complex in &base.children {
for relative_selector in &relative_selector_list.children {
let mut complex_selector = ComplexSelector {
span: relative_selector.span,
children: Default::default(),
};
let is_non_relative = relative_selector.combinator.is_none()
&& relative_selector.selector.children.iter().any(|s| match s {
ComplexSelectorChildren::CompoundSelector(s) => {
s.nesting_selector.is_some()
}
_ => false,
});
if let Some(combinator) = &relative_selector.combinator {
complex_selector
.children
.extend(base_complex.children.clone());
complex_selector
.children
.push(ComplexSelectorChildren::Combinator(combinator.clone()))
} else if !is_non_relative {
complex_selector
.children
.extend(base_complex.children.clone());
complex_selector
.children
.push(ComplexSelectorChildren::Combinator(Combinator {
span: DUMMY_SP,
value: CombinatorValue::Descendant,
}))
}
for relative_complex_selector_children in &relative_selector.selector.children {
match relative_complex_selector_children {
ComplexSelectorChildren::CompoundSelector(compound) => {
self.append_compound(
base,
&mut complex_selector,
base_complex,
compound,
);
}
ComplexSelectorChildren::Combinator(combinator) => {
complex_selector
.children
.push(ComplexSelectorChildren::Combinator(combinator.clone()));
}
}
}
children.push(complex_selector);
}
}
SelectorList {
span: relative_selector_list.span,
children,
}
}
/// Prepend current selector /// Prepend current selector
fn process_prelude(&mut self, prelude: &QualifiedRulePrelude, to: &mut QualifiedRulePrelude) { fn process_prelude(&mut self, base: &QualifiedRulePrelude, to: &mut QualifiedRulePrelude) {
if let ( if let (
QualifiedRulePrelude::SelectorList(prelude), QualifiedRulePrelude::SelectorList(base),
QualifiedRulePrelude::SelectorList(selectors), QualifiedRulePrelude::RelativeSelectorList(relative_selector_list),
) = (prelude, to) ) = (base, &to)
{ {
self.process_complex_selectors(prelude, &mut selectors.children); let selector_list =
self.relative_selector_list_to_selector_list(base, relative_selector_list);
*to = QualifiedRulePrelude::SelectorList(selector_list);
} }
} }
@ -167,14 +231,21 @@ impl NestingHandler {
for value in rule.block.value.take() { for value in rule.block.value.take() {
match value { match value {
ComponentValue::StyleBlock(StyleBlock::QualifiedRule(mut q)) => { ComponentValue::StyleBlock(StyleBlock::QualifiedRule(mut nested)) => {
self.process_prelude(&rule.prelude, &mut q.prelude); self.process_prelude(&rule.prelude, &mut nested.prelude);
nested_rules.push(Rule::QualifiedRule(nested));
nested_rules.push(Rule::QualifiedRule(q));
continue; continue;
} }
ComponentValue::StyleBlock(StyleBlock::AtRule(ref at_rule)) => { ComponentValue::StyleBlock(StyleBlock::AtRule(ref at_rule)) => {
if let Some(AtRulePrelude::MediaPrelude(..)) = at_rule.prelude.as_deref() { if let Some(
AtRulePrelude::MediaPrelude(..)
| AtRulePrelude::SupportsPrelude(..)
| AtRulePrelude::ContainerPrelude(..)
| AtRulePrelude::DocumentPrelude(..),
) = at_rule.prelude.as_deref()
{
if let Some(block) = &at_rule.block { if let Some(block) = &at_rule.block {
let mut decls_of_media = vec![]; let mut decls_of_media = vec![];
let mut nested_of_media = vec![]; let mut nested_of_media = vec![];
@ -183,6 +254,7 @@ impl NestingHandler {
match n { match n {
ComponentValue::StyleBlock(StyleBlock::QualifiedRule(n)) => { ComponentValue::StyleBlock(StyleBlock::QualifiedRule(n)) => {
let mut q = n.clone(); let mut q = n.clone();
self.process_prelude(&rule.prelude, &mut q.prelude); self.process_prelude(&rule.prelude, &mut q.prelude);
let rules = self.extract_nested_rules(&mut q); let rules = self.extract_nested_rules(&mut q);
@ -209,6 +281,7 @@ impl NestingHandler {
..block.clone() ..block.clone()
}, },
}); });
nested_of_media.insert( nested_of_media.insert(
0, 0,
ComponentValue::StyleBlock(StyleBlock::QualifiedRule(rule)), ComponentValue::StyleBlock(StyleBlock::QualifiedRule(rule)),
@ -222,6 +295,7 @@ impl NestingHandler {
}), }),
..*at_rule.clone() ..*at_rule.clone()
}))); })));
continue; continue;
} }
} }
@ -231,6 +305,7 @@ impl NestingHandler {
block_values.push(value); block_values.push(value);
} }
rule.block.value = block_values; rule.block.value = block_values;
nested_rules nested_rules
@ -239,13 +314,15 @@ impl NestingHandler {
impl VisitMut for NestingHandler { impl VisitMut for NestingHandler {
fn visit_mut_rules(&mut self, n: &mut Vec<Rule>) { fn visit_mut_rules(&mut self, n: &mut Vec<Rule>) {
n.visit_mut_children_with(self);
let mut new = vec![]; let mut new = vec![];
for n in n.take() { for n in n.take() {
match n { match n {
Rule::QualifiedRule(mut n) => { Rule::QualifiedRule(mut n) => {
let rules = self.extract_nested_rules(&mut n); let mut rules = self.extract_nested_rules(&mut n);
rules.visit_mut_with(self);
new.push(Rule::QualifiedRule(n)); new.push(Rule::QualifiedRule(n));
new.extend(rules); new.extend(rules);
} }
@ -254,18 +331,20 @@ impl VisitMut for NestingHandler {
} }
} }
} }
*n = new; *n = new;
} }
fn visit_mut_component_values(&mut self, n: &mut Vec<ComponentValue>) { fn visit_mut_component_values(&mut self, n: &mut Vec<ComponentValue>) {
n.visit_mut_children_with(self);
let mut new = vec![]; let mut new = vec![];
for n in n.take() { for n in n.take() {
match n { match n {
ComponentValue::StyleBlock(StyleBlock::QualifiedRule(mut n)) => { ComponentValue::StyleBlock(StyleBlock::QualifiedRule(mut n)) => {
let rules = self.extract_nested_rules(&mut n); let mut rules = self.extract_nested_rules(&mut n);
rules.visit_mut_with(self);
new.push(ComponentValue::StyleBlock(StyleBlock::QualifiedRule(n))); new.push(ComponentValue::StyleBlock(StyleBlock::QualifiedRule(n)));
new.extend(rules.into_iter().map(rule_to_component_value)); new.extend(rules.into_iter().map(rule_to_component_value));
} }
@ -275,6 +354,7 @@ impl VisitMut for NestingHandler {
} }
} }
} }
*n = new; *n = new;
} }
} }

View File

@ -67,3 +67,242 @@ figure {
color: blue; color: blue;
&__bar { color: red; } &__bar { color: red; }
} }
.foo {
color: red;
> bar {
color: green;
> baz {
color: blue;
}
}
}
.foo {
color: red;
& > bar {
color: green;
& > baz {
color: blue;
}
}
}
.foo {
color: red;
&.bar {
color: green;
&.one, &.two {
color: blue;
}
}
}
.foo {
color: red;
& > bar-1, & > bar-2 {
color: green;
> baz-1, > baz-2 {
color: blue;
}
}
}
.foo {
color: blue;
& > .bar { color: red; }
> .baz { color: green; }
}
.foo {
color: blue;
&.bar { color: red; }
}
.foo, .bar {
color: blue;
+ .baz, &.qux { color: red; }
}
.foo {
color: blue;
& .bar & .baz & .qux { color: red; }
}
.foo {
color: red;
.parent & {
color: blue;
}
}
/* TODO FIX ME */
.foo {
color: red;
:not(&) {
color: blue;
}
}
.foo {
color: red;
+ .bar + & { color: blue; }
}
.foo {
color: blue;
& { padding: 2ch; }
}
.error, #id {
&:hover > .baz { color: red; }
}
.ancestor .el {
.other-ancestor & { color: red; }
}
figure {
margin: 0;
> figcaption {
background: hsl(0 0% 0% / 50%);
> p {
font-size: .9rem;
}
}
}
/* TODO FIX ME */
@layer base {
html {
block-size: 100%;
& body {
min-block-size: 100%;
}
}
}
/* TODO FIX ME */
@layer base {
html {
block-size: 100%;
@layer base.support {
& body {
min-block-size: 100%;
}
}
}
}
.foo {
display: grid;
@media (orientation: landscape) {
grid-auto-flow: column;
}
}
/* TODO FIX ME */
.foo {
display: grid;
@media (orientation: landscape) {
grid-auto-flow: column;
@media (min-width > 1024px) {
max-inline-size: 1024px;
}
}
}
.foo {
display: grid;
@supports (grid-auto-flow: column) {
grid-auto-flow: column;
}
}
/* TODO FIX ME */
.foo {
display: grid;
@supports (grid-auto-flow: column) {
grid-auto-flow: column;
@supports (max-inline-size: 1024px) {
max-inline-size: 1024px;
}
}
}
.foo {
display: grid;
@container (min-width: 700px) {
grid-auto-flow: column;
}
}
/* TODO FIX ME */
.foo {
display: grid;
@container (min-width: 700px) {
grid-auto-flow: column;
@container (min-width: 400px) {
max-inline-size: 1024px;
}
}
}
article {
color: green;
& { color: blue; }
color: red;
}
/* TODO FIX ME */
.foo, .foo::before, .foo::after {
color: red;
&:hover { color: blue; }
}
.foo {
color: red;
.bar {
color: blue;
}
}
.foo {
color: red;
[test="test"] {
color: blue;
}
}
.foo {
color: red;
#id {
color: blue;
}
}

View File

@ -31,8 +31,8 @@ table.colortable th {
color: blue; color: blue;
} }
.foo + .baz, .foo + .baz,
.bar + .baz,
.foo.qux, .foo.qux,
.bar + .baz,
.bar.qux { .bar.qux {
color: red; color: red;
} }
@ -73,3 +73,228 @@ figure > figcaption > p {
__bar.foo { __bar.foo {
color: red; color: red;
} }
.foo {
color: red;
}
.foo > bar {
color: green;
}
.foo > bar > baz {
color: blue;
}
.foo {
color: red;
}
.foo > bar {
color: green;
}
.foo > bar > baz {
color: blue;
}
.foo {
color: red;
}
.foo.bar {
color: green;
}
.foo.bar.one,
.foo.bar.two {
color: blue;
}
.foo {
color: red;
}
.foo > bar-1,
.foo > bar-2 {
color: green;
}
.foo > bar-1 > baz-1,
.foo > bar-1 > baz-2,
.foo > bar-2 > baz-1,
.foo > bar-2 > baz-2 {
color: blue;
}
.foo {
color: blue;
}
.foo > .bar {
color: red;
}
.foo > .baz {
color: green;
}
.foo {
color: blue;
}
.foo.bar {
color: red;
}
.foo,
.bar {
color: blue;
}
.foo + .baz,
.foo.qux,
.bar + .baz,
.bar.qux {
color: red;
}
.foo {
color: blue;
}
.foo .bar .foo .baz .foo .qux {
color: red;
}
.foo {
color: red;
}
.parent .foo {
color: blue;
}
.foo {
color: red;
}
.foo :not(&) {
color: blue;
}
.foo {
color: red;
}
.foo + .bar + .foo {
color: blue;
}
.foo {
color: blue;
}
.foo {
padding: 2ch;
}
.error,
#id {}
.error:hover > .baz,
#id:hover > .baz {
color: red;
}
.ancestor .el {}
.other-ancestor .ancestor .el {
color: red;
}
figure {
margin: 0;
}
figure > figcaption {
background: hsl(0 0% 0% / 50%);
}
figure > figcaption > p {
font-size: .9rem;
}
@layer base {
html {
block-size: 100%;
& body {
min-block-size: 100%;
}
}
}
@layer base {
html {
block-size: 100%;
@layer base.support {
& body {
min-block-size: 100%;
}
}
}
}
.foo {
display: grid;
}
@media (orientation: landscape) {
.foo {
grid-auto-flow: column;
}
}
.foo {
display: grid;
}
@media (orientation: landscape) {
.foo {
grid-auto-flow: column;
@media (min-width > 1024px) {
max-inline-size: 1024px;
}
}
}
.foo {
display: grid;
}
@supports (grid-auto-flow: column) {
.foo {
grid-auto-flow: column;
}
}
.foo {
display: grid;
}
@supports (grid-auto-flow: column) {
.foo {
grid-auto-flow: column;
@supports (max-inline-size: 1024px) {
max-inline-size: 1024px;
}
}
}
.foo {
display: grid;
}
@container (min-width: 700px) {
.foo {
grid-auto-flow: column;
}
}
.foo {
display: grid;
}
@container (min-width: 700px) {
.foo {
grid-auto-flow: column;
@container (min-width: 400px) {
max-inline-size: 1024px;
}
}
}
article {
color: green;
color: red;
}
article {
color: blue;
}
.foo,
.foo::before,
.foo::after {
color: red;
}
.foo:hover,
.foo::before:hover,
.foo::after:hover {
color: blue;
}
.foo {
color: red;
}
.foo .bar {
color: blue;
}
.foo {
color: red;
}
.foo [test="test"] {
color: blue;
}
.foo {
color: red;
}
.foo #id {
color: blue;
}

View File

@ -1,3 +1,8 @@
.foo { .foo {
&:is(.bar, &.baz) { color: red; } &:is(.bar, &.baz) { color: red; }
} }
/* TODO FIX ME */
.foo {
& :is(.bar, &.baz) { color: red; }
}

View File

@ -2,3 +2,7 @@
.foo:is(.bar, .foo.baz) { .foo:is(.bar, .foo.baz) {
color: red; color: red;
} }
.foo {}
.foo :is(.bar, &.baz) {
color: red;
}

View File

@ -155,6 +155,21 @@ impl Compressor {
} }
} }
fn merge_relative_selector_list(
&self,
left: &RelativeSelectorList,
right: &RelativeSelectorList,
) -> RelativeSelectorList {
let mut children = left.children.clone();
children.extend(right.children.clone());
RelativeSelectorList {
span: Span::new(left.span_lo(), right.span_hi(), SyntaxContext::empty()),
children,
}
}
fn merge_simple_block(&self, left: &SimpleBlock, right: &SimpleBlock) -> SimpleBlock { fn merge_simple_block(&self, left: &SimpleBlock, right: &SimpleBlock) -> SimpleBlock {
let mut value = left.value.clone(); let mut value = left.value.clone();
@ -168,21 +183,31 @@ impl Compressor {
} }
fn can_merge_qualified_rules(&self, left: &QualifiedRule, right: &QualifiedRule) -> bool { fn can_merge_qualified_rules(&self, left: &QualifiedRule, right: &QualifiedRule) -> bool {
let left_selector_list = match &left.prelude { match (&left.prelude, &right.prelude) {
QualifiedRulePrelude::SelectorList(selector_list) => selector_list, (
_ => return false, QualifiedRulePrelude::SelectorList(left_selector_list),
}; QualifiedRulePrelude::SelectorList(right_selector_list),
let right_selector_list = match &right.prelude { ) => {
QualifiedRulePrelude::SelectorList(selector_list) => selector_list, let mut checker = CompatibilityChecker::default();
_ => return false,
};
let mut checker = CompatibilityChecker::default(); left_selector_list.visit_with(&mut checker);
right_selector_list.visit_with(&mut checker);
left_selector_list.visit_with(&mut checker); checker.allow_to_merge
right_selector_list.visit_with(&mut checker); }
(
QualifiedRulePrelude::RelativeSelectorList(left_relative_selector_list),
QualifiedRulePrelude::RelativeSelectorList(right_relative_selector_list),
) => {
let mut checker = CompatibilityChecker::default();
checker.allow_to_merge left_relative_selector_list.visit_with(&mut checker);
right_relative_selector_list.visit_with(&mut checker);
checker.allow_to_merge
}
_ => false,
}
} }
fn try_merge_qualified_rules( fn try_merge_qualified_rules(
@ -197,26 +222,42 @@ impl Compressor {
// Merge when declarations are exactly equal // Merge when declarations are exactly equal
// e.g. h1 { color: red } h2 { color: red } // e.g. h1 { color: red } h2 { color: red }
if left.block.eq_ignore_span(&right.block) { if left.block.eq_ignore_span(&right.block) {
if let ( match (&left.prelude, &right.prelude) {
QualifiedRulePrelude::SelectorList(prev_selector_list), (
QualifiedRulePrelude::SelectorList(current_selector_list), QualifiedRulePrelude::SelectorList(prev_selector_list),
) = (&left.prelude, &right.prelude) QualifiedRulePrelude::SelectorList(current_selector_list),
{ ) => {
let selector_list = let selector_list =
self.merge_selector_list(prev_selector_list, current_selector_list); self.merge_selector_list(prev_selector_list, current_selector_list);
let mut qualified_rule = QualifiedRule { let mut qualified_rule = QualifiedRule {
span: Span::new( span: Span::new(left.span_lo(), right.span_hi(), SyntaxContext::empty()),
left.span.span_lo(), prelude: QualifiedRulePrelude::SelectorList(selector_list),
right.span.span_lo(), block: left.block.clone(),
SyntaxContext::empty(), };
),
prelude: QualifiedRulePrelude::SelectorList(selector_list),
block: left.block.clone(),
};
qualified_rule.visit_mut_children_with(self); qualified_rule.visit_mut_children_with(self);
return Some(qualified_rule); return Some(qualified_rule);
}
(
QualifiedRulePrelude::RelativeSelectorList(prev_relative_selector_list),
QualifiedRulePrelude::RelativeSelectorList(current_relative_selector_list),
) => {
let relative_selector_list = self.merge_relative_selector_list(
prev_relative_selector_list,
current_relative_selector_list,
);
let mut qualified_rule = QualifiedRule {
span: Span::new(left.span_lo(), right.span_hi(), SyntaxContext::empty()),
prelude: QualifiedRulePrelude::RelativeSelectorList(relative_selector_list),
block: left.block.clone(),
};
qualified_rule.visit_mut_children_with(self);
return Some(qualified_rule);
}
_ => {}
} }
} }
@ -225,11 +266,7 @@ impl Compressor {
if left.prelude.eq_ignore_span(&right.prelude) { if left.prelude.eq_ignore_span(&right.prelude) {
let block = self.merge_simple_block(&left.block, &right.block); let block = self.merge_simple_block(&left.block, &right.block);
let mut qualified_rule = QualifiedRule { let mut qualified_rule = QualifiedRule {
span: Span::new( span: Span::new(left.span_lo(), right.span_hi(), SyntaxContext::empty()),
left.span.span_lo(),
right.span.span_hi(),
SyntaxContext::empty(),
),
prelude: left.prelude.clone(), prelude: left.prelude.clone(),
block, block,
}; };

View File

@ -41,8 +41,8 @@ pub struct ParserConfig {
#[serde(default)] #[serde(default)]
pub css_modules: bool, pub css_modules: bool,
/// If this is `true`, the nested selectors without `&` will be parsed as /// If this is `true`, the nested selector starts with an identifier will be
/// valid selectors /// parsed as valid selectors (i.e. `ul { color: red; li { color: blue } }`)
/// ///
/// Defaults to `false`. /// Defaults to `false`.
#[serde(default)] #[serde(default)]
@ -68,6 +68,7 @@ impl Default for BlockContentsGrammar {
struct Ctx { struct Ctx {
is_top_level: bool, is_top_level: bool,
block_contents_grammar: BlockContentsGrammar, block_contents_grammar: BlockContentsGrammar,
mixed_with_declarations: bool,
in_keyframes_at_rule: bool, in_keyframes_at_rule: bool,
in_supports_at_rule: bool, in_supports_at_rule: bool,
@ -75,7 +76,6 @@ struct Ctx {
in_page_at_rule: bool, in_page_at_rule: bool,
in_container_at_rule: bool, in_container_at_rule: bool,
in_font_feature_values_at_rule: bool, in_font_feature_values_at_rule: bool,
is_trying_legacy_nesting: bool,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View File

@ -307,46 +307,34 @@ where
Ok(QualifiedRulePrelude::ListOfComponentValues( Ok(QualifiedRulePrelude::ListOfComponentValues(
list_of_component_values, list_of_component_values,
)) ))
} else if p.ctx.mixed_with_declarations {
match p.parse_according_to_grammar::<RelativeSelectorList>(
&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 { } else {
match p.parse_according_to_grammar::<SelectorList>( match p.parse_according_to_grammar::<SelectorList>(
&list_of_component_values, &list_of_component_values,
|parser| parser.parse(), |parser| parser.parse(),
) { ) {
Ok(selector_list) => { Ok(selector_list) => Ok(QualifiedRulePrelude::SelectorList(selector_list)),
if p.ctx.is_trying_legacy_nesting {
let selector_list = p
.legacy_nested_selector_list_to_modern_selector_list(
selector_list,
)?;
Ok(QualifiedRulePrelude::SelectorList(selector_list))
} else {
Ok(QualifiedRulePrelude::SelectorList(selector_list))
}
}
Err(err) => { Err(err) => {
if p.ctx.is_trying_legacy_nesting { p.errors.push(err);
match p.parse_according_to_grammar::<RelativeSelectorList>(
&list_of_component_values,
|parser| parser.parse(),
) {
Ok(relative_selector_list) => {
let selector_list = p
.legacy_relative_selector_list_to_modern_selector_list(
relative_selector_list,
)?;
Ok(QualifiedRulePrelude::SelectorList(selector_list)) Ok(QualifiedRulePrelude::ListOfComponentValues(
} list_of_component_values,
_ => Err(err), ))
}
} else {
p.errors.push(err);
Ok(QualifiedRulePrelude::ListOfComponentValues(
list_of_component_values,
))
}
} }
} }
} }
@ -369,6 +357,21 @@ where
} }
match cur!(self) { match cur!(self) {
// <semicolon-token>
// If mixed with declarations is true, this is a parse error; return nothing.
// Otherwise, append a <semicolon-token> to the qualified rules prelude.
tok!(";") => {
if self.ctx.mixed_with_declarations {
return Err(Error::new(
span!(self, span.lo),
ErrorKind::EofButExpected("'{'"),
));
} else {
let component_value = self.parse_as::<ComponentValue>()?;
prelude.push(component_value);
}
}
// <{-token> // <{-token>
// Consume a simple block and assign it to the qualified rules block. Return the // Consume a simple block and assign it to the qualified rules block. Return the
// qualified rule. // qualified rule.
@ -466,13 +469,23 @@ where
rules.push(StyleBlock::AtRule(Box::new(at_rule))); rules.push(StyleBlock::AtRule(Box::new(at_rule)));
} }
// <ident-token> // <ident-token>
// Initialize a temporary list initially filled with the current input token. As // <function-token>
// long as the next input token is anything other than a <semicolon-token> or // <function>
//
// Reconsume the current input token. Initialize a temporary list, initially empty.
// As long as the next input token is anything other than a <semicolon-token> or
// <EOF-token>, consume a component value and append it to the temporary list. // <EOF-token>, consume a component value and append it to the temporary list.
// Consume a declaration from the temporary list. If anything was returned, append // Consume a declaration from the temporary list. If anything was returned, append
// it to decls. // it to decls.
tok!("ident") => { tok!("ident") | tok!("function") => {
if self.config.legacy_nesting { // Legacy nested parsing conflict with custom properties, but selectors can't
// start with `--`, so it is safe to ignore them.
//
// Constructions like `a { prop: {value}; }` still affected this problem, but
// `{`/`}` doesn't used in declarations
if self.config.legacy_nesting
&& matches!(self.input.cur(), Some(Token::Ident { value, .. }) if !value.starts_with("--"))
{
if let Some(legacy_nested) = self.try_to_parse_legacy_nesting() { if let Some(legacy_nested) = self.try_to_parse_legacy_nesting() {
rules.push(StyleBlock::QualifiedRule(Box::new(legacy_nested))); rules.push(StyleBlock::QualifiedRule(Box::new(legacy_nested)));
@ -508,82 +521,48 @@ where
declarations.push(decl_or_list_of_component_values); declarations.push(decl_or_list_of_component_values);
} }
// <delim-token> with a value of "&" (U+0026 AMPERSAND) // anything else
// Reconsume the current input token. Consume a qualified rule. If anything was // Reconsume the current input token. Consume a qualified rule, with mixed with
// returned, append it to rules. // declarations set to true. If anything was returned, append it to rules.
tok!("&") => { _ => {
let state = self.input.state(); let state = self.input.state();
let qualified_rule = match self.parse() { let qualified_rule = self
Ok(v) => StyleBlock::QualifiedRule(v), .with_ctx(Ctx {
mixed_with_declarations: true,
..self.ctx
})
.parse_as::<Box<QualifiedRule>>();
match qualified_rule {
Ok(i) => rules.push(StyleBlock::QualifiedRule(i)),
Err(err) => { Err(err) => {
self.errors.push(err); self.errors.push(err);
self.input.reset(&state); self.input.reset(&state);
let span = self.input.cur_span(); let span = self.input.cur_span();
let mut children = vec![];
while !is_one_of!(self, EOF, "}") { self.errors
if let Some(token_and_span) = self.input.bump() { .push(Error::new(span, ErrorKind::Unexpected("token")));
children.push(ComponentValue::PreservedToken(token_and_span));
}
if is!(self, ";") { // For recovery mode
if let Some(token_and_span) = self.input.bump() { let mut list_of_component_values = ListOfComponentValues {
children span: Default::default(),
.push(ComponentValue::PreservedToken(token_and_span)); children: vec![],
} };
break; // TODO verify error recovery (copied from prev spec)
} while !is_one_of!(self, ";", EOF) {
let component_value = self.parse_as::<ComponentValue>()?;
list_of_component_values.children.push(component_value);
} }
StyleBlock::ListOfComponentValues(ListOfComponentValues { list_of_component_values.span = span!(self, span.lo);
span: span!(self, span.lo),
children, declarations
}) .push(StyleBlock::ListOfComponentValues(list_of_component_values));
} }
}; };
rules.push(qualified_rule);
}
// anything else
// This is a parse error. Reconsume the current input token. As long as the next
// input token is anything other than a <semicolon-token> or <EOF-token>, consume a
// component value and throw away the returned value.
_ => {
if self.config.legacy_nesting {
if let Some(legacy_nested) = self.try_to_parse_legacy_nesting() {
rules.push(StyleBlock::QualifiedRule(Box::new(legacy_nested)));
continue;
}
}
let span = self.input.cur_span();
self.errors.push(Error::new(
span,
ErrorKind::Expected(
"whitespace, semicolon, EOF, at-keyword, '&' or ident token",
),
));
// For recovery mode
let mut list_of_component_values = ListOfComponentValues {
span: Default::default(),
children: vec![],
};
while !is_one_of!(self, ";", EOF) {
let component_value = self.parse_as::<ComponentValue>()?;
list_of_component_values.children.push(component_value);
}
list_of_component_values.span = span!(self, span.lo);
declarations.push(StyleBlock::ListOfComponentValues(list_of_component_values));
} }
} }
} }
@ -720,9 +699,21 @@ where
fn parse(&mut self) -> PResult<Declaration> { fn parse(&mut self) -> PResult<Declaration> {
// To consume a declaration: // To consume a declaration:
// Consume the next input token. Create a new declaration with its name set // Let decl be a new declaration, with an initially empty name and a value set
// to the value of the current input token and its value initially set to an // to an empty list.
// empty list.
// TODO improve me
// Consume a component value.
// <ident-token>
// Set decls name to the value of the <ident-token>.
//
// anything else
// This is a parse error.
//
// While the next input token is anything but a <semicolon-token> or
// <eof-token>, consume a component value and throw it away.
//
// Return nothing.
let span = self.input.cur_span(); let span = self.input.cur_span();
let is_dashed_ident = match cur!(self) { let is_dashed_ident = match cur!(self) {
Token::Ident { value, .. } => value.starts_with("--"), Token::Ident { value, .. } => value.starts_with("--"),
@ -742,19 +733,19 @@ where
important: None, important: None,
}; };
// 1. While the next input token is a <whitespace-token>, consume the next input // 2. While the next input token is a <whitespace-token>, consume the next input
// token. // token.
self.input.skip_ws(); self.input.skip_ws();
// 2. If the next input token is anything other than a <colon-token>, this is a // 3. If the next input token is anything other than a <colon-token>, this is a
// parse error. Return nothing. Otherwise, consume the next input token. // parse error. Return nothing. Otherwise, consume the next input token.
expect!(self, ":"); expect!(self, ":");
// 3. While the next input token is a <whitespace-token>, consume the next input // 4. While the next input token is a <whitespace-token>, consume the next input
// token. // token.
self.input.skip_ws(); self.input.skip_ws();
// 4. As long as the next input token is anything other than an <EOF-token>, // 5. As long as the next input token is anything other than an <EOF-token>,
// consume a component value and append it to the declarations value. // consume a component value and append it to the declarations value.
let mut last_whitespaces = (0, 0, 0); let mut last_whitespaces = (0, 0, 0);
let mut exclamation_point_span = None; let mut exclamation_point_span = None;
@ -840,7 +831,7 @@ where
declaration.value.push(component_value); declaration.value.push(component_value);
} }
// 5. If the last two non-<whitespace-token>s in the declarations value are a // 6. If the last two non-<whitespace-token>s in the declarations value are a
// <delim-token> with the value "!" followed by an <ident-token> with a value // <delim-token> with the value "!" followed by an <ident-token> with a value
// that is an ASCII case-insensitive match for "important", remove them from the // that is an ASCII case-insensitive match for "important", remove them from the
// declarations value and set the declarations important flag to true. // declarations value and set the declarations important flag to true.
@ -867,7 +858,7 @@ where
declaration.important = Some(ImportantFlag { span, value }); declaration.important = Some(ImportantFlag { span, value });
} }
// 6. While the last token in the declarations value is a <whitespace-token>, // 7. While the last token in the declarations value is a <whitespace-token>,
// remove that token. // remove that token.
let len = if declaration.important.is_some() { let len = if declaration.important.is_some() {
declaration.value.len() declaration.value.len()
@ -884,7 +875,7 @@ where
if is_dashed_ident { if is_dashed_ident {
// Don't parse custom properties // Don't parse custom properties
// //
// 7. Return the declaration. // 8. Return the declaration.
return Ok(declaration); return Ok(declaration);
} }
@ -914,7 +905,7 @@ where
} }
}; };
// 7. Return the declaration. // 8. Return the declaration.
Ok(declaration) Ok(declaration)
} }
} }

View File

@ -73,11 +73,12 @@ where
pub(super) fn try_to_parse_legacy_nesting(&mut self) -> Option<QualifiedRule> { pub(super) fn try_to_parse_legacy_nesting(&mut self) -> Option<QualifiedRule> {
let state = self.input.state(); let state = self.input.state();
let ctx = Ctx { let qualified_rule = self
is_trying_legacy_nesting: true, .with_ctx(Ctx {
..self.ctx mixed_with_declarations: true,
}; ..self.ctx
let qualified_rule = self.with_ctx(ctx).parse_as::<QualifiedRule>(); })
.parse_as::<QualifiedRule>();
match qualified_rule { match qualified_rule {
Ok(qualified_rule) => Some(qualified_rule), Ok(qualified_rule) => Some(qualified_rule),
@ -88,84 +89,6 @@ where
} }
} }
} }
pub(super) fn legacy_nested_selector_list_to_modern_selector_list(
&mut self,
mut selector_list: SelectorList,
) -> PResult<SelectorList> {
for s in selector_list.children.iter_mut() {
if s.children.iter().any(|s| match s {
ComplexSelectorChildren::CompoundSelector(s) => s.nesting_selector.is_some(),
_ => false,
}) {
continue;
}
s.children.insert(
0,
ComplexSelectorChildren::CompoundSelector(CompoundSelector {
span: DUMMY_SP,
nesting_selector: Some(NestingSelector { span: DUMMY_SP }),
type_selector: Default::default(),
subclass_selectors: Default::default(),
}),
);
s.children.insert(
1,
ComplexSelectorChildren::Combinator(Combinator {
span: DUMMY_SP,
value: CombinatorValue::Descendant,
}),
);
}
Ok(selector_list)
}
pub(super) fn legacy_relative_selector_list_to_modern_selector_list(
&mut self,
relative_selector_list: RelativeSelectorList,
) -> PResult<SelectorList> {
let mut selector_list = SelectorList {
span: relative_selector_list.span,
children: Vec::with_capacity(relative_selector_list.children.len()),
};
for relative_selector in relative_selector_list.children.into_iter() {
let mut complex_selector = relative_selector.selector.clone();
complex_selector.children.insert(
0,
ComplexSelectorChildren::CompoundSelector(CompoundSelector {
span: DUMMY_SP,
nesting_selector: Some(NestingSelector { span: DUMMY_SP }),
type_selector: Default::default(),
subclass_selectors: Default::default(),
}),
);
match relative_selector.combinator {
Some(combinator) => {
complex_selector
.children
.insert(1, ComplexSelectorChildren::Combinator(combinator));
}
_ => {
complex_selector.children.insert(
1,
ComplexSelectorChildren::Combinator(Combinator {
span: DUMMY_SP,
value: CombinatorValue::Descendant,
}),
);
}
}
selector_list.children.push(complex_selector);
}
Ok(selector_list)
}
} }
pub(super) struct WithCtx<'w, I: 'w + ParserInput> { pub(super) struct WithCtx<'w, I: 'w + ParserInput> {

View File

@ -1033,7 +1033,7 @@
"ctxt": 0 "ctxt": 0
}, },
"prelude": { "prelude": {
"type": "SelectorList", "type": "RelativeSelectorList",
"span": { "span": {
"start": 295, "start": 295,
"end": 300, "end": 300,
@ -1041,51 +1041,60 @@
}, },
"children": [ "children": [
{ {
"type": "ComplexSelector", "type": "RelativeSelector",
"span": { "span": {
"start": 295, "start": 295,
"end": 300, "end": 300,
"ctxt": 0 "ctxt": 0
}, },
"children": [ "combinator": null,
{ "selector": {
"type": "CompoundSelector", "type": "ComplexSelector",
"span": { "span": {
"start": 295, "start": 295,
"end": 300, "end": 300,
"ctxt": 0 "ctxt": 0
}, },
"nestingSelector": { "children": [
"type": "NestingSelector", {
"type": "CompoundSelector",
"span": { "span": {
"start": 295, "start": 295,
"end": 296, "end": 300,
"ctxt": 0 "ctxt": 0
} },
}, "nestingSelector": {
"typeSelector": null, "type": "NestingSelector",
"subclassSelectors": [
{
"type": "ClassSelector",
"span": { "span": {
"start": 296, "start": 295,
"end": 300, "end": 296,
"ctxt": 0 "ctxt": 0
}, }
"text": { },
"type": "Ident", "typeSelector": null,
"subclassSelectors": [
{
"type": "ClassSelector",
"span": { "span": {
"start": 297, "start": 296,
"end": 300, "end": 300,
"ctxt": 0 "ctxt": 0
}, },
"value": "baz", "text": {
"raw": "baz" "type": "Ident",
"span": {
"start": 297,
"end": 300,
"ctxt": 0
},
"value": "baz",
"raw": "baz"
}
} }
} ]
] }
} ]
] }
} }
] ]
}, },

View File

@ -1115,7 +1115,13 @@
29 | `-> } 29 | `-> }
`---- `----
x SelectorList x RelativeSelectorList
,-[$DIR/tests/fixture/at-rule/nest/input.css:27:9]
27 | &.baz {
: ^^^^^
`----
x RelativeSelector
,-[$DIR/tests/fixture/at-rule/nest/input.css:27:9] ,-[$DIR/tests/fixture/at-rule/nest/input.css:27:9]
27 | &.baz { 27 | &.baz {
: ^^^^^ : ^^^^^

View File

@ -67,3 +67,110 @@ figure {
color: blue; color: blue;
&__bar { color: red; } &__bar { color: red; }
} }
.foo {
color: red;
.bar {
color: blue;
}
}
.foo {
color: red;
+ .bar {
color: blue;
}
}
.foo {
color: blue;
& > .bar { color: red; }
> .baz { color: green; }
}
div {
color: red;
& input { margin: 1em; }
/* valid, no longer starts with an identifier */
:is(input) { margin: 1em; }
/* valid, starts with a colon,
and equivalent to the previous rule. */
}
.foo, .bar {
color: blue;
+ .baz, &.qux { color: red; }
}
.foo {
color: blue;
& .bar & .baz & .qux { color: red; }
}
.foo {
color: red;
.parent & {
color: blue;
}
}
.foo {
color: red;
:not(&) {
color: blue;
}
}
.foo {
color: red;
+ .bar + & { color: blue; }
}
.ancestor .el {
.other-ancestor & { color: red; }
}
.foo {
& :is(.bar, &.baz) { color: red; }
}
@layer base {
html {
block-size: 100%;
& body {
min-block-size: 100%;
}
}
}
@layer base {
html {
block-size: 100%;
@layer base.support {
& body {
min-block-size: 100%;
}
}
}
}
article {
color: green;
& { color: blue; }
color: red;
}
.foo {
color: red;
@media (min-width: 480px) {
& h1, & h2 {
color: blue;
}
}
}

View File

@ -1164,7 +1164,7 @@
"ctxt": 0 "ctxt": 0
}, },
"prelude": { "prelude": {
"type": "SelectorList", "type": "RelativeSelectorList",
"span": { "span": {
"start": 477, "start": 477,
"end": 487, "end": 487,
@ -1172,152 +1172,170 @@
}, },
"children": [ "children": [
{ {
"type": "ComplexSelector", "type": "RelativeSelector",
"span": { "span": {
"start": 477, "start": 477,
"end": 481, "end": 481,
"ctxt": 0 "ctxt": 0
}, },
"children": [ "combinator": null,
{ "selector": {
"type": "CompoundSelector", "type": "ComplexSelector",
"span": { "span": {
"start": 477, "start": 477,
"end": 478, "end": 481,
"ctxt": 0 "ctxt": 0
}, },
"nestingSelector": { "children": [
"type": "NestingSelector", {
"type": "CompoundSelector",
"span": { "span": {
"start": 477, "start": 477,
"end": 478, "end": 478,
"ctxt": 0 "ctxt": 0
} },
"nestingSelector": {
"type": "NestingSelector",
"span": {
"start": 477,
"end": 478,
"ctxt": 0
}
},
"typeSelector": null,
"subclassSelectors": []
}, },
"typeSelector": null, {
"subclassSelectors": [] "type": "Combinator",
}, "span": {
{ "start": 478,
"type": "Combinator", "end": 479,
"span": { "ctxt": 0
"start": 478, },
"end": 479, "value": " "
"ctxt": 0
}, },
"value": " " {
}, "type": "CompoundSelector",
{
"type": "CompoundSelector",
"span": {
"start": 479,
"end": 481,
"ctxt": 0
},
"nestingSelector": null,
"typeSelector": {
"type": "TagNameSelector",
"span": { "span": {
"start": 479, "start": 479,
"end": 481, "end": 481,
"ctxt": 0 "ctxt": 0
}, },
"name": { "nestingSelector": null,
"type": "WqName", "typeSelector": {
"type": "TagNameSelector",
"span": { "span": {
"start": 479, "start": 479,
"end": 481, "end": 481,
"ctxt": 0 "ctxt": 0
}, },
"prefix": null, "name": {
"value": { "type": "WqName",
"type": "Ident",
"span": { "span": {
"start": 479, "start": 479,
"end": 481, "end": 481,
"ctxt": 0 "ctxt": 0
}, },
"value": "h1", "prefix": null,
"raw": "h1" "value": {
"type": "Ident",
"span": {
"start": 479,
"end": 481,
"ctxt": 0
},
"value": "h1",
"raw": "h1"
}
} }
} },
}, "subclassSelectors": []
"subclassSelectors": [] }
} ]
] }
}, },
{ {
"type": "ComplexSelector", "type": "RelativeSelector",
"span": { "span": {
"start": 483, "start": 483,
"end": 487, "end": 487,
"ctxt": 0 "ctxt": 0
}, },
"children": [ "combinator": null,
{ "selector": {
"type": "CompoundSelector", "type": "ComplexSelector",
"span": { "span": {
"start": 483, "start": 483,
"end": 484, "end": 487,
"ctxt": 0 "ctxt": 0
}, },
"nestingSelector": { "children": [
"type": "NestingSelector", {
"type": "CompoundSelector",
"span": { "span": {
"start": 483, "start": 483,
"end": 484, "end": 484,
"ctxt": 0 "ctxt": 0
} },
"nestingSelector": {
"type": "NestingSelector",
"span": {
"start": 483,
"end": 484,
"ctxt": 0
}
},
"typeSelector": null,
"subclassSelectors": []
}, },
"typeSelector": null, {
"subclassSelectors": [] "type": "Combinator",
}, "span": {
{ "start": 484,
"type": "Combinator", "end": 485,
"span": { "ctxt": 0
"start": 484, },
"end": 485, "value": " "
"ctxt": 0
}, },
"value": " " {
}, "type": "CompoundSelector",
{
"type": "CompoundSelector",
"span": {
"start": 485,
"end": 487,
"ctxt": 0
},
"nestingSelector": null,
"typeSelector": {
"type": "TagNameSelector",
"span": { "span": {
"start": 485, "start": 485,
"end": 487, "end": 487,
"ctxt": 0 "ctxt": 0
}, },
"name": { "nestingSelector": null,
"type": "WqName", "typeSelector": {
"type": "TagNameSelector",
"span": { "span": {
"start": 485, "start": 485,
"end": 487, "end": 487,
"ctxt": 0 "ctxt": 0
}, },
"prefix": null, "name": {
"value": { "type": "WqName",
"type": "Ident",
"span": { "span": {
"start": 485, "start": 485,
"end": 487, "end": 487,
"ctxt": 0 "ctxt": 0
}, },
"value": "h2", "prefix": null,
"raw": "h2" "value": {
"type": "Ident",
"span": {
"start": 485,
"end": 487,
"ctxt": 0
},
"value": "h2",
"raw": "h2"
}
} }
} },
}, "subclassSelectors": []
"subclassSelectors": [] }
} ]
] }
} }
] ]
}, },
@ -1584,7 +1602,7 @@
"ctxt": 0 "ctxt": 0
}, },
"prelude": { "prelude": {
"type": "SelectorList", "type": "RelativeSelectorList",
"span": { "span": {
"start": 594, "start": 594,
"end": 602, "end": 602,
@ -1592,71 +1610,80 @@
}, },
"children": [ "children": [
{ {
"type": "ComplexSelector", "type": "RelativeSelector",
"span": { "span": {
"start": 594, "start": 594,
"end": 602, "end": 602,
"ctxt": 0 "ctxt": 0
}, },
"children": [ "combinator": null,
{ "selector": {
"type": "CompoundSelector", "type": "ComplexSelector",
"span": { "span": {
"start": 594, "start": 594,
"end": 595, "end": 602,
"ctxt": 0 "ctxt": 0
}, },
"nestingSelector": { "children": [
"type": "NestingSelector", {
"type": "CompoundSelector",
"span": { "span": {
"start": 594, "start": 594,
"end": 595, "end": 595,
"ctxt": 0 "ctxt": 0
} },
}, "nestingSelector": {
"typeSelector": null, "type": "NestingSelector",
"subclassSelectors": []
},
{
"type": "Combinator",
"span": {
"start": 595,
"end": 596,
"ctxt": 0
},
"value": " "
},
{
"type": "CompoundSelector",
"span": {
"start": 596,
"end": 602,
"ctxt": 0
},
"nestingSelector": null,
"typeSelector": null,
"subclassSelectors": [
{
"type": "ClassSelector",
"span": { "span": {
"start": 596, "start": 594,
"end": 602, "end": 595,
"ctxt": 0 "ctxt": 0
}, }
"text": { },
"type": "Ident", "typeSelector": null,
"subclassSelectors": []
},
{
"type": "Combinator",
"span": {
"start": 595,
"end": 596,
"ctxt": 0
},
"value": " "
},
{
"type": "CompoundSelector",
"span": {
"start": 596,
"end": 602,
"ctxt": 0
},
"nestingSelector": null,
"typeSelector": null,
"subclassSelectors": [
{
"type": "ClassSelector",
"span": { "span": {
"start": 597, "start": 596,
"end": 602, "end": 602,
"ctxt": 0 "ctxt": 0
}, },
"value": "class", "text": {
"raw": "class" "type": "Ident",
"span": {
"start": 597,
"end": 602,
"ctxt": 0
},
"value": "class",
"raw": "class"
}
} }
} ]
] }
} ]
] }
} }
] ]
}, },
@ -1954,7 +1981,7 @@
"ctxt": 0 "ctxt": 0
}, },
"prelude": { "prelude": {
"type": "SelectorList", "type": "RelativeSelectorList",
"span": { "span": {
"start": 725, "start": 725,
"end": 733, "end": 733,
@ -1962,71 +1989,80 @@
}, },
"children": [ "children": [
{ {
"type": "ComplexSelector", "type": "RelativeSelector",
"span": { "span": {
"start": 725, "start": 725,
"end": 733, "end": 733,
"ctxt": 0 "ctxt": 0
}, },
"children": [ "combinator": null,
{ "selector": {
"type": "CompoundSelector", "type": "ComplexSelector",
"span": { "span": {
"start": 725, "start": 725,
"end": 726, "end": 733,
"ctxt": 0 "ctxt": 0
}, },
"nestingSelector": { "children": [
"type": "NestingSelector", {
"type": "CompoundSelector",
"span": { "span": {
"start": 725, "start": 725,
"end": 726, "end": 726,
"ctxt": 0 "ctxt": 0
} },
}, "nestingSelector": {
"typeSelector": null, "type": "NestingSelector",
"subclassSelectors": []
},
{
"type": "Combinator",
"span": {
"start": 726,
"end": 727,
"ctxt": 0
},
"value": " "
},
{
"type": "CompoundSelector",
"span": {
"start": 727,
"end": 733,
"ctxt": 0
},
"nestingSelector": null,
"typeSelector": null,
"subclassSelectors": [
{
"type": "ClassSelector",
"span": { "span": {
"start": 727, "start": 725,
"end": 733, "end": 726,
"ctxt": 0 "ctxt": 0
}, }
"text": { },
"type": "Ident", "typeSelector": null,
"subclassSelectors": []
},
{
"type": "Combinator",
"span": {
"start": 726,
"end": 727,
"ctxt": 0
},
"value": " "
},
{
"type": "CompoundSelector",
"span": {
"start": 727,
"end": 733,
"ctxt": 0
},
"nestingSelector": null,
"typeSelector": null,
"subclassSelectors": [
{
"type": "ClassSelector",
"span": { "span": {
"start": 728, "start": 727,
"end": 733, "end": 733,
"ctxt": 0 "ctxt": 0
}, },
"value": "class", "text": {
"raw": "class" "type": "Ident",
"span": {
"start": 728,
"end": 733,
"ctxt": 0
},
"value": "class",
"raw": "class"
}
} }
} ]
] }
} ]
] }
} }
] ]
}, },
@ -2200,7 +2236,7 @@
"ctxt": 0 "ctxt": 0
}, },
"prelude": { "prelude": {
"type": "SelectorList", "type": "RelativeSelectorList",
"span": { "span": {
"start": 789, "start": 789,
"end": 794, "end": 794,
@ -2208,51 +2244,60 @@
}, },
"children": [ "children": [
{ {
"type": "ComplexSelector", "type": "RelativeSelector",
"span": { "span": {
"start": 789, "start": 789,
"end": 794, "end": 794,
"ctxt": 0 "ctxt": 0
}, },
"children": [ "combinator": null,
{ "selector": {
"type": "CompoundSelector", "type": "ComplexSelector",
"span": { "span": {
"start": 789, "start": 789,
"end": 794, "end": 794,
"ctxt": 0 "ctxt": 0
}, },
"nestingSelector": { "children": [
"type": "NestingSelector", {
"type": "CompoundSelector",
"span": { "span": {
"start": 789, "start": 789,
"end": 790, "end": 794,
"ctxt": 0 "ctxt": 0
} },
}, "nestingSelector": {
"typeSelector": null, "type": "NestingSelector",
"subclassSelectors": [
{
"type": "ClassSelector",
"span": { "span": {
"start": 790, "start": 789,
"end": 794, "end": 790,
"ctxt": 0 "ctxt": 0
}, }
"text": { },
"type": "Ident", "typeSelector": null,
"subclassSelectors": [
{
"type": "ClassSelector",
"span": { "span": {
"start": 791, "start": 790,
"end": 794, "end": 794,
"ctxt": 0 "ctxt": 0
}, },
"value": "foo", "text": {
"raw": "foo" "type": "Ident",
"span": {
"start": 791,
"end": 794,
"ctxt": 0
},
"value": "foo",
"raw": "foo"
}
} }
} ]
] }
} ]
] }
} }
] ]
}, },
@ -2439,7 +2484,7 @@
"ctxt": 0 "ctxt": 0
}, },
"prelude": { "prelude": {
"type": "SelectorList", "type": "RelativeSelectorList",
"span": { "span": {
"start": 895, "start": 895,
"end": 903, "end": 903,
@ -2447,71 +2492,80 @@
}, },
"children": [ "children": [
{ {
"type": "ComplexSelector", "type": "RelativeSelector",
"span": { "span": {
"start": 895, "start": 895,
"end": 903, "end": 903,
"ctxt": 0 "ctxt": 0
}, },
"children": [ "combinator": null,
{ "selector": {
"type": "CompoundSelector", "type": "ComplexSelector",
"span": { "span": {
"start": 895, "start": 895,
"end": 896, "end": 903,
"ctxt": 0 "ctxt": 0
}, },
"nestingSelector": { "children": [
"type": "NestingSelector", {
"type": "CompoundSelector",
"span": { "span": {
"start": 895, "start": 895,
"end": 896, "end": 896,
"ctxt": 0 "ctxt": 0
} },
}, "nestingSelector": {
"typeSelector": null, "type": "NestingSelector",
"subclassSelectors": []
},
{
"type": "Combinator",
"span": {
"start": 896,
"end": 897,
"ctxt": 0
},
"value": " "
},
{
"type": "CompoundSelector",
"span": {
"start": 897,
"end": 903,
"ctxt": 0
},
"nestingSelector": null,
"typeSelector": null,
"subclassSelectors": [
{
"type": "ClassSelector",
"span": { "span": {
"start": 897, "start": 895,
"end": 903, "end": 896,
"ctxt": 0 "ctxt": 0
}, }
"text": { },
"type": "Ident", "typeSelector": null,
"subclassSelectors": []
},
{
"type": "Combinator",
"span": {
"start": 896,
"end": 897,
"ctxt": 0
},
"value": " "
},
{
"type": "CompoundSelector",
"span": {
"start": 897,
"end": 903,
"ctxt": 0
},
"nestingSelector": null,
"typeSelector": null,
"subclassSelectors": [
{
"type": "ClassSelector",
"span": { "span": {
"start": 898, "start": 897,
"end": 903, "end": 903,
"ctxt": 0 "ctxt": 0
}, },
"value": "class", "text": {
"raw": "class" "type": "Ident",
"span": {
"start": 898,
"end": 903,
"ctxt": 0
},
"value": "class",
"raw": "class"
}
} }
} ]
] }
} ]
] }
} }
] ]
}, },

View File

@ -1456,12 +1456,18 @@
: ^^^^^^^^^^^^^^^^^^^^^^^^^^^ : ^^^^^^^^^^^^^^^^^^^^^^^^^^^
`---- `----
x SelectorList x RelativeSelectorList
,-[$DIR/tests/fixture/style-block/input.css:33:9] ,-[$DIR/tests/fixture/style-block/input.css:33:9]
33 | & h1, & h2 { color: blue; } 33 | & h1, & h2 { color: blue; }
: ^^^^^^^^^^ : ^^^^^^^^^^
`---- `----
x RelativeSelector
,-[$DIR/tests/fixture/style-block/input.css:33:9]
33 | & h1, & h2 { color: blue; }
: ^^^^
`----
x ComplexSelector x ComplexSelector
,-[$DIR/tests/fixture/style-block/input.css:33:9] ,-[$DIR/tests/fixture/style-block/input.css:33:9]
33 | & h1, & h2 { color: blue; } 33 | & h1, & h2 { color: blue; }
@ -1516,6 +1522,12 @@
: ^^ : ^^
`---- `----
x RelativeSelector
,-[$DIR/tests/fixture/style-block/input.css:33:9]
33 | & h1, & h2 { color: blue; }
: ^^^^
`----
x ComplexSelector x ComplexSelector
,-[$DIR/tests/fixture/style-block/input.css:33:9] ,-[$DIR/tests/fixture/style-block/input.css:33:9]
33 | & h1, & h2 { color: blue; } 33 | & h1, & h2 { color: blue; }
@ -1879,7 +1891,13 @@
: ^^^^^^^^^^^^^^^^^^^^^^^ : ^^^^^^^^^^^^^^^^^^^^^^^
`---- `----
x SelectorList x RelativeSelectorList
,-[$DIR/tests/fixture/style-block/input.css:41:9]
41 | & .class { color: red }
: ^^^^^^^^
`----
x RelativeSelector
,-[$DIR/tests/fixture/style-block/input.css:41:9] ,-[$DIR/tests/fixture/style-block/input.css:41:9]
41 | & .class { color: red } 41 | & .class { color: red }
: ^^^^^^^^ : ^^^^^^^^
@ -2290,7 +2308,13 @@
: ^^^^^^^^^^^^^^^^^^^^^^^ : ^^^^^^^^^^^^^^^^^^^^^^^
`---- `----
x SelectorList x RelativeSelectorList
,-[$DIR/tests/fixture/style-block/input.css:51:9]
51 | & .class { color: red }
: ^^^^^^^^
`----
x RelativeSelector
,-[$DIR/tests/fixture/style-block/input.css:51:9] ,-[$DIR/tests/fixture/style-block/input.css:51:9]
51 | & .class { color: red } 51 | & .class { color: red }
: ^^^^^^^^ : ^^^^^^^^
@ -2554,7 +2578,13 @@
60 | `-> } 60 | `-> }
`---- `----
x SelectorList x RelativeSelectorList
,-[$DIR/tests/fixture/style-block/input.css:58:5]
58 | &.foo {
: ^^^^^
`----
x RelativeSelector
,-[$DIR/tests/fixture/style-block/input.css:58:5] ,-[$DIR/tests/fixture/style-block/input.css:58:5]
58 | &.foo { 58 | &.foo {
: ^^^^^ : ^^^^^
@ -2831,7 +2861,13 @@
: ^^^^^^^^^^^^^^^^^^^^^^^ : ^^^^^^^^^^^^^^^^^^^^^^^
`---- `----
x SelectorList x RelativeSelectorList
,-[$DIR/tests/fixture/style-block/input.css:65:9]
65 | & .class { color: red }
: ^^^^^^^^
`----
x RelativeSelector
,-[$DIR/tests/fixture/style-block/input.css:65:9] ,-[$DIR/tests/fixture/style-block/input.css:65:9]
65 | & .class { color: red } 65 | & .class { color: red }
: ^^^^^^^^ : ^^^^^^^^

View File

@ -1,24 +1,54 @@
x Expected whitespace, semicolon, EOF, at-keyword, '&' or ident token
,-[$DIR/tests/recovery/cdo-and-cdc/input.css:5:1]
5 | <!--
: ^^^^
`----
x Expected whitespace, semicolon, EOF, at-keyword, '&' or ident token
,-[$DIR/tests/recovery/cdo-and-cdc/input.css:14:1]
14 | <!--
: ^^^^
`----
x Expected whitespace, semicolon, EOF, at-keyword, '&' or ident token
,-[$DIR/tests/recovery/cdo-and-cdc/input.css:25:1]
25 | <!--
: ^^^^
`----
x Invalid selector x Invalid selector
,-[$DIR/tests/recovery/cdo-and-cdc/input.css:1:1] ,-[$DIR/tests/recovery/cdo-and-cdc/input.css:1:1]
1 | <!-- 123 --> 1 | <!-- 123 -->
: ^^^ : ^^^
`---- `----
x Unexpected end of file, but expected '{'
,-[$DIR/tests/recovery/cdo-and-cdc/input.css:5:1]
5 | ,-> <!--
6 | |
7 | | test
8 | |
9 | `-> -->
10 | }
`----
x Unexpected end of file, but expected '{'
,-[$DIR/tests/recovery/cdo-and-cdc/input.css:14:1]
14 | ,-> <!--
15 | |
16 | | test
17 | |
18 | | -->
19 | |
20 | `-> color: blue;
`----
x Unexpected end of file, but expected '{'
,-[$DIR/tests/recovery/cdo-and-cdc/input.css:25:1]
25 | ,-> <!--
26 | |
27 | | test
28 | |
29 | `-> -->;
`----
x Unexpected token
,-[$DIR/tests/recovery/cdo-and-cdc/input.css:5:1]
5 | <!--
: ^^^^
`----
x Unexpected token
,-[$DIR/tests/recovery/cdo-and-cdc/input.css:14:1]
14 | <!--
: ^^^^
`----
x Unexpected token
,-[$DIR/tests/recovery/cdo-and-cdc/input.css:25:1]
25 | <!--
: ^^^^
`----

View File

@ -8522,37 +8522,6 @@
"token": "LBrace" "token": "LBrace"
}, },
"value": [ "value": [
{
"type": "Declaration",
"span": {
"start": 2568,
"end": 2578,
"ctxt": 0
},
"name": {
"type": "Ident",
"span": {
"start": 2568,
"end": 2573,
"ctxt": 0
},
"value": "color",
"raw": "color"
},
"value": [
{
"type": "Ident",
"span": {
"start": 2575,
"end": 2578,
"ctxt": 0
},
"value": "red",
"raw": "red"
}
],
"important": null
},
{ {
"type": "ListOfComponentValues", "type": "ListOfComponentValues",
"span": { "span": {
@ -8634,6 +8603,37 @@
"token": "Semi" "token": "Semi"
} }
] ]
},
{
"type": "Declaration",
"span": {
"start": 2568,
"end": 2578,
"ctxt": 0
},
"name": {
"type": "Ident",
"span": {
"start": 2568,
"end": 2573,
"ctxt": 0
},
"value": "color",
"raw": "color"
},
"value": [
{
"type": "Ident",
"span": {
"start": 2575,
"end": 2578,
"ctxt": 0
},
"value": "red",
"raw": "red"
}
],
"important": null
} }
] ]
} }

View File

@ -11,173 +11,334 @@
: ^ : ^
`---- `----
x Expected whitespace, semicolon, EOF, at-keyword, '&' or ident token
,-[$DIR/tests/recovery/hacks/input.css:13:1]
13 | .selector { (;property: value;); }
: ^
`----
x Expected whitespace, semicolon, EOF, at-keyword, '&' or ident token
,-[$DIR/tests/recovery/hacks/input.css:14:1]
14 | .selector { [;property: value;]; }
: ^
`----
x Expected whitespace, semicolon, EOF, at-keyword, '&' or ident token
,-[$DIR/tests/recovery/hacks/input.css:104:5]
104 | !property: value;
: ^
`----
x Expected whitespace, semicolon, EOF, at-keyword, '&' or ident token
,-[$DIR/tests/recovery/hacks/input.css:108:5]
108 | $property: value;
: ^
`----
x Expected whitespace, semicolon, EOF, at-keyword, '&' or ident token
,-[$DIR/tests/recovery/hacks/input.css:116:5]
116 | *property: value;
: ^
`----
x Expected whitespace, semicolon, EOF, at-keyword, '&' or ident token
,-[$DIR/tests/recovery/hacks/input.css:120:5]
120 | )property: value;
: ^
`----
x Expected whitespace, semicolon, EOF, at-keyword, '&' or ident token
,-[$DIR/tests/recovery/hacks/input.css:124:5]
124 | =property: value;
: ^
`----
x Expected whitespace, semicolon, EOF, at-keyword, '&' or ident token
,-[$DIR/tests/recovery/hacks/input.css:128:5]
128 | %property: value;
: ^
`----
x Expected whitespace, semicolon, EOF, at-keyword, '&' or ident token
,-[$DIR/tests/recovery/hacks/input.css:132:5]
132 | +property: value;
: ^
`----
x Expected whitespace, semicolon, EOF, at-keyword, '&' or ident token
,-[$DIR/tests/recovery/hacks/input.css:140:5]
140 | ,property: value;
: ^
`----
x Expected whitespace, semicolon, EOF, at-keyword, '&' or ident token
,-[$DIR/tests/recovery/hacks/input.css:144:5]
144 | .property: value;
: ^
`----
x Expected whitespace, semicolon, EOF, at-keyword, '&' or ident token
,-[$DIR/tests/recovery/hacks/input.css:148:5]
148 | /property: value;
: ^
`----
x Expected whitespace, semicolon, EOF, at-keyword, '&' or ident token
,-[$DIR/tests/recovery/hacks/input.css:152:5]
152 | `property: value;
: ^
`----
x Expected whitespace, semicolon, EOF, at-keyword, '&' or ident token
,-[$DIR/tests/recovery/hacks/input.css:156:5]
156 | ]property: value;
: ^
`----
x Expected whitespace, semicolon, EOF, at-keyword, '&' or ident token
,-[$DIR/tests/recovery/hacks/input.css:160:5]
160 | #property: value;
: ^^^^^^^^^
`----
x Expected whitespace, semicolon, EOF, at-keyword, '&' or ident token
,-[$DIR/tests/recovery/hacks/input.css:164:5]
164 | ~property: value;
: ^
`----
x Expected whitespace, semicolon, EOF, at-keyword, '&' or ident token
,-[$DIR/tests/recovery/hacks/input.css:168:5]
168 | ?property: value;
: ^
`----
x Expected whitespace, semicolon, EOF, at-keyword, '&' or ident token
,-[$DIR/tests/recovery/hacks/input.css:172:5]
172 | :property: value;
: ^
`----
x Expected whitespace, semicolon, EOF, at-keyword, '&' or ident token
,-[$DIR/tests/recovery/hacks/input.css:176:5]
176 | |property: value;
: ^
`----
x Expected whitespace, semicolon, EOF, at-keyword, '&' or ident token
,-[$DIR/tests/recovery/hacks/input.css:204:1]
204 | .selector { (;property: value;); }
: ^
`----
x Expected whitespace, semicolon, EOF, at-keyword, '&' or ident token
,-[$DIR/tests/recovery/hacks/input.css:205:1]
205 | .selector { [;property: value;]; }
: ^
`----
x Expected whitespace, semicolon, EOF, at-keyword, '&' or ident token
,-[$DIR/tests/recovery/hacks/input.css:225:1]
225 | .selector { (;property: value;); }
: ^
`----
x Expected whitespace, semicolon, EOF, at-keyword, '&' or ident token
,-[$DIR/tests/recovery/hacks/input.css:226:1]
226 | .selector { [;property: value;]; }
: ^
`----
x Expected whitespace, semicolon, EOF, at-keyword, '&' or ident token
,-[$DIR/tests/recovery/hacks/input.css:244:5]
244 | *color : black;
: ^
`----
x Expected whitespace, semicolon, EOF, at-keyword, '&' or ident token
,-[$DIR/tests/recovery/hacks/input.css:247:5]
247 | $(var)-size: 100%;
: ^
`----
x Expected whitespace, semicolon, EOF, at-keyword, '&' or ident token
,-[$DIR/tests/recovery/hacks/input.css:250:1]
250 | a{*b:c}
: ^
`----
x Unexpected '!' in <declaration-value> x Unexpected '!' in <declaration-value>
,-[$DIR/tests/recovery/hacks/input.css:180:1] ,-[$DIR/tests/recovery/hacks/input.css:180:1]
180 | .selector { property: value !ie; } 180 | .selector { property: value !ie; }
: ^ : ^
`---- `----
x Unexpected end of file, but expected '{'
,-[$DIR/tests/recovery/hacks/input.css:13:1]
13 | .selector { (;property: value;); }
: ^^^^^^^^^^^^^^^^^^^
`----
x Unexpected end of file, but expected '{'
,-[$DIR/tests/recovery/hacks/input.css:14:1]
14 | .selector { [;property: value;]; }
: ^^^^^^^^^^^^^^^^^^^
`----
x Unexpected end of file, but expected '{'
,-[$DIR/tests/recovery/hacks/input.css:104:5]
104 | !property: value;
: ^^^^^^^^^^^^^^^^
`----
x Unexpected end of file, but expected '{'
,-[$DIR/tests/recovery/hacks/input.css:108:5]
108 | $property: value;
: ^^^^^^^^^^^^^^^^
`----
x Unexpected end of file, but expected '{' x Unexpected end of file, but expected '{'
,-[$DIR/tests/recovery/hacks/input.css:112:5] ,-[$DIR/tests/recovery/hacks/input.css:112:5]
112 | ,-> &property: value; 112 | &property: value;
113 | `-> color: red; : ^^^^^^^^^^^^^^^^
114 | } `----
x Unexpected end of file, but expected '{'
,-[$DIR/tests/recovery/hacks/input.css:116:5]
116 | *property: value;
: ^^^^^^^^^^^^^^^^
`----
x Unexpected end of file, but expected '{'
,-[$DIR/tests/recovery/hacks/input.css:120:5]
120 | )property: value;
: ^^^^^^^^^^^^^^^^
`----
x Unexpected end of file, but expected '{'
,-[$DIR/tests/recovery/hacks/input.css:124:5]
124 | =property: value;
: ^^^^^^^^^^^^^^^^
`----
x Unexpected end of file, but expected '{'
,-[$DIR/tests/recovery/hacks/input.css:128:5]
128 | %property: value;
: ^^^^^^^^^^^^^^^^
`----
x Unexpected end of file, but expected '{'
,-[$DIR/tests/recovery/hacks/input.css:132:5]
132 | +property: value;
: ^^^^^^^^^^^^^^^^
`----
x Unexpected end of file, but expected '{'
,-[$DIR/tests/recovery/hacks/input.css:140:5]
140 | ,property: value;
: ^^^^^^^^^^^^^^^^
`----
x Unexpected end of file, but expected '{'
,-[$DIR/tests/recovery/hacks/input.css:144:5]
144 | .property: value;
: ^^^^^^^^^^^^^^^^
`----
x Unexpected end of file, but expected '{'
,-[$DIR/tests/recovery/hacks/input.css:148:5]
148 | /property: value;
: ^^^^^^^^^^^^^^^^
`----
x Unexpected end of file, but expected '{'
,-[$DIR/tests/recovery/hacks/input.css:152:5]
152 | `property: value;
: ^^^^^^^^^^^^^^^^
`----
x Unexpected end of file, but expected '{'
,-[$DIR/tests/recovery/hacks/input.css:156:5]
156 | ]property: value;
: ^^^^^^^^^^^^^^^^
`----
x Unexpected end of file, but expected '{'
,-[$DIR/tests/recovery/hacks/input.css:160:5]
160 | #property: value;
: ^^^^^^^^^^^^^^^^
`----
x Unexpected end of file, but expected '{'
,-[$DIR/tests/recovery/hacks/input.css:164:5]
164 | ~property: value;
: ^^^^^^^^^^^^^^^^
`----
x Unexpected end of file, but expected '{'
,-[$DIR/tests/recovery/hacks/input.css:168:5]
168 | ?property: value;
: ^^^^^^^^^^^^^^^^
`----
x Unexpected end of file, but expected '{'
,-[$DIR/tests/recovery/hacks/input.css:172:5]
172 | :property: value;
: ^^^^^^^^^^^^^^^^
`----
x Unexpected end of file, but expected '{'
,-[$DIR/tests/recovery/hacks/input.css:176:5]
176 | |property: value;
: ^^^^^^^^^^^^^^^^
`----
x Unexpected end of file, but expected '{'
,-[$DIR/tests/recovery/hacks/input.css:204:1]
204 | .selector { (;property: value;); }
: ^^^^^^^^^^^^^^^^^^^
`----
x Unexpected end of file, but expected '{'
,-[$DIR/tests/recovery/hacks/input.css:205:1]
205 | .selector { [;property: value;]; }
: ^^^^^^^^^^^^^^^^^^^
`----
x Unexpected end of file, but expected '{'
,-[$DIR/tests/recovery/hacks/input.css:225:1]
225 | .selector { (;property: value;); }
: ^^^^^^^^^^^^^^^^^^^
`----
x Unexpected end of file, but expected '{'
,-[$DIR/tests/recovery/hacks/input.css:226:1]
226 | .selector { [;property: value;]; }
: ^^^^^^^^^^^^^^^^^^^
`----
x Unexpected end of file, but expected '{'
,-[$DIR/tests/recovery/hacks/input.css:244:5]
244 | *color : black;
: ^^^^^^^^^^^^^^
`----
x Unexpected end of file, but expected '{'
,-[$DIR/tests/recovery/hacks/input.css:247:5]
247 | $(var)-size: 100%;
: ^^^^^^^^^^^^^^^^^
`----
x Unexpected end of file, but expected '{'
,-[$DIR/tests/recovery/hacks/input.css:250:1]
250 | a{*b:c}
: ^^^^
`----
x Unexpected token
,-[$DIR/tests/recovery/hacks/input.css:13:1]
13 | .selector { (;property: value;); }
: ^
`----
x Unexpected token
,-[$DIR/tests/recovery/hacks/input.css:14:1]
14 | .selector { [;property: value;]; }
: ^
`----
x Unexpected token
,-[$DIR/tests/recovery/hacks/input.css:104:5]
104 | !property: value;
: ^
`----
x Unexpected token
,-[$DIR/tests/recovery/hacks/input.css:108:5]
108 | $property: value;
: ^
`----
x Unexpected token
,-[$DIR/tests/recovery/hacks/input.css:112:5]
112 | &property: value;
: ^
`----
x Unexpected token
,-[$DIR/tests/recovery/hacks/input.css:116:5]
116 | *property: value;
: ^
`----
x Unexpected token
,-[$DIR/tests/recovery/hacks/input.css:120:5]
120 | )property: value;
: ^
`----
x Unexpected token
,-[$DIR/tests/recovery/hacks/input.css:124:5]
124 | =property: value;
: ^
`----
x Unexpected token
,-[$DIR/tests/recovery/hacks/input.css:128:5]
128 | %property: value;
: ^
`----
x Unexpected token
,-[$DIR/tests/recovery/hacks/input.css:132:5]
132 | +property: value;
: ^
`----
x Unexpected token
,-[$DIR/tests/recovery/hacks/input.css:140:5]
140 | ,property: value;
: ^
`----
x Unexpected token
,-[$DIR/tests/recovery/hacks/input.css:144:5]
144 | .property: value;
: ^
`----
x Unexpected token
,-[$DIR/tests/recovery/hacks/input.css:148:5]
148 | /property: value;
: ^
`----
x Unexpected token
,-[$DIR/tests/recovery/hacks/input.css:152:5]
152 | `property: value;
: ^
`----
x Unexpected token
,-[$DIR/tests/recovery/hacks/input.css:156:5]
156 | ]property: value;
: ^
`----
x Unexpected token
,-[$DIR/tests/recovery/hacks/input.css:160:5]
160 | #property: value;
: ^^^^^^^^^
`----
x Unexpected token
,-[$DIR/tests/recovery/hacks/input.css:164:5]
164 | ~property: value;
: ^
`----
x Unexpected token
,-[$DIR/tests/recovery/hacks/input.css:168:5]
168 | ?property: value;
: ^
`----
x Unexpected token
,-[$DIR/tests/recovery/hacks/input.css:172:5]
172 | :property: value;
: ^
`----
x Unexpected token
,-[$DIR/tests/recovery/hacks/input.css:176:5]
176 | |property: value;
: ^
`----
x Unexpected token
,-[$DIR/tests/recovery/hacks/input.css:204:1]
204 | .selector { (;property: value;); }
: ^
`----
x Unexpected token
,-[$DIR/tests/recovery/hacks/input.css:205:1]
205 | .selector { [;property: value;]; }
: ^
`----
x Unexpected token
,-[$DIR/tests/recovery/hacks/input.css:225:1]
225 | .selector { (;property: value;); }
: ^
`----
x Unexpected token
,-[$DIR/tests/recovery/hacks/input.css:226:1]
226 | .selector { [;property: value;]; }
: ^
`----
x Unexpected token
,-[$DIR/tests/recovery/hacks/input.css:244:5]
244 | *color : black;
: ^
`----
x Unexpected token
,-[$DIR/tests/recovery/hacks/input.css:247:5]
247 | $(var)-size: 100%;
: ^
`----
x Unexpected token
,-[$DIR/tests/recovery/hacks/input.css:250:1]
250 | a{*b:c}
: ^
`---- `----
x Unexpected tokens in at-rule prelude x Unexpected tokens in at-rule prelude

View File

@ -8227,48 +8227,6 @@
: ^ : ^
`---- `----
x ComponentValue
,-[$DIR/tests/recovery/hacks/input.css:113:5]
113 | color: red;
: ^^^^^^^^^^
`----
x StyleBlock
,-[$DIR/tests/recovery/hacks/input.css:113:5]
113 | color: red;
: ^^^^^^^^^^
`----
x Declaration
,-[$DIR/tests/recovery/hacks/input.css:113:5]
113 | color: red;
: ^^^^^^^^^^
`----
x DeclarationName
,-[$DIR/tests/recovery/hacks/input.css:113:5]
113 | color: red;
: ^^^^^
`----
x Ident
,-[$DIR/tests/recovery/hacks/input.css:113:5]
113 | color: red;
: ^^^^^
`----
x ComponentValue
,-[$DIR/tests/recovery/hacks/input.css:113:5]
113 | color: red;
: ^^^
`----
x Ident
,-[$DIR/tests/recovery/hacks/input.css:113:5]
113 | color: red;
: ^^^
`----
x ComponentValue x ComponentValue
,-[$DIR/tests/recovery/hacks/input.css:112:5] ,-[$DIR/tests/recovery/hacks/input.css:112:5]
112 | &property: value; 112 | &property: value;
@ -8353,6 +8311,48 @@
: ^ : ^
`---- `----
x ComponentValue
,-[$DIR/tests/recovery/hacks/input.css:113:5]
113 | color: red;
: ^^^^^^^^^^
`----
x StyleBlock
,-[$DIR/tests/recovery/hacks/input.css:113:5]
113 | color: red;
: ^^^^^^^^^^
`----
x Declaration
,-[$DIR/tests/recovery/hacks/input.css:113:5]
113 | color: red;
: ^^^^^^^^^^
`----
x DeclarationName
,-[$DIR/tests/recovery/hacks/input.css:113:5]
113 | color: red;
: ^^^^^
`----
x Ident
,-[$DIR/tests/recovery/hacks/input.css:113:5]
113 | color: red;
: ^^^^^
`----
x ComponentValue
,-[$DIR/tests/recovery/hacks/input.css:113:5]
113 | color: red;
: ^^^
`----
x Ident
,-[$DIR/tests/recovery/hacks/input.css:113:5]
113 | color: red;
: ^^^
`----
x Rule x Rule
,-[$DIR/tests/recovery/hacks/input.css:115:1] ,-[$DIR/tests/recovery/hacks/input.css:115:1]
115 | ,-> .selector { 115 | ,-> .selector {

View File

@ -119,7 +119,7 @@
"ctxt": 0 "ctxt": 0
}, },
"prelude": { "prelude": {
"type": "SelectorList", "type": "RelativeSelectorList",
"span": { "span": {
"start": 32, "start": 32,
"end": 37, "end": 37,
@ -127,51 +127,60 @@
}, },
"children": [ "children": [
{ {
"type": "ComplexSelector", "type": "RelativeSelector",
"span": { "span": {
"start": 32, "start": 32,
"end": 37, "end": 37,
"ctxt": 0 "ctxt": 0
}, },
"children": [ "combinator": null,
{ "selector": {
"type": "CompoundSelector", "type": "ComplexSelector",
"span": { "span": {
"start": 32, "start": 32,
"end": 37, "end": 37,
"ctxt": 0 "ctxt": 0
}, },
"nestingSelector": { "children": [
"type": "NestingSelector", {
"type": "CompoundSelector",
"span": { "span": {
"start": 32, "start": 32,
"end": 33, "end": 37,
"ctxt": 0 "ctxt": 0
} },
}, "nestingSelector": {
"typeSelector": null, "type": "NestingSelector",
"subclassSelectors": [
{
"type": "ClassSelector",
"span": { "span": {
"start": 33, "start": 32,
"end": 37, "end": 33,
"ctxt": 0 "ctxt": 0
}, }
"text": { },
"type": "Ident", "typeSelector": null,
"subclassSelectors": [
{
"type": "ClassSelector",
"span": { "span": {
"start": 34, "start": 33,
"end": 37, "end": 37,
"ctxt": 0 "ctxt": 0
}, },
"value": "foo", "text": {
"raw": "foo" "type": "Ident",
"span": {
"start": 34,
"end": 37,
"ctxt": 0
},
"value": "foo",
"raw": "foo"
}
} }
} ]
] }
} ]
] }
} }
] ]
}, },
@ -423,7 +432,7 @@
"ctxt": 0 "ctxt": 0
}, },
"prelude": { "prelude": {
"type": "SelectorList", "type": "RelativeSelectorList",
"span": { "span": {
"start": 150, "start": 150,
"end": 158, "end": 158,
@ -431,71 +440,80 @@
}, },
"children": [ "children": [
{ {
"type": "ComplexSelector", "type": "RelativeSelector",
"span": { "span": {
"start": 150, "start": 150,
"end": 158, "end": 158,
"ctxt": 0 "ctxt": 0
}, },
"children": [ "combinator": null,
{ "selector": {
"type": "CompoundSelector", "type": "ComplexSelector",
"span": { "span": {
"start": 150, "start": 150,
"end": 151, "end": 158,
"ctxt": 0 "ctxt": 0
}, },
"nestingSelector": { "children": [
"type": "NestingSelector", {
"type": "CompoundSelector",
"span": { "span": {
"start": 150, "start": 150,
"end": 151, "end": 151,
"ctxt": 0 "ctxt": 0
} },
}, "nestingSelector": {
"typeSelector": null, "type": "NestingSelector",
"subclassSelectors": []
},
{
"type": "Combinator",
"span": {
"start": 151,
"end": 152,
"ctxt": 0
},
"value": " "
},
{
"type": "CompoundSelector",
"span": {
"start": 152,
"end": 158,
"ctxt": 0
},
"nestingSelector": null,
"typeSelector": null,
"subclassSelectors": [
{
"type": "ClassSelector",
"span": { "span": {
"start": 152, "start": 150,
"end": 158, "end": 151,
"ctxt": 0 "ctxt": 0
}, }
"text": { },
"type": "Ident", "typeSelector": null,
"subclassSelectors": []
},
{
"type": "Combinator",
"span": {
"start": 151,
"end": 152,
"ctxt": 0
},
"value": " "
},
{
"type": "CompoundSelector",
"span": {
"start": 152,
"end": 158,
"ctxt": 0
},
"nestingSelector": null,
"typeSelector": null,
"subclassSelectors": [
{
"type": "ClassSelector",
"span": { "span": {
"start": 153, "start": 152,
"end": 158, "end": 158,
"ctxt": 0 "ctxt": 0
}, },
"value": "class", "text": {
"raw": "class" "type": "Ident",
"span": {
"start": 153,
"end": 158,
"ctxt": 0
},
"value": "class",
"raw": "class"
}
} }
} ]
] }
} ]
] }
} }
] ]
}, },
@ -738,7 +756,7 @@
"ctxt": 0 "ctxt": 0
}, },
"prelude": { "prelude": {
"type": "SelectorList", "type": "RelativeSelectorList",
"span": { "span": {
"start": 254, "start": 254,
"end": 259, "end": 259,
@ -746,51 +764,60 @@
}, },
"children": [ "children": [
{ {
"type": "ComplexSelector", "type": "RelativeSelector",
"span": { "span": {
"start": 254, "start": 254,
"end": 259, "end": 259,
"ctxt": 0 "ctxt": 0
}, },
"children": [ "combinator": null,
{ "selector": {
"type": "CompoundSelector", "type": "ComplexSelector",
"span": { "span": {
"start": 254, "start": 254,
"end": 259, "end": 259,
"ctxt": 0 "ctxt": 0
}, },
"nestingSelector": { "children": [
"type": "NestingSelector", {
"type": "CompoundSelector",
"span": { "span": {
"start": 254, "start": 254,
"end": 255, "end": 259,
"ctxt": 0 "ctxt": 0
} },
}, "nestingSelector": {
"typeSelector": null, "type": "NestingSelector",
"subclassSelectors": [
{
"type": "ClassSelector",
"span": { "span": {
"start": 255, "start": 254,
"end": 259, "end": 255,
"ctxt": 0 "ctxt": 0
}, }
"text": { },
"type": "Ident", "typeSelector": null,
"subclassSelectors": [
{
"type": "ClassSelector",
"span": { "span": {
"start": 256, "start": 255,
"end": 259, "end": 259,
"ctxt": 0 "ctxt": 0
}, },
"value": "foo", "text": {
"raw": "foo" "type": "Ident",
"span": {
"start": 256,
"end": 259,
"ctxt": 0
},
"value": "foo",
"raw": "foo"
}
} }
} ]
] }
} ]
] }
} }
] ]
}, },
@ -1010,7 +1037,7 @@
"ctxt": 0 "ctxt": 0
}, },
"prelude": { "prelude": {
"type": "SelectorList", "type": "RelativeSelectorList",
"span": { "span": {
"start": 373, "start": 373,
"end": 381, "end": 381,
@ -1018,71 +1045,80 @@
}, },
"children": [ "children": [
{ {
"type": "ComplexSelector", "type": "RelativeSelector",
"span": { "span": {
"start": 373, "start": 373,
"end": 381, "end": 381,
"ctxt": 0 "ctxt": 0
}, },
"children": [ "combinator": null,
{ "selector": {
"type": "CompoundSelector", "type": "ComplexSelector",
"span": { "span": {
"start": 373, "start": 373,
"end": 374, "end": 381,
"ctxt": 0 "ctxt": 0
}, },
"nestingSelector": { "children": [
"type": "NestingSelector", {
"type": "CompoundSelector",
"span": { "span": {
"start": 373, "start": 373,
"end": 374, "end": 374,
"ctxt": 0 "ctxt": 0
} },
}, "nestingSelector": {
"typeSelector": null, "type": "NestingSelector",
"subclassSelectors": []
},
{
"type": "Combinator",
"span": {
"start": 374,
"end": 375,
"ctxt": 0
},
"value": " "
},
{
"type": "CompoundSelector",
"span": {
"start": 375,
"end": 381,
"ctxt": 0
},
"nestingSelector": null,
"typeSelector": null,
"subclassSelectors": [
{
"type": "ClassSelector",
"span": { "span": {
"start": 375, "start": 373,
"end": 381, "end": 374,
"ctxt": 0 "ctxt": 0
}, }
"text": { },
"type": "Ident", "typeSelector": null,
"subclassSelectors": []
},
{
"type": "Combinator",
"span": {
"start": 374,
"end": 375,
"ctxt": 0
},
"value": " "
},
{
"type": "CompoundSelector",
"span": {
"start": 375,
"end": 381,
"ctxt": 0
},
"nestingSelector": null,
"typeSelector": null,
"subclassSelectors": [
{
"type": "ClassSelector",
"span": { "span": {
"start": 376, "start": 375,
"end": 381, "end": 381,
"ctxt": 0 "ctxt": 0
}, },
"value": "class", "text": {
"raw": "class" "type": "Ident",
"span": {
"start": 376,
"end": 381,
"ctxt": 0
},
"value": "class",
"raw": "class"
}
} }
} ]
] }
} ]
] }
} }
] ]
}, },
@ -1425,7 +1461,7 @@
"ctxt": 0 "ctxt": 0
}, },
"prelude": { "prelude": {
"type": "SelectorList", "type": "RelativeSelectorList",
"span": { "span": {
"start": 499, "start": 499,
"end": 504, "end": 504,
@ -1433,51 +1469,60 @@
}, },
"children": [ "children": [
{ {
"type": "ComplexSelector", "type": "RelativeSelector",
"span": { "span": {
"start": 499, "start": 499,
"end": 504, "end": 504,
"ctxt": 0 "ctxt": 0
}, },
"children": [ "combinator": null,
{ "selector": {
"type": "CompoundSelector", "type": "ComplexSelector",
"span": { "span": {
"start": 499, "start": 499,
"end": 504, "end": 504,
"ctxt": 0 "ctxt": 0
}, },
"nestingSelector": { "children": [
"type": "NestingSelector", {
"type": "CompoundSelector",
"span": { "span": {
"start": 499, "start": 499,
"end": 500, "end": 504,
"ctxt": 0 "ctxt": 0
} },
}, "nestingSelector": {
"typeSelector": null, "type": "NestingSelector",
"subclassSelectors": [
{
"type": "ClassSelector",
"span": { "span": {
"start": 500, "start": 499,
"end": 504, "end": 500,
"ctxt": 0 "ctxt": 0
}, }
"text": { },
"type": "Ident", "typeSelector": null,
"subclassSelectors": [
{
"type": "ClassSelector",
"span": { "span": {
"start": 501, "start": 500,
"end": 504, "end": 504,
"ctxt": 0 "ctxt": 0
}, },
"value": "foo", "text": {
"raw": "foo" "type": "Ident",
"span": {
"start": 501,
"end": 504,
"ctxt": 0
},
"value": "foo",
"raw": "foo"
}
} }
} ]
] }
} ]
] }
} }
] ]
}, },

View File

@ -32,6 +32,11 @@
x Unexpected end of file, but expected '{' x Unexpected end of file, but expected '{'
,-[$DIR/tests/recovery/style-blocks-contents/basic/input.css:39:5] ,-[$DIR/tests/recovery/style-blocks-contents/basic/input.css:39:5]
39 | & test; 39 | & test;
: ^^^^^^^^ : ^^^^^^
40 | } `----
x Unexpected token
,-[$DIR/tests/recovery/style-blocks-contents/basic/input.css:39:5]
39 | & test;
: ^
`---- `----

View File

@ -221,7 +221,13 @@
7 | `-> } 7 | `-> }
`---- `----
x SelectorList x RelativeSelectorList
,-[$DIR/tests/recovery/style-blocks-contents/basic/input.css:4:5]
4 | &.foo {
: ^^^^^
`----
x RelativeSelector
,-[$DIR/tests/recovery/style-blocks-contents/basic/input.css:4:5] ,-[$DIR/tests/recovery/style-blocks-contents/basic/input.css:4:5]
4 | &.foo { 4 | &.foo {
: ^^^^^ : ^^^^^
@ -572,7 +578,13 @@
15 | `-> } 15 | `-> }
`---- `----
x SelectorList x RelativeSelectorList
,-[$DIR/tests/recovery/style-blocks-contents/basic/input.css:12:9]
12 | & .class {
: ^^^^^^^^
`----
x RelativeSelector
,-[$DIR/tests/recovery/style-blocks-contents/basic/input.css:12:9] ,-[$DIR/tests/recovery/style-blocks-contents/basic/input.css:12:9]
12 | & .class { 12 | & .class {
: ^^^^^^^^ : ^^^^^^^^
@ -911,7 +923,13 @@
25 | `-> } 25 | `-> }
`---- `----
x SelectorList x RelativeSelectorList
,-[$DIR/tests/recovery/style-blocks-contents/basic/input.css:22:5]
22 | &.foo {
: ^^^^^
`----
x RelativeSelector
,-[$DIR/tests/recovery/style-blocks-contents/basic/input.css:22:5] ,-[$DIR/tests/recovery/style-blocks-contents/basic/input.css:22:5]
22 | &.foo { 22 | &.foo {
: ^^^^^ : ^^^^^
@ -1243,7 +1261,13 @@
33 | `-> } 33 | `-> }
`---- `----
x SelectorList x RelativeSelectorList
,-[$DIR/tests/recovery/style-blocks-contents/basic/input.css:30:9]
30 | & .class {
: ^^^^^^^^
`----
x RelativeSelector
,-[$DIR/tests/recovery/style-blocks-contents/basic/input.css:30:9] ,-[$DIR/tests/recovery/style-blocks-contents/basic/input.css:30:9]
30 | & .class { 30 | & .class {
: ^^^^^^^^ : ^^^^^^^^
@ -1647,7 +1671,13 @@
46 | `-> } 46 | `-> }
`---- `----
x SelectorList x RelativeSelectorList
,-[$DIR/tests/recovery/style-blocks-contents/basic/input.css:44:5]
44 | &.foo {
: ^^^^^
`----
x RelativeSelector
,-[$DIR/tests/recovery/style-blocks-contents/basic/input.css:44:5] ,-[$DIR/tests/recovery/style-blocks-contents/basic/input.css:44:5]
44 | &.foo { 44 | &.foo {
: ^^^^^ : ^^^^^

View File

@ -111,37 +111,6 @@
], ],
"important": null "important": null
}, },
{
"type": "Declaration",
"span": {
"start": 42,
"end": 58,
"ctxt": 0
},
"name": {
"type": "Ident",
"span": {
"start": 42,
"end": 52,
"ctxt": 0
},
"value": "background",
"raw": "background"
},
"value": [
{
"type": "Ident",
"span": {
"start": 54,
"end": 57,
"ctxt": 0
},
"value": "red",
"raw": "red"
}
],
"important": null
},
{ {
"type": "ListOfComponentValues", "type": "ListOfComponentValues",
"span": { "span": {
@ -200,6 +169,37 @@
"token": "Semi" "token": "Semi"
} }
] ]
},
{
"type": "Declaration",
"span": {
"start": 42,
"end": 58,
"ctxt": 0
},
"name": {
"type": "Ident",
"span": {
"start": 42,
"end": 52,
"ctxt": 0
},
"value": "background",
"raw": "background"
},
"value": [
{
"type": "Ident",
"span": {
"start": 54,
"end": 57,
"ctxt": 0
},
"value": "red",
"raw": "red"
}
],
"important": null
} }
] ]
} }
@ -355,7 +355,7 @@
"ctxt": 0 "ctxt": 0
}, },
"prelude": { "prelude": {
"type": "SelectorList", "type": "RelativeSelectorList",
"span": { "span": {
"start": 93, "start": 93,
"end": 94, "end": 94,
@ -363,32 +363,41 @@
}, },
"children": [ "children": [
{ {
"type": "ComplexSelector", "type": "RelativeSelector",
"span": { "span": {
"start": 93, "start": 93,
"end": 94, "end": 94,
"ctxt": 0 "ctxt": 0
}, },
"children": [ "combinator": null,
{ "selector": {
"type": "CompoundSelector", "type": "ComplexSelector",
"span": { "span": {
"start": 93, "start": 93,
"end": 94, "end": 94,
"ctxt": 0 "ctxt": 0
}, },
"nestingSelector": { "children": [
"type": "NestingSelector", {
"type": "CompoundSelector",
"span": { "span": {
"start": 93, "start": 93,
"end": 94, "end": 94,
"ctxt": 0 "ctxt": 0
} },
}, "nestingSelector": {
"typeSelector": null, "type": "NestingSelector",
"subclassSelectors": [] "span": {
} "start": 93,
] "end": 94,
"ctxt": 0
}
},
"typeSelector": null,
"subclassSelectors": []
}
]
}
} }
] ]
}, },
@ -451,7 +460,7 @@
"ctxt": 0 "ctxt": 0
}, },
"prelude": { "prelude": {
"type": "SelectorList", "type": "RelativeSelectorList",
"span": { "span": {
"start": 132, "start": 132,
"end": 137, "end": 137,
@ -459,51 +468,60 @@
}, },
"children": [ "children": [
{ {
"type": "ComplexSelector", "type": "RelativeSelector",
"span": { "span": {
"start": 132, "start": 132,
"end": 137, "end": 137,
"ctxt": 0 "ctxt": 0
}, },
"children": [ "combinator": null,
{ "selector": {
"type": "CompoundSelector", "type": "ComplexSelector",
"span": { "span": {
"start": 132, "start": 132,
"end": 137, "end": 137,
"ctxt": 0 "ctxt": 0
}, },
"nestingSelector": { "children": [
"type": "NestingSelector", {
"type": "CompoundSelector",
"span": { "span": {
"start": 132, "start": 132,
"end": 133, "end": 137,
"ctxt": 0 "ctxt": 0
} },
}, "nestingSelector": {
"typeSelector": null, "type": "NestingSelector",
"subclassSelectors": [
{
"type": "ClassSelector",
"span": { "span": {
"start": 133, "start": 132,
"end": 137, "end": 133,
"ctxt": 0 "ctxt": 0
}, }
"text": { },
"type": "Ident", "typeSelector": null,
"subclassSelectors": [
{
"type": "ClassSelector",
"span": { "span": {
"start": 134, "start": 133,
"end": 137, "end": 137,
"ctxt": 0 "ctxt": 0
}, },
"value": "foo", "text": {
"raw": "foo" "type": "Ident",
"span": {
"start": 134,
"end": 137,
"ctxt": 0
},
"value": "foo",
"raw": "foo"
}
} }
} ]
] }
} ]
] }
} }
] ]
}, },

View File

@ -1,7 +1,12 @@
x Unexpected end of file, but expected '{' x Unexpected end of file, but expected '{'
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested-2/input.css:3:5] ,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested-2/input.css:3:5]
3 | ,-> & test; 3 | & test;
4 | `-> background: red : ^^^^^^
5 | } `----
x Unexpected token
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested-2/input.css:3:5]
3 | & test;
: ^
`---- `----

View File

@ -126,51 +126,6 @@
: ^^^ : ^^^
`---- `----
x ComponentValue
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested-2/input.css:4:5]
4 | background: red
: ^^^^^^^^^^^^^^^^
5 | }
`----
x StyleBlock
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested-2/input.css:4:5]
4 | background: red
: ^^^^^^^^^^^^^^^^
5 | }
`----
x Declaration
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested-2/input.css:4:5]
4 | background: red
: ^^^^^^^^^^^^^^^^
5 | }
`----
x DeclarationName
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested-2/input.css:4:5]
4 | background: red
: ^^^^^^^^^^
`----
x Ident
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested-2/input.css:4:5]
4 | background: red
: ^^^^^^^^^^
`----
x ComponentValue
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested-2/input.css:4:5]
4 | background: red
: ^^^
`----
x Ident
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested-2/input.css:4:5]
4 | background: red
: ^^^
`----
x ComponentValue x ComponentValue
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested-2/input.css:3:5] ,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested-2/input.css:3:5]
3 | & test; 3 | & test;
@ -231,6 +186,51 @@
: ^ : ^
`---- `----
x ComponentValue
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested-2/input.css:4:5]
4 | background: red
: ^^^^^^^^^^^^^^^^
5 | }
`----
x StyleBlock
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested-2/input.css:4:5]
4 | background: red
: ^^^^^^^^^^^^^^^^
5 | }
`----
x Declaration
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested-2/input.css:4:5]
4 | background: red
: ^^^^^^^^^^^^^^^^
5 | }
`----
x DeclarationName
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested-2/input.css:4:5]
4 | background: red
: ^^^^^^^^^^
`----
x Ident
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested-2/input.css:4:5]
4 | background: red
: ^^^^^^^^^^
`----
x ComponentValue
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested-2/input.css:4:5]
4 | background: red
: ^^^
`----
x Ident
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested-2/input.css:4:5]
4 | background: red
: ^^^
`----
x Rule x Rule
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested-2/input.css:7:1] ,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested-2/input.css:7:1]
7 | ,-> article { 7 | ,-> article {
@ -411,7 +411,13 @@
: ^^^^^^^^^^^^^^^^^^ : ^^^^^^^^^^^^^^^^^^
`---- `----
x SelectorList x RelativeSelectorList
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested-2/input.css:9:5]
9 | & { color: blue; }
: ^
`----
x RelativeSelector
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested-2/input.css:9:5] ,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested-2/input.css:9:5]
9 | & { color: blue; } 9 | & { color: blue; }
: ^ : ^
@ -507,7 +513,13 @@
: ^^^^^^^^^^^^^^^^^^^^^^^^ : ^^^^^^^^^^^^^^^^^^^^^^^^
`---- `----
x SelectorList x RelativeSelectorList
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested-2/input.css:11:5]
11 | &.foo { color: yellow; } /* valid! */
: ^^^^^
`----
x RelativeSelector
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested-2/input.css:11:5] ,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested-2/input.css:11:5]
11 | &.foo { color: yellow; } /* valid! */ 11 | &.foo { color: yellow; } /* valid! */
: ^^^^^ : ^^^^^

View File

@ -111,37 +111,6 @@
], ],
"important": null "important": null
}, },
{
"type": "Declaration",
"span": {
"start": 42,
"end": 57,
"ctxt": 0
},
"name": {
"type": "Ident",
"span": {
"start": 42,
"end": 52,
"ctxt": 0
},
"value": "background",
"raw": "background"
},
"value": [
{
"type": "Ident",
"span": {
"start": 54,
"end": 57,
"ctxt": 0
},
"value": "red",
"raw": "red"
}
],
"important": null
},
{ {
"type": "ListOfComponentValues", "type": "ListOfComponentValues",
"span": { "span": {
@ -200,6 +169,37 @@
"token": "Semi" "token": "Semi"
} }
] ]
},
{
"type": "Declaration",
"span": {
"start": 42,
"end": 57,
"ctxt": 0
},
"name": {
"type": "Ident",
"span": {
"start": 42,
"end": 52,
"ctxt": 0
},
"value": "background",
"raw": "background"
},
"value": [
{
"type": "Ident",
"span": {
"start": 54,
"end": 57,
"ctxt": 0
},
"value": "red",
"raw": "red"
}
],
"important": null
} }
] ]
} }
@ -355,7 +355,7 @@
"ctxt": 0 "ctxt": 0
}, },
"prelude": { "prelude": {
"type": "SelectorList", "type": "RelativeSelectorList",
"span": { "span": {
"start": 94, "start": 94,
"end": 95, "end": 95,
@ -363,32 +363,41 @@
}, },
"children": [ "children": [
{ {
"type": "ComplexSelector", "type": "RelativeSelector",
"span": { "span": {
"start": 94, "start": 94,
"end": 95, "end": 95,
"ctxt": 0 "ctxt": 0
}, },
"children": [ "combinator": null,
{ "selector": {
"type": "CompoundSelector", "type": "ComplexSelector",
"span": { "span": {
"start": 94, "start": 94,
"end": 95, "end": 95,
"ctxt": 0 "ctxt": 0
}, },
"nestingSelector": { "children": [
"type": "NestingSelector", {
"type": "CompoundSelector",
"span": { "span": {
"start": 94, "start": 94,
"end": 95, "end": 95,
"ctxt": 0 "ctxt": 0
} },
}, "nestingSelector": {
"typeSelector": null, "type": "NestingSelector",
"subclassSelectors": [] "span": {
} "start": 94,
] "end": 95,
"ctxt": 0
}
},
"typeSelector": null,
"subclassSelectors": []
}
]
}
} }
] ]
}, },
@ -451,7 +460,7 @@
"ctxt": 0 "ctxt": 0
}, },
"prelude": { "prelude": {
"type": "SelectorList", "type": "RelativeSelectorList",
"span": { "span": {
"start": 133, "start": 133,
"end": 138, "end": 138,
@ -459,51 +468,60 @@
}, },
"children": [ "children": [
{ {
"type": "ComplexSelector", "type": "RelativeSelector",
"span": { "span": {
"start": 133, "start": 133,
"end": 138, "end": 138,
"ctxt": 0 "ctxt": 0
}, },
"children": [ "combinator": null,
{ "selector": {
"type": "CompoundSelector", "type": "ComplexSelector",
"span": { "span": {
"start": 133, "start": 133,
"end": 138, "end": 138,
"ctxt": 0 "ctxt": 0
}, },
"nestingSelector": { "children": [
"type": "NestingSelector", {
"type": "CompoundSelector",
"span": { "span": {
"start": 133, "start": 133,
"end": 134, "end": 138,
"ctxt": 0 "ctxt": 0
} },
}, "nestingSelector": {
"typeSelector": null, "type": "NestingSelector",
"subclassSelectors": [
{
"type": "ClassSelector",
"span": { "span": {
"start": 134, "start": 133,
"end": 138, "end": 134,
"ctxt": 0 "ctxt": 0
}, }
"text": { },
"type": "Ident", "typeSelector": null,
"subclassSelectors": [
{
"type": "ClassSelector",
"span": { "span": {
"start": 135, "start": 134,
"end": 138, "end": 138,
"ctxt": 0 "ctxt": 0
}, },
"value": "foo", "text": {
"raw": "foo" "type": "Ident",
"span": {
"start": 135,
"end": 138,
"ctxt": 0
},
"value": "foo",
"raw": "foo"
}
} }
} ]
] }
} ]
] }
} }
] ]
}, },

View File

@ -1,7 +1,12 @@
x Unexpected end of file, but expected '{' x Unexpected end of file, but expected '{'
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested/input.css:3:5] ,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested/input.css:3:5]
3 | ,-> & test; 3 | & test;
4 | `-> background: red; : ^^^^^^
5 | } `----
x Unexpected token
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested/input.css:3:5]
3 | & test;
: ^
`---- `----

View File

@ -126,48 +126,6 @@
: ^^^ : ^^^
`---- `----
x ComponentValue
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested/input.css:4:5]
4 | background: red;
: ^^^^^^^^^^^^^^^
`----
x StyleBlock
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested/input.css:4:5]
4 | background: red;
: ^^^^^^^^^^^^^^^
`----
x Declaration
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested/input.css:4:5]
4 | background: red;
: ^^^^^^^^^^^^^^^
`----
x DeclarationName
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested/input.css:4:5]
4 | background: red;
: ^^^^^^^^^^
`----
x Ident
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested/input.css:4:5]
4 | background: red;
: ^^^^^^^^^^
`----
x ComponentValue
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested/input.css:4:5]
4 | background: red;
: ^^^
`----
x Ident
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested/input.css:4:5]
4 | background: red;
: ^^^
`----
x ComponentValue x ComponentValue
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested/input.css:3:5] ,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested/input.css:3:5]
3 | & test; 3 | & test;
@ -228,6 +186,48 @@
: ^ : ^
`---- `----
x ComponentValue
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested/input.css:4:5]
4 | background: red;
: ^^^^^^^^^^^^^^^
`----
x StyleBlock
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested/input.css:4:5]
4 | background: red;
: ^^^^^^^^^^^^^^^
`----
x Declaration
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested/input.css:4:5]
4 | background: red;
: ^^^^^^^^^^^^^^^
`----
x DeclarationName
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested/input.css:4:5]
4 | background: red;
: ^^^^^^^^^^
`----
x Ident
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested/input.css:4:5]
4 | background: red;
: ^^^^^^^^^^
`----
x ComponentValue
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested/input.css:4:5]
4 | background: red;
: ^^^
`----
x Ident
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested/input.css:4:5]
4 | background: red;
: ^^^
`----
x Rule x Rule
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested/input.css:7:1] ,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested/input.css:7:1]
7 | ,-> article { 7 | ,-> article {
@ -408,7 +408,13 @@
: ^^^^^^^^^^^^^^^^^^ : ^^^^^^^^^^^^^^^^^^
`---- `----
x SelectorList x RelativeSelectorList
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested/input.css:9:5]
9 | & { color: blue; }
: ^
`----
x RelativeSelector
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested/input.css:9:5] ,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested/input.css:9:5]
9 | & { color: blue; } 9 | & { color: blue; }
: ^ : ^
@ -504,7 +510,13 @@
: ^^^^^^^^^^^^^^^^^^^^^^^^ : ^^^^^^^^^^^^^^^^^^^^^^^^
`---- `----
x SelectorList x RelativeSelectorList
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested/input.css:11:5]
11 | &.foo { color: yellow; } /* valid! */
: ^^^^^
`----
x RelativeSelector
,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested/input.css:11:5] ,-[$DIR/tests/recovery/style-blocks-contents/invalid-nested/input.css:11:5]
11 | &.foo { color: yellow; } /* valid! */ 11 | &.foo { color: yellow; } /* valid! */
: ^^^^^ : ^^^^^

View File

@ -119,8 +119,9 @@ define!({
} }
pub enum QualifiedRulePrelude { pub enum QualifiedRulePrelude {
ListOfComponentValues(ListOfComponentValues),
SelectorList(SelectorList), SelectorList(SelectorList),
RelativeSelectorList(RelativeSelectorList),
ListOfComponentValues(ListOfComponentValues),
} }
pub enum StyleBlock { pub enum StyleBlock {