Skip to content

Commit bbb64b7

Browse files
committed
Store unsupported functional pseudo classes and elements as raw tokens
Part of parcel-bundler#227. We now also warn on unsupported selectors.
1 parent 9916cae commit bbb64b7

File tree

8 files changed

+72
-53
lines changed

8 files changed

+72
-53
lines changed

node/src/lib.rs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -243,11 +243,7 @@ struct Drafts {
243243

244244
fn compile<'i>(code: &'i str, config: &Config) -> Result<TransformResult<'i>, CompileError<'i>> {
245245
let drafts = config.drafts.as_ref();
246-
let warnings = if config.error_recovery.unwrap_or_default() {
247-
Some(Arc::new(RwLock::new(Vec::new())))
248-
} else {
249-
None
250-
};
246+
let warnings = Some(Arc::new(RwLock::new(Vec::new())));
251247

252248
let filename = config.filename.clone().unwrap_or_default();
253249
let mut source_map = if config.source_map.unwrap_or_default() {
@@ -343,11 +339,7 @@ fn compile_bundle<'i>(
343339
} else {
344340
None
345341
};
346-
let warnings = if config.error_recovery.unwrap_or_default() {
347-
Some(Arc::new(RwLock::new(Vec::new())))
348-
} else {
349-
None
350-
};
342+
let warnings = Some(Arc::new(RwLock::new(Vec::new())));
351343
let res = {
352344
let drafts = config.drafts.as_ref();
353345
let parser_options = ParserOptions {

selectors/parser.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ macro_rules! with_bounds {
244244
}
245245

246246
with_bounds! {
247-
[Clone + Eq]
247+
[Clone + PartialEq]
248248
[From<CowRcStr<'i>>]
249249
}
250250

@@ -335,7 +335,7 @@ pub trait Parser<'i> {
335335
}
336336
}
337337

338-
#[derive(Clone, Debug, Eq, PartialEq)]
338+
#[derive(Clone, Debug, PartialEq)]
339339
pub struct SelectorList<'i, Impl: SelectorImpl<'i>>(pub SmallVec<[Selector<'i, Impl>; 1]>);
340340

341341
/// How to treat invalid selectors in a selector list.
@@ -615,7 +615,7 @@ pub fn namespace_empty_string<'i, Impl: SelectorImpl<'i>>() -> Impl::NamespaceUr
615615
///
616616
/// This reordering doesn't change the semantics of selector matching, and we
617617
/// handle it in to_css to make it invisible to serialization.
618-
#[derive(Clone, Eq, PartialEq)]
618+
#[derive(Clone, PartialEq)]
619619
pub struct Selector<'i, Impl: SelectorImpl<'i>>(SpecificityAndFlags, Vec<Component<'i, Impl>>);
620620

621621
impl<'i, Impl: SelectorImpl<'i>> Selector<'i, Impl> {
@@ -1067,7 +1067,7 @@ impl Combinator {
10671067
/// optimal packing and cache performance, see [1].
10681068
///
10691069
/// [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1357973
1070-
#[derive(Clone, Eq, PartialEq)]
1070+
#[derive(Clone, PartialEq)]
10711071
pub enum Component<'i, Impl: SelectorImpl<'i>> {
10721072
Combinator(Combinator),
10731073

src/declaration.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::borrow::Cow;
44
use std::ops::Range;
55

66
use crate::context::PropertyHandlerContext;
7-
use crate::error::{Error, ParserError, PrinterError};
7+
use crate::error::{ParserError, PrinterError};
88
use crate::parser::ParserOptions;
99
use crate::printer::Printer;
1010
use crate::properties::box_shadow::BoxShadowHandler;
@@ -70,11 +70,7 @@ impl<'i> DeclarationBlock<'i> {
7070
while let Some(res) = parser.next() {
7171
if let Err((err, _)) = res {
7272
if options.error_recovery {
73-
if let Some(warnings) = &options.warnings {
74-
if let Ok(mut warnings) = warnings.write() {
75-
warnings.push(Error::from(err, options.filename.clone()));
76-
}
77-
}
73+
options.warn(err);
7874
continue;
7975
}
8076
return Err(err);

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5332,6 +5332,9 @@ mod tests {
53325332
"::cue(v[voice='active']) {color: yellow;}",
53335333
"::cue(v[voice=active]){color:#ff0}",
53345334
);
5335+
minify_test(":foo(bar) { color: yellow }", ":foo(bar){color:#ff0}");
5336+
minify_test("::foo(bar) { color: yellow }", "::foo(bar){color:#ff0}");
5337+
minify_test("::foo(*) { color: yellow }", "::foo(*){color:#ff0}");
53355338
}
53365339

53375340
#[test]

src/parser.rs

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,17 @@ pub struct ParserOptions<'o, 'i> {
5252
pub warnings: Option<Arc<RwLock<Vec<Error<ParserError<'i>>>>>>,
5353
}
5454

55+
impl<'o, 'i> ParserOptions<'o, 'i> {
56+
#[inline]
57+
pub(crate) fn warn(&self, warning: ParseError<'i, ParserError<'i>>) {
58+
if let Some(warnings) = &self.warnings {
59+
if let Ok(mut warnings) = warnings.write() {
60+
warnings.push(Error::from(warning, self.filename.clone()));
61+
}
62+
}
63+
}
64+
}
65+
5566
#[derive(PartialEq, PartialOrd)]
5667
enum State {
5768
Start = 1,
@@ -324,11 +335,7 @@ impl<'a, 'o, 'b, 'i> NestedRuleParser<'a, 'o, 'i> {
324335
Ok(rule) => rules.push(rule),
325336
Err((e, _)) => {
326337
if self.options.error_recovery {
327-
if let Some(warnings) = &self.options.warnings {
328-
if let Ok(mut warnings) = warnings.write() {
329-
warnings.push(Error::from(e, self.options.filename.clone()));
330-
}
331-
}
338+
self.options.warn(e);
332339
continue;
333340
}
334341
return Err(e);
@@ -600,7 +607,7 @@ impl<'a, 'o, 'b, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'o, 'i> {
600607
default_namespace: self.default_namespace,
601608
namespace_prefixes: self.namespace_prefixes,
602609
is_nesting_allowed: false,
603-
css_modules: self.options.css_modules.is_some(),
610+
options: &self.options,
604611
};
605612
SelectorList::parse(&selector_parser, input, NestingRequirement::None)
606613
}
@@ -663,11 +670,7 @@ fn parse_declarations_and_nested_rules<'a, 'o, 'i, 't>(
663670
while let Some(result) = iter.next() {
664671
if let Err((err, _)) = result {
665672
if options.error_recovery {
666-
if let Some(warnings) = &options.warnings {
667-
if let Ok(mut warnings) = warnings.write() {
668-
warnings.push(Error::from(err, options.filename.clone()));
669-
}
670-
}
673+
options.warn(err);
671674
continue;
672675
}
673676
return Err(err);
@@ -740,7 +743,7 @@ impl<'a, 'o, 'i> AtRuleParser<'i> for StyleRuleParser<'a, 'o, 'i> {
740743
default_namespace: self.default_namespace,
741744
namespace_prefixes: self.namespace_prefixes,
742745
is_nesting_allowed: true,
743-
css_modules: self.options.css_modules.is_some()
746+
options: &self.options,
744747
};
745748
let selectors = SelectorList::parse(&selector_parser, input, NestingRequirement::Contained)?;
746749
Ok(AtRulePrelude::Nest(selectors))
@@ -867,7 +870,7 @@ impl<'a, 'o, 'b, 'i> QualifiedRuleParser<'i> for StyleRuleParser<'a, 'o, 'i> {
867870
default_namespace: self.default_namespace,
868871
namespace_prefixes: self.namespace_prefixes,
869872
is_nesting_allowed: true,
870-
css_modules: self.options.css_modules.is_some(),
873+
options: &self.options,
871874
};
872875
SelectorList::parse(&selector_parser, input, NestingRequirement::Prefixed)
873876
}

src/properties/custom.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ impl<'i> TokenOrValue<'i> {
128128
}
129129

130130
impl<'i> TokenList<'i> {
131-
fn parse<'t>(
131+
pub(crate) fn parse<'t>(
132132
input: &mut Parser<'i, 't>,
133133
options: &ParserOptions,
134134
depth: usize,

src/selector.rs

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
use crate::compat::Feature;
22
use crate::error::{ParserError, PrinterError};
33
use crate::printer::Printer;
4+
use crate::properties::custom::TokenList;
45
use crate::rules::{StyleContext, ToCssWithContext};
5-
use crate::stylesheet::PrinterOptions;
6+
use crate::stylesheet::{ParserOptions, PrinterOptions};
67
use crate::targets::Browsers;
78
use crate::traits::{Parse, ToCss};
89
use crate::vendor_prefix::VendorPrefix;
@@ -89,20 +90,20 @@ impl<'i> SelectorImpl<'i> for Selectors {
8990
}
9091
}
9192

92-
pub struct SelectorParser<'a, 'i> {
93+
pub struct SelectorParser<'a, 'o, 'i> {
9394
pub default_namespace: &'a Option<CowArcStr<'i>>,
9495
pub namespace_prefixes: &'a HashMap<CowArcStr<'i>, CowArcStr<'i>>,
9596
pub is_nesting_allowed: bool,
96-
pub css_modules: bool,
97+
pub options: &'a ParserOptions<'o, 'i>,
9798
}
9899

99-
impl<'a, 'i> parcel_selectors::parser::Parser<'i> for SelectorParser<'a, 'i> {
100+
impl<'a, 'o, 'i> parcel_selectors::parser::Parser<'i> for SelectorParser<'a, 'o, 'i> {
100101
type Impl = Selectors;
101102
type Error = ParserError<'i>;
102103

103104
fn parse_non_ts_pseudo_class(
104105
&self,
105-
_: SourceLocation,
106+
loc: SourceLocation,
106107
name: CowRcStr<'i>,
107108
) -> Result<PseudoClass<'i>, ParseError<'i, Self::Error>> {
108109
use PseudoClass::*;
@@ -188,7 +189,10 @@ impl<'a, 'i> parcel_selectors::parser::Parser<'i> for SelectorParser<'a, 'i> {
188189
"corner-present" => WebKitScrollbar(WebKitScrollbarPseudoClass::CornerPresent),
189190
"window-inactive" => WebKitScrollbar(WebKitScrollbarPseudoClass::WindowInactive),
190191

191-
_ => Custom(name.into())
192+
_ => {
193+
self.options.warn(loc.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name.clone())));
194+
Custom(name.into())
195+
}
192196
};
193197

194198
Ok(pseudo_class)
@@ -210,9 +214,12 @@ impl<'a, 'i> parcel_selectors::parser::Parser<'i> for SelectorParser<'a, 'i> {
210214
Lang(langs)
211215
},
212216
"dir" => Dir(Direction::parse(parser)?),
213-
"local" if self.css_modules => Local(Box::new(parcel_selectors::parser::Selector::parse(self, parser)?)),
214-
"global" if self.css_modules => Global(Box::new(parcel_selectors::parser::Selector::parse(self, parser)?)),
215-
_ => return Err(parser.new_custom_error(parcel_selectors::parser::SelectorParseErrorKind::UnexpectedIdent(name.clone()))),
217+
"local" if self.options.css_modules.is_some() => Local(Box::new(parcel_selectors::parser::Selector::parse(self, parser)?)),
218+
"global" if self.options.css_modules.is_some() => Global(Box::new(parcel_selectors::parser::Selector::parse(self, parser)?)),
219+
_ => {
220+
self.options.warn(parser.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name.clone())));
221+
CustomFunction(name.into(), TokenList::parse(parser, &self.options, 0)?)
222+
},
216223
};
217224

218225
Ok(pseudo_class)
@@ -228,7 +235,7 @@ impl<'a, 'i> parcel_selectors::parser::Parser<'i> for SelectorParser<'a, 'i> {
228235

229236
fn parse_pseudo_element(
230237
&self,
231-
_: SourceLocation,
238+
loc: SourceLocation,
232239
name: CowRcStr<'i>,
233240
) -> Result<PseudoElement<'i>, ParseError<'i, Self::Error>> {
234241
use PseudoElement::*;
@@ -260,7 +267,10 @@ impl<'a, 'i> parcel_selectors::parser::Parser<'i> for SelectorParser<'a, 'i> {
260267
"-webkit-scrollbar-corner" => WebKitScrollbar(WebKitScrollbarPseudoElement::Corner),
261268
"-webkit-resizer" => WebKitScrollbar(WebKitScrollbarPseudoElement::Resizer),
262269

263-
_ => Custom(name.into())
270+
_ => {
271+
self.options.warn(loc.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name.clone())));
272+
Custom(name.into())
273+
}
264274
};
265275

266276
Ok(pseudo_element)
@@ -275,7 +285,10 @@ impl<'a, 'i> parcel_selectors::parser::Parser<'i> for SelectorParser<'a, 'i> {
275285
let pseudo_element = match_ignore_ascii_case! { &name,
276286
"cue" => CueFunction(Box::new(Selector::parse(self, arguments)?)),
277287
"cue-region" => CueRegionFunction(Box::new(Selector::parse(self, arguments)?)),
278-
_ => return Err(arguments.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name)))
288+
_ => {
289+
self.options.warn(arguments.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name.clone())));
290+
CustomFunction(name.into(), TokenList::parse(arguments, &self.options, 0)?)
291+
}
279292
};
280293

281294
Ok(pseudo_element)
@@ -324,7 +337,7 @@ enum_property! {
324337
}
325338

326339
/// https://drafts.csswg.org/selectors-4/#structural-pseudos
327-
#[derive(Clone, Eq, PartialEq)]
340+
#[derive(Clone, PartialEq)]
328341
pub enum PseudoClass<'i> {
329342
// https://drafts.csswg.org/selectors-4/#linguistic-pseudos
330343
Lang(Vec<CowArcStr<'i>>),
@@ -395,6 +408,7 @@ pub enum PseudoClass<'i> {
395408
WebKitScrollbar(WebKitScrollbarPseudoClass),
396409

397410
Custom(CowArcStr<'i>),
411+
CustomFunction(CowArcStr<'i>, TokenList<'i>),
398412
}
399413

400414
/// https://webkit.org/blog/363/styling-scrollbars/
@@ -622,6 +636,13 @@ impl<'a, 'i> ToCssWithContext<'a, 'i> for PseudoClass<'i> {
622636
dest.write_char(':')?;
623637
return dest.write_str(&val);
624638
}
639+
CustomFunction(name, args) => {
640+
dest.write_char(':')?;
641+
dest.write_str(name)?;
642+
dest.write_char('(')?;
643+
args.to_css(dest, false)?;
644+
dest.write_char(')')
645+
}
625646
}
626647
}
627648
}
@@ -665,7 +686,7 @@ impl<'i> PseudoClass<'i> {
665686
}
666687
}
667688

668-
#[derive(PartialEq, Eq, Clone, Debug)]
689+
#[derive(PartialEq, Clone, Debug)]
669690
pub enum PseudoElement<'i> {
670691
After,
671692
Before,
@@ -682,6 +703,7 @@ pub enum PseudoElement<'i> {
682703
CueFunction(Box<Selector<'i, Selectors>>),
683704
CueRegionFunction(Box<Selector<'i, Selectors>>),
684705
Custom(CowArcStr<'i>),
706+
CustomFunction(CowArcStr<'i>, TokenList<'i>),
685707
}
686708

687709
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
@@ -796,6 +818,13 @@ impl<'i> ToCss for PseudoElement<'i> {
796818
dest.write_str("::")?;
797819
return dest.write_str(val);
798820
}
821+
CustomFunction(name, args) => {
822+
dest.write_str("::")?;
823+
dest.write_str(name)?;
824+
dest.write_char('(')?;
825+
args.to_css(dest, false)?;
826+
dest.write_char(')')
827+
}
799828
}
800829
}
801830
}

src/stylesheet.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -124,11 +124,7 @@ impl<'i, 'o> StyleSheet<'i, 'o> {
124124
Ok((_, rule)) => rule,
125125
Err((e, _)) => {
126126
if options.error_recovery {
127-
if let Some(warnings) = &options.warnings {
128-
if let Ok(mut warnings) = warnings.write() {
129-
warnings.push(Error::from(e, options.filename.clone()));
130-
}
131-
}
127+
options.warn(e);
132128
continue;
133129
}
134130

0 commit comments

Comments
 (0)