Skip to content

Commit 79bd086

Browse files
committed
feat: add styled-components engine
1 parent 0fc5d3a commit 79bd086

File tree

5 files changed

+123
-39
lines changed

5 files changed

+123
-39
lines changed

install.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* Install stailwc
33
*
4-
* @param {{wasm?: string, tailwindPath?: string, silent?: boolean, strict?: boolean}} options
4+
* @param {{wasm?: string, tailwindPath?: string, silent?: boolean, strict?: boolean, engine: "emotion" | "styled-components"}} options
55
* @returns A nextjs plugin configuration
66
*/
77
module.exports = (options = {}) => {
@@ -46,5 +46,8 @@ module.exports = (options = {}) => {
4646
{}
4747
);
4848

49-
return [options?.wasm ?? "stailwc", { config, strict: options?.strict ?? false }];
49+
return [
50+
options?.wasm ?? "stailwc",
51+
{ config, strict: options?.strict ?? false, engine: options?.engine ?? "emotion" },
52+
];
5053
};

src/engine/emotion.rs

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
use swc_core::{
2+
common::Span,
3+
ecma::{
4+
ast::{
5+
Expr, Ident, JSXAttr, JSXAttrName, JSXAttrOrSpread, JSXAttrValue, JSXElementName,
6+
JSXExpr, JSXExprContainer, JSXOpeningElement, TaggedTpl, Tpl, TplElement,
7+
},
8+
atoms::Atom,
9+
},
10+
};
11+
12+
use crate::TransformVisitor;
13+
14+
impl<'a> TransformVisitor<'a> {
15+
pub fn emotion_global(&mut self, span: Span, n: &mut JSXOpeningElement, atom: Atom) {
16+
n.name = JSXElementName::Ident(Ident::new("Global".into(), span));
17+
n.attrs.push(JSXAttrOrSpread::JSXAttr(JSXAttr {
18+
span: n.span,
19+
name: JSXAttrName::Ident(Ident::new("styles".into(), n.span)),
20+
value: Some(JSXAttrValue::JSXExprContainer(JSXExprContainer {
21+
span: n.span,
22+
expr: JSXExpr::Expr(Box::new(Expr::TaggedTpl(TaggedTpl {
23+
span: n.span,
24+
tag: Box::new(Expr::Ident(Ident {
25+
span: n.span,
26+
sym: "css".into(),
27+
optional: false,
28+
})),
29+
type_params: None,
30+
tpl: Tpl {
31+
span: n.span,
32+
exprs: vec![],
33+
quasis: vec![TplElement {
34+
cooked: Some(atom.clone()),
35+
raw: atom,
36+
span: n.span,
37+
tail: true,
38+
}],
39+
},
40+
}))),
41+
})),
42+
}));
43+
}
44+
}

src/engine/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pub mod emotion;
2+
pub mod styled_components;

src/engine/styled_components.rs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
use swc_core::{
2+
common::Span,
3+
ecma::{
4+
ast::{Ident, JSXElementName, JSXOpeningElement},
5+
atoms::Atom,
6+
},
7+
};
8+
9+
use crate::TransformVisitor;
10+
11+
impl<'a> TransformVisitor<'a> {
12+
pub fn styled_components_global(&mut self, span: Span, n: &mut JSXOpeningElement, atom: Atom) {
13+
n.name = JSXElementName::Ident(Ident::new("Global".into(), span));
14+
}
15+
}

src/lib.rs

+57-37
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
#![allow(clippy::not_unsafe_ptr_arg_deref)]
77

88
mod css;
9+
mod engine;
910
#[cfg(test)]
1011
mod test;
1112

1213
use nom_locate::LocatedSpan;
14+
use serde::Deserialize;
1315
use swc_core::{
1416
common::{
1517
errors::{DiagnosticBuilder, Handler},
@@ -18,10 +20,11 @@ use swc_core::{
1820
},
1921
ecma::{
2022
ast::{
21-
ArrayLit, CallExpr, Callee, Expr, ExprOrSpread, Ident, ImportDecl, JSXAttr,
22-
JSXAttrName, JSXAttrOrSpread, JSXAttrValue, JSXElementName, JSXExpr, JSXExprContainer,
23-
JSXOpeningElement, Lit, MemberExpr, MemberProp, Module, ModuleDecl, ModuleItem,
24-
ObjectLit, Program, Str, TaggedTpl, Tpl, TplElement,
23+
ArrayLit, CallExpr, Callee, Expr, ExprOrSpread, Ident, ImportDecl,
24+
ImportNamedSpecifier, ImportSpecifier, JSXAttr, JSXAttrName, JSXAttrOrSpread,
25+
JSXAttrValue, JSXElementName, JSXExpr, JSXExprContainer, JSXOpeningElement, Lit,
26+
MemberExpr, MemberProp, Module, ModuleDecl, ModuleItem, ObjectLit, Program, Str,
27+
TaggedTpl, Tpl, TplElement,
2528
},
2629
atoms::Atom,
2730
visit::{as_folder, FoldWith, VisitMut, VisitMutWith},
@@ -39,6 +42,21 @@ pub struct AppConfig<'a> {
3942
/// Strict mode throws an error when an unknown class is used.
4043
#[serde(default)]
4144
pub strict: bool,
45+
46+
pub engine: Engine,
47+
}
48+
49+
#[derive(Deserialize, Debug)]
50+
#[serde(rename_all = "kebab-case")]
51+
pub enum Engine {
52+
Emotion,
53+
StyledComponents,
54+
}
55+
56+
impl Default for Engine {
57+
fn default() -> Self {
58+
Self::Emotion
59+
}
4260
}
4361

4462
#[derive(Debug)]
@@ -55,6 +73,7 @@ enum TplTransform {
5573
pub struct TransformVisitor<'a> {
5674
config: TailwindConfig<'a>,
5775
strict: bool,
76+
engine: Engine,
5877
/// This is treated as a stack because tw attrs can be nested
5978
tw_attr_stack: DepthStack<(Span, ObjectLit)>,
6079
tw_tpl: Option<TplTransform>,
@@ -130,6 +149,7 @@ impl<'a> TransformVisitor<'a> {
130149
Self {
131150
config: config.config,
132151
strict: config.strict,
152+
engine: config.engine,
133153
..Default::default()
134154
}
135155
}
@@ -224,44 +244,16 @@ impl<'a> VisitMut for TransformVisitor<'a> {
224244
/// b) extract any tw attributes and transform them into a css declarations
225245
fn visit_mut_jsx_opening_element(&mut self, n: &mut JSXOpeningElement) {
226246
if self.tw_style_imported && let JSXElementName::Ident(i) = &n.name && i.sym.eq("TailwindStyle") {
227-
228247
let atom: Atom = css::format_css(
229248
true,
230249
self.config.theme.font_family.get("sans").map(|v| v.as_slice()).unwrap_or(&[]),
231250
self.config.theme.font_family.get("mono").map(|v| v.as_slice()).unwrap_or(&[])
232251
).into();
233252

234-
n.name = JSXElementName::Ident(Ident::new("Global".into(), i.span));
235-
n.attrs.push(JSXAttrOrSpread::JSXAttr(
236-
JSXAttr {
237-
span: n.span,
238-
name: JSXAttrName::Ident(Ident::new("styles".into(), n.span)),
239-
value: Some(JSXAttrValue::JSXExprContainer(JSXExprContainer {
240-
span: n.span,
241-
expr: JSXExpr::Expr(Box::new(Expr::TaggedTpl(
242-
TaggedTpl {
243-
span: n.span,
244-
tag: Box::new(Expr::Ident(Ident {
245-
span: n.span,
246-
sym: "css".into(),
247-
optional: false
248-
})),
249-
type_params: None, tpl: Tpl{
250-
span: n.span,
251-
exprs: vec![],
252-
quasis: vec![TplElement{
253-
cooked: Some(atom.clone()),
254-
raw: atom,
255-
span: n.span,
256-
tail: true
257-
}],
258-
}
259-
})
260-
261-
))
262-
})),
263-
}
264-
));
253+
match self.engine {
254+
Engine::Emotion => self.emotion_global(i.span, n, atom),
255+
Engine::StyledComponents => self.styled_components_global(i.span, n, atom),
256+
}
265257
}
266258

267259
self.tw_attr_stack.inc_depth();
@@ -483,7 +475,6 @@ impl<'a> VisitMut for TransformVisitor<'a> {
483475
{
484476
let has_style_import = specifiers.iter().any(|s| match s {
485477
ImportSpecifier::Named(ImportNamedSpecifier { local, .. }) => {
486-
println!("{:?}", local);
487478
local.sym.eq("TailwindStyle")
488479
}
489480
_ => false,
@@ -497,6 +488,35 @@ impl<'a> VisitMut for TransformVisitor<'a> {
497488

498489
false
499490
});
491+
492+
if self.tw_style_imported {
493+
match self.engine {
494+
Engine::Emotion => {
495+
// no-op
496+
}
497+
Engine::StyledComponents => {
498+
// import createGlobalStyle and create the Global object
499+
n.body
500+
.push(ModuleItem::ModuleDecl(ModuleDecl::Import(ImportDecl {
501+
src: Box::new(Str {
502+
raw: Some("styled-components".into()),
503+
value: "styled-components".into(),
504+
span: DUMMY_SP,
505+
}),
506+
span: DUMMY_SP,
507+
specifiers: vec![ImportSpecifier::Named(ImportNamedSpecifier {
508+
span: DUMMY_SP,
509+
local: Ident::new("createGlobalStyle".into(), DUMMY_SP),
510+
is_type_only: false,
511+
imported: None,
512+
})],
513+
asserts: None,
514+
type_only: false,
515+
})))
516+
}
517+
}
518+
}
519+
500520
n.visit_mut_children_with(self);
501521
}
502522
}

0 commit comments

Comments
 (0)