Skip to content

Commit

Permalink
fix(stlyled-jsx): Apply placeholder hack for swc mode (#256)
Browse files Browse the repository at this point in the history
  • Loading branch information
kdy1 authored Jan 25, 2024
1 parent d592781 commit a148115
Show file tree
Hide file tree
Showing 24 changed files with 119 additions and 106 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/styled-jsx/transform/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description = "AST transforms visitor for styled-jsx"
edition = "2021"
license = "Apache-2.0"
name = "styled_jsx"
version = "0.73.0"
version = "0.73.1"

[features]
custom_transform = ["swc_common/concurrent"]
Expand Down
2 changes: 1 addition & 1 deletion packages/styled-jsx/transform/src/style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub struct LocalStyle {
#[allow(clippy::vec_box)]
pub expressions: Vec<Box<Expr>>,

/// If true, `format!("__styled-jsx-placeholder-{}__: 0", i)` is used.
/// If true, `format!("--styled-jsx-placeholder-{}__: 0", i)` is used.
pub is_expr_property: Vec<bool>,
}

Expand Down
14 changes: 9 additions & 5 deletions packages/styled-jsx/transform/src/transform_css_lightningcss.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use tracing::{debug, error, trace};
use crate::{
style::LocalStyle,
utils::{hash_string, string_literal_expr},
visitor::NativeConfig,
};

fn report(
Expand Down Expand Up @@ -69,14 +70,15 @@ fn report(

#[cfg_attr(
debug_assertions,
tracing::instrument(skip(cm, style_info, class_name, browsers))
tracing::instrument(skip(cm, style_info, class_name, browsers, native))
)]
pub fn transform_css(
cm: Arc<SourceMap>,
style_info: &LocalStyle,
is_global: bool,
class_name: &Option<String>,
browsers: &Versions,
native: &NativeConfig,
) -> Result<Expr, Error> {
let mut file_lines_cache = None;

Expand Down Expand Up @@ -150,7 +152,7 @@ pub fn transform_css(
})
.expect("failed to minify/auto-prefix css");

let res = ss
let mut res = ss
.to_css(PrinterOptions {
minify: true,
targets: Targets {
Expand All @@ -161,13 +163,15 @@ pub fn transform_css(
})
.context("failed to print css")?;

res.code = native.invoke_css_transform(style_info.css_span, res.code);

debug!("Transformed CSS: \n{}", res.code);

if style_info.expressions.is_empty() {
return Ok(string_literal_expr(&res.code));
}

let mut parts: Vec<&str> = res.code.split("__styled-jsx-placeholder-").collect();
let mut parts: Vec<&str> = res.code.split("--styled-jsx-placeholder-").collect();
let mut final_expressions = vec![];
for i in parts.iter_mut().skip(1) {
let (num_len, expression_index) = read_number(i, &style_info.is_expr_property);
Expand Down Expand Up @@ -211,7 +215,7 @@ fn convert_browsers(browsers: &Versions) -> Browsers {
}
}

fn strip_comments(s: &str) -> Cow<str> {
pub(super) fn strip_comments(s: &str) -> Cow<str> {
if !s.contains("//") {
return Cow::Borrowed(s);
}
Expand All @@ -233,7 +237,7 @@ fn strip_comments(s: &str) -> Cow<str> {
}

/// Returns `(length, expression_index)`
fn read_number(s: &str, is_expr_property: &[bool]) -> (usize, usize) {
pub(super) fn read_number(s: &str, is_expr_property: &[bool]) -> (usize, usize) {
for (idx, c) in s.char_indices() {
if c.is_ascii_digit() {
continue;
Expand Down
33 changes: 10 additions & 23 deletions packages/styled-jsx/transform/src/transform_css_swc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,29 +30,30 @@ use tracing::{debug, trace};

use crate::{
style::LocalStyle,
transform_css_lightningcss::{read_number, strip_comments},
utils::{hash_string, string_literal_expr},
visitor::NativeConfig,
};

pub fn transform_css(
_cm: Arc<SourceMap>,
style_info: &LocalStyle,
is_global: bool,
class_name: &Option<String>,
native: &NativeConfig,
) -> Result<Expr, Error> {
debug!("CSS: \n{}", style_info.css);

let css_str = strip_comments(&style_info.css);

// TODO use `parse_string_input` in future
let config = ParserConfig {
allow_wrong_line_comments: true,
css_modules: false,
..Default::default()
};
let lexer = Lexer::new(
StringInput::new(
&style_info.css,
style_info.css_span.lo,
style_info.css_span.hi,
),
StringInput::new(&css_str, style_info.css_span.lo, style_info.css_span.hi),
None,
config,
);
Expand Down Expand Up @@ -98,14 +99,16 @@ pub fn transform_css(
gen.emit(&ss).unwrap();
}

s = native.invoke_css_transform(style_info.css_span, s);

if style_info.expressions.is_empty() {
return Ok(string_literal_expr(&s));
}

let mut parts: Vec<&str> = s.split("__styled-jsx-placeholder-").collect();
let mut parts: Vec<&str> = s.split("--styled-jsx-placeholder-").collect();
let mut final_expressions = vec![];
for i in parts.iter_mut().skip(1) {
let (num_len, expression_index) = read_number(i);
let (num_len, expression_index) = read_number(i, &style_info.is_expr_property);
final_expressions.push(style_info.expressions[expression_index].clone());
let substr = &i[(num_len + 2)..];
*i = substr;
Expand All @@ -128,22 +131,6 @@ pub fn transform_css(
}))
}

/// Returns `(length, value)`
fn read_number(s: &str) -> (usize, usize) {
for (idx, c) in s.char_indices() {
if c.is_ascii_digit() {
continue;
}

// For 10, we reach here after `0`.
let value = s[0..idx].parse().expect("failed to parse");

return (idx, value);
}

unreachable!("read_number(`{}`) is invalid because it is empty", s)
}

struct Namespacer {
class_name: String,
is_global: bool,
Expand Down
62 changes: 33 additions & 29 deletions packages/styled-jsx/transform/src/visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,25 @@ pub struct NativeConfig<'a> {
pub process_css: Option<Box<dyn 'a + for<'aa> Fn(&'aa str) -> Result<String, Error>>>,
}

impl NativeConfig<'_> {
pub(crate) fn invoke_css_transform(&self, span: Span, css: String) -> String {
if let Some(process_css) = &self.process_css {
match process_css(&css) {
Ok(new_css) => return new_css,
Err(err) => {
HANDLER.with(|handler| {
handler
.struct_span_err(span, &format!("Error while processing css: {}.", err))
.emit()
});
}
}
}

css
}
}

pub fn styled_jsx<'a>(
cm: Arc<SourceMap>,
file_name: FileName,
Expand Down Expand Up @@ -516,15 +535,22 @@ impl StyledJSXTransformer<'_> {
let before = &*quasis[i].raw;
let before = before.trim();

let after = quasis.get(i + 1).map(|v| v.raw.trim());

let placeholder = if i == quasis.len() - 1 {
is_expr_property.push(false);
String::new()
} else if self.config.use_lightningcss && before.ends_with([';', '{']) {
} else if before.ends_with([';', '{'])
&& match after {
Some(after) => !after.starts_with(':'),
None => true,
}
{
is_expr_property.push(true);
format!("__styled-jsx-placeholder-{}__: 0", i)
format!("--styled-jsx-placeholder-{}__: 0", i)
} else {
is_expr_property.push(false);
format!("__styled-jsx-placeholder-{}__", i)
format!("--styled-jsx-placeholder-{}__", i)
};
s.push_str(&quasis[i].raw);
s.push_str(&placeholder);
Expand Down Expand Up @@ -585,22 +611,22 @@ impl StyledJSXTransformer<'_> {

match &style_info {
JSXStyle::Local(style_info) => {
let style_info = self.invoke_css_transform(style_info);

let css = if self.config.use_lightningcss {
crate::transform_css_lightningcss::transform_css(
self.cm.clone(),
&style_info,
is_global,
&self.static_class_name,
&self.config.browsers,
&self.native_config,
)?
} else {
crate::transform_css_swc::transform_css(
self.cm.clone(),
&style_info,
is_global,
&self.static_class_name,
&self.native_config,
)?
};

Expand Down Expand Up @@ -653,21 +679,22 @@ impl StyledJSXTransformer<'_> {
bail!("This shouldn't happen, we already know that this is a template literal");
};

let style = self.invoke_css_transform(style);
let css = if self.config.use_lightningcss {
crate::transform_css_lightningcss::transform_css(
self.cm.clone(),
&style,
tag == "global",
&static_class_name,
&self.config.browsers,
&self.native_config,
)?
} else {
crate::transform_css_swc::transform_css(
self.cm.clone(),
&style,
tag == "global",
&static_class_name,
&self.native_config,
)?
};
if tag == "resolve" {
Expand Down Expand Up @@ -720,29 +747,6 @@ impl StyledJSXTransformer<'_> {
self.class_name = None;
self.styles = vec![];
}

fn invoke_css_transform(&self, style: &LocalStyle) -> LocalStyle {
let mut css = style.css.clone();
if let Some(process_css) = &self.native_config.process_css {
match process_css(&css) {
Ok(new_css) => css = new_css,
Err(err) => {
HANDLER.with(|handler| {
handler
.struct_span_err(
style.css_span,
&format!("Error while processing css: {}.", err),
)
.emit()
});
}
}
}
LocalStyle {
css,
..style.clone()
}
}
}

fn is_styled_jsx(el: &JSXElement) -> bool {
Expand Down
69 changes: 38 additions & 31 deletions packages/styled-jsx/transform/tests/fixture.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::path::PathBuf;

use styled_jsx::visitor::styled_jsx;
use anyhow::bail;
use lightningcss::stylesheet::ParserOptions;
use styled_jsx::visitor::{styled_jsx, NativeConfig};
use swc_common::{chain, FileName, Mark, Span, DUMMY_SP};
use swc_ecma_parser::{EsConfig, Syntax};
use swc_ecma_transforms::resolver;
Expand Down Expand Up @@ -33,37 +35,43 @@ fn run(input: PathBuf, use_lightningcss: bool) {
use_lightningcss,
..Default::default()
},
Default::default()
)
)
},
&input,
&output,
Default::default(),
);
if use_lightningcss {
Default::default()
} else {
NativeConfig {
process_css: Some(Box::new(move |css| {
let ss = lightningcss::stylesheet::StyleSheet::parse(
css,
ParserOptions {
error_recovery: true,
..Default::default()
},
);

test_fixture(
syntax(),
&|t| {
// `resolver` uses `Mark` which is stored in a thread-local storage (namely
// swc_common::GLOBALS), and this loop will make `Mark` to be different from the
// invocation above.
//
// 1000 is used because in future I (kdy1) may optimize logic of resolver.
for _ in 0..1000 {
let _mark = Mark::fresh(Mark::root());
}
let ss = match ss {
Ok(v) => v,
Err(err) => {
bail!(
"failed to parse css using lightningcss: {}\nCode: {}",
err,
css
)
}
};

chain!(
resolver(Mark::new(), Mark::new(), false),
styled_jsx(
t.cm.clone(),
FileName::Real(PathBuf::from("/some-project/src/some-file.js")),
styled_jsx::visitor::Config {
use_lightningcss,
..Default::default()
},
Default::default()
let output =
ss.to_css(lightningcss::stylesheet::PrinterOptions {
minify: true,
source_map: None,
project_root: None,
targets: Default::default(),
analyze_dependencies: None,
pseudo_classes: None,
})?;
Ok(output.code)
})),
}
}
)
)
},
Expand All @@ -78,7 +86,6 @@ fn styled_jsx_fixture_lightningcs(input: PathBuf) {
run(input, true);
}

#[fixture("tests/fixture-swc-only/**/input.js")]
#[fixture("tests/fixture/**/input.js")]
fn styled_jsx_fixture_swc(input: PathBuf) {
run(input, false);
Expand Down
Loading

0 comments on commit a148115

Please sign in to comment.