Skip to content

Commit 7e867ee

Browse files
authored
Implement new nesting spec (parcel-bundler#340)
1 parent dcbb9b8 commit 7e867ee

File tree

6 files changed

+673
-227
lines changed

6 files changed

+673
-227
lines changed

selectors/builder.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@ impl<'i, Impl: SelectorImpl<'i>> SelectorBuilder<'i, Impl> {
9090
!self.combinators.is_empty()
9191
}
9292

93+
pub fn add_nesting_prefix(&mut self) {
94+
self.combinators.insert(0, (Combinator::Descendant, 1));
95+
self.simple_selectors.insert(0, Component::Nesting);
96+
}
97+
9398
/// Consumes the builder, producing a Selector.
9499
#[inline(always)]
95100
pub fn build(

selectors/parser.rs

Lines changed: 63 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,7 @@ pub enum NestingRequirement {
354354
None,
355355
Prefixed,
356356
Contained,
357+
Implicit,
357358
}
358359

359360
impl<'i, Impl: SelectorImpl<'i>> SelectorList<'i, Impl> {
@@ -422,12 +423,30 @@ impl<'i, Impl: SelectorImpl<'i>> SelectorList<'i, Impl> {
422423
}
423424
}
424425

426+
pub fn parse_relative<'t, P>(
427+
parser: &P,
428+
input: &mut CssParser<'i, 't>,
429+
nesting_requirement: NestingRequirement,
430+
) -> Result<Self, ParseError<'i, P::Error>>
431+
where
432+
P: Parser<'i, Impl = Impl>,
433+
{
434+
Self::parse_relative_with_state(
435+
parser,
436+
input,
437+
&mut SelectorParsingState::empty(),
438+
ParseErrorRecovery::DiscardList,
439+
nesting_requirement,
440+
)
441+
}
442+
425443
#[inline]
426-
fn parse_relative<'t, P>(
444+
fn parse_relative_with_state<'t, P>(
427445
parser: &P,
428446
input: &mut CssParser<'i, 't>,
429447
state: &mut SelectorParsingState,
430448
recovery: ParseErrorRecovery,
449+
nesting_requirement: NestingRequirement,
431450
) -> Result<Self, ParseError<'i, P::Error>>
432451
where
433452
P: Parser<'i, Impl = Impl>,
@@ -437,7 +456,7 @@ impl<'i, Impl: SelectorImpl<'i>> SelectorList<'i, Impl> {
437456
loop {
438457
let selector = input.parse_until_before(Delimiter::Comma, |input| {
439458
let mut selector_state = original_state;
440-
let result = parse_relative_selector(parser, input, &mut selector_state);
459+
let result = parse_relative_selector(parser, input, &mut selector_state, nesting_requirement);
441460
if selector_state.contains(SelectorParsingState::AFTER_NESTING) {
442461
state.insert(SelectorParsingState::AFTER_NESTING)
443462
}
@@ -1743,6 +1762,18 @@ where
17431762
input.reset(&state);
17441763
}
17451764

1765+
// In the implicit nesting mode, selectors may not start with an ident or function token.
1766+
if nesting_requirement == NestingRequirement::Implicit {
1767+
let state = input.state();
1768+
match input.next()? {
1769+
Token::Ident(..) | Token::Function(..) => {
1770+
return Err(input.new_custom_error(SelectorParseErrorKind::MissingNestingPrefix));
1771+
}
1772+
_ => {}
1773+
}
1774+
input.reset(&state);
1775+
}
1776+
17461777
let mut builder = SelectorBuilder::default();
17471778

17481779
let mut has_pseudo_element = false;
@@ -1806,8 +1837,16 @@ where
18061837
builder.push_combinator(combinator);
18071838
}
18081839

1809-
if nesting_requirement == NestingRequirement::Contained && !state.contains(SelectorParsingState::AFTER_NESTING) {
1810-
return Err(input.new_custom_error(SelectorParseErrorKind::MissingNestingSelector));
1840+
if !state.contains(SelectorParsingState::AFTER_NESTING) {
1841+
match nesting_requirement {
1842+
NestingRequirement::Implicit => {
1843+
builder.add_nesting_prefix();
1844+
}
1845+
NestingRequirement::Contained | NestingRequirement::Prefixed => {
1846+
return Err(input.new_custom_error(SelectorParseErrorKind::MissingNestingSelector));
1847+
}
1848+
_ => {}
1849+
}
18111850
}
18121851

18131852
let (spec, components) = builder.build(has_pseudo_element, slotted, part);
@@ -1834,6 +1873,7 @@ fn parse_relative_selector<'i, 't, P, Impl>(
18341873
parser: &P,
18351874
input: &mut CssParser<'i, 't>,
18361875
state: &mut SelectorParsingState,
1876+
mut nesting_requirement: NestingRequirement,
18371877
) -> Result<Selector<'i, Impl>, ParseError<'i, P::Error>>
18381878
where
18391879
P: Parser<'i, Impl = Impl>,
@@ -1851,11 +1891,21 @@ where
18511891
}
18521892
};
18531893

1854-
let mut selector = parse_selector(parser, input, state, NestingRequirement::None)?;
1894+
let scope = if nesting_requirement == NestingRequirement::Implicit {
1895+
Component::Nesting
1896+
} else {
1897+
Component::Scope
1898+
};
1899+
1900+
if combinator.is_some() {
1901+
nesting_requirement = NestingRequirement::None;
1902+
}
1903+
1904+
let mut selector = parse_selector(parser, input, state, nesting_requirement)?;
18551905
if let Some(combinator) = combinator {
18561906
// https://www.w3.org/TR/selectors/#absolutizing
18571907
selector.1.push(Component::Combinator(combinator));
1858-
selector.1.push(Component::Scope);
1908+
selector.1.push(scope);
18591909
}
18601910

18611911
Ok(selector)
@@ -2399,7 +2449,13 @@ where
23992449
Impl: SelectorImpl<'i>,
24002450
{
24012451
let mut child_state = *state;
2402-
let inner = SelectorList::parse_relative(parser, input, &mut child_state, parser.is_and_where_error_recovery())?;
2452+
let inner = SelectorList::parse_relative_with_state(
2453+
parser,
2454+
input,
2455+
&mut child_state,
2456+
parser.is_and_where_error_recovery(),
2457+
NestingRequirement::None,
2458+
)?;
24032459
if child_state.contains(SelectorParsingState::AFTER_NESTING) {
24042460
state.insert(SelectorParsingState::AFTER_NESTING)
24052461
}

src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ pub enum ParserError<'i> {
7575
InvalidMediaQuery,
7676
/// Invalid CSS nesting.
7777
InvalidNesting,
78+
/// The @nest rule is deprecated.
79+
DeprecatedNestRule,
7880
/// An invalid selector in an `@page` rule.
7981
InvalidPageSelector,
8082
/// An invalid value was encountered.
@@ -103,6 +105,7 @@ impl<'i> fmt::Display for ParserError<'i> {
103105
InvalidDeclaration => write!(f, "Invalid declaration"),
104106
InvalidMediaQuery => write!(f, "Invalid media query"),
105107
InvalidNesting => write!(f, "Invalid nesting"),
108+
DeprecatedNestRule => write!(f, "The @nest rule is deprecated"),
106109
InvalidPageSelector => write!(f, "Invalid page selector"),
107110
InvalidValue => write!(f, "Invalid value"),
108111
QualifiedRuleInvalid => write!(f, "Invalid qualified rule"),

0 commit comments

Comments
 (0)