Skip to content

Commit 8d94ea1

Browse files
committed
Avoid splitting and vendor prefixing :is() when we can safely unwrap it
Fixes parcel-bundler#509
1 parent fbf0d24 commit 8d94ea1

File tree

6 files changed

+94
-16
lines changed

6 files changed

+94
-16
lines changed

src/context.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ impl<'i, 'o> PropertyHandlerContext<'i, 'o> {
5858
ltr: Vec::new(),
5959
rtl: Vec::new(),
6060
context,
61-
unused_symbols: self.unused_symbols
61+
unused_symbols: self.unused_symbols,
6262
}
6363
}
6464

src/lib.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9576,6 +9576,61 @@ mod tests {
95769576
..Browsers::default()
95779577
},
95789578
);
9579+
9580+
prefix_test(
9581+
r#"
9582+
.foo :is(.bar) {
9583+
color: red;
9584+
}
9585+
"#,
9586+
indoc! {r#"
9587+
.foo .bar {
9588+
color: red;
9589+
}
9590+
"#},
9591+
Browsers {
9592+
chrome: Some(87 << 16),
9593+
..Browsers::default()
9594+
},
9595+
);
9596+
9597+
prefix_test(
9598+
r#"
9599+
.foo :is(.bar), .bar :is(.baz) {
9600+
color: red;
9601+
}
9602+
"#,
9603+
indoc! {r#"
9604+
.foo .bar, .bar .baz {
9605+
color: red;
9606+
}
9607+
"#},
9608+
Browsers {
9609+
chrome: Some(87 << 16),
9610+
..Browsers::default()
9611+
},
9612+
);
9613+
9614+
prefix_test(
9615+
r#"
9616+
.foo :is(.bar:focus-visible), .bar :is(.baz:hover) {
9617+
color: red;
9618+
}
9619+
"#,
9620+
indoc! {r#"
9621+
.bar .baz:hover {
9622+
color: red;
9623+
}
9624+
9625+
.foo .bar:focus-visible {
9626+
color: red;
9627+
}
9628+
"#},
9629+
Browsers {
9630+
chrome: Some(85 << 16),
9631+
..Browsers::default()
9632+
},
9633+
);
95799634
}
95809635

95819636
#[test]

src/properties/transition.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,8 @@ fn expand_properties<'i>(
374374

375375
// Expand mask properties, which use different vendor-prefixed names.
376376
if let Some(property_id) = get_webkit_mask_property(&properties[i]) {
377-
if context.targets
377+
if context
378+
.targets
378379
.prefixes(VendorPrefix::None, Feature::MaskBorder)
379380
.contains(VendorPrefix::WebKit)
380381
{
@@ -387,7 +388,8 @@ fn expand_properties<'i>(
387388
rtl_properties[i].set_prefixes_for_targets(context.targets);
388389

389390
if let Some(property_id) = get_webkit_mask_property(&rtl_properties[i]) {
390-
if context.targets
391+
if context
392+
.targets
391393
.prefixes(VendorPrefix::None, Feature::MaskBorder)
392394
.contains(VendorPrefix::WebKit)
393395
{

src/rules/mod.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ use self::font_palette_values::FontPaletteValuesRule;
5959
use self::layer::{LayerBlockRule, LayerStatementRule};
6060
use self::property::PropertyRule;
6161
use crate::context::PropertyHandlerContext;
62-
use crate::declaration::{DeclarationHandler, DeclarationBlock};
62+
use crate::declaration::{DeclarationBlock, DeclarationHandler};
6363
use crate::dependencies::{Dependency, ImportDependency};
6464
use crate::error::{MinifyError, ParserError, PrinterError, PrinterErrorKind};
6565
use crate::parser::{
@@ -672,15 +672,17 @@ impl<'i, T: Clone> CssRuleList<'i, T> {
672672
// If the rule has nested rules, and we have extra rules to insert such as for logical properties,
673673
// we need to split the rule in two so we can insert the extra rules in between the declarations from
674674
// the main rule and the nested rules.
675-
let nested_rule = if !style.rules.0.is_empty() && (!logical.is_empty() || !supports.is_empty() || !incompatible_rules.is_empty()) {
676-
let mut rules = CssRuleList(vec![]);
675+
let nested_rule = if !style.rules.0.is_empty()
676+
&& (!logical.is_empty() || !supports.is_empty() || !incompatible_rules.is_empty())
677+
{
678+
let mut rules = CssRuleList(vec![]);
677679
std::mem::swap(&mut style.rules, &mut rules);
678680
Some(StyleRule {
679681
selectors: style.selectors.clone(),
680682
declarations: DeclarationBlock::default(),
681683
rules,
682684
vendor_prefix: style.vendor_prefix,
683-
loc: style.loc
685+
loc: style.loc,
684686
})
685687
} else {
686688
None

src/rules/style.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ impl<'i, T: Clone> StyleRule<'i, T> {
8585
}
8686

8787
self.vendor_prefix = get_prefix(&self.selectors);
88-
if self.vendor_prefix.contains(VendorPrefix::None) & context.targets.should_compile_selectors() {
88+
if self.vendor_prefix.contains(VendorPrefix::None) && context.targets.should_compile_selectors() {
8989
self.vendor_prefix = downlevel_selectors(self.selectors.0.as_mut_slice(), *context.targets);
9090
}
9191

src/selector.rs

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1479,12 +1479,9 @@ where
14791479
Component::Where(..) => dest.write_str(":where(")?,
14801480
Component::Is(ref selectors) => {
14811481
// If there's only one simple selector, serialize it directly.
1482-
if selectors.len() == 1 {
1483-
let first = selectors.first().unwrap();
1484-
if !has_type_selector(first) && is_simple(first) {
1485-
serialize_selector(first, dest, context, false)?;
1486-
return Ok(());
1487-
}
1482+
if should_unwrap_is(selectors) {
1483+
serialize_selector(selectors.first().unwrap(), dest, context, false)?;
1484+
return Ok(());
14881485
}
14891486

14901487
let vp = dest.vendor_prefix;
@@ -1544,6 +1541,17 @@ where
15441541
}
15451542
}
15461543

1544+
fn should_unwrap_is<'i>(selectors: &Box<[Selector<'i>]>) -> bool {
1545+
if selectors.len() == 1 {
1546+
let first = selectors.first().unwrap();
1547+
if !has_type_selector(first) && is_simple(first) {
1548+
return true;
1549+
}
1550+
}
1551+
1552+
false
1553+
}
1554+
15471555
fn serialize_nesting<W>(
15481556
dest: &mut Printer<W>,
15491557
context: Option<&StyleContext>,
@@ -1735,7 +1743,15 @@ pub(crate) fn is_compatible(selectors: &[Selector], targets: Targets) -> bool {
17351743
}
17361744

17371745
// These support forgiving selector lists, so no need to check nested selectors.
1738-
Component::Is(_) | Component::Where(_) | Component::Nesting => Feature::IsSelector,
1746+
Component::Is(selectors) => {
1747+
// ... except if we are going to unwrap them.
1748+
if should_unwrap_is(selectors) && is_compatible(selectors, targets) {
1749+
continue;
1750+
}
1751+
1752+
Feature::IsSelector
1753+
}
1754+
Component::Where(_) | Component::Nesting => Feature::IsSelector,
17391755
Component::Any(..) => Feature::AnyPseudo,
17401756
Component::Has(selectors) => {
17411757
if !targets.is_compatible(Feature::HasSelector) || !is_compatible(&*selectors, targets) {
@@ -1946,7 +1962,10 @@ fn downlevel_component<'i>(component: &mut Component<'i>, targets: Targets) -> V
19461962

19471963
// Convert :is to :-webkit-any/:-moz-any if needed.
19481964
// All selectors must be simple, no combinators are supported.
1949-
if should_compile!(targets, IsSelector) && selectors.iter().all(|selector| !selector.has_combinator()) {
1965+
if should_compile!(targets, IsSelector)
1966+
&& !should_unwrap_is(selectors)
1967+
&& selectors.iter().all(|selector| !selector.has_combinator())
1968+
{
19501969
necessary_prefixes |= targets.prefixes(VendorPrefix::None, crate::prefixes::Feature::AnyPseudo)
19511970
} else {
19521971
necessary_prefixes |= VendorPrefix::empty()

0 commit comments

Comments
 (0)