Skip to content
This repository was archived by the owner on Jul 6, 2025. It is now read-only.

Commit 3398492

Browse files
committed
Record jsx static class names
1 parent 389a0a1 commit 3398492

File tree

5 files changed

+136
-71
lines changed

5 files changed

+136
-71
lines changed

compiler/mod.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export type TransformOptions = {
3535
react?: ReactOptions
3636
sourceMap?: boolean
3737
isDev?: boolean
38-
externalRemoteDeps?: boolean
38+
httpExternal?: boolean
3939
bundleMode?: boolean
4040
bundleExternals?: string[]
4141
inlineStylePreprocess?(key: string, type: string, tpl: string): Promise<string>
@@ -48,6 +48,7 @@ export type TransformResult = {
4848
ssgPathsFn?: boolean
4949
denoHooks?: string[]
5050
starExports?: string[]
51+
jsxStaticClassNames?: string[]
5152
map?: string
5253
}
5354

compiler/src/jsx.rs

Lines changed: 111 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -7,41 +7,41 @@ use swc_ecma_ast::*;
77
use swc_ecma_utils::quote_ident;
88
use swc_ecma_visit::{noop_fold_type, Fold, FoldWith};
99

10-
pub fn aleph_jsx_fold(resolver: Rc<RefCell<Resolver>>, source: Rc<SourceMap>) -> impl Fold {
11-
AlephJsxFold {
10+
pub fn jsx_magic_fold(resolver: Rc<RefCell<Resolver>>, source: Rc<SourceMap>) -> impl Fold {
11+
JSXMagicFold {
1212
resolver: resolver.clone(),
1313
source,
1414
inline_style_idx: 0,
1515
}
1616
}
1717

18-
pub fn aleph_jsx_pass2_fold(
18+
pub fn jsx_magic_pass_2_fold(
1919
resolver: Rc<RefCell<Resolver>>,
2020
source: Rc<SourceMap>,
2121
is_dev: bool,
2222
) -> impl Fold {
23-
AlephJsxPass2Fold {
23+
JSXMagicPass2Fold {
2424
resolver: resolver.clone(),
2525
source,
2626
is_dev,
2727
}
2828
}
2929

30-
/// aleph.js jsx fold, core functions include:
30+
/// JSX magic fold, core functions include:
3131
/// - add `__sourceFile` prop in development mode
3232
/// - resolve `a` to `Anchor`
3333
/// - resolve `head` to `Head`
3434
/// - resolve `link` to `StyleLink`
3535
/// - resolve `style` to `InlineStyle`
3636
/// - resolve `script` to `CustomScript`
3737
/// - optimize `img` in producation mode
38-
struct AlephJsxFold {
38+
struct JSXMagicFold {
3939
resolver: Rc<RefCell<Resolver>>,
4040
source: Rc<SourceMap>,
4141
inline_style_idx: i32,
4242
}
4343

44-
impl AlephJsxFold {
44+
impl JSXMagicFold {
4545
fn new_inline_style_ident(&mut self) -> String {
4646
let resolver = self.resolver.borrow();
4747
let mut ident: String = "inline-style-".to_owned();
@@ -234,7 +234,7 @@ impl AlephJsxFold {
234234
}
235235
}
236236

237-
impl Fold for AlephJsxFold {
237+
impl Fold for JSXMagicFold {
238238
noop_fold_type!();
239239

240240
fn fold_jsx_element(&mut self, mut el: JSXElement) -> JSXElement {
@@ -264,8 +264,7 @@ impl Fold for AlephJsxFold {
264264
es.push(raw.into());
265265
}
266266
for quasi in quasis {
267-
let raw = self.source.span_to_snippet(quasi.span.clone()).unwrap();
268-
qs.push(raw.into());
267+
qs.push(quasi.raw.value.to_string());
269268
}
270269
let (t, id) = inline_style;
271270
resolver.inline_styles.insert(
@@ -306,13 +305,63 @@ impl Fold for AlephJsxFold {
306305
}
307306
}
308307

309-
struct AlephJsxPass2Fold {
308+
struct JSXMagicPass2Fold {
310309
resolver: Rc<RefCell<Resolver>>,
311310
source: Rc<SourceMap>,
312311
is_dev: bool,
313312
}
314313

315-
impl AlephJsxPass2Fold {
314+
impl JSXMagicPass2Fold {
315+
fn record_jsx_class_name(&mut self, expr: Box<Expr>) {
316+
match expr.as_ref() {
317+
Expr::Lit(Lit::Str(Str { value, .. })) => {
318+
let s = value.as_ref();
319+
if s != "" {
320+
self
321+
.resolver
322+
.borrow_mut()
323+
.jsx_static_class_names
324+
.insert(s.into());
325+
}
326+
}
327+
Expr::Lit(Lit::JSXText(JSXText { value, .. })) => {
328+
let s = value.as_ref();
329+
if s != "" {
330+
self
331+
.resolver
332+
.borrow_mut()
333+
.jsx_static_class_names
334+
.insert(s.into());
335+
}
336+
}
337+
Expr::Cond(CondExpr { cons, alt, .. }) => {
338+
self.record_jsx_class_name(cons.clone());
339+
self.record_jsx_class_name(alt.clone());
340+
}
341+
Expr::Bin(BinExpr {
342+
op, left, right, ..
343+
}) => {
344+
if *op == BinaryOp::Add {
345+
self.record_jsx_class_name(left.clone());
346+
self.record_jsx_class_name(right.clone());
347+
}
348+
}
349+
Expr::Tpl(Tpl { exprs, quasis, .. }) => {
350+
for expr in exprs {
351+
self.record_jsx_class_name(expr.clone());
352+
}
353+
let mut resolver = self.resolver.borrow_mut();
354+
for quasi in quasis {
355+
let s = quasi.raw.value.as_ref();
356+
if s != "" {
357+
resolver.jsx_static_class_names.insert(s.into());
358+
}
359+
}
360+
}
361+
_ => {}
362+
}
363+
}
364+
316365
fn fold_jsx_opening_element(&mut self, mut el: JSXOpeningElement) -> JSXOpeningElement {
317366
let extra_imports = self.resolver.borrow().extra_imports.clone();
318367
let mut css_modules = false;
@@ -324,21 +373,22 @@ impl AlephJsxPass2Fold {
324373
}
325374
}
326375

327-
if css_modules {
328-
let mut i: Option<usize> = None;
329-
let mut class_name_value_expr: Option<Box<Expr>> = None;
330-
331-
for (index, attr) in el.attrs.iter().enumerate() {
332-
match &attr {
333-
JSXAttrOrSpread::JSXAttr(JSXAttr {
334-
name: JSXAttrName::Ident(id),
335-
value: Some(value),
336-
..
337-
}) => {
338-
if id.sym.eq("className") {
339-
match value {
340-
JSXAttrValue::Lit(lit) => {
341-
class_name_value_expr = Some(Box::new(Expr::Call(CallExpr {
376+
let mut class_name_index: Option<usize> = None;
377+
let mut class_name_cx_expr: Option<Box<Expr>> = None;
378+
379+
for (index, attr) in el.attrs.iter().enumerate() {
380+
match &attr {
381+
JSXAttrOrSpread::JSXAttr(JSXAttr {
382+
name: JSXAttrName::Ident(id),
383+
value: Some(value),
384+
..
385+
}) => {
386+
if id.sym.eq("className") {
387+
match value {
388+
JSXAttrValue::Lit(lit) => {
389+
self.record_jsx_class_name(Box::new(Expr::Lit(lit.clone())));
390+
if css_modules {
391+
class_name_cx_expr = Some(Box::new(Expr::Call(CallExpr {
342392
span: DUMMY_SP,
343393
callee: ExprOrSuper::Expr(Box::new(Expr::Ident(quote_ident!("__ALEPH__CX")))),
344394
args: vec![ExprOrSpread {
@@ -348,9 +398,12 @@ impl AlephJsxPass2Fold {
348398
type_args: None,
349399
})))
350400
}
351-
JSXAttrValue::JSXExprContainer(JSXExprContainer { expr, .. }) => {
352-
if let JSXExpr::Expr(expr) = expr {
353-
class_name_value_expr = Some(Box::new(Expr::Call(CallExpr {
401+
}
402+
JSXAttrValue::JSXExprContainer(JSXExprContainer { expr, .. }) => {
403+
if let JSXExpr::Expr(expr) = expr {
404+
self.record_jsx_class_name(expr.clone());
405+
if css_modules {
406+
class_name_cx_expr = Some(Box::new(Expr::Call(CallExpr {
354407
span: DUMMY_SP,
355408
callee: ExprOrSuper::Expr(Box::new(Expr::Ident(quote_ident!("__ALEPH__CX")))),
356409
args: vec![ExprOrSpread {
@@ -361,33 +414,33 @@ impl AlephJsxPass2Fold {
361414
})))
362415
}
363416
}
364-
_ => {}
365-
};
366-
i = Some(index);
367-
break;
368-
}
417+
}
418+
_ => {}
419+
};
420+
class_name_index = Some(index);
421+
break;
369422
}
370-
_ => {}
371-
};
372-
}
423+
}
424+
_ => {}
425+
};
426+
}
373427

374-
if let Some(index) = i {
375-
if let Some(expr) = class_name_value_expr {
376-
el.attrs[index] = JSXAttrOrSpread::JSXAttr(JSXAttr {
428+
if let Some(index) = class_name_index {
429+
if let Some(expr) = class_name_cx_expr {
430+
el.attrs[index] = JSXAttrOrSpread::JSXAttr(JSXAttr {
431+
span: DUMMY_SP,
432+
name: JSXAttrName::Ident(quote_ident!("className")),
433+
value: Some(JSXAttrValue::JSXExprContainer(JSXExprContainer {
377434
span: DUMMY_SP,
378-
name: JSXAttrName::Ident(quote_ident!("className")),
379-
value: Some(JSXAttrValue::JSXExprContainer(JSXExprContainer {
380-
span: DUMMY_SP,
381-
expr: JSXExpr::Expr(expr),
382-
})),
383-
});
384-
}
435+
expr: JSXExpr::Expr(expr),
436+
})),
437+
});
385438
}
386439
}
387440

388441
// copy from https://github.com/swc-project/swc/blob/master/ecmascript/transforms/src/react/jsx_src.rs
389442
if self.is_dev {
390-
let resolver = self.resolver.borrow_mut();
443+
let resolver = self.resolver.borrow();
391444
match self.source.span_to_lines(el.span) {
392445
Ok(file_lines) => {
393446
el.attrs.push(JSXAttrOrSpread::JSXAttr(JSXAttr {
@@ -430,7 +483,7 @@ impl AlephJsxPass2Fold {
430483
}
431484
}
432485

433-
impl Fold for AlephJsxPass2Fold {
486+
impl Fold for JSXMagicPass2Fold {
434487
noop_fold_type!();
435488

436489
fn fold_jsx_element(&mut self, el: JSXElement) -> JSXElement {
@@ -482,6 +535,7 @@ mod tests {
482535
}
483536
"#;
484537
let (code, resolver) = st("/pages/index.tsx", source, false);
538+
let r = resolver.borrow_mut();
485539
assert!(code.contains(
486540
"import __ALEPH__Anchor from \"../-/deno.land/x/[email protected]/framework/react/components/Anchor.js\""
487541
));
@@ -502,12 +556,11 @@ mod tests {
502556
assert!(code.contains("href: \"/style/index.css\""));
503557
assert!(code.contains(
504558
format!(
505-
"import \"../style/index.css.js#{}@000000\"",
559+
"import \"../style/index.css.js#{}@000000\"",
506560
"/style/index.css"
507561
)
508562
.as_str()
509563
));
510-
let r = resolver.borrow_mut();
511564
assert_eq!(
512565
r.deps
513566
.iter()
@@ -535,12 +588,14 @@ mod tests {
535588
<link rel="stylesheet" href="../style/app.module.css" />
536589
<link rel="stylesheet" href="../style/index.module.css" />
537590
<h2 className="$title $bold">Hi :)</h2>
538-
<p className={'$' + 'desc'}>Welcom</p>
591+
<p className={'$' + 'desc'}>Welcome</p>
592+
<p className={`bold ${'lg'}`}>Thanks</p>
539593
</>
540594
)
541595
}
542596
"#;
543597
let (code, resolver) = st("/pages/index.tsx", source, false);
598+
let r = resolver.borrow_mut();
544599
assert!(code.contains(
545600
"import __ALEPH__StyleLink from \"../-/deno.land/x/[email protected]/framework/react/components/StyleLink.js\""
546601
));
@@ -563,7 +618,8 @@ mod tests {
563618
assert!(code.contains("const __ALEPH__CX = (c)=>typeof c === \"string\" ? c.split(\" \").map((n)=>n.charAt(0) === \"$\" ? __ALEPH__CSS_MODULES_ALL[n.slice(1)] || n : n\n ).join(\" \") : c"));
564619
assert!(code.contains("className: __ALEPH__CX(\"$title $bold\")"));
565620
assert!(code.contains("className: __ALEPH__CX('$' + 'desc')"));
566-
let r = resolver.borrow_mut();
621+
assert!(code.contains("className: __ALEPH__CX(`bold ${'lg'}`)"));
622+
assert_eq!(r.jsx_static_class_names.len(), 5);
567623
assert_eq!(
568624
r.deps
569625
.iter()
@@ -601,12 +657,12 @@ mod tests {
601657
}
602658
"#;
603659
let (code, resolver) = st("/pages/index.tsx", source, false);
660+
let r = resolver.borrow_mut();
604661
assert!(code.contains(
605662
"import __ALEPH__InlineStyle from \"../-/deno.land/x/[email protected]/framework/react/components/InlineStyle.js\""
606663
));
607664
assert!(code.contains("React.createElement(__ALEPH__InlineStyle,"));
608665
assert!(code.contains("__styleId: \"inline-style-"));
609-
let r = resolver.borrow_mut();
610666
assert!(r.inline_styles.len() == 2);
611667
}
612668
}

compiler/src/lib.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ pub struct Options {
3636
pub swc_options: SWCOptions,
3737

3838
#[serde(default)]
39-
pub external_remote_deps: bool,
39+
pub http_external: bool,
4040

4141
#[serde(default)]
4242
pub bundle_mode: bool,
@@ -112,6 +112,9 @@ pub struct TransformOutput {
112112
#[serde(skip_serializing_if = "Vec::is_empty")]
113113
pub star_exports: Vec<String>,
114114

115+
#[serde(skip_serializing_if = "Vec::is_empty")]
116+
pub jsx_static_class_names: Vec<String>,
117+
115118
#[serde(skip_serializing_if = "Option::is_none")]
116119
pub map: Option<String>,
117120
}
@@ -158,6 +161,7 @@ pub fn strip_ssr_code(specifier: &str, code: &str, options: JsValue) -> Result<J
158161
ssr_props_fn: None,
159162
ssg_paths_fn: None,
160163
deno_hooks: vec![],
164+
jsx_static_class_names: vec![],
161165
map,
162166
})
163167
.unwrap(),
@@ -176,7 +180,7 @@ pub fn transform_sync(specifier: &str, code: &str, options: JsValue) -> Result<J
176180
specifier,
177181
options.working_dir.as_str(),
178182
options.import_map,
179-
options.external_remote_deps,
183+
options.http_external,
180184
options.bundle_mode,
181185
options.bundle_externals,
182186
options.aleph_pkg_uri,
@@ -206,6 +210,7 @@ pub fn transform_sync(specifier: &str, code: &str, options: JsValue) -> Result<J
206210
ssr_props_fn: r.ssr_props_fn.clone(),
207211
ssg_paths_fn: r.ssg_paths_fn.clone(),
208212
deno_hooks: r.deno_hooks.clone(),
213+
jsx_static_class_names: r.jsx_static_class_names.clone().into_iter().collect(),
209214
map,
210215
})
211216
.unwrap(),

0 commit comments

Comments
 (0)