New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update swc #32210
Update swc #32210
Changes from all commits
9991358
2232a50
f0d6e40
2f54305
c76cc38
2284654
d52f3e3
65d6d09
f8cdf22
c8fb5ec
c08a40d
7c67c3a
2f152a8
6d57e20
3f9a734
acb8e8c
99c6afc
5395085
218e13c
77d9fe3
a16a1dd
68b0957
2bc25fc
ac8e874
6b62d48
9b9c4f3
b31c9c1
7c8a678
7defcf1
7c8defa
975e14a
4850822
791ed9a
9822ae8
e99afd1
7ea875a
04bcbc3
7fa2f90
03256bd
964bbe4
e47af03
885b49a
6ca31c6
34ad44b
5e3ef5b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
use easy_error::{bail, Error}; | ||
use std::panic; | ||
use swc_common::util::take::Take; | ||
use swc_common::{source_map::Pos, BytePos, Span, SyntaxContext, DUMMY_SP}; | ||
use swc_css::ast::*; | ||
use swc_css::codegen::{ | ||
|
@@ -11,6 +12,7 @@ use swc_css::visit::{VisitMut, VisitMutWith}; | |
use swc_ecmascript::ast::{Expr, Str, StrKind, Tpl, TplElement}; | ||
use swc_ecmascript::utils::HANDLER; | ||
use swc_stylis::prefixer::prefixer; | ||
use tracing::{debug, trace}; | ||
|
||
use super::{hash_string, string_literal_expr, LocalStyle}; | ||
|
||
|
@@ -19,6 +21,8 @@ pub fn transform_css( | |
is_global: bool, | ||
class_name: &Option<String>, | ||
) -> Result<Expr, Error> { | ||
debug!("CSS: \n{}", style_info.css); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This expression (and corresponding expression statement) will be completely noop on release builds. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cc @padmaia There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't quite understand what it does. Doesn't seem to be spamming the terminal by default so I think I'm okay with it, but it would help to understand how it works. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We enable There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This logging statement will print the input passed to the css parser while testing. Logging level is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As a side note, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
||
let result: Result<Stylesheet, _> = parse_str( | ||
&style_info.css, | ||
style_info.css_span.lo, | ||
|
@@ -52,8 +56,8 @@ pub fn transform_css( | |
} | ||
}; | ||
// ? Do we need to support optionally prefixing? | ||
ss.visit_mut_with(&mut FixedPrefixer); | ||
ss.visit_mut_with(&mut CssFixer); | ||
ss.visit_mut_with(&mut prefixer()); | ||
ss.visit_mut_with(&mut CssPlaceholderFixer); | ||
ss.visit_mut_with(&mut Namespacer { | ||
class_name: match class_name { | ||
Some(s) => s.clone(), | ||
|
@@ -122,27 +126,17 @@ fn read_number(s: &str) -> (usize, usize) { | |
unreachable!("read_number(`{}`) is invalid because it is empty", s) | ||
} | ||
|
||
/// Applies `prefixer`, but this avoids bug of `swc_stylis::prefixer()`. | ||
/// This fixes invalid css which is created from interpolated expressions. | ||
/// | ||
/// TODO(kdy1): Remove this when we upgrade crates related to css. (The crate | ||
/// update is blocked by `ComplexSelectorChildren` issue) | ||
struct FixedPrefixer; | ||
|
||
impl VisitMut for FixedPrefixer { | ||
fn visit_mut_style_rule(&mut self, n: &mut StyleRule) { | ||
n.visit_mut_with(&mut prefixer()); | ||
} | ||
} | ||
|
||
/// This fixes invalid css. | ||
struct CssFixer; | ||
/// `__styled-jsx-placeholder-` is handled at here. | ||
struct CssPlaceholderFixer; | ||
|
||
impl VisitMut for CssFixer { | ||
impl VisitMut for CssPlaceholderFixer { | ||
fn visit_mut_media_query(&mut self, q: &mut MediaQuery) { | ||
q.visit_mut_children_with(self); | ||
|
||
match q { | ||
MediaQuery::Text(q) => { | ||
MediaQuery::Ident(q) => { | ||
if q.raw.starts_with("__styled-jsx-placeholder-") { | ||
// TODO(kdy1): Remove this once we have CST for media query. | ||
// We need good error recovery for media queries to handle this. | ||
|
@@ -162,88 +156,177 @@ struct Namespacer { | |
|
||
impl VisitMut for Namespacer { | ||
fn visit_mut_complex_selector(&mut self, node: &mut ComplexSelector) { | ||
#[cfg(debug_assertions)] | ||
let _tracing = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This block explictly uses |
||
// This will add information to the log messages, only for debug build. | ||
// Note that we use cargo feature to remove all logging on production builds. | ||
|
||
let mut code = String::new(); | ||
{ | ||
let mut wr = BasicCssWriter::new(&mut code, BasicCssWriterConfig { indent: " " }); | ||
let mut gen = CodeGenerator::new(&mut wr, CodegenConfig { minify: true }); | ||
|
||
gen.emit(&*node).unwrap(); | ||
} | ||
|
||
tracing::span!( | ||
tracing::Level::TRACE, | ||
"Namespacer::visit_mut_complex_selector", | ||
class_name = &*self.class_name, | ||
is_global = self.is_global, | ||
is_dynamic = self.is_dynamic, | ||
input = &*code | ||
) | ||
.entered() | ||
}; | ||
|
||
let mut new_selectors = vec![]; | ||
for selector in &node.selectors { | ||
match self.get_transformed_selectors(selector.clone()) { | ||
Ok(transformed_selectors) => new_selectors.extend(transformed_selectors), | ||
Err(_) => { | ||
HANDLER.with(|handler| { | ||
handler | ||
.struct_span_err( | ||
selector.span, | ||
"Failed to transform one off global selector", | ||
) | ||
.emit() | ||
}); | ||
new_selectors.push(selector.clone()); | ||
let mut combinator = None; | ||
for sel in node.children.take() { | ||
match &sel { | ||
ComplexSelectorChildren::CompoundSelector(selector) => { | ||
match self.get_transformed_selectors(combinator, selector.clone()) { | ||
Ok(transformed_selectors) => new_selectors.extend(transformed_selectors), | ||
Err(_) => { | ||
HANDLER.with(|handler| { | ||
handler | ||
.struct_span_err( | ||
selector.span, | ||
"Failed to transform one off global selector", | ||
) | ||
.emit() | ||
}); | ||
new_selectors.push(sel); | ||
} | ||
} | ||
|
||
combinator = None; | ||
} | ||
ComplexSelectorChildren::Combinator(v) => match v.value { | ||
CombinatorValue::Descendant => {} | ||
CombinatorValue::NextSibling | ||
| CombinatorValue::Child | ||
| CombinatorValue::LaterSibling => { | ||
combinator = Some(v.clone()); | ||
|
||
new_selectors.push(sel); | ||
} | ||
}, | ||
}; | ||
} | ||
node.selectors = new_selectors; | ||
node.children = new_selectors; | ||
} | ||
} | ||
|
||
impl Namespacer { | ||
fn get_transformed_selectors( | ||
&mut self, | ||
combinator: Option<Combinator>, | ||
mut node: CompoundSelector, | ||
) -> Result<Vec<CompoundSelector>, Error> { | ||
) -> Result<Vec<ComplexSelectorChildren>, Error> { | ||
let mut pseudo_index = None; | ||
|
||
let empty_tokens = Tokens { | ||
span: node.span, | ||
tokens: vec![], | ||
}; | ||
|
||
for (i, selector) in node.subclass_selectors.iter().enumerate() { | ||
if let SubclassSelector::Pseudo(PseudoSelector { name, args, .. }) = selector { | ||
// One off global selector | ||
if &name.value == "global" { | ||
let block_tokens = get_block_tokens(&args); | ||
let mut front_tokens = get_front_selector_tokens(&args); | ||
let mut args = args.clone(); | ||
front_tokens.extend(args.tokens); | ||
front_tokens.extend(block_tokens); | ||
args.tokens = front_tokens; | ||
let complex_selectors = panic::catch_unwind(|| { | ||
let x: ComplexSelector = parse_tokens( | ||
&args, | ||
ParserConfig { | ||
parse_values: false, | ||
allow_wrong_line_comments: true, | ||
}, | ||
// TODO(kdy1): We might be able to report syntax errors. | ||
&mut vec![], | ||
) | ||
.unwrap(); | ||
return x; | ||
}); | ||
|
||
return match complex_selectors { | ||
Ok(complex_selectors) => { | ||
let mut v = complex_selectors.selectors[1..] | ||
.iter() | ||
.cloned() | ||
.collect::<Vec<_>>(); | ||
|
||
if v.is_empty() { | ||
bail!("Failed to transform one off global selector"); | ||
let (name, args) = match selector { | ||
SubclassSelector::PseudoClass(PseudoClassSelector { name, children, .. }) => { | ||
match children { | ||
Some(PseudoSelectorChildren::Nth(_)) => todo!("nth"), | ||
Some(PseudoSelectorChildren::Tokens(v)) => (name, v), | ||
None => (name, &empty_tokens), | ||
} | ||
} | ||
SubclassSelector::PseudoElement(PseudoElementSelector { | ||
name, children, .. | ||
}) => match children { | ||
Some(children) => (name, children), | ||
None => (name, &empty_tokens), | ||
}, | ||
_ => continue, | ||
}; | ||
|
||
// One off global selector | ||
if &name.value == "global" { | ||
let block_tokens = get_block_tokens(&args); | ||
let mut front_tokens = get_front_selector_tokens(&args); | ||
let mut args = args.clone(); | ||
front_tokens.extend(args.tokens); | ||
front_tokens.extend(block_tokens); | ||
args.tokens = front_tokens; | ||
|
||
let complex_selectors = panic::catch_unwind(|| { | ||
let x: ComplexSelector = parse_tokens( | ||
&args, | ||
ParserConfig { | ||
parse_values: false, | ||
allow_wrong_line_comments: true, | ||
}, | ||
// TODO(kdy1): We might be able to report syntax errors. | ||
&mut vec![], | ||
) | ||
.unwrap(); | ||
return x; | ||
}); | ||
|
||
return match complex_selectors { | ||
Ok(complex_selectors) => { | ||
let mut v = complex_selectors.children[1..] | ||
.iter() | ||
.cloned() | ||
.collect::<Vec<_>>(); | ||
|
||
match v[0] { | ||
ComplexSelectorChildren::Combinator(Combinator { | ||
value: CombinatorValue::Descendant, | ||
.. | ||
}) => { | ||
v.remove(0); | ||
} | ||
_ => {} | ||
} | ||
|
||
if node.combinator.is_some() && v[0].combinator.is_some() { | ||
bail!("Failed to transform one off global selector"); | ||
} else if node.combinator.is_some() { | ||
v[0].combinator = node.combinator; | ||
if v.is_empty() { | ||
bail!("Failed to transform one off global selector"); | ||
} | ||
|
||
trace!("Combinator: {:?}", combinator); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cc @padmaia There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same answer as the |
||
trace!("v[0]: {:?}", v[0]); | ||
|
||
if combinator.is_some() { | ||
match v.get(0) { | ||
Some(ComplexSelectorChildren::Combinator(..)) => {} | ||
Some(..) => {} | ||
_ => { | ||
v.push(ComplexSelectorChildren::Combinator( | ||
combinator.unwrap(), | ||
)); | ||
} | ||
} | ||
} | ||
|
||
v.iter_mut().for_each(|sel| { | ||
if i < node.subclass_selectors.len() { | ||
sel.subclass_selectors | ||
.extend(node.subclass_selectors[i + 1..].to_vec()); | ||
v.iter_mut().for_each(|sel| { | ||
if i < node.subclass_selectors.len() { | ||
match sel { | ||
ComplexSelectorChildren::CompoundSelector(sel) => { | ||
sel.subclass_selectors.extend( | ||
node.subclass_selectors[i + 1..].iter().cloned(), | ||
); | ||
} | ||
_ => {} | ||
} | ||
}); | ||
} | ||
}); | ||
|
||
Ok(v) | ||
} | ||
Err(_) => bail!("Failed to transform one off global selector"), | ||
}; | ||
} else if pseudo_index.is_none() { | ||
pseudo_index = Some(i); | ||
} | ||
Ok(v) | ||
} | ||
Err(_) => bail!("Failed to transform one off global selector"), | ||
}; | ||
} else if pseudo_index.is_none() { | ||
pseudo_index = Some(i); | ||
} | ||
} | ||
|
||
|
@@ -260,7 +343,7 @@ impl Namespacer { | |
insert_index, | ||
SubclassSelector::Class(ClassSelector { | ||
span: DUMMY_SP, | ||
text: Text { | ||
text: Ident { | ||
raw: subclass_selector.into(), | ||
value: subclass_selector.into(), | ||
span: DUMMY_SP, | ||
|
@@ -269,7 +352,7 @@ impl Namespacer { | |
); | ||
} | ||
|
||
Ok(vec![node]) | ||
Ok(vec![ComplexSelectorChildren::CompoundSelector(node)]) | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a major difference.