Skip to content

Commit 1e79fa7

Browse files
committed
Fix nesting with combinators in parent rule
Fixes parcel-bundler#67
1 parent e1dd904 commit 1e79fa7

File tree

2 files changed

+149
-9
lines changed

2 files changed

+149
-9
lines changed

src/lib.rs

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8535,6 +8535,126 @@ mod tests {
85358535
"#}
85368536
);
85378537

8538+
nesting_test(
8539+
r#"
8540+
.foo .bar {
8541+
&h1 {
8542+
background: green;
8543+
}
8544+
}
8545+
"#,
8546+
indoc!{r#"
8547+
h1:is(.foo .bar) {
8548+
background: green;
8549+
}
8550+
"#}
8551+
);
8552+
8553+
nesting_test(
8554+
r#"
8555+
.foo .bar {
8556+
@nest h1& {
8557+
background: green;
8558+
}
8559+
}
8560+
"#,
8561+
indoc!{r#"
8562+
h1:is(.foo .bar) {
8563+
background: green;
8564+
}
8565+
"#}
8566+
);
8567+
8568+
nesting_test(
8569+
r#"
8570+
.foo.bar {
8571+
&h1 {
8572+
background: green;
8573+
}
8574+
}
8575+
"#,
8576+
indoc!{r#"
8577+
h1.foo.bar {
8578+
background: green;
8579+
}
8580+
"#}
8581+
);
8582+
8583+
nesting_test(
8584+
r#"
8585+
.foo .bar {
8586+
&h1 .baz {
8587+
background: green;
8588+
}
8589+
}
8590+
"#,
8591+
indoc!{r#"
8592+
h1:is(.foo .bar) .baz {
8593+
background: green;
8594+
}
8595+
"#}
8596+
);
8597+
8598+
nesting_test(
8599+
r#"
8600+
.foo .bar {
8601+
@nest h1 .baz& {
8602+
background: green;
8603+
}
8604+
}
8605+
"#,
8606+
indoc!{r#"
8607+
h1 .baz:is(.foo .bar) {
8608+
background: green;
8609+
}
8610+
"#}
8611+
);
8612+
8613+
nesting_test(
8614+
r#"
8615+
.foo .bar {
8616+
&.baz {
8617+
background: green;
8618+
}
8619+
}
8620+
"#,
8621+
indoc!{r#"
8622+
.foo .bar.baz {
8623+
background: green;
8624+
}
8625+
"#}
8626+
);
8627+
8628+
nesting_test(
8629+
r#"
8630+
.foo .bar {
8631+
@nest .baz& {
8632+
background: green;
8633+
}
8634+
}
8635+
"#,
8636+
indoc!{r#"
8637+
.baz:is(.foo .bar) {
8638+
background: green;
8639+
}
8640+
"#}
8641+
);
8642+
8643+
nesting_test(
8644+
r#"
8645+
.foo .bar {
8646+
@nest .baz & {
8647+
background: green;
8648+
}
8649+
}
8650+
"#,
8651+
indoc!{r#"
8652+
.baz .foo .bar {
8653+
background: green;
8654+
}
8655+
"#}
8656+
);
8657+
85388658
nesting_test_no_targets(
85398659
r#"
85408660
.foo {

src/selector.rs

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -748,7 +748,7 @@ impl ToCssWithContext for parcel_selectors::parser::Selector<Selectors> {
748748
// Iterate over everything so we serialize the namespace
749749
// too.
750750
let mut iter = compound.iter();
751-
let swap_nesting = has_leading_nesting && context.is_some() && is_type_selector(compound.get(first_index));
751+
let swap_nesting = has_leading_nesting && context.is_some();
752752
if swap_nesting {
753753
// Swap nesting and type selector (e.g. &div -> div&).
754754
iter.next();
@@ -780,15 +780,15 @@ impl ToCssWithContext for parcel_selectors::parser::Selector<Selectors> {
780780
// following code tries to match.
781781
if perform_step_2 {
782782
let mut iter = compound.iter();
783-
if has_leading_nesting && context.is_some() && is_type_selector(compound.get(first_index)) {
783+
if has_leading_nesting && context.is_some() && is_type_selector(compound.get(first_non_namespace)) {
784784
// Swap nesting and type selector (e.g. &div -> div&).
785785
// This ensures that the compiled selector is valid. e.g. (div.foo is valid, .foodiv is not).
786786
let nesting = iter.next().unwrap();
787787
let local = iter.next().unwrap();
788788
local.to_css_with_context(dest, context)?;
789789

790790
// Also check the next item in case of namespaces.
791-
if is_type_selector(compound.get(first_index + 1)) {
791+
if first_non_namespace > first_index {
792792
let local = iter.next().unwrap();
793793
local.to_css_with_context(dest, context)?;
794794
}
@@ -918,11 +918,11 @@ impl ToCssWithContext for Component<Selectors> {
918918

919919
fn serialize_nesting<W>(dest: &mut Printer<W>, context: Option<&StyleContext>, first: bool)-> Result<(), PrinterError> where W: fmt::Write {
920920
if let Some(ctx) = context {
921-
// If there's only one selector, just serialize it directly.
921+
// If there's only one simple selector, just serialize it directly.
922922
// Otherwise, use an :is() pseudo class.
923923
// Type selectors are only allowed at the start of a compound selector,
924924
// so use :is() if that is not the case.
925-
if ctx.rule.selectors.0.len() == 1 && (first || !has_type_selector(&ctx.rule.selectors.0[0])) {
925+
if ctx.rule.selectors.0.len() == 1 && (first || (!has_type_selector(&ctx.rule.selectors.0[0]) && is_simple(&ctx.rule.selectors.0[0]))) {
926926
ctx.rule.selectors.0.first().unwrap().to_css_with_context(dest, ctx.parent)
927927
} else {
928928
dest.write_str(":is(")?;
@@ -934,21 +934,41 @@ fn serialize_nesting<W>(dest: &mut Printer<W>, context: Option<&StyleContext>, f
934934
}
935935
}
936936

937+
#[inline]
937938
fn has_type_selector(selector: &parcel_selectors::parser::Selector<Selectors>) -> bool {
938939
let mut iter = selector.iter_raw_parse_order_from(0);
939940
let first = iter.next();
940-
is_type_selector(first)
941+
if is_namespace(first) {
942+
is_type_selector(iter.next())
943+
} else {
944+
is_type_selector(first)
945+
}
946+
}
947+
948+
#[inline]
949+
fn is_simple(selector: &parcel_selectors::parser::Selector<Selectors>) -> bool {
950+
!selector
951+
.iter_raw_match_order()
952+
.any(|component| component.is_combinator())
941953
}
942954

955+
#[inline]
943956
fn is_type_selector(component: Option<&Component<Selectors>>) -> bool {
957+
matches!(
958+
component,
959+
Some(Component::LocalName(_)) |
960+
Some(Component::ExplicitUniversalType)
961+
)
962+
}
963+
964+
#[inline]
965+
fn is_namespace(component: Option<&Component<Selectors>>) -> bool {
944966
matches!(
945967
component,
946968
Some(Component::ExplicitAnyNamespace) |
947969
Some(Component::ExplicitNoNamespace) |
948970
Some(Component::Namespace(..)) |
949-
Some(Component::DefaultNamespace(_)) |
950-
Some(Component::LocalName(_)) |
951-
Some(Component::ExplicitUniversalType)
971+
Some(Component::DefaultNamespace(_))
952972
)
953973
}
954974

0 commit comments

Comments
 (0)