Skip to content

Commit d398c1b

Browse files
committed
Detect @supports features from value, and apply exclusions during printing
1 parent 3ffe09e commit d398c1b

File tree

14 files changed

+185
-50
lines changed

14 files changed

+185
-50
lines changed

selectors/parser.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3394,7 +3394,7 @@ pub mod tests {
33943394
}
33953395
}
33963396

3397-
fn parse<'i>(input: &'i str) -> Result<SelectorList<DummySelectorImpl>, SelectorParseError<'i>> {
3397+
fn parse<'i>(input: &'i str) -> Result<SelectorList<'i, DummySelectorImpl>, SelectorParseError<'i>> {
33983398
parse_ns(input, &DummyParser::default())
33993399
}
34003400

src/lib.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29404,6 +29404,69 @@ mod tests {
2940429404
},
2940529405
);
2940629406

29407+
prefix_test(
29408+
r#"
29409+
@supports (color: lab(40% 56.6 39)) {
29410+
.foo {
29411+
color: lab(40% 56.6 39);
29412+
}
29413+
}
29414+
"#,
29415+
indoc! {r#"
29416+
@supports (color: lab(40% 56.6 39)) {
29417+
.foo {
29418+
color: lab(40% 56.6 39);
29419+
}
29420+
}
29421+
"#},
29422+
Browsers {
29423+
chrome: Some(4 << 16),
29424+
..Browsers::default()
29425+
},
29426+
);
29427+
29428+
prefix_test(
29429+
r#"
29430+
@supports (background-color: lab(40% 56.6 39)) {
29431+
.foo {
29432+
background-color: lab(40% 56.6 39);
29433+
}
29434+
}
29435+
"#,
29436+
indoc! {r#"
29437+
@supports (background-color: lab(40% 56.6 39)) {
29438+
.foo {
29439+
background-color: lab(40% 56.6 39);
29440+
}
29441+
}
29442+
"#},
29443+
Browsers {
29444+
chrome: Some(4 << 16),
29445+
..Browsers::default()
29446+
},
29447+
);
29448+
29449+
prefix_test(
29450+
r#"
29451+
@supports (color: light-dark(#f00, #00f)) {
29452+
.foo {
29453+
color: light-dark(#ff0, #0ff);
29454+
}
29455+
}
29456+
"#,
29457+
indoc! {r#"
29458+
@supports (color: light-dark(#f00, #00f)) {
29459+
.foo {
29460+
color: light-dark(#ff0, #0ff);
29461+
}
29462+
}
29463+
"#},
29464+
Browsers {
29465+
chrome: Some(4 << 16),
29466+
..Browsers::default()
29467+
},
29468+
);
29469+
2940729470
// NOTE: fallback for lab is not necessary
2940829471
prefix_test(
2940929472
r#"

src/media_query.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -754,12 +754,12 @@ where
754754
{
755755
let mut iter = conditions.iter();
756756
let first = iter.next().unwrap();
757-
to_css_with_parens_if_needed(first, dest, first.needs_parens(Some(operator), &dest.targets))?;
757+
to_css_with_parens_if_needed(first, dest, first.needs_parens(Some(operator), &dest.targets.current))?;
758758
for item in iter {
759759
dest.write_char(' ')?;
760760
operator.to_css(dest)?;
761761
dest.write_char(' ')?;
762-
to_css_with_parens_if_needed(item, dest, item.needs_parens(Some(operator), &dest.targets))?;
762+
to_css_with_parens_if_needed(item, dest, item.needs_parens(Some(operator), &dest.targets.current))?;
763763
}
764764

765765
Ok(())
@@ -787,7 +787,7 @@ impl<'i> ToCss for MediaCondition<'i> {
787787
negated.to_css(dest)
788788
} else {
789789
dest.write_str("not ")?;
790-
to_css_with_parens_if_needed(&**c, dest, c.needs_parens(None, &dest.targets))
790+
to_css_with_parens_if_needed(&**c, dest, c.needs_parens(None, &dest.targets.current))
791791
}
792792
}
793793
MediaCondition::Operation {
@@ -1087,7 +1087,7 @@ impl<'i, FeatureId: FeatureToCss> ToCss for QueryFeature<'i, FeatureId> {
10871087
}
10881088
QueryFeature::Range { name, operator, value } => {
10891089
// If range syntax is unsupported, use min/max prefix if possible.
1090-
if should_compile!(dest.targets, MediaRangeSyntax) {
1090+
if should_compile!(dest.targets.current, MediaRangeSyntax) {
10911091
return write_min_max(operator, name, value, dest, false);
10921092
}
10931093

@@ -1103,7 +1103,7 @@ impl<'i, FeatureId: FeatureToCss> ToCss for QueryFeature<'i, FeatureId> {
11031103
end,
11041104
end_operator,
11051105
} => {
1106-
if should_compile!(dest.targets, MediaIntervalSyntax) {
1106+
if should_compile!(dest.targets.current, MediaIntervalSyntax) {
11071107
write_min_max(&start_operator.opposite(), name, start, dest, true)?;
11081108
dest.write_str(" and ")?;
11091109
return write_min_max(end_operator, name, end, dest, true);

src/printer.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::dependencies::{Dependency, DependencyOptions};
55
use crate::error::{Error, ErrorLocation, PrinterError, PrinterErrorKind};
66
use crate::rules::{Location, StyleContext};
77
use crate::selector::SelectorList;
8-
use crate::targets::Targets;
8+
use crate::targets::{Targets, TargetsWithSupportsScope};
99
use crate::vendor_prefix::VendorPrefix;
1010
use cssparser::{serialize_identifier, serialize_name};
1111
#[cfg(feature = "sourcemap")]
@@ -77,7 +77,7 @@ pub struct Printer<'a, 'b, 'c, W> {
7777
line: u32,
7878
col: u32,
7979
pub(crate) minify: bool,
80-
pub(crate) targets: Targets,
80+
pub(crate) targets: TargetsWithSupportsScope,
8181
/// Vendor prefix override. When non-empty, it overrides
8282
/// the vendor prefix of whatever is being printed.
8383
pub(crate) vendor_prefix: VendorPrefix,
@@ -108,7 +108,7 @@ impl<'a, 'b, 'c, W: std::fmt::Write + Sized> Printer<'a, 'b, 'c, W> {
108108
line: 0,
109109
col: 0,
110110
minify: options.minify,
111-
targets: options.targets,
111+
targets: TargetsWithSupportsScope::new(options.targets),
112112
vendor_prefix: VendorPrefix::empty(),
113113
in_calc: false,
114114
css_module: None,

src/properties/custom.rs

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::printer::Printer;
77
use crate::properties::PropertyId;
88
use crate::rules::supports::SupportsCondition;
99
use crate::stylesheet::ParserOptions;
10-
use crate::targets::{should_compile, Targets};
10+
use crate::targets::{should_compile, Features, Targets};
1111
use crate::traits::{Parse, ParseWithOptions, ToCss};
1212
use crate::values::angle::Angle;
1313
use crate::values::color::{
@@ -1176,6 +1176,44 @@ impl<'i> TokenList<'i> {
11761176
res
11771177
}
11781178

1179+
pub(crate) fn get_features(&self) -> Features {
1180+
let mut features = Features::empty();
1181+
for token in &self.0 {
1182+
match token {
1183+
TokenOrValue::Color(color) => {
1184+
features |= color.get_features();
1185+
}
1186+
TokenOrValue::UnresolvedColor(unresolved_color) => {
1187+
features |= Features::SpaceSeparatedColorNotation;
1188+
match unresolved_color {
1189+
UnresolvedColor::LightDark { light, dark } => {
1190+
features |= Features::LightDark;
1191+
features |= light.get_features();
1192+
features |= dark.get_features();
1193+
}
1194+
_ => {}
1195+
}
1196+
}
1197+
TokenOrValue::Function(f) => {
1198+
features |= f.arguments.get_features();
1199+
}
1200+
TokenOrValue::Var(v) => {
1201+
if let Some(fallback) = &v.fallback {
1202+
features |= fallback.get_features();
1203+
}
1204+
}
1205+
TokenOrValue::Env(v) => {
1206+
if let Some(fallback) = &v.fallback {
1207+
features |= fallback.get_features();
1208+
}
1209+
}
1210+
_ => {}
1211+
}
1212+
}
1213+
1214+
features
1215+
}
1216+
11791217
/// Substitutes variables with the provided values.
11801218
#[cfg(feature = "substitute_variables")]
11811219
#[cfg_attr(docsrs, doc(cfg(feature = "substitute_variables")))]
@@ -1605,7 +1643,7 @@ impl<'i> UnresolvedColor<'i> {
16051643

16061644
match self {
16071645
UnresolvedColor::RGB { r, g, b, alpha } => {
1608-
if should_compile!(dest.targets, SpaceSeparatedColorNotation) {
1646+
if should_compile!(dest.targets.current, SpaceSeparatedColorNotation) {
16091647
dest.write_str("rgba(")?;
16101648
c(r).to_css(dest)?;
16111649
dest.delim(',', false)?;
@@ -1629,7 +1667,7 @@ impl<'i> UnresolvedColor<'i> {
16291667
dest.write_char(')')
16301668
}
16311669
UnresolvedColor::HSL { h, s, l, alpha } => {
1632-
if should_compile!(dest.targets, SpaceSeparatedColorNotation) {
1670+
if should_compile!(dest.targets.current, SpaceSeparatedColorNotation) {
16331671
dest.write_str("hsla(")?;
16341672
h.to_css(dest)?;
16351673
dest.delim(',', false)?;
@@ -1653,7 +1691,7 @@ impl<'i> UnresolvedColor<'i> {
16531691
dest.write_char(')')
16541692
}
16551693
UnresolvedColor::LightDark { light, dark } => {
1656-
if should_compile!(dest.targets, LightDark) {
1694+
if should_compile!(dest.targets.current, LightDark) {
16571695
dest.write_str("var(--lightningcss-light")?;
16581696
dest.delim(',', false)?;
16591697
light.to_css(dest, is_custom_property)?;

src/rules/container.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ impl<'i> ToCss for ContainerCondition<'i> {
218218
ContainerCondition::Feature(ref f) => f.to_css(dest),
219219
ContainerCondition::Not(ref c) => {
220220
dest.write_str("not ")?;
221-
to_css_with_parens_if_needed(&**c, dest, c.needs_parens(None, &dest.targets))
221+
to_css_with_parens_if_needed(&**c, dest, c.needs_parens(None, &dest.targets.current))
222222
}
223223
ContainerCondition::Operation {
224224
ref conditions,
@@ -243,7 +243,7 @@ impl<'i> ToCss for StyleQuery<'i> {
243243
StyleQuery::Property(ref f) => f.to_css(dest),
244244
StyleQuery::Not(ref c) => {
245245
dest.write_str("not ")?;
246-
to_css_with_parens_if_needed(&**c, dest, c.needs_parens(None, &dest.targets))
246+
to_css_with_parens_if_needed(&**c, dest, c.needs_parens(None, &dest.targets.current))
247247
}
248248
StyleQuery::Operation {
249249
ref conditions,
@@ -313,10 +313,10 @@ impl<'a, 'i, T: ToCss> ToCss for ContainerRule<'i, T> {
313313
}
314314

315315
// Don't downlevel range syntax in container queries.
316-
let exclude = dest.targets.exclude;
317-
dest.targets.exclude.insert(Features::MediaQueries);
316+
let exclude = dest.targets.current.exclude;
317+
dest.targets.current.exclude.insert(Features::MediaQueries);
318318
self.condition.to_css(dest)?;
319-
dest.targets.exclude = exclude;
319+
dest.targets.current.exclude = exclude;
320320

321321
dest.whitespace()?;
322322
dest.write_char('{')?;

src/rules/style.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ impl<'a, 'i, T: ToCss> StyleRule<'i, T> {
245245
W: std::fmt::Write,
246246
{
247247
// If supported, or there are no targets, preserve nesting. Otherwise, write nested rules after parent.
248-
let supports_nesting = self.rules.0.is_empty() || !should_compile!(dest.targets, Nesting);
248+
let supports_nesting = self.rules.0.is_empty() || !should_compile!(dest.targets.current, Nesting);
249249
let len = self.declarations.declarations.len() + self.declarations.important_declarations.len();
250250
let has_declarations = supports_nesting || len > 0 || self.rules.0.is_empty();
251251

src/rules/supports.rs

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ use super::{CssRuleList, MinifyContext};
77
use crate::error::{MinifyError, ParserError, PrinterError};
88
use crate::parser::DefaultAtRule;
99
use crate::printer::Printer;
10+
use crate::properties::custom::TokenList;
1011
use crate::properties::PropertyId;
1112
use crate::targets::{Features, FeaturesIterator, Targets};
1213
use crate::traits::{Parse, ToCss};
13-
use crate::values::color::ColorFallbackKind;
1414
use crate::values::string::CowArcStr;
1515
use crate::vendor_prefix::VendorPrefix;
1616
#[cfg(feature = "visitor")]
@@ -72,7 +72,13 @@ impl<'a, 'i, T: ToCss> ToCss for SupportsRule<'i, T> {
7272
dest.write_char('{')?;
7373
dest.indent();
7474
dest.newline()?;
75+
76+
let inserted = dest.targets.enter_supports(self.condition.get_supported_features());
7577
self.rules.to_css(dest)?;
78+
if inserted {
79+
dest.targets.exit_supports();
80+
}
81+
7682
dest.dedent();
7783
dest.newline()?;
7884
dest.write_char('}')
@@ -163,24 +169,21 @@ impl<'i> SupportsCondition<'i> {
163169
}
164170

165171
fn get_supported_features(&self) -> Features {
166-
const COLOR_P3_SUPPORTS_CONDITION: &str = ColorFallbackKind::P3.supports_condition_value();
167-
const COLOR_LAB_SUPPORTS_CONDITION: &str = ColorFallbackKind::LAB.supports_condition_value();
168172
fn get_supported_features_internal(value: &SupportsCondition) -> Option<Features> {
169173
match value {
170174
SupportsCondition::And(list) => list.iter().map(|c| get_supported_features_internal(c)).try_union_all(),
171-
SupportsCondition::Declaration { property_id, value } => match property_id {
172-
PropertyId::Color => Some(match value.as_ref() {
173-
COLOR_P3_SUPPORTS_CONDITION => Features::P3Colors | Features::ColorFunction,
174-
COLOR_LAB_SUPPORTS_CONDITION => Features::LabColors,
175-
_ => Features::empty(),
176-
}),
177-
_ => Some(Features::empty()),
178-
},
175+
SupportsCondition::Declaration { value, .. } => {
176+
let mut input = ParserInput::new(&value);
177+
let mut parser = Parser::new(&mut input);
178+
if let Ok(tokens) = TokenList::parse(&mut parser, &Default::default(), 0) {
179+
Some(tokens.get_features())
180+
} else {
181+
Some(Features::empty())
182+
}
183+
}
179184
// bail out if "not" or "or" exists for now
180185
SupportsCondition::Not(_) | SupportsCondition::Or(_) => None,
181-
SupportsCondition::Selector(_) | SupportsCondition::Unknown(_) => {
182-
Some(Features::empty())
183-
}
186+
SupportsCondition::Selector(_) | SupportsCondition::Unknown(_) => Some(Features::empty()),
184187
}
185188
}
186189

src/selector.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1371,7 +1371,7 @@ where
13711371

13721372
let mut combinators = selector.iter_raw_match_order().rev().filter_map(|x| x.as_combinator());
13731373
let compound_selectors = selector.iter_raw_match_order().as_slice().split(|x| x.is_combinator()).rev();
1374-
let should_compile_nesting = should_compile!(dest.targets, Nesting);
1374+
let should_compile_nesting = should_compile!(dest.targets.current, Nesting);
13751375

13761376
let mut first = true;
13771377
let mut combinators_exhausted = false;
@@ -1681,7 +1681,7 @@ where
16811681
} else {
16821682
// If there is no context, we are at the root if nesting is supported. This is equivalent to :scope.
16831683
// Otherwise, if nesting is supported, serialize the nesting selector directly.
1684-
if should_compile!(dest.targets, Nesting) {
1684+
if should_compile!(dest.targets.current, Nesting) {
16851685
dest.write_str(":scope")
16861686
} else {
16871687
dest.write_char('&')

src/values/calc.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ impl<V: ToCss + std::ops::Mul<f32, Output = V> + TrySign + Clone + std::fmt::Deb
161161
}
162162
MathFunction::Clamp(a, b, c) => {
163163
// If clamp() is unsupported by targets, output min()/max()
164-
if should_compile!(dest.targets, ClampFunction) {
164+
if should_compile!(dest.targets.current, ClampFunction) {
165165
dest.write_str("max(")?;
166166
a.to_css(dest)?;
167167
dest.delim(',', false)?;

0 commit comments

Comments
 (0)