chore: Update swc_core to v0.40.7 (#41613)

This commit is contained in:
Donny/강동윤 2022-10-23 04:59:06 +09:00 committed by GitHub
parent 2e2cc5597a
commit 78138af505
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
282 changed files with 318 additions and 11130 deletions

File diff suppressed because it is too large Load diff

View file

@ -19,10 +19,10 @@ pathdiff = "0.2.0"
regex = "1.5" regex = "1.5"
serde = "1" serde = "1"
serde_json = "1" serde_json = "1"
swc_emotion = {path="../emotion"} swc_emotion = "0.28.1"
styled_components = {path="../styled_components"} styled_components = "0.52.1"
styled_jsx = {path="../styled_jsx"} styled_jsx = "0.29.1"
modularize_imports = {path="../modularize_imports"} modularize_imports = "0.25.1"
tracing = { version = "0.1.32", features = ["release_max_level_info"] } tracing = { version = "0.1.32", features = ["release_max_level_info"] }
swc_core = { features = [ swc_core = { features = [
@ -42,9 +42,9 @@ swc_core = { features = [
"ecma_parser_typescript", "ecma_parser_typescript",
"cached", "cached",
"base" "base"
], version = "0.39.7" } ], version = "0.40.7" }
[dev-dependencies] [dev-dependencies]
swc_core = { features = ["testing_transform"], version = "0.39.7" } swc_core = { features = ["testing_transform"], version = "0.40.7" }
testing = "0.31.9" testing = "0.31.10"
walkdir = "2.3.2" walkdir = "2.3.2"

View file

@ -162,7 +162,10 @@ where
_ => Either::Right(noop()), _ => Either::Right(noop()),
}, },
if opts.styled_jsx { if opts.styled_jsx {
Either::Left(styled_jsx::styled_jsx(cm.clone(), file.name.clone())) Either::Left(styled_jsx::visitor::styled_jsx(
cm.clone(),
file.name.clone(),
))
} else { } else {
Either::Right(noop()) Either::Right(noop())
}, },

View file

@ -1,27 +0,0 @@
[package]
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
edition = "2018"
description = "AST Transforms for emotion"
license = "Apache-2.0"
name = "swc_emotion"
repository = "https://github.com/vercel/next.js.git"
version = "0.22.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
base64 = "0.13"
byteorder = "1"
fxhash = "0.2.1"
once_cell = "1.13.0"
radix_fmt = "1"
regex = "1.5"
serde = "1"
sourcemap = "6.0.1"
tracing = { version = "0.1.32", features = ["release_max_level_info"] }
swc_core = { features = ["common", "ecma_ast","ecma_codegen", "ecma_utils", "ecma_visit", "trace_macro"], version = "0.39.7" }
[dev-dependencies]
swc_core = { features = ["testing_transform", "ecma_transforms_react"], version = "0.39.7" }
testing = "0.31.9"
serde_json = "1"

View file

@ -1,62 +0,0 @@
// Ported from https://github.com/aappleby/smhasher/blob/61a0530f28277f2e850bfc39600ce61d02b518de/src/MurmurHash2.cpp#L37-L86
use byteorder::{ByteOrder, LittleEndian};
const M: u32 = 0x5bd1_e995;
pub(crate) fn murmurhash2(key: &[u8], initial_state: u32) -> u32 {
let mut h: u32 = initial_state;
let mut four_bytes_chunks = key.chunks_exact(4);
for chunk in four_bytes_chunks.by_ref() {
let mut k: u32 = LittleEndian::read_u32(chunk);
k = k.wrapping_mul(M);
k ^= k >> 24;
h = k.wrapping_mul(M) ^ h.wrapping_mul(M);
}
let remainder = four_bytes_chunks.remainder();
// Handle the last few bytes of the input array
match remainder.len() {
3 => {
h ^= u32::from(remainder[2]) << 16;
}
2 => {
h ^= u32::from(remainder[1]) << 8;
}
1 => {
h ^= u32::from(remainder[0]);
h = h.wrapping_mul(M);
}
_ => {}
}
h ^= h >> 13;
h = h.wrapping_mul(M);
h ^ (h >> 15)
}
#[cfg(test)]
mod test {
use super::murmurhash2;
#[test]
fn test_murmur2() {
let s1 = "abcdef";
let s2 = "abcdeg";
for i in 0..5 {
assert_eq!(
murmurhash2(s1[i..5].as_bytes(), 0),
murmurhash2(s2[i..5].as_bytes(), 0)
);
}
}
#[test]
fn verify_hash() {
assert_eq!(
murmurhash2("something".as_bytes(), 0),
u32::from_str_radix("crsxd7", 36).unwrap()
);
}
}

View file

@ -1,81 +0,0 @@
use serde::{Deserialize, Serialize};
use swc_core::{common::collections::AHashMap, ecma::atoms::JsWord};
use crate::{EmotionModuleConfig, ExportItem};
/// key: `importSource`
pub type ImportMap = AHashMap<JsWord, ImportMapValue>;
/// key: `localExportName`
pub type ImportMapValue = AHashMap<JsWord, Config>;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Config {
canonical_import: ImportItem,
}
/// `(packageName, exportName)`
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct ImportItem(JsWord, JsWord);
pub(crate) fn expand_import_map(
map: Option<&ImportMap>,
mut imports: Vec<EmotionModuleConfig>,
) -> Vec<EmotionModuleConfig> {
if let Some(map) = map {
map.iter().for_each(|(import_source, value)| {
value
.iter()
.for_each(|(local_export_name, Config { canonical_import })| {
let ImportItem(package_name, export_name) = canonical_import;
if &**package_name == "@emotion/react" && &**export_name == "jsx" {
return;
}
let package_transformers = imports
.iter()
.find(|v| v.module_name == *package_name)
.unwrap_or_else(|| {
panic!(
"There is no transformer for the export '{}' in '{}'",
export_name, package_name
)
})
.clone();
let kind = package_transformers
.exported_names
.iter()
.find(|v| v.name == **export_name)
.map(|v| v.kind)
.or_else(|| {
if export_name == "default" {
package_transformers.default_export
} else {
None
}
})
.unwrap_or_else(|| {
panic!(
"failed to find export '{}' from package '{}'",
export_name, package_name
)
});
imports.push(EmotionModuleConfig {
module_name: import_source.clone(),
exported_names: vec![ExportItem {
name: local_export_name.to_string(),
kind,
}],
default_export: package_transformers.default_export,
});
})
});
}
imports
}

View file

@ -1,845 +0,0 @@
use std::borrow::Cow;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use fxhash::FxHashMap;
use import_map::ImportMap;
use once_cell::sync::Lazy;
use regex::Regex;
use serde::{Deserialize, Serialize};
use sourcemap::{RawToken, SourceMap as RawSourcemap};
use swc_core::{
common::{comments::Comments, util::take::Take, BytePos, SourceMapperDyn, DUMMY_SP},
ecma::utils::ExprFactory,
ecma::visit::{Fold, FoldWith},
ecma::{
ast::{
ArrayLit, CallExpr, Callee, Expr, ExprOrSpread, Id, Ident, ImportDecl, ImportSpecifier,
JSXAttr, JSXAttrName, JSXAttrOrSpread, JSXAttrValue, JSXElement, JSXElementName,
JSXExpr, JSXExprContainer, JSXObject, KeyValueProp, MemberProp, ModuleExportName,
ObjectLit, Pat, Prop, PropName, PropOrSpread, SourceMapperExt, Tpl, VarDeclarator,
},
atoms::JsWord,
},
trace_macro::swc_trace,
};
mod hash;
mod import_map;
static EMOTION_OFFICIAL_LIBRARIES: Lazy<Vec<EmotionModuleConfig>> = Lazy::new(|| {
vec![
EmotionModuleConfig {
module_name: "@emotion/css".into(),
exported_names: vec![ExportItem {
name: "css".to_owned(),
kind: ExprKind::Css,
}],
default_export: Some(ExprKind::Css),
},
EmotionModuleConfig {
module_name: "@emotion/styled".into(),
exported_names: vec![],
default_export: Some(ExprKind::Styled),
},
EmotionModuleConfig {
module_name: "@emotion/react".into(),
exported_names: vec![
ExportItem {
name: "css".to_owned(),
kind: ExprKind::Css,
},
ExportItem {
name: "keyframes".to_owned(),
kind: ExprKind::Css,
},
ExportItem {
name: "Global".to_owned(),
kind: ExprKind::GlobalJSX,
},
],
..Default::default()
},
EmotionModuleConfig {
module_name: "@emotion/primitives".into(),
exported_names: vec![ExportItem {
name: "css".to_owned(),
kind: ExprKind::Css,
}],
default_export: Some(ExprKind::Styled),
},
EmotionModuleConfig {
module_name: "@emotion/native".into(),
exported_names: vec![ExportItem {
name: "css".to_owned(),
kind: ExprKind::Css,
}],
default_export: Some(ExprKind::Styled),
},
]
});
static SPACE_AROUND_COLON: Lazy<Regex> =
Lazy::new(|| Regex::new(r"\s*(?P<s>[:;,\{,\}])\s*").unwrap());
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct EmotionOptions {
pub enabled: Option<bool>,
pub sourcemap: Option<bool>,
pub auto_label: Option<bool>,
pub label_format: Option<String>,
pub import_map: Option<ImportMap>,
}
impl Default for EmotionOptions {
fn default() -> Self {
EmotionOptions {
enabled: Some(false),
sourcemap: Some(true),
auto_label: Some(true),
label_format: Some("[local]".to_owned()),
import_map: None,
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct EmotionModuleConfig {
module_name: JsWord,
exported_names: Vec<ExportItem>,
default_export: Option<ExprKind>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
struct ExportItem {
name: String,
kind: ExprKind,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
enum ImportType {
Named,
Namespace,
Default,
}
impl Default for ImportType {
fn default() -> Self {
ImportType::Named
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
enum ExprKind {
Css,
Styled,
GlobalJSX,
}
impl Default for ExprKind {
fn default() -> Self {
ExprKind::Css
}
}
#[derive(Debug)]
enum PackageMeta {
Named(ExprKind),
Namespace(EmotionModuleConfig),
}
pub fn emotion<C: Comments>(
emotion_options: EmotionOptions,
path: &Path,
cm: Arc<SourceMapperDyn>,
comments: C,
) -> impl Fold {
EmotionTransformer::new(emotion_options, path, cm, comments)
}
pub struct EmotionTransformer<C: Comments> {
pub options: EmotionOptions,
filepath_hash: Option<u32>,
filepath: PathBuf,
dir: Option<String>,
filename: Option<String>,
cm: Arc<SourceMapperDyn>,
comments: C,
import_packages: FxHashMap<Id, PackageMeta>,
emotion_target_class_name_count: usize,
current_context: Option<String>,
// skip `css` transformation if it in JSX Element/Attribute
in_jsx_element: bool,
registered_imports: Vec<EmotionModuleConfig>,
}
#[swc_trace]
impl<C: Comments> EmotionTransformer<C> {
pub fn new(
options: EmotionOptions,
path: &Path,
cm: Arc<SourceMapperDyn>,
comments: C,
) -> Self {
let registered_imports = self::import_map::expand_import_map(
options.import_map.as_ref(),
EMOTION_OFFICIAL_LIBRARIES.to_vec(),
);
EmotionTransformer {
options,
filepath_hash: None,
filepath: path.to_owned(),
dir: path.parent().and_then(|p| p.to_str()).map(|s| s.to_owned()),
filename: path
.file_name()
.and_then(|filename| filename.to_str())
.map(|s| s.to_owned()),
cm,
comments,
import_packages: FxHashMap::default(),
emotion_target_class_name_count: 0,
current_context: None,
in_jsx_element: false,
registered_imports,
}
}
#[inline]
// Compute file hash on demand
// Memorize the hash of the file name
fn get_filename_hash(&mut self) -> u32 {
if self.filepath_hash.is_none() {
self.filepath_hash = Some(hash::murmurhash2(
self.filepath.to_string_lossy().as_bytes(),
0,
));
}
self.filepath_hash.unwrap()
}
fn create_label(&self, with_prefix: bool) -> String {
let prefix = if with_prefix { "label:" } else { "" };
let mut label = format!(
"{}{}",
prefix,
self.options
.label_format
.clone()
.unwrap_or_else(|| "[local]".to_owned())
);
if let Some(current_context) = &self.current_context {
label = label.replace("[local]", current_context);
if let Some(filename) = self.filename.as_ref() {
label = label.replace("[filename]", filename);
}
if let Some(dir) = self.dir.as_ref() {
label = label.replace("[dir]", dir);
};
}
label
}
fn create_sourcemap(&mut self, pos: BytePos) -> Option<String> {
if self.options.sourcemap.unwrap_or(false) {
let loc = self.cm.get_code_map().lookup_char_pos(pos);
let filename = self.filepath.to_str().map(|s| s.to_owned());
let cm = RawSourcemap::new(
filename.clone(),
vec![RawToken {
dst_line: 0,
dst_col: 0,
src_line: loc.line as u32 - 1,
src_col: loc.col_display as u32,
src_id: 0,
name_id: 0,
}],
Vec::new(),
vec![filename.unwrap_or_default()],
Some(vec![Some(loc.file.src.to_string())]),
);
let mut writer = Vec::new();
if cm.to_writer(&mut writer).is_ok() {
return Some(format!(
"/*# sourceMappingURL=data:application/json;charset=utf-8;base64,{} */",
base64::encode(writer)
));
}
}
None
}
// Find the imported name from modules
// These import statements are supported:
// import styled from '@emotion/styled'
// import { default as whateverStyled } from '@emotion/styled'
// import { css } from '@emotion/react'
// import * as emotionCss from '@emotion/react'
fn generate_import_info(&mut self, expr: &ImportDecl) {
for c in self.registered_imports.iter() {
if expr.src.value == c.module_name {
for specifier in expr.specifiers.iter() {
match specifier {
ImportSpecifier::Named(named) => {
for exported in c.exported_names.iter() {
let matched = match &named.imported {
Some(imported) => match imported {
ModuleExportName::Ident(v) => v.sym == exported.name,
ModuleExportName::Str(v) => v.value == exported.name,
},
_ => named.local.as_ref() == exported.name,
};
if matched {
self.import_packages.insert(
named.local.to_id(),
PackageMeta::Named(exported.kind),
);
}
}
}
ImportSpecifier::Default(default) => {
if let Some(kind) = c.default_export {
self.import_packages
.insert(default.local.to_id(), PackageMeta::Named(kind));
}
}
ImportSpecifier::Namespace(namespace) => {
self.import_packages
.insert(namespace.local.to_id(), PackageMeta::Namespace(c.clone()));
}
}
}
}
}
}
fn create_label_prop_node(&mut self, key: &str) -> PropOrSpread {
let stable_class_name = format!(
"e{}{}",
radix_fmt::radix_36(self.get_filename_hash()),
self.emotion_target_class_name_count
);
self.emotion_target_class_name_count += 1;
PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
key: PropName::Ident(Ident::new(key.into(), DUMMY_SP)),
value: stable_class_name.into(),
})))
}
fn create_args_from_tagged_tpl(&self, tagged_tpl: &mut Tpl) -> Vec<ExprOrSpread> {
let args_len = tagged_tpl.exprs.len() + tagged_tpl.quasis.len();
// 2 more capacity is for `label` and `sourceMap`
let mut args = Vec::with_capacity(args_len + 2);
for index in 0..args_len {
let i = index / 2;
if index % 2 == 0 {
if let Some(q) = tagged_tpl.quasis.get_mut(i) {
let q = q.take();
let minified = minify_css_string(&q.raw, index == 0, index == args_len - 1);
// Compress one more spaces into one space
if minified.replace(' ', "").is_empty() {
if index != 0 && index != args_len - 1 {
args.push(" ".as_arg());
}
} else {
args.push(minified.as_arg())
}
}
} else if let Some(e) = tagged_tpl.exprs.get_mut(i) {
args.push(e.take().as_arg());
}
}
args
}
fn rewrite_styles_attr(&mut self, attrs: &mut [JSXAttrOrSpread], pos: BytePos) {
if let Some(attr_value) = attrs.iter_mut().find_map(|attr| {
if let JSXAttrOrSpread::JSXAttr(JSXAttr {
name: JSXAttrName::Ident(i),
value,
..
}) = attr
{
if i.as_ref() == "styles" {
return value.as_mut();
}
}
None
}) {
if let Some(raw_attr) = match attr_value {
JSXAttrValue::Lit(lit) => Some(Box::new(Expr::Lit(lit.clone()))),
JSXAttrValue::JSXExprContainer(JSXExprContainer {
expr: JSXExpr::Expr(expr),
..
}) => Some(expr.take()),
_ => None,
} {
*attr_value = self.create_styles_attr(raw_attr, pos);
self.in_jsx_element = true;
}
}
}
fn create_styles_attr(&mut self, mut raw_attr: Box<Expr>, pos: BytePos) -> JSXAttrValue {
if let Expr::Array(array_lit) = raw_attr.as_mut() {
if let Some(cm) = self.create_sourcemap(pos) {
array_lit.elems.push(Some(cm.as_arg()));
}
JSXAttrValue::JSXExprContainer(JSXExprContainer {
span: DUMMY_SP,
expr: JSXExpr::Expr(raw_attr),
})
} else {
JSXAttrValue::JSXExprContainer(JSXExprContainer {
span: DUMMY_SP,
expr: JSXExpr::Expr(Box::new(Expr::Array(ArrayLit {
span: DUMMY_SP,
elems: {
let mut elements = Vec::with_capacity(2);
elements.push(Some(raw_attr.as_arg()));
if let Some(cm) = self.create_sourcemap(pos) {
elements.push(Some(cm.as_arg()));
}
elements
},
}))),
})
}
}
}
impl<C: Comments> Fold for EmotionTransformer<C> {
// Collect import modules that indicator if this file need to be transformed
fn fold_import_decl(&mut self, expr: ImportDecl) -> ImportDecl {
if expr.type_only {
return expr;
}
self.generate_import_info(&expr);
expr
}
fn fold_var_declarator(&mut self, dec: VarDeclarator) -> VarDeclarator {
if let Pat::Ident(i) = &dec.name {
self.current_context = Some(i.id.as_ref().to_owned());
}
dec.fold_children_with(self)
}
fn fold_call_expr(&mut self, mut expr: CallExpr) -> CallExpr {
// If no package that we care about is imported, skip the following
// transformation logic.
if self.import_packages.is_empty() {
return expr;
}
if let Callee::Expr(e) = &mut expr.callee {
match e.as_mut() {
// css({})
Expr::Ident(i) => {
if let Some(package) = self.import_packages.get(&i.to_id()) {
if !expr.args.is_empty() {
if let PackageMeta::Named(kind) = package {
if matches!(kind, ExprKind::Css) && !self.in_jsx_element {
self.comments.add_pure_comment(expr.span.lo());
if self.options.auto_label.unwrap_or(false) {
expr.args.push(self.create_label(true).as_arg());
}
if let Some(cm) = self.create_sourcemap(expr.span.lo) {
expr.args.push(cm.as_arg());
}
}
}
}
}
}
// styled('div')({})
Expr::Call(c) => {
if let Callee::Expr(callee_exp) = &c.callee {
if let Expr::Ident(i) = callee_exp.as_ref() {
if let Some(PackageMeta::Named(ExprKind::Styled)) =
self.import_packages.get(&i.to_id())
{
if !c.args.is_empty() {
let mut args_props = Vec::with_capacity(2);
args_props.push(self.create_label_prop_node("target"));
self.comments.add_pure_comment(expr.span.lo());
if self.options.auto_label.unwrap_or(false) {
args_props.push(PropOrSpread::Prop(Box::new(
Prop::KeyValue(KeyValueProp {
key: PropName::Ident(Ident::new(
"label".into(),
DUMMY_SP,
)),
value: self.create_label(false).into(),
}),
)));
}
if let Some(cm) = self.create_sourcemap(expr.span.lo()) {
expr.args.push(cm.as_arg());
}
if let Some(ExprOrSpread { expr, .. }) = c.args.get_mut(1) {
if let Expr::Object(ObjectLit { props, .. }) = expr.as_mut()
{
props.extend(args_props);
} else {
c.args.push(
Expr::Object(ObjectLit {
span: DUMMY_SP,
props: args_props,
})
.as_arg(),
);
}
} else {
c.args.push(
Expr::Object(ObjectLit {
span: DUMMY_SP,
props: args_props,
})
.as_arg(),
);
}
}
}
}
}
}
// styled.div({})
// customEmotionReact.css({})
Expr::Member(m) => {
if let Expr::Ident(i) = m.obj.as_ref() {
if let Some(package) = self.import_packages.get(&i.to_id()) {
if let PackageMeta::Named(kind) = package {
if matches!(kind, ExprKind::Styled) {
if let MemberProp::Ident(prop) = &m.prop {
let mut args_props = Vec::with_capacity(2);
args_props.push(self.create_label_prop_node("target"));
let mut args = vec![prop.sym.as_ref().as_arg()];
if !self.in_jsx_element {
self.comments.add_pure_comment(expr.span.lo());
if self.options.auto_label.unwrap_or(false) {
args_props.push(PropOrSpread::Prop(Box::new(
Prop::KeyValue(KeyValueProp {
key: PropName::Ident(Ident::new(
"label".into(),
DUMMY_SP,
)),
value: self.create_label(false).into(),
}),
)));
}
args.push(
Expr::Object(ObjectLit {
span: DUMMY_SP,
props: args_props,
})
.as_arg(),
);
if let Some(cm) = self.create_sourcemap(expr.span.lo())
{
expr.args.push(cm.as_arg());
}
}
return CallExpr {
span: expr.span,
type_args: expr.type_args,
args: expr.args,
callee: CallExpr {
span: DUMMY_SP,
type_args: None,
callee: Ident::new(i.sym.clone(), i.span)
.as_callee(),
args,
}
.as_callee(),
};
}
}
}
if let PackageMeta::Namespace(c) = package {
if c.exported_names
.iter()
.any(|n| match_css_export(n, &m.prop))
{
self.comments.add_pure_comment(expr.span.lo());
if self.options.auto_label.unwrap_or(false) {
expr.args.push(self.create_label(true).as_arg());
}
if let Some(cm) = self.create_sourcemap(expr.span.lo()) {
expr.args.push(cm.as_arg());
}
}
}
}
}
}
_ => {}
}
}
expr
}
fn fold_expr(&mut self, mut expr: Expr) -> Expr {
if let Expr::TaggedTpl(tagged_tpl) = &mut expr {
// styled('div')``
match tagged_tpl.tag.as_mut() {
Expr::Call(call) => {
if let Callee::Expr(callee) = &call.callee {
if let Expr::Ident(i) = callee.as_ref() {
if let Some(PackageMeta::Named(ExprKind::Styled)) =
self.import_packages.get(&i.to_id())
{
let mut callee = call.take();
let mut object_props = Vec::with_capacity(2);
object_props.push(self.create_label_prop_node("target"));
self.comments.add_pure_comment(callee.span.lo());
if self.options.auto_label.unwrap_or(false) {
object_props.push(PropOrSpread::Prop(Box::new(
Prop::KeyValue(KeyValueProp {
key: PropName::Ident(Ident::new(
"label".into(),
DUMMY_SP,
)),
value: self.create_label(false).into(),
}),
)));
}
if let Some(ExprOrSpread { expr, .. }) = callee.args.get_mut(1) {
if let Expr::Object(ObjectLit { props, .. }) = expr.as_mut() {
props.extend(object_props);
} else {
callee.args.push(
Expr::Object(ObjectLit {
span: DUMMY_SP,
props: object_props,
})
.as_arg(),
);
}
} else {
callee.args.push(
Expr::Object(ObjectLit {
span: DUMMY_SP,
props: object_props,
})
.as_arg(),
);
}
return Expr::Call(CallExpr {
span: DUMMY_SP,
callee: callee.as_callee(),
args: {
let mut args: Vec<ExprOrSpread> = self
.create_args_from_tagged_tpl(&mut tagged_tpl.tpl)
.into_iter()
.map(|exp| exp.fold_children_with(self))
.collect();
if let Some(cm) =
self.create_sourcemap(tagged_tpl.span.lo())
{
args.push(cm.as_arg());
}
args
},
type_args: None,
});
}
}
}
}
// css``
Expr::Ident(i) => {
if let Some(PackageMeta::Named(ExprKind::Css)) =
self.import_packages.get(&i.to_id())
{
let mut args = self.create_args_from_tagged_tpl(&mut tagged_tpl.tpl);
if !self.in_jsx_element {
self.comments.add_pure_comment(i.span.lo());
if self.options.auto_label.unwrap_or(false) {
args.push(self.create_label(false).as_arg());
}
if let Some(cm) = self.create_sourcemap(tagged_tpl.span.lo()) {
args.push(cm.as_arg());
}
}
return Expr::Call(CallExpr {
span: DUMMY_SP,
callee: i.take().as_callee(),
args,
type_args: None,
});
}
}
// styled.div``
// customEmotionReact.css``
Expr::Member(member_expr) => {
if let Expr::Ident(i) = member_expr.obj.as_mut() {
if let Some(p) = self.import_packages.get(&i.to_id()) {
match p {
PackageMeta::Named(ExprKind::Styled) => {
if let MemberProp::Ident(prop) = &mut member_expr.prop {
let mut object_props = Vec::with_capacity(2);
object_props.push(self.create_label_prop_node("target"));
if self.options.auto_label.unwrap_or(false) {
object_props.push(PropOrSpread::Prop(Box::new(
Prop::KeyValue(KeyValueProp {
key: PropName::Ident(Ident::new(
"label".into(),
DUMMY_SP,
)),
value: self.create_label(false).into(),
}),
)));
}
let mut args =
self.create_args_from_tagged_tpl(&mut tagged_tpl.tpl);
if let Some(cm) =
self.create_sourcemap(tagged_tpl.span.lo())
{
args.push(cm.as_arg());
}
self.comments.add_pure_comment(member_expr.span.lo());
return Expr::Call(CallExpr {
span: DUMMY_SP,
type_args: None,
callee: CallExpr {
type_args: None,
span: DUMMY_SP,
callee: i.take().as_callee(),
args: vec![
prop.take().sym.as_arg(),
Expr::Object(ObjectLit {
span: DUMMY_SP,
props: object_props,
})
.as_arg(),
],
}
.as_callee(),
args,
});
}
}
PackageMeta::Namespace(c) => {
if c.exported_names
.iter()
.any(|item| match_css_export(item, &member_expr.prop))
{
self.comments.add_pure_comment(member_expr.span.lo());
return Expr::Call(CallExpr {
span: DUMMY_SP,
callee: member_expr.take().as_callee(),
args: {
let mut args = self.create_args_from_tagged_tpl(
&mut tagged_tpl.tpl,
);
if self.options.auto_label.unwrap_or(false) {
args.push(self.create_label(true).as_arg());
}
if let Some(cm) =
self.create_sourcemap(tagged_tpl.span.lo())
{
args.push(cm.as_arg());
}
args
},
type_args: None,
});
}
}
_ => {}
}
}
}
}
_ => {}
}
}
expr.fold_children_with(self)
}
fn fold_jsx_element(&mut self, mut expr: JSXElement) -> JSXElement {
match &mut expr.opening.name {
JSXElementName::Ident(i) => {
if let Some(PackageMeta::Named(ExprKind::GlobalJSX)) =
self.import_packages.get(&i.to_id())
{
self.rewrite_styles_attr(&mut expr.opening.attrs, i.span.lo());
}
}
JSXElementName::JSXMemberExpr(member_exp) => {
if let JSXObject::Ident(i) = &member_exp.obj {
if let Some(PackageMeta::Namespace(EmotionModuleConfig {
exported_names,
..
})) = self.import_packages.get(&i.to_id())
{
if exported_names.iter().any(|item| {
matches!(item.kind, ExprKind::GlobalJSX)
&& item.name == member_exp.prop.as_ref()
}) {
self.rewrite_styles_attr(&mut expr.opening.attrs, i.span.lo());
}
}
}
}
_ => {}
};
let dest_expr = expr.fold_children_with(self);
self.in_jsx_element = false;
dest_expr
}
}
fn match_css_export(item: &ExportItem, prop: &MemberProp) -> bool {
if matches!(item.kind, ExprKind::Css) {
if let MemberProp::Ident(prop) = prop {
if item.name.as_str() == prop.sym.as_ref() {
return true;
}
}
}
false
}
#[inline]
fn minify_css_string(input: &str, is_first_item: bool, is_last_item: bool) -> Cow<str> {
let pattern = |c| c == '\n';
let pattern_trim_spaces = |c| c == ' ' || c == '\n';
SPACE_AROUND_COLON.replace_all(
input
.trim_start_matches(if is_first_item {
pattern_trim_spaces
} else {
pattern
})
.trim_end_matches(if is_last_item {
pattern_trim_spaces
} else {
pattern
}),
"$s",
)
}
#[allow(unused_imports)]
mod test_emotion {
use super::minify_css_string;
#[test]
fn should_not_trim_end_space_in_first_item() {
assert_eq!(
minify_css_string(
r#"
box-shadow: inset 0px 0px 0px "#,
true,
false
),
"box-shadow:inset 0px 0px 0px "
);
}
}

View file

@ -1,64 +0,0 @@
use std::path::PathBuf;
use swc_core::{
common::{chain, comments::SingleThreadedComments, Mark},
ecma::parser::{Syntax, TsConfig},
ecma::transforms::react::{jsx, Runtime},
ecma::transforms::testing::test_fixture,
};
use swc_emotion::EmotionOptions;
use testing::fixture;
fn ts_syntax() -> Syntax {
Syntax::Typescript(TsConfig {
tsx: true,
..Default::default()
})
}
#[fixture("tests/fixture/**/input.tsx")]
fn next_emotion_fixture(input: PathBuf) {
let output = input.parent().unwrap().join("output.ts");
test_fixture(
ts_syntax(),
&|tr| {
let top_level_mark = Mark::fresh(Mark::root());
let jsx = jsx::<SingleThreadedComments>(
tr.cm.clone(),
Some(tr.comments.as_ref().clone()),
swc_core::ecma::transforms::react::Options {
next: false.into(),
runtime: Some(Runtime::Automatic),
throw_if_namespace: false.into(),
development: false.into(),
use_builtins: true.into(),
use_spread: true.into(),
..Default::default()
},
top_level_mark,
);
let test_import_map =
serde_json::from_str(include_str!("./testImportMap.json")).unwrap();
chain!(
swc_emotion::emotion(
EmotionOptions {
enabled: Some(true),
sourcemap: Some(true),
auto_label: Some(true),
import_map: Some(test_import_map),
..Default::default()
},
&PathBuf::from("input.ts"),
tr.cm.clone(),
tr.comments.as_ref().clone(),
),
jsx
)
},
&input,
&output,
Default::default(),
);
}

View file

@ -1,25 +0,0 @@
import { css } from '@emotion/react'
import styled from '@emotion/styled'
const unitNormal = '1rem'
const unitLarge = '2rem'
const Example = styled.div`
margin: ${unitNormal} ${unitLarge};
`
export const Animated = styled.div`
& code {
background-color: linen;
}
animation: ${({ animation }) => animation} 0.2s infinite ease-in-out alternate;
`
const shadowBorder = ({ width = '1px', color }) =>
css`
box-shadow: inset 0px 0px 0px ${width} ${color};
`
const StyledInput = styled.input`
${shadowBorder({ color: 'red', width: '4px' })}
`

View file

@ -1,22 +0,0 @@
import { css } from '@emotion/react';
import styled from '@emotion/styled';
const unitNormal = '1rem';
const unitLarge = '2rem';
const Example = /*#__PURE__*/ styled("div", {
target: "ekie5mj0",
label: "Example"
})("margin:", unitNormal, " ", unitLarge, ";", "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5wdXQudHMiLCJzb3VyY2VzIjpbImlucHV0LnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGNzcyB9IGZyb20gJ0BlbW90aW9uL3JlYWN0J1xuaW1wb3J0IHN0eWxlZCBmcm9tICdAZW1vdGlvbi9zdHlsZWQnXG5cbmNvbnN0IHVuaXROb3JtYWwgPSAnMXJlbSdcbmNvbnN0IHVuaXRMYXJnZSA9ICcycmVtJ1xuXG5jb25zdCBFeGFtcGxlID0gc3R5bGVkLmRpdmBcbiAgbWFyZ2luOiAke3VuaXROb3JtYWx9ICR7dW5pdExhcmdlfTtcbmBcblxuZXhwb3J0IGNvbnN0IEFuaW1hdGVkID0gc3R5bGVkLmRpdmBcbiAgJiBjb2RlIHtcbiAgICBiYWNrZ3JvdW5kLWNvbG9yOiBsaW5lbjtcbiAgfVxuICBhbmltYXRpb246ICR7KHsgYW5pbWF0aW9uIH0pID0+IGFuaW1hdGlvbn0gMC4ycyBpbmZpbml0ZSBlYXNlLWluLW91dCBhbHRlcm5hdGU7XG5gXG5cbmNvbnN0IHNoYWRvd0JvcmRlciA9ICh7IHdpZHRoID0gJzFweCcsIGNvbG9yIH0pID0+XG4gIGNzc2BcbiAgICBib3gtc2hhZG93OiBpbnNldCAwcHggMHB4IDBweCAke3dpZHRofSAke2NvbG9yfTtcbiAgYFxuXG5jb25zdCBTdHlsZWRJbnB1dCA9IHN0eWxlZC5pbnB1dGBcbiAgJHtzaGFkb3dCb3JkZXIoeyBjb2xvcjogJ3JlZCcsIHdpZHRoOiAnNHB4JyB9KX1cbmBcbiJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFNZ0IifQ== */");
export const Animated = /*#__PURE__*/ styled("div", {
target: "ekie5mj1",
label: "Animated"
})("& code{background-color:linen;}animation:", ({ animation })=>animation
, " 0.2s infinite ease-in-out alternate;", "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5wdXQudHMiLCJzb3VyY2VzIjpbImlucHV0LnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGNzcyB9IGZyb20gJ0BlbW90aW9uL3JlYWN0J1xuaW1wb3J0IHN0eWxlZCBmcm9tICdAZW1vdGlvbi9zdHlsZWQnXG5cbmNvbnN0IHVuaXROb3JtYWwgPSAnMXJlbSdcbmNvbnN0IHVuaXRMYXJnZSA9ICcycmVtJ1xuXG5jb25zdCBFeGFtcGxlID0gc3R5bGVkLmRpdmBcbiAgbWFyZ2luOiAke3VuaXROb3JtYWx9ICR7dW5pdExhcmdlfTtcbmBcblxuZXhwb3J0IGNvbnN0IEFuaW1hdGVkID0gc3R5bGVkLmRpdmBcbiAgJiBjb2RlIHtcbiAgICBiYWNrZ3JvdW5kLWNvbG9yOiBsaW5lbjtcbiAgfVxuICBhbmltYXRpb246ICR7KHsgYW5pbWF0aW9uIH0pID0+IGFuaW1hdGlvbn0gMC4ycyBpbmZpbml0ZSBlYXNlLWluLW91dCBhbHRlcm5hdGU7XG5gXG5cbmNvbnN0IHNoYWRvd0JvcmRlciA9ICh7IHdpZHRoID0gJzFweCcsIGNvbG9yIH0pID0+XG4gIGNzc2BcbiAgICBib3gtc2hhZG93OiBpbnNldCAwcHggMHB4IDBweCAke3dpZHRofSAke2NvbG9yfTtcbiAgYFxuXG5jb25zdCBTdHlsZWRJbnB1dCA9IHN0eWxlZC5pbnB1dGBcbiAgJHtzaGFkb3dCb3JkZXIoeyBjb2xvcjogJ3JlZCcsIHdpZHRoOiAnNHB4JyB9KX1cbmBcbiJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFVd0IifQ== */");
const shadowBorder = ({ width ='1px' , color })=>/*#__PURE__*/ css("box-shadow:inset 0px 0px 0px ", width, " ", color, ";", "shadowBorder", "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5wdXQudHMiLCJzb3VyY2VzIjpbImlucHV0LnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGNzcyB9IGZyb20gJ0BlbW90aW9uL3JlYWN0J1xuaW1wb3J0IHN0eWxlZCBmcm9tICdAZW1vdGlvbi9zdHlsZWQnXG5cbmNvbnN0IHVuaXROb3JtYWwgPSAnMXJlbSdcbmNvbnN0IHVuaXRMYXJnZSA9ICcycmVtJ1xuXG5jb25zdCBFeGFtcGxlID0gc3R5bGVkLmRpdmBcbiAgbWFyZ2luOiAke3VuaXROb3JtYWx9ICR7dW5pdExhcmdlfTtcbmBcblxuZXhwb3J0IGNvbnN0IEFuaW1hdGVkID0gc3R5bGVkLmRpdmBcbiAgJiBjb2RlIHtcbiAgICBiYWNrZ3JvdW5kLWNvbG9yOiBsaW5lbjtcbiAgfVxuICBhbmltYXRpb246ICR7KHsgYW5pbWF0aW9uIH0pID0+IGFuaW1hdGlvbn0gMC4ycyBpbmZpbml0ZSBlYXNlLWluLW91dCBhbHRlcm5hdGU7XG5gXG5cbmNvbnN0IHNoYWRvd0JvcmRlciA9ICh7IHdpZHRoID0gJzFweCcsIGNvbG9yIH0pID0+XG4gIGNzc2BcbiAgICBib3gtc2hhZG93OiBpbnNldCAwcHggMHB4IDBweCAke3dpZHRofSAke2NvbG9yfTtcbiAgYFxuXG5jb25zdCBTdHlsZWRJbnB1dCA9IHN0eWxlZC5pbnB1dGBcbiAgJHtzaGFkb3dCb3JkZXIoeyBjb2xvcjogJ3JlZCcsIHdpZHRoOiAnNHB4JyB9KX1cbmBcbiJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFrQkUifQ== */")
;
const StyledInput = /*#__PURE__*/ styled("input", {
target: "ekie5mj2",
label: "StyledInput"
})(shadowBorder({
color: 'red',
width: '4px'
}), "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5wdXQudHMiLCJzb3VyY2VzIjpbImlucHV0LnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGNzcyB9IGZyb20gJ0BlbW90aW9uL3JlYWN0J1xuaW1wb3J0IHN0eWxlZCBmcm9tICdAZW1vdGlvbi9zdHlsZWQnXG5cbmNvbnN0IHVuaXROb3JtYWwgPSAnMXJlbSdcbmNvbnN0IHVuaXRMYXJnZSA9ICcycmVtJ1xuXG5jb25zdCBFeGFtcGxlID0gc3R5bGVkLmRpdmBcbiAgbWFyZ2luOiAke3VuaXROb3JtYWx9ICR7dW5pdExhcmdlfTtcbmBcblxuZXhwb3J0IGNvbnN0IEFuaW1hdGVkID0gc3R5bGVkLmRpdmBcbiAgJiBjb2RlIHtcbiAgICBiYWNrZ3JvdW5kLWNvbG9yOiBsaW5lbjtcbiAgfVxuICBhbmltYXRpb246ICR7KHsgYW5pbWF0aW9uIH0pID0+IGFuaW1hdGlvbn0gMC4ycyBpbmZpbml0ZSBlYXNlLWluLW91dCBhbHRlcm5hdGU7XG5gXG5cbmNvbnN0IHNoYWRvd0JvcmRlciA9ICh7IHdpZHRoID0gJzFweCcsIGNvbG9yIH0pID0+XG4gIGNzc2BcbiAgICBib3gtc2hhZG93OiBpbnNldCAwcHggMHB4IDBweCAke3dpZHRofSAke2NvbG9yfTtcbiAgYFxuXG5jb25zdCBTdHlsZWRJbnB1dCA9IHN0eWxlZC5pbnB1dGBcbiAgJHtzaGFkb3dCb3JkZXIoeyBjb2xvcjogJ3JlZCcsIHdpZHRoOiAnNHB4JyB9KX1cbmBcbiJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFzQm9CIn0= */");

View file

@ -1,86 +0,0 @@
import { css, Global } from '@emotion/react'
import styled from '@emotion/styled'
import { PureComponent } from 'react'
import ReactDOM from 'react-dom'
const stylesInCallback = (props: any) =>
css({
color: 'red',
background: 'yellow',
width: `${props.scale * 100}px`,
})
const styles = css({
color: 'red',
width: '20px',
})
const styles2 = css`
color: red;
width: 20px;
`
const DivContainer = styled.div({
background: 'red',
})
const DivContainer2 = styled.div`
background: red;
`
const ContainerWithOptions = styled('div', {
shouldForwardProp: (propertyName: string) => !propertyName.startsWith('$'),
})`
color: hotpink;
`
const SpanContainer = styled('span')({
background: 'yellow',
})
export const DivContainerExtended = styled(DivContainer)``
export const DivContainerExtended2 = styled(DivContainer)({})
const Container = styled('button')`
background: red;
${stylesInCallback}
${() =>
css({
background: 'red',
})}
color: yellow;
font-size: 12px;
`
const Container2 = styled.div`
background: red;
`
export class SimpleComponent extends PureComponent {
render() {
return (
<Container
css={css`
color: hotpink;
`}
>
<Global
styles={css`
html,
body {
padding: 3rem 1rem;
margin: 0;
background: papayawhip;
min-height: 100%;
font-family: Helvetica, Arial, sans-serif;
font-size: 24px;
}
`}
/>
<span>hello</span>
</Container>
)
}
}
ReactDOM.render(<SimpleComponent />, document.querySelector('#app'))

File diff suppressed because one or more lines are too long

View file

@ -1,8 +0,0 @@
import * as React from 'react'
import { SomeGlobalFromCore } from 'package-two'
const getBgColor = () => ({ backgroundColor: '#fff' })
export default () => (
<SomeGlobalFromCore styles={{ color: 'hotpink', ...getBgColor() }} />
)

View file

@ -1,15 +0,0 @@
import { jsx as _jsx } from "react/jsx-runtime";
import * as React from 'react';
import { SomeGlobalFromCore } from 'package-two';
const getBgColor = ()=>({
backgroundColor: '#fff'
});
export default (()=>/*#__PURE__*/ _jsx(SomeGlobalFromCore, {
styles: [
{
color: 'hotpink',
...getBgColor()
},
"/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5wdXQudHMiLCJzb3VyY2VzIjpbImlucHV0LnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgU29tZUdsb2JhbEZyb21Db3JlIH0gZnJvbSAncGFja2FnZS10d28nXG5cbmNvbnN0IGdldEJnQ29sb3IgPSAoKSA9PiAoeyBiYWNrZ3JvdW5kQ29sb3I6ICcjZmZmJyB9KVxuXG5leHBvcnQgZGVmYXVsdCAoKSA9PiAoXG4gIDxTb21lR2xvYmFsRnJvbUNvcmUgc3R5bGVzPXt7IGNvbG9yOiAnaG90cGluaycsIC4uLmdldEJnQ29sb3IoKSB9fSAvPlxuKVxuIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQU1HIn0= */"
]
}));

View file

@ -1,4 +0,0 @@
import * as React from 'react'
import { SomeGlobalFromCore } from 'package-two'
export default () => <SomeGlobalFromCore styles={{ color: 'hotpink' }} />

View file

@ -1,11 +0,0 @@
import { jsx as _jsx } from "react/jsx-runtime";
import * as React from 'react';
import { SomeGlobalFromCore } from 'package-two';
export default (()=>/*#__PURE__*/ _jsx(SomeGlobalFromCore, {
styles: [
{
color: 'hotpink'
},
"/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5wdXQudHMiLCJzb3VyY2VzIjpbImlucHV0LnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgU29tZUdsb2JhbEZyb21Db3JlIH0gZnJvbSAncGFja2FnZS10d28nXG5cbmV4cG9ydCBkZWZhdWx0ICgpID0+IDxTb21lR2xvYmFsRnJvbUNvcmUgc3R5bGVzPXt7IGNvbG9yOiAnaG90cGluaycgfX0gLz5cbiJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFHc0IifQ== */"
]
}));

View file

@ -1,3 +0,0 @@
import { nonDefaultStyled as someAlias } from 'package-one'
let SomeComp = someAlias.div({ color: 'hotpink' })

View file

@ -1,7 +0,0 @@
import { nonDefaultStyled as someAlias } from 'package-one';
let SomeComp = /*#__PURE__*/ someAlias("div", {
target: "ekie5mj0",
label: "SomeComp"
})({
color: 'hotpink'
}, "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5wdXQudHMiLCJzb3VyY2VzIjpbImlucHV0LnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IG5vbkRlZmF1bHRTdHlsZWQgYXMgc29tZUFsaWFzIH0gZnJvbSAncGFja2FnZS1vbmUnXG5cbmxldCBTb21lQ29tcCA9IHNvbWVBbGlhcy5kaXYoeyBjb2xvcjogJ2hvdHBpbmsnIH0pXG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBRWUifQ== */");

View file

@ -1,3 +0,0 @@
import { nonDefaultStyled } from 'package-one'
let SomeComp = nonDefaultStyled.div({ color: 'hotpink' })

View file

@ -1,7 +0,0 @@
import { nonDefaultStyled } from 'package-one';
let SomeComp = /*#__PURE__*/ nonDefaultStyled("div", {
target: "ekie5mj0",
label: "SomeComp"
})({
color: 'hotpink'
}, "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5wdXQudHMiLCJzb3VyY2VzIjpbImlucHV0LnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IG5vbkRlZmF1bHRTdHlsZWQgfSBmcm9tICdwYWNrYWdlLW9uZSdcblxubGV0IFNvbWVDb21wID0gbm9uRGVmYXVsdFN0eWxlZC5kaXYoeyBjb2xvcjogJ2hvdHBpbmsnIH0pXG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBRWUifQ== */");

View file

@ -1,3 +0,0 @@
import { nonDefaultStyled } from 'package-four'
let SomeComp = nonDefaultStyled.div({ color: 'hotpink' })

View file

@ -1,7 +0,0 @@
import { nonDefaultStyled } from 'package-four';
let SomeComp = /*#__PURE__*/ nonDefaultStyled("div", {
target: "ekie5mj0",
label: "SomeComp"
})({
color: 'hotpink'
}, "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5wdXQudHMiLCJzb3VyY2VzIjpbImlucHV0LnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IG5vbkRlZmF1bHRTdHlsZWQgfSBmcm9tICdwYWNrYWdlLWZvdXInXG5cbmxldCBTb21lQ29tcCA9IG5vbkRlZmF1bHRTdHlsZWQuZGl2KHsgY29sb3I6ICdob3RwaW5rJyB9KVxuIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVlIn0= */");

View file

@ -1,3 +0,0 @@
import { something } from 'package-three'
something({ color: 'green' })

View file

@ -1,4 +0,0 @@
import { something } from 'package-three';
/*#__PURE__*/ something({
color: 'green'
}, "label:[local]", "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5wdXQudHMiLCJzb3VyY2VzIjpbImlucHV0LnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IHNvbWV0aGluZyB9IGZyb20gJ3BhY2thZ2UtdGhyZWUnXG5cbnNvbWV0aGluZyh7IGNvbG9yOiAnZ3JlZW4nIH0pXG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBRUEifQ== */");

View file

@ -1,9 +0,0 @@
import styled from '@emotion/styled'
const SelectedComponent = styled.p`
color: red;
&:after {
content: ' | ';
}
`

View file

@ -1,5 +0,0 @@
import styled from '@emotion/styled';
const SelectedComponent = /*#__PURE__*/ styled("p", {
target: "ekie5mj0",
label: "SelectedComponent"
})("color:red;&:after{content:' | ';}", "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5wdXQudHMiLCJzb3VyY2VzIjpbImlucHV0LnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBzdHlsZWQgZnJvbSAnQGVtb3Rpb24vc3R5bGVkJ1xuXG5jb25zdCBTZWxlY3RlZENvbXBvbmVudCA9IHN0eWxlZC5wYFxuICBjb2xvcjogcmVkO1xuXG4gICY6YWZ0ZXIge1xuICAgIGNvbnRlbnQ6ICcgfCAnO1xuICB9XG5gXG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBRTBCIn0= */");

View file

@ -1,32 +0,0 @@
import * as emotionReact from '@emotion/react'
import { PureComponent } from 'react'
import ReactDOM from 'react-dom'
const stylesInCallback = (props: any) =>
emotionReact.css({
color: 'red',
background: 'yellow',
width: `${props.scale * 100}px`,
})
const styles = emotionReact.css({
color: 'red',
width: '20px',
})
const styles2 = emotionReact.css`
color: red;
width: 20px;
`
export class SimpleComponent extends PureComponent {
render() {
return (
<div className={styles}>
<span>hello</span>
</div>
)
}
}
ReactDOM.render(<SimpleComponent />, document.querySelector('#app'))

View file

@ -1,26 +0,0 @@
import { jsx as _jsx } from "react/jsx-runtime";
import * as emotionReact from '@emotion/react';
import { PureComponent } from 'react';
import ReactDOM from 'react-dom';
const stylesInCallback = (props: any)=>/*#__PURE__*/ emotionReact.css({
color: 'red',
background: 'yellow',
width: `${props.scale * 100}px`
}, "label:stylesInCallback", "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5wdXQudHMiLCJzb3VyY2VzIjpbImlucHV0LnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGVtb3Rpb25SZWFjdCBmcm9tICdAZW1vdGlvbi9yZWFjdCdcbmltcG9ydCB7IFB1cmVDb21wb25lbnQgfSBmcm9tICdyZWFjdCdcbmltcG9ydCBSZWFjdERPTSBmcm9tICdyZWFjdC1kb20nXG5cbmNvbnN0IHN0eWxlc0luQ2FsbGJhY2sgPSAocHJvcHM6IGFueSkgPT5cbiAgZW1vdGlvblJlYWN0LmNzcyh7XG4gICAgY29sb3I6ICdyZWQnLFxuICAgIGJhY2tncm91bmQ6ICd5ZWxsb3cnLFxuICAgIHdpZHRoOiBgJHtwcm9wcy5zY2FsZSAqIDEwMH1weGAsXG4gIH0pXG5cbmNvbnN0IHN0eWxlcyA9IGVtb3Rpb25SZWFjdC5jc3Moe1xuICBjb2xvcjogJ3JlZCcsXG4gIHdpZHRoOiAnMjBweCcsXG59KVxuXG5jb25zdCBzdHlsZXMyID0gZW1vdGlvblJlYWN0LmNzc2BcbiAgY29sb3I6IHJlZDtcbiAgd2lkdGg6IDIwcHg7XG5gXG5cbmV4cG9ydCBjbGFzcyBTaW1wbGVDb21wb25lbnQgZXh0ZW5kcyBQdXJlQ29tcG9uZW50IHtcbiAgcmVuZGVyKCkge1xuICAgIHJldHVybiAoXG4gICAgICA8ZGl2IGNsYXNzTmFtZT17c3R5bGVzfT5cbiAgICAgICAgPHNwYW4+aGVsbG88L3NwYW4+XG4gICAgICA8L2Rpdj5cbiAgICApXG4gIH1cbn1cblxuUmVhY3RET00ucmVuZGVyKDxTaW1wbGVDb21wb25lbnQgLz4sIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJyNhcHAnKSlcbiJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFLRSJ9 */")
;
const styles = /*#__PURE__*/ emotionReact.css({
color: 'red',
width: '20px'
}, "label:styles", "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5wdXQudHMiLCJzb3VyY2VzIjpbImlucHV0LnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGVtb3Rpb25SZWFjdCBmcm9tICdAZW1vdGlvbi9yZWFjdCdcbmltcG9ydCB7IFB1cmVDb21wb25lbnQgfSBmcm9tICdyZWFjdCdcbmltcG9ydCBSZWFjdERPTSBmcm9tICdyZWFjdC1kb20nXG5cbmNvbnN0IHN0eWxlc0luQ2FsbGJhY2sgPSAocHJvcHM6IGFueSkgPT5cbiAgZW1vdGlvblJlYWN0LmNzcyh7XG4gICAgY29sb3I6ICdyZWQnLFxuICAgIGJhY2tncm91bmQ6ICd5ZWxsb3cnLFxuICAgIHdpZHRoOiBgJHtwcm9wcy5zY2FsZSAqIDEwMH1weGAsXG4gIH0pXG5cbmNvbnN0IHN0eWxlcyA9IGVtb3Rpb25SZWFjdC5jc3Moe1xuICBjb2xvcjogJ3JlZCcsXG4gIHdpZHRoOiAnMjBweCcsXG59KVxuXG5jb25zdCBzdHlsZXMyID0gZW1vdGlvblJlYWN0LmNzc2BcbiAgY29sb3I6IHJlZDtcbiAgd2lkdGg6IDIwcHg7XG5gXG5cbmV4cG9ydCBjbGFzcyBTaW1wbGVDb21wb25lbnQgZXh0ZW5kcyBQdXJlQ29tcG9uZW50IHtcbiAgcmVuZGVyKCkge1xuICAgIHJldHVybiAoXG4gICAgICA8ZGl2IGNsYXNzTmFtZT17c3R5bGVzfT5cbiAgICAgICAgPHNwYW4+aGVsbG88L3NwYW4+XG4gICAgICA8L2Rpdj5cbiAgICApXG4gIH1cbn1cblxuUmVhY3RET00ucmVuZGVyKDxTaW1wbGVDb21wb25lbnQgLz4sIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJyNhcHAnKSlcbiJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFXZSJ9 */");
const styles2 = /*#__PURE__*/ emotionReact.css("color:red;width:20px;", "label:styles2", "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5wdXQudHMiLCJzb3VyY2VzIjpbImlucHV0LnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGVtb3Rpb25SZWFjdCBmcm9tICdAZW1vdGlvbi9yZWFjdCdcbmltcG9ydCB7IFB1cmVDb21wb25lbnQgfSBmcm9tICdyZWFjdCdcbmltcG9ydCBSZWFjdERPTSBmcm9tICdyZWFjdC1kb20nXG5cbmNvbnN0IHN0eWxlc0luQ2FsbGJhY2sgPSAocHJvcHM6IGFueSkgPT5cbiAgZW1vdGlvblJlYWN0LmNzcyh7XG4gICAgY29sb3I6ICdyZWQnLFxuICAgIGJhY2tncm91bmQ6ICd5ZWxsb3cnLFxuICAgIHdpZHRoOiBgJHtwcm9wcy5zY2FsZSAqIDEwMH1weGAsXG4gIH0pXG5cbmNvbnN0IHN0eWxlcyA9IGVtb3Rpb25SZWFjdC5jc3Moe1xuICBjb2xvcjogJ3JlZCcsXG4gIHdpZHRoOiAnMjBweCcsXG59KVxuXG5jb25zdCBzdHlsZXMyID0gZW1vdGlvblJlYWN0LmNzc2BcbiAgY29sb3I6IHJlZDtcbiAgd2lkdGg6IDIwcHg7XG5gXG5cbmV4cG9ydCBjbGFzcyBTaW1wbGVDb21wb25lbnQgZXh0ZW5kcyBQdXJlQ29tcG9uZW50IHtcbiAgcmVuZGVyKCkge1xuICAgIHJldHVybiAoXG4gICAgICA8ZGl2IGNsYXNzTmFtZT17c3R5bGVzfT5cbiAgICAgICAgPHNwYW4+aGVsbG88L3NwYW4+XG4gICAgICA8L2Rpdj5cbiAgICApXG4gIH1cbn1cblxuUmVhY3RET00ucmVuZGVyKDxTaW1wbGVDb21wb25lbnQgLz4sIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJyNhcHAnKSlcbiJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFnQmdCIn0= */");
export class SimpleComponent extends PureComponent {
render() {
return /*#__PURE__*/ _jsx("div", {
className: styles,
children: /*#__PURE__*/ _jsx("span", {
children: "hello"
})
});
}
}
ReactDOM.render(/*#__PURE__*/ _jsx(SimpleComponent, {}), document.querySelector('#app'));

View file

@ -1,19 +0,0 @@
{
"package-one": {
"nonDefaultStyled": { "canonicalImport": ["@emotion/styled", "default"] }
},
"package-two": {
"someJsx": { "canonicalImport": ["@emotion/react", "jsx"] },
"someCssFromCore": { "canonicalImport": ["@emotion/react", "css"] },
"SomeGlobalFromCore": { "canonicalImport": ["@emotion/react", "Global"] }
},
"package-three": {
"something": { "canonicalImport": ["@emotion/css", "css"] }
},
"package-four": {
"nonDefaultStyled": {
"canonicalImport": ["@emotion/styled", "default"],
"styledBaseImport": ["package-four/base", "something"]
}
}
}

View file

@ -1,22 +0,0 @@
[package]
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
description = "AST Transforms for import modularizer"
edition = "2018"
license = "Apache-2.0"
name = "modularize_imports"
repository = "https://github.com/vercel/next.js.git"
version = "0.20.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
convert_case = "0.5.0"
handlebars = "4.2.1"
once_cell = "1.13.0"
regex = "1.5"
serde = "1"
swc_core = { features = ["cached", "ecma_ast", "ecma_visit"], version = "0.39.7" }
[dev-dependencies]
swc_core = { features = ["testing_transform"], version = "0.39.7" }
testing = "0.31.9"

View file

@ -1,249 +0,0 @@
use std::collections::HashMap;
use convert_case::{Case, Casing};
use handlebars::{Context, Handlebars, Helper, HelperResult, Output, RenderContext};
use once_cell::sync::Lazy;
use regex::{Captures, Regex};
use serde::{Deserialize, Serialize};
use swc_core::{
cached::regex::CachedRegex,
ecma::ast::*,
ecma::visit::{noop_fold_type, Fold},
};
static DUP_SLASH_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"//").unwrap());
#[derive(Clone, Debug, Deserialize)]
#[serde(transparent)]
pub struct Config {
pub packages: HashMap<String, PackageConfig>,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PackageConfig {
pub transform: String,
#[serde(default)]
pub prevent_full_import: bool,
#[serde(default)]
pub skip_default_conversion: bool,
}
struct FoldImports {
renderer: handlebars::Handlebars<'static>,
packages: Vec<(CachedRegex, PackageConfig)>,
}
struct Rewriter<'a> {
renderer: &'a handlebars::Handlebars<'static>,
key: &'a str,
config: &'a PackageConfig,
group: Vec<&'a str>,
}
impl<'a> Rewriter<'a> {
fn rewrite(&self, old_decl: &ImportDecl) -> Vec<ImportDecl> {
if old_decl.type_only || old_decl.asserts.is_some() {
return vec![old_decl.clone()];
}
let mut out: Vec<ImportDecl> = Vec::with_capacity(old_decl.specifiers.len());
for spec in &old_decl.specifiers {
match spec {
ImportSpecifier::Named(named_spec) => {
#[derive(Serialize)]
#[serde(untagged)]
enum Data<'a> {
Plain(&'a str),
Array(&'a [&'a str]),
}
let mut ctx: HashMap<&str, Data> = HashMap::new();
ctx.insert("matches", Data::Array(&self.group[..]));
ctx.insert(
"member",
Data::Plain(
named_spec
.imported
.as_ref()
.map(|x| match x {
ModuleExportName::Ident(x) => x.as_ref(),
ModuleExportName::Str(x) => x.value.as_ref(),
})
.unwrap_or_else(|| named_spec.local.as_ref()),
),
);
let new_path = self
.renderer
.render_template(&self.config.transform, &ctx)
.unwrap_or_else(|e| {
panic!("error rendering template for '{}': {}", self.key, e);
});
let new_path = DUP_SLASH_REGEX.replace_all(&new_path, |_: &Captures| "/");
let specifier = if self.config.skip_default_conversion {
ImportSpecifier::Named(named_spec.clone())
} else {
ImportSpecifier::Default(ImportDefaultSpecifier {
local: named_spec.local.clone(),
span: named_spec.span,
})
};
out.push(ImportDecl {
specifiers: vec![specifier],
src: Box::new(Str::from(new_path.as_ref())),
span: old_decl.span,
type_only: false,
asserts: None,
});
}
_ => {
if self.config.prevent_full_import {
panic!(
"import {:?} causes the entire module to be imported",
old_decl
);
} else {
// Give up
return vec![old_decl.clone()];
}
}
}
}
out
}
}
impl FoldImports {
fn should_rewrite<'a>(&'a self, name: &'a str) -> Option<Rewriter<'a>> {
for (regex, config) in &self.packages {
let group = regex.captures(name);
if let Some(group) = group {
let group = group
.iter()
.map(|x| x.map(|x| x.as_str()).unwrap_or_default())
.collect::<Vec<&str>>();
return Some(Rewriter {
renderer: &self.renderer,
key: name,
config,
group,
});
}
}
None
}
}
impl Fold for FoldImports {
noop_fold_type!();
fn fold_module(&mut self, mut module: Module) -> Module {
let mut new_items: Vec<ModuleItem> = vec![];
for item in module.body {
match item {
ModuleItem::ModuleDecl(ModuleDecl::Import(decl)) => {
match self.should_rewrite(&decl.src.value) {
Some(rewriter) => {
let rewritten = rewriter.rewrite(&decl);
new_items.extend(
rewritten
.into_iter()
.map(|x| ModuleItem::ModuleDecl(ModuleDecl::Import(x))),
);
}
None => new_items.push(ModuleItem::ModuleDecl(ModuleDecl::Import(decl))),
}
}
x => {
new_items.push(x);
}
}
}
module.body = new_items;
module
}
}
pub fn modularize_imports(config: Config) -> impl Fold {
let mut folder = FoldImports {
renderer: handlebars::Handlebars::new(),
packages: vec![],
};
folder
.renderer
.register_helper("lowerCase", Box::new(helper_lower_case));
folder
.renderer
.register_helper("upperCase", Box::new(helper_upper_case));
folder
.renderer
.register_helper("camelCase", Box::new(helper_camel_case));
folder
.renderer
.register_helper("kebabCase", Box::new(helper_kebab_case));
for (mut k, v) in config.packages {
// XXX: Should we keep this hack?
if !k.starts_with('^') && !k.ends_with('$') {
k = format!("^{}$", k);
}
folder.packages.push((
CachedRegex::new(&k).expect("transform-imports: invalid regex"),
v,
));
}
folder
}
fn helper_lower_case(
h: &Helper<'_, '_>,
_: &Handlebars<'_>,
_: &Context,
_: &mut RenderContext<'_, '_>,
out: &mut dyn Output,
) -> HelperResult {
// get parameter from helper or throw an error
let param = h.param(0).and_then(|v| v.value().as_str()).unwrap_or("");
out.write(param.to_lowercase().as_ref())?;
Ok(())
}
fn helper_upper_case(
h: &Helper<'_, '_>,
_: &Handlebars<'_>,
_: &Context,
_: &mut RenderContext<'_, '_>,
out: &mut dyn Output,
) -> HelperResult {
// get parameter from helper or throw an error
let param = h.param(0).and_then(|v| v.value().as_str()).unwrap_or("");
out.write(param.to_uppercase().as_ref())?;
Ok(())
}
fn helper_camel_case(
h: &Helper<'_, '_>,
_: &Handlebars<'_>,
_: &Context,
_: &mut RenderContext<'_, '_>,
out: &mut dyn Output,
) -> HelperResult {
// get parameter from helper or throw an error
let param = h.param(0).and_then(|v| v.value().as_str()).unwrap_or("");
out.write(param.to_case(Case::Camel).as_ref())?;
Ok(())
}
fn helper_kebab_case(
h: &Helper<'_, '_>,
_: &Handlebars<'_>,
_: &Context,
_: &mut RenderContext<'_, '_>,
out: &mut dyn Output,
) -> HelperResult {
// get parameter from helper or throw an error
let param = h.param(0).and_then(|v| v.value().as_str()).unwrap_or("");
out.write(param.to_case(Case::Kebab).as_ref())?;
Ok(())
}

View file

@ -1,68 +0,0 @@
use std::path::PathBuf;
use modularize_imports::{modularize_imports, PackageConfig};
use swc_core::{
ecma::parser::{EsConfig, Syntax},
ecma::transforms::testing::{test_fixture, FixtureTestConfig},
};
use testing::fixture;
fn syntax() -> Syntax {
Syntax::Es(EsConfig {
jsx: true,
..Default::default()
})
}
#[fixture("tests/fixture/**/input.js")]
fn modularize_imports_fixture(input: PathBuf) {
let output = input.parent().unwrap().join("output.js");
test_fixture(
syntax(),
&|_tr| {
modularize_imports(modularize_imports::Config {
packages: vec![
(
"react-bootstrap".to_string(),
PackageConfig {
transform: "react-bootstrap/lib/{{member}}".into(),
prevent_full_import: false,
skip_default_conversion: false,
},
),
(
"my-library/?(((\\w*)?/?)*)".to_string(),
PackageConfig {
transform: "my-library/{{ matches.[1] }}/{{member}}".into(),
prevent_full_import: false,
skip_default_conversion: false,
},
),
(
"my-library-2".to_string(),
PackageConfig {
transform: "my-library-2/{{ camelCase member }}".into(),
prevent_full_import: false,
skip_default_conversion: true,
},
),
(
"my-library-3".to_string(),
PackageConfig {
transform: "my-library-3/{{ kebabCase member }}".into(),
prevent_full_import: false,
skip_default_conversion: true,
},
),
]
.into_iter()
.collect(),
})
},
&input,
&output,
FixtureTestConfig {
..Default::default()
},
);
}

View file

@ -1,3 +0,0 @@
import { MyModule } from 'my-library';
import { App } from 'my-library/components';
import { Header, Footer } from 'my-library/components/App';

View file

@ -1,4 +0,0 @@
import MyModule from "my-library/MyModule";
import App from "my-library/components/App";
import Header from "my-library/components/App/Header";
import Footer from "my-library/components/App/Footer";

View file

@ -1,3 +0,0 @@
import { Grid, Row, Col as Col1 } from 'react-bootstrap';
import { MyModule, Widget } from 'my-library-2';
import { MyModule } from 'my-library-3';

View file

@ -1,6 +0,0 @@
import Grid from "react-bootstrap/lib/Grid";
import Row from "react-bootstrap/lib/Row";
import Col1 from "react-bootstrap/lib/Col";
import { MyModule } from "my-library-2/myModule";
import { Widget } from "my-library-2/widget";
import { MyModule } from "my-library-3/my-module";

View file

@ -50,7 +50,7 @@ swc_core = { features = [
"ecma_transforms_typescript", "ecma_transforms_typescript",
"ecma_utils", "ecma_utils",
"ecma_visit", "ecma_visit",
], version = "0.39.7" } ], version = "0.40.7" }
tracing = { version = "0.1.32", features = ["release_max_level_info"] } tracing = { version = "0.1.32", features = ["release_max_level_info"] }
tracing-futures = "0.2.5" tracing-futures = "0.2.5"
tracing-subscriber = "0.3.9" tracing-subscriber = "0.3.9"

View file

@ -1,33 +0,0 @@
[package]
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
description = "AST Transforms for styled-components"
edition = "2018"
include = ["Cargo.toml", "src/**/*.rs"]
license = "Apache-2.0"
name = "styled_components"
repository = "https://github.com/vercel/next.js.git"
version = "0.45.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
Inflector = "0.11.4"
once_cell = "1.13.0"
regex = {version = "1.5.4", features = ["std", "perf"], default-features = false}
serde = {version = "1.0.130", features = ["derive"]}
tracing = "0.1.32"
swc_core = { features = [
"common",
"ecma_ast",
"ecma_utils",
"ecma_visit"
], version = "0.39.7" }
[dev-dependencies]
serde_json = "1"
testing = "0.31.9"
swc_core = { features = [
"ecma_parser",
"ecma_transforms",
"testing_transform"
], version = "0.39.7" }

View file

@ -1,91 +0,0 @@
#![deny(unused)]
pub use crate::{
utils::{analyze, analyzer, State},
visitors::{
display_name_and_id::display_name_and_id, transpile_css_prop::transpile::transpile_css_prop,
},
};
use serde::Deserialize;
use std::{cell::RefCell, rc::Rc};
use swc_core::{
common::{chain, pass::Optional, FileName},
ecma::atoms::JsWord,
ecma::visit::{Fold, VisitMut},
};
mod css;
mod utils;
mod visitors;
#[derive(Debug, Default, Clone, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct Config {
#[serde(default = "true_by_default")]
pub display_name: bool,
#[serde(default = "true_by_default")]
pub ssr: bool,
#[serde(default = "true_by_default")]
pub file_name: bool,
#[serde(default = "default_index_file_name")]
pub meaningless_file_names: Vec<String>,
#[serde(default)]
pub namespace: String,
#[serde(default)]
pub top_level_import_paths: Vec<JsWord>,
#[serde(default)]
pub transpile_template_literals: bool,
#[serde(default)]
pub minify: bool,
#[serde(default)]
pub pure: bool,
#[serde(default = "true_by_default")]
pub css_prop: bool,
}
fn true_by_default() -> bool {
true
}
fn default_index_file_name() -> Vec<String> {
vec!["index".to_string()]
}
impl Config {
pub(crate) fn use_namespace(&self) -> String {
if self.namespace.is_empty() {
return String::new();
}
format!("{}__", self.namespace)
}
}
/// NOTE: **This is not complete**.
///
/// Only [analyzer] and [display_name_and_id] is implemented.
pub fn styled_components(
file_name: FileName,
src_file_hash: u128,
config: Config,
) -> impl Fold + VisitMut {
let state: Rc<RefCell<State>> = Default::default();
let config = Rc::new(config);
chain!(
analyzer(config.clone(), state.clone()),
Optional {
enabled: config.css_prop,
visitor: transpile_css_prop(state.clone())
},
display_name_and_id(file_name, src_file_hash, config.clone(), state)
)
}

View file

@ -1,131 +0,0 @@
use super::State;
use crate::Config;
use std::{cell::RefCell, rc::Rc};
use swc_core::{
ecma::ast::*,
ecma::visit::{
as_folder, noop_visit_mut_type, noop_visit_type, Fold, Visit, VisitMut, VisitWith,
},
};
pub fn analyzer(config: Rc<Config>, state: Rc<RefCell<State>>) -> impl VisitMut + Fold {
as_folder(AsAnalyzer { config, state })
}
struct AsAnalyzer {
config: Rc<Config>,
state: Rc<RefCell<State>>,
}
impl VisitMut for AsAnalyzer {
noop_visit_mut_type!();
fn visit_mut_module(&mut self, p: &mut Module) {
let mut v = Analyzer {
config: &self.config,
state: &mut self.state.borrow_mut(),
};
p.visit_with(&mut v);
}
fn visit_mut_script(&mut self, p: &mut Script) {
let mut v = Analyzer {
config: &self.config,
state: &mut self.state.borrow_mut(),
};
p.visit_with(&mut v);
}
}
pub fn analyze(config: &Config, program: &Program) -> State {
let mut state = State::default();
let mut v = Analyzer {
config,
state: &mut state,
};
program.visit_with(&mut v);
state
}
struct Analyzer<'a> {
config: &'a Config,
state: &'a mut State,
}
impl Visit for Analyzer<'_> {
noop_visit_type!();
fn visit_var_declarator(&mut self, v: &VarDeclarator) {
v.visit_children_with(self);
if let (
Pat::Ident(name),
Some(Expr::Call(CallExpr {
callee: Callee::Expr(callee),
args,
..
})),
) = (&v.name, v.init.as_deref())
{
if let Expr::Ident(callee) = &**callee {
if &*callee.sym == "require" && args.len() == 1 && args[0].spread.is_none() {
if let Expr::Lit(Lit::Str(v)) = &*args[0].expr {
let is_styled = if self.config.top_level_import_paths.is_empty() {
&*v.value == "styled-components"
|| v.value.starts_with("styled-components/")
} else {
self.config.top_level_import_paths.contains(&v.value)
};
if is_styled {
self.state.styled_required = Some(name.id.to_id());
self.state.unresolved_ctxt = Some(callee.span.ctxt);
}
}
}
}
}
}
fn visit_import_decl(&mut self, i: &ImportDecl) {
let is_custom = !self.config.top_level_import_paths.is_empty();
let is_styled = if self.config.top_level_import_paths.is_empty() {
&*i.src.value == "styled-components" || i.src.value.starts_with("styled-components/")
} else {
self.config.top_level_import_paths.contains(&i.src.value)
};
if is_styled {
for s in &i.specifiers {
match s {
ImportSpecifier::Named(s) => {
if is_custom
&& s.imported
.as_ref()
.map(|v| match v {
ModuleExportName::Ident(v) => &*v.sym,
ModuleExportName::Str(v) => &*v.value,
})
.unwrap_or(&*s.local.sym)
== "styled"
{
self.state.imported_local_name = Some(s.local.to_id());
}
}
ImportSpecifier::Default(s) => {
self.state.imported_local_name = Some(s.local.to_id());
}
ImportSpecifier::Namespace(s) => {
self.state.imported_local_ns = Some(s.local.to_id());
}
}
}
}
}
}

View file

@ -1,324 +0,0 @@
pub use self::analyzer::{analyze, analyzer};
use std::{borrow::Cow, cell::RefCell};
use swc_core::{
common::{collections::AHashMap, SyntaxContext},
ecma::ast::*,
ecma::atoms::js_word,
};
mod analyzer;
pub(crate) fn get_prop_key_as_expr(p: &Prop) -> Cow<Expr> {
match p {
Prop::Shorthand(p) => Cow::Owned(Expr::Ident(p.clone())),
Prop::KeyValue(p) => prop_name_to_expr(&p.key),
Prop::Assign(p) => Cow::Owned(Expr::Ident(p.key.clone())),
Prop::Getter(p) => prop_name_to_expr(&p.key),
Prop::Setter(p) => prop_name_to_expr(&p.key),
Prop::Method(p) => prop_name_to_expr(&p.key),
}
}
pub(crate) fn prop_name_to_expr(p: &PropName) -> Cow<Expr> {
match p {
PropName::Ident(p) => Cow::Owned(Expr::Ident(p.clone())),
PropName::Str(p) => Cow::Owned(Expr::Lit(Lit::Str(p.clone()))),
PropName::Num(p) => Cow::Owned(Expr::Lit(Lit::Num(p.clone()))),
PropName::BigInt(p) => Cow::Owned(Expr::Lit(Lit::BigInt(p.clone()))),
PropName::Computed(e) => Cow::Borrowed(&e.expr),
}
}
pub(crate) fn get_prop_name(p: &Prop) -> Option<&PropName> {
match p {
Prop::Shorthand(..) => None,
Prop::KeyValue(p) => Some(&p.key),
Prop::Assign(..) => None,
Prop::Getter(p) => Some(&p.key),
Prop::Setter(p) => Some(&p.key),
Prop::Method(p) => Some(&p.key),
}
}
pub(crate) fn get_prop_name2(p: &Prop) -> PropName {
match p {
Prop::Shorthand(ident) => PropName::Ident(ident.clone()),
Prop::KeyValue(p) => p.key.clone(),
Prop::Assign(x) => PropName::Ident(x.key.clone()),
Prop::Getter(p) => p.key.clone(),
Prop::Setter(p) => p.key.clone(),
Prop::Method(p) => p.key.clone(),
}
}
/// This is created once per file.
#[derive(Debug, Default)]
pub struct State {
pub(crate) styled_required: Option<Id>,
unresolved_ctxt: Option<SyntaxContext>,
imported_local_name: Option<Id>,
/// Namespace imports
imported_local_ns: Option<Id>,
import_name_cache: RefCell<AHashMap<Id, Id>>,
}
impl State {
pub(crate) fn is_styled(&self, tag: &Expr) -> bool {
if let Expr::Call(CallExpr {
callee: Callee::Expr(callee),
..
}) = tag
{
if let Expr::Member(MemberExpr {
obj,
prop: MemberProp::Ident(prop),
..
}) = &**callee
{
if prop.sym != js_word!("default") {
return self.is_styled(obj);
}
}
}
match tag {
Expr::Member(MemberExpr {
obj,
prop: MemberProp::Ident(prop),
..
}) => {
if let Expr::Ident(obj) = &**obj {
if Some(obj.to_id()) == self.import_local_name("default", Some(obj))
&& !self.is_helper(&Expr::Ident(prop.clone()))
{
return true;
}
}
}
Expr::Call(CallExpr {
callee: Callee::Expr(callee),
..
}) => {
if let Expr::Ident(callee) = &**callee {
if Some(callee.to_id()) == self.import_local_name("default", Some(callee)) {
return true;
}
}
}
_ => {}
}
// styled-components might be imported using a require()
if let Some(style_required) = self.styled_required.clone() {
match tag {
Expr::Member(MemberExpr {
obj,
prop: MemberProp::Ident(..),
..
}) => {
if let Expr::Member(MemberExpr {
obj: obj_of_obj,
prop: MemberProp::Ident(prop),
..
}) = &**obj
{
if let Expr::Ident(obj_of_obj) = &**obj_of_obj {
if prop.sym == js_word!("default")
&& obj_of_obj.to_id() == style_required
{
return true;
}
}
}
}
Expr::Call(CallExpr {
callee: Callee::Expr(callee),
..
}) => {
if let Expr::Member(MemberExpr {
obj: tag_callee_object,
prop: MemberProp::Ident(tag_callee_property),
..
}) = &**callee
{
if let Expr::Ident(tag_callee_object) = &**tag_callee_object {
if tag_callee_property.sym == js_word!("default")
&& tag_callee_object.to_id() == style_required
{
return true;
}
}
}
}
_ => {}
}
}
if let Some(import_local_name) = self.import_local_name("default", None) {
match tag {
Expr::Member(MemberExpr {
obj,
prop: MemberProp::Ident(..),
..
}) => {
if let Expr::Member(MemberExpr {
obj: obj_of_obj,
prop: MemberProp::Ident(prop),
..
}) = &**obj
{
if let Expr::Ident(obj_of_obj) = &**obj_of_obj {
if prop.sym == js_word!("default")
&& obj_of_obj.to_id() == import_local_name
{
return true;
}
}
}
}
Expr::Call(CallExpr {
callee: Callee::Expr(callee),
..
}) => {
if let Expr::Member(MemberExpr {
obj: tag_callee_object,
prop: MemberProp::Ident(tag_callee_property),
..
}) = &**callee
{
if let Expr::Ident(tag_callee_object) = &**tag_callee_object {
if tag_callee_property.sym == js_word!("default")
&& tag_callee_object.to_id() == import_local_name
{
return true;
}
}
}
}
_ => {}
}
}
false
}
pub(crate) fn import_local_name(
&self,
name: &str,
cache_identifier: Option<&Ident>,
) -> Option<Id> {
if name == "default" {
if let Some(cached) = self.imported_local_name.clone() {
return Some(cached);
}
if let Some(cached) = self.imported_local_ns.clone() {
return Some(cached);
}
}
if let Some(..) = self.imported_local_ns {
return Some((name.into(), Default::default()));
}
let cache_key = cache_identifier.map(|i| i.to_id()).unwrap_or_default();
let ctxt = self.unresolved_ctxt.unwrap_or_default();
let local_name = if self.styled_required.is_some() {
Some(if name == "default" {
"styled".into()
} else {
name.into()
})
} else {
None
};
if let Some(cached) = self.import_name_cache.borrow().get(&cache_key) {
return Some(cached.clone());
}
let name = local_name.map(|word| (word, ctxt));
if let Some(name) = name.clone() {
self.import_name_cache.borrow_mut().insert(cache_key, name);
}
name
}
pub(crate) fn set_import_name(&mut self, id: Id) {
self.imported_local_name = Some(id);
}
fn is_helper(&self, e: &Expr) -> bool {
self.is_create_global_style_helper(e)
|| self.is_css_helper(e)
|| self.is_inject_global_helper(e)
|| self.is_use_theme(e)
|| self.is_keyframes_helper(e)
|| self.is_with_theme_helper(e)
}
fn is_css_helper(&self, e: &Expr) -> bool {
match e {
Expr::Ident(e) => Some(e.to_id()) == self.import_local_name("css", None),
_ => false,
}
}
fn is_create_global_style_helper(&self, e: &Expr) -> bool {
match e {
Expr::Ident(e) => Some(e.to_id()) == self.import_local_name("createGlobalStyle", None),
_ => false,
}
}
fn is_inject_global_helper(&self, e: &Expr) -> bool {
match e {
Expr::Ident(e) => Some(e.to_id()) == self.import_local_name("injectGlobal", None),
_ => false,
}
}
fn is_keyframes_helper(&self, e: &Expr) -> bool {
match e {
Expr::Ident(e) => Some(e.to_id()) == self.import_local_name("keyframes", None),
_ => false,
}
}
fn is_with_theme_helper(&self, e: &Expr) -> bool {
match e {
Expr::Ident(e) => Some(e.to_id()) == self.import_local_name("withTheme", None),
_ => false,
}
}
fn is_use_theme(&self, e: &Expr) -> bool {
match e {
Expr::Ident(e) => Some(e.to_id()) == self.import_local_name("useTheme", None),
_ => false,
}
}
}
pub fn prefix_leading_digit(s: &str) -> Cow<str> {
if s.chars()
.next()
.map(|c| c.is_ascii_digit())
.unwrap_or(false)
{
Cow::Owned(format!("sc-{}", s))
} else {
Cow::Borrowed(s)
}
}

View file

@ -1,469 +0,0 @@
use crate::{
utils::{get_prop_name, prefix_leading_digit, State},
Config,
};
use once_cell::sync::Lazy;
use regex::Regex;
use std::{cell::RefCell, convert::TryInto, path::Path, rc::Rc};
use swc_core::{
common::{util::take::Take, FileName, DUMMY_SP},
ecma::ast::*,
ecma::atoms::{js_word, JsWord},
ecma::utils::{quote_ident, ExprFactory},
ecma::visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith},
};
use tracing::{debug, span, trace, Level};
pub fn display_name_and_id(
file_name: FileName,
src_file_hash: u128,
config: Rc<Config>,
state: Rc<RefCell<State>>,
) -> impl Fold + VisitMut {
as_folder(DisplayNameAndId {
file_name,
src_file_hash,
config,
state,
cur_display_name: Default::default(),
component_id: 0,
})
}
static DISPLAY_NAME_REGEX: Lazy<Regex> =
Lazy::new(|| Regex::new(r"^[a-zA-Z][a-zA-Z0-9]$").unwrap());
#[derive(Debug)]
struct DisplayNameAndId {
file_name: FileName,
src_file_hash: u128,
config: Rc<Config>,
state: Rc<RefCell<State>>,
cur_display_name: Option<JsWord>,
component_id: usize,
}
impl DisplayNameAndId {
fn get_block_name(&self, p: &Path) -> String {
match p.file_stem().map(|s| s.to_string_lossy()) {
Some(file_stem)
if !self
.config
.meaningless_file_names
.iter()
.any(|meaningless| file_stem.as_ref() == meaningless) =>
{
file_stem.into()
}
_ => self.get_block_name(
p.parent()
.expect("path only contains meaningless filenames (e.g. /index/index)?"),
),
}
}
fn get_display_name(&mut self, _: &Expr) -> JsWord {
let component_name = self.cur_display_name.clone().unwrap_or(js_word!(""));
match &self.file_name {
FileName::Real(f) if self.config.file_name => {
let block_name = self.get_block_name(f);
if block_name == *component_name {
return component_name;
}
if component_name.is_empty() {
return prefix_leading_digit(&block_name).into();
}
format!("{}__{}", prefix_leading_digit(&block_name), component_name).into()
}
_ => component_name,
}
}
fn next_id(&mut self) -> usize {
let ret = self.component_id;
self.component_id += 1;
ret
}
fn get_component_id(&mut self) -> String {
// Prefix the identifier with a character because CSS classes cannot start with
// a number
let next_id = self.next_id();
let hash = {
let base = self.src_file_hash;
let base = base.to_be_bytes();
let a = u32::from_be_bytes(base[0..4].try_into().unwrap());
let b = u32::from_be_bytes(base[4..8].try_into().unwrap());
let c = u32::from_be_bytes(base[8..12].try_into().unwrap());
let d = u32::from_be_bytes(base[12..16].try_into().unwrap());
a ^ b ^ c ^ d
};
format!("{}sc-{:x}-{}", self.config.use_namespace(), hash, next_id)
}
fn add_config(
&mut self,
e: &mut Expr,
display_name: Option<JsWord>,
component_id: Option<JsWord>,
) {
if display_name.is_none() && component_id.is_none() {
return;
}
let mut with_config_props = vec![];
if let Some(display_name) = display_name {
with_config_props.push(PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
key: PropName::Ident(quote_ident!("displayName")),
value: Box::new(Expr::Lit(Lit::Str(Str {
span: DUMMY_SP,
value: display_name,
raw: None,
}))),
}))))
}
if let Some(component_id) = component_id {
with_config_props.push(PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
key: PropName::Ident(quote_ident!("componentId")),
value: Box::new(Expr::Lit(Lit::Str(Str {
span: DUMMY_SP,
value: component_id,
raw: None,
}))),
}))))
}
get_existing_config(e, |e| {
if let Expr::Call(CallExpr { args, .. }) = e {
if let Some(Expr::Object(existing_config)) = args.get_mut(0).map(|v| &mut *v.expr) {
if !already_has(existing_config) {
existing_config.props.extend(with_config_props.take());
}
}
}
});
if with_config_props.is_empty() {
return;
}
if let Expr::Call(CallExpr {
callee: Callee::Expr(callee),
args,
..
}) = e
{
if let Expr::Member(MemberExpr {
prop: MemberProp::Ident(prop),
..
}) = &**callee
{
if &*prop.sym == "withConfig" {
if let Some(first_arg) = args.get_mut(0) {
if first_arg.spread.is_none() && first_arg.expr.is_object() {
if let Expr::Object(obj) = &mut *first_arg.expr {
if !already_has(&*obj) {
obj.props.extend(with_config_props);
return;
}
}
}
}
}
}
}
if let Expr::TaggedTpl(e) = e {
e.tag = Box::new(Expr::Call(CallExpr {
span: DUMMY_SP,
callee: e
.tag
.take()
.make_member(quote_ident!("withConfig"))
.as_callee(),
args: vec![ObjectLit {
span: DUMMY_SP,
props: with_config_props,
}
.as_arg()],
type_args: Default::default(),
}));
return;
}
if let Expr::Call(CallExpr {
callee: Callee::Expr(callee),
..
}) = e
{
*callee = Box::new(Expr::Call(CallExpr {
span: DUMMY_SP,
callee: callee
.take()
.make_member(quote_ident!("withConfig"))
.as_callee(),
args: vec![ObjectLit {
span: DUMMY_SP,
props: with_config_props,
}
.as_arg()],
type_args: Default::default(),
}));
return;
}
unreachable!("expr should be tagged tpl or call expr");
}
}
impl VisitMut for DisplayNameAndId {
noop_visit_mut_type!();
fn visit_mut_assign_expr(&mut self, e: &mut AssignExpr) {
let old = self.cur_display_name.clone();
if old.is_none() {
self.cur_display_name = e.left.as_ident().map(|v| v.sym.clone());
}
e.visit_mut_children_with(self);
self.cur_display_name = old;
}
fn visit_mut_class_prop(&mut self, e: &mut ClassProp) {
let old = self.cur_display_name.take();
if let PropName::Ident(i) = &e.key {
self.cur_display_name = Some(i.sym.clone());
}
e.visit_mut_children_with(self);
self.cur_display_name = old;
}
fn visit_mut_expr(&mut self, expr: &mut Expr) {
expr.visit_mut_children_with(self);
let is_styled = match expr {
Expr::TaggedTpl(e) => self.state.borrow().is_styled(&e.tag),
Expr::Call(CallExpr {
callee: Callee::Expr(callee),
args,
..
}) => {
(
// styled()
self.state.borrow().is_styled(&*callee)
&& get_property_as_ident(callee)
.map(|v| v == "withConfig")
.unwrap_or(false)
) || (
// styled(x)({})
self.state.borrow().is_styled(&*callee)
&& !get_callee(callee)
.map(|callee| callee.is_member())
.unwrap_or(false)
) || (
// styled(x).attrs()({})
self.state.borrow().is_styled(callee)
&& get_callee(callee)
.map(|callee| {
callee.is_member()
&& get_property_as_ident(callee)
.map(|v| v == "withConfig")
.unwrap_or(false)
})
.unwrap_or(false)
) || (
// styled(x).withConfig({})
self.state.borrow().is_styled(&*callee)
&& get_callee(callee)
.map(|callee| {
callee.is_member()
&& get_property_as_ident(callee)
.map(|v| v == "withConfig")
.unwrap_or(false)
&& !args.is_empty()
&& args[0].spread.is_none()
&& match &*args[0].expr {
Expr::Object(first_arg) => {
!first_arg.props.iter().any(|prop| match prop {
PropOrSpread::Prop(prop) => {
match get_prop_name(prop) {
Some(PropName::Ident(prop_name)) => {
matches!(
&*prop_name.sym,
"componentId" | "displayName"
)
}
_ => false,
}
}
_ => false,
})
}
_ => false,
}
})
.unwrap_or(false)
)
}
_ => false,
};
if !is_styled {
return;
}
debug!("Found styled component");
let _tracing = if cfg!(debug_assertions) {
Some(span!(Level::ERROR, "display_name_and_id").entered())
} else {
None
};
let display_name = self
.config
.display_name
.then(|| self.get_display_name(expr));
trace!("display_name: {:?}", display_name);
let component_id = self.config.ssr.then(|| self.get_component_id().into());
trace!("component_id: {:?}", display_name);
self.add_config(
expr,
display_name.map(|s| DISPLAY_NAME_REGEX.replace_all(&s, "").into()),
component_id,
)
}
fn visit_mut_key_value_prop(&mut self, e: &mut KeyValueProp) {
let old = self.cur_display_name.take();
if let PropName::Ident(name) = &e.key {
self.cur_display_name = Some(name.sym.clone());
}
e.visit_mut_children_with(self);
self.cur_display_name = old;
}
fn visit_mut_var_declarator(&mut self, v: &mut VarDeclarator) {
let old = self.cur_display_name.take();
if let Pat::Ident(name) = &v.name {
self.cur_display_name = Some(name.id.sym.clone());
}
v.visit_mut_children_with(self);
self.cur_display_name = old;
}
}
fn get_callee(e: &Expr) -> Option<&Expr> {
match e {
Expr::Call(CallExpr {
callee: Callee::Expr(callee),
..
}) => Some(callee),
_ => None,
}
}
fn get_property_as_ident(e: &Expr) -> Option<&JsWord> {
if let Expr::Member(MemberExpr {
prop: MemberProp::Ident(p),
..
}) = e
{
return Some(&p.sym);
}
None
}
fn already_has(obj: &ObjectLit) -> bool {
obj.props
.iter()
.filter_map(|v| match v {
PropOrSpread::Prop(p) => Some(p),
_ => None,
})
.filter_map(|v| get_prop_name(v))
.any(|prop| match prop {
PropName::Ident(ident) => &*ident.sym == "componentId" || &*ident.sym == "displayName",
_ => false,
})
}
fn get_existing_config<F>(e: &mut Expr, op: F)
where
F: FnOnce(&mut Expr),
{
if let Expr::Call(CallExpr {
callee: Callee::Expr(callee),
..
}) = e
{
if let Expr::Call(CallExpr {
callee: Callee::Expr(callee_callee),
..
}) = &mut **callee
{
if let Expr::Member(MemberExpr {
prop: MemberProp::Ident(prop),
..
}) = &**callee_callee
{
if &*prop.sym == "withConfig" {
return op(callee);
}
}
if let Expr::Member(MemberExpr {
obj,
prop: MemberProp::Ident(..),
..
}) = &mut **callee_callee
{
if let Expr::Call(CallExpr {
callee: Callee::Expr(callee),
..
}) = &**obj
{
if let Expr::Member(MemberExpr {
prop: MemberProp::Ident(prop),
..
}) = &**callee
{
if &*prop.sym == "withConfig" {
op(obj)
}
}
}
}
}
}
}

View file

@ -1,4 +0,0 @@
pub mod assign_style_required;
pub mod display_name_and_id;
pub mod minify;
pub mod transpile_css_prop;

View file

@ -1,2 +0,0 @@
mod top_level_binding_collector;
pub mod transpile;

View file

@ -1,93 +0,0 @@
use swc_core::{
common::collections::AHashSet,
ecma::ast::{
ArrowExpr, ClassDecl, FnDecl, Function, Id, ImportDefaultSpecifier, ImportNamedSpecifier,
ImportStarAsSpecifier, ObjectPatProp, Pat, VarDeclarator,
},
ecma::visit::{noop_visit_type, Visit, VisitWith},
};
// Modified from swc_ecma_utils/src/lib.rs:BindingCollector.
pub struct TopLevelBindingCollector {
bindings: AHashSet<Id>,
in_pat_decl: bool,
}
impl TopLevelBindingCollector {
fn add(&mut self, i: &Id) {
self.bindings.insert(i.clone());
}
}
impl Visit for TopLevelBindingCollector {
noop_visit_type!();
fn visit_class_decl(&mut self, node: &ClassDecl) {
self.add(&node.ident.to_id());
}
fn visit_fn_decl(&mut self, node: &FnDecl) {
self.add(&node.ident.to_id());
}
fn visit_pat(&mut self, node: &Pat) {
if !self.in_pat_decl {
return;
}
match node {
Pat::Ident(i) => self.add(&i.id.to_id()),
Pat::Object(o) => {
for prop in o.props.iter() {
match prop {
ObjectPatProp::Assign(a) => self.add(&a.key.to_id()),
ObjectPatProp::KeyValue(k) => k.value.visit_with(self),
ObjectPatProp::Rest(_) => {}
}
}
}
Pat::Array(a) => {
for elem in a.elems.iter() {
elem.visit_with(self);
}
}
Pat::Assign(a) => {
a.left.visit_with(self);
}
_ => {}
}
}
fn visit_arrow_expr(&mut self, _: &ArrowExpr) {}
fn visit_function(&mut self, _: &Function) {}
fn visit_import_default_specifier(&mut self, node: &ImportDefaultSpecifier) {
self.add(&node.local.to_id());
}
fn visit_import_named_specifier(&mut self, node: &ImportNamedSpecifier) {
self.add(&node.local.to_id());
}
fn visit_import_star_as_specifier(&mut self, node: &ImportStarAsSpecifier) {
self.add(&node.local.to_id());
}
fn visit_var_declarator(&mut self, node: &VarDeclarator) {
let old = self.in_pat_decl;
self.in_pat_decl = true;
node.name.visit_with(self);
self.in_pat_decl = old;
}
}
pub fn collect_top_level_decls<N>(n: &N) -> AHashSet<Id>
where
N: VisitWith<TopLevelBindingCollector>,
{
let mut v = TopLevelBindingCollector {
bindings: Default::default(),
in_pat_decl: false,
};
n.visit_with(&mut v);
v.bindings
}

View file

@ -1,653 +0,0 @@
//! Port of https://github.com/styled-components/babel-plugin-styled-components/blob/a20c3033508677695953e7a434de4746168eeb4e/src/visitors/transpileCssProp.js
use std::cell::RefCell;
use std::rc::Rc;
use std::{borrow::Cow, collections::HashMap};
use crate::State;
use inflector::Inflector;
use once_cell::sync::Lazy;
use regex::Regex;
use swc_core::{
common::{
collections::{AHashMap, AHashSet},
util::take::Take,
Spanned, DUMMY_SP,
},
ecma::ast::*,
ecma::atoms::{js_word, JsWord},
ecma::utils::{prepend_stmt, private_ident, quote_ident, ExprFactory},
ecma::visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith},
};
use crate::utils::{get_prop_key_as_expr, get_prop_name, get_prop_name2};
use super::top_level_binding_collector::collect_top_level_decls;
static TAG_NAME_REGEX: Lazy<Regex> =
Lazy::new(|| Regex::new("^[a-z][a-z\\d]*(\\-[a-z][a-z\\d]*)?$").unwrap());
pub fn transpile_css_prop(state: Rc<RefCell<State>>) -> impl Fold + VisitMut {
as_folder(TranspileCssProp {
state,
..Default::default()
})
}
#[derive(Default)]
struct TranspileCssProp {
state: Rc<RefCell<State>>,
import_name: Option<Ident>,
injected_nodes: Vec<Stmt>,
interleaved_injections: AHashMap<Id, Vec<Stmt>>,
identifier_idx: usize,
styled_idx: HashMap<JsWord, usize>,
top_level_decls: Option<AHashSet<Id>>,
}
impl TranspileCssProp {
fn next_styled_idx(&mut self, key: JsWord) -> usize {
let idx = self.styled_idx.entry(key).or_insert(0);
*idx += 1;
*idx
}
#[allow(clippy::wrong_self_convention)]
fn is_top_level_ident(&mut self, ident: &Ident) -> bool {
self.top_level_decls
.as_ref()
.map(|decls| decls.contains(&ident.to_id()))
.unwrap_or(false)
}
}
impl VisitMut for TranspileCssProp {
noop_visit_mut_type!();
fn visit_mut_jsx_element(&mut self, elem: &mut JSXElement) {
elem.visit_mut_children_with(self);
let mut extra_attrs = vec![];
for attr in elem.opening.attrs.iter_mut() {
match &mut *attr {
JSXAttrOrSpread::JSXAttr(attr) => {
if !matches!(&attr.name, JSXAttrName::Ident(i) if &*i.sym == "css") {
continue;
}
let import_name = if let Some(ident) = self
.state
.borrow()
.import_local_name("default", None)
.map(Ident::from)
{
ident
} else {
self.import_name
.get_or_insert_with(|| private_ident!("_styled"))
.clone()
};
let name = get_name_ident(&elem.opening.name);
let id_sym = name.sym.to_class_case();
// Match the original plugin's behavior.
let id_sym = id_sym.trim_end_matches(char::is_numeric);
let id_sym = JsWord::from(id_sym);
let styled_idx = self.next_styled_idx(id_sym.clone());
let id = quote_ident!(
elem.opening.name.span(),
append_if_gt_one(&format!("_Styled{}", id_sym), styled_idx)
);
let (styled, inject_after) = if TAG_NAME_REGEX.is_match(&name.sym) {
(
(Expr::Call(CallExpr {
span: DUMMY_SP,
callee: import_name.as_callee(),
args: vec![Lit::Str(Str {
span: DUMMY_SP,
value: name.sym,
raw: None,
})
.as_arg()],
type_args: Default::default(),
})),
None::<Ident>,
)
} else {
let name_expr = get_name_expr(&elem.opening.name);
(
Expr::Call(CallExpr {
span: DUMMY_SP,
callee: import_name.as_callee(),
args: vec![name_expr.as_arg()],
type_args: Default::default(),
}),
if self.is_top_level_ident(&name) {
Some(name)
} else {
None
},
)
};
let mut css = match &mut attr.value {
Some(css) => {
//
match css {
JSXAttrValue::Lit(Lit::Str(v)) => Expr::Tpl(Tpl {
span: DUMMY_SP,
exprs: Default::default(),
quasis: vec![TplElement {
span: DUMMY_SP,
tail: true,
cooked: None,
raw: (&*v.value).into(),
}],
}),
JSXAttrValue::JSXExprContainer(JSXExprContainer {
expr: JSXExpr::Expr(v),
..
}) => match &mut **v {
Expr::Tpl(..) => *v.take(),
Expr::TaggedTpl(v)
if match &*v.tag {
Expr::Ident(i) => &*i.sym == "css",
_ => false,
} =>
{
Expr::Tpl(v.tpl.take())
}
Expr::Object(..) => *v.take(),
_ => Expr::Tpl(Tpl {
span: DUMMY_SP,
exprs: vec![v.take()],
quasis: vec![
TplElement {
span: DUMMY_SP,
tail: false,
cooked: None,
raw: "".into(),
},
TplElement {
span: DUMMY_SP,
tail: true,
cooked: None,
raw: "".into(),
},
],
}),
},
_ => continue,
}
}
None => continue,
};
// Remove this attribute
attr.name = JSXAttrName::Ident(Take::dummy());
elem.opening.name = JSXElementName::Ident(id.clone());
if let Some(closing) = &mut elem.closing {
closing.name = JSXElementName::Ident(id.clone());
}
// object syntax
if let Expr::Object(css_obj) = &mut css {
// Original plugin says
//
//
// for objects as CSS props, we have to recurse through the object and
// replace any object key/value scope references with generated props
// similar to how the template literal transform above creates dynamic
// interpolations
let p = quote_ident!("p");
let mut reducer = PropertyReducer {
p: p.clone(),
replace_object_with_prop_function: false,
extra_attrs: Default::default(),
identifier_idx: &mut self.identifier_idx,
};
css_obj.props = css_obj
.props
.take()
.into_iter()
.fold(vec![], |acc, property| {
reducer.reduce_object_properties(acc, property)
});
extra_attrs.extend(reducer.extra_attrs);
if reducer.replace_object_with_prop_function {
css = Expr::Arrow(ArrowExpr {
span: DUMMY_SP,
params: vec![Pat::Ident(p.clone().into())],
body: BlockStmtOrExpr::Expr(Box::new(css.take())),
is_async: false,
is_generator: false,
type_params: Default::default(),
return_type: Default::default(),
});
}
} else {
// tagged template literal
let mut tpl = css.expect_tpl();
tpl.exprs =
tpl.exprs
.take()
.into_iter()
.fold(vec![], |mut acc, mut expr| {
if expr.is_fn_expr() || expr.is_arrow() {
acc.push(expr);
return acc;
} else if let Some(root) = trace_root_value(&mut expr) {
let direct_access = match root {
Expr::Lit(_) => true,
Expr::Ident(id) if self.is_top_level_ident(id) => true,
_ => false,
};
if direct_access {
acc.push(expr);
return acc;
}
}
let identifier =
get_local_identifier(&mut self.identifier_idx, &expr);
let p = quote_ident!("p");
extra_attrs.push(JSXAttrOrSpread::JSXAttr(JSXAttr {
span: DUMMY_SP,
name: JSXAttrName::Ident(identifier.clone()),
value: Some(JSXAttrValue::JSXExprContainer(
JSXExprContainer {
span: DUMMY_SP,
expr: JSXExpr::Expr(expr.take()),
},
)),
}));
acc.push(Box::new(Expr::Arrow(ArrowExpr {
span: DUMMY_SP,
params: vec![Pat::Ident(p.clone().into())],
body: BlockStmtOrExpr::Expr(Box::new(
p.make_member(identifier),
)),
is_async: false,
is_generator: false,
type_params: Default::default(),
return_type: Default::default(),
})));
acc
});
css = Expr::Tpl(tpl);
}
let var = VarDeclarator {
span: DUMMY_SP,
name: Pat::Ident(id.clone().into()),
init: Some(match css {
Expr::Object(..) | Expr::Arrow(..) => Box::new(Expr::Call(CallExpr {
span: DUMMY_SP,
callee: styled.as_callee(),
args: vec![css.as_arg()],
type_args: Default::default(),
})),
_ => Box::new(Expr::TaggedTpl(TaggedTpl {
span: DUMMY_SP,
tag: Box::new(styled),
type_params: Default::default(),
tpl: css.expect_tpl(),
})),
}),
definite: false,
};
let stmt = Stmt::Decl(Decl::Var(Box::new(VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Var,
declare: false,
decls: vec![var],
})));
match inject_after {
Some(injector) => {
let id = injector.to_id();
self.interleaved_injections
.entry(id)
.or_default()
.push(stmt);
}
None => {
self.injected_nodes.push(stmt);
}
}
}
JSXAttrOrSpread::SpreadElement(_) => {}
}
}
elem.opening.attrs.retain(|attr| {
match attr {
JSXAttrOrSpread::JSXAttr(attr) => {
if matches!(
attr.name,
JSXAttrName::Ident(Ident {
sym: js_word!(""),
..
})
) {
return false;
}
}
JSXAttrOrSpread::SpreadElement(_) => {}
}
true
});
elem.opening.attrs.extend(extra_attrs);
}
fn visit_mut_module(&mut self, n: &mut Module) {
// TODO: Skip if there are no css prop usage
self.top_level_decls = Some(collect_top_level_decls(n));
n.visit_mut_children_with(self);
self.top_level_decls = None;
if let Some(import_name) = self.import_name.take() {
self.state.borrow_mut().set_import_name(import_name.to_id());
let specifier = ImportSpecifier::Default(ImportDefaultSpecifier {
span: DUMMY_SP,
local: import_name,
});
prepend_stmt(
&mut n.body,
ModuleItem::ModuleDecl(ModuleDecl::Import(ImportDecl {
span: DUMMY_SP,
specifiers: vec![specifier],
src: Box::new(Str {
span: DUMMY_SP,
value: "styled-components".into(),
raw: None,
}),
type_only: Default::default(),
asserts: Default::default(),
})),
);
}
let mut serialized_body: Vec<ModuleItem> = vec![];
let body = std::mem::take(&mut n.body);
for item in body {
serialized_body.push(item.clone());
if let ModuleItem::Stmt(Stmt::Decl(Decl::Var(vd))) = &item {
for decl in &vd.decls {
if let Pat::Ident(ident) = &decl.name {
let id = ident.to_id();
let stmts = self.interleaved_injections.remove(&id);
if let Some(stmts) = stmts {
serialized_body.extend(stmts.into_iter().rev().map(ModuleItem::Stmt));
}
}
}
}
}
n.body = serialized_body;
let mut remaining = std::mem::take(&mut self.interleaved_injections)
.into_iter()
.collect::<Vec<_>>();
remaining.sort_by_key(|x| x.0.clone());
remaining
.into_iter()
.for_each(|(_, stmts)| n.body.extend(stmts.into_iter().map(ModuleItem::Stmt)));
n.body
.extend(self.injected_nodes.take().into_iter().map(ModuleItem::Stmt));
}
}
fn get_name_expr(name: &JSXElementName) -> Box<Expr> {
fn get_name_expr_jsx_object(name: &JSXObject) -> Box<Expr> {
match name {
JSXObject::Ident(n) => Box::new(Expr::Ident(n.clone())),
JSXObject::JSXMemberExpr(n) => Box::new(Expr::Member(MemberExpr {
span: DUMMY_SP,
obj: get_name_expr_jsx_object(&n.obj),
prop: MemberProp::Ident(n.prop.clone()),
})),
}
}
match name {
JSXElementName::Ident(n) => Box::new(Expr::Ident(n.clone())),
JSXElementName::JSXMemberExpr(n) => Box::new(Expr::Member(MemberExpr {
span: DUMMY_SP,
obj: get_name_expr_jsx_object(&n.obj),
prop: MemberProp::Ident(n.prop.clone()),
})),
JSXElementName::JSXNamespacedName(..) => {
unimplemented!("get_name_expr for JSXNamespacedName")
}
}
}
struct PropertyReducer<'a> {
p: Ident,
replace_object_with_prop_function: bool,
extra_attrs: Vec<JSXAttrOrSpread>,
identifier_idx: &'a mut usize,
}
impl PropertyReducer<'_> {
fn reduce_object_properties(
&mut self,
mut acc: Vec<PropOrSpread>,
mut property: PropOrSpread,
) -> Vec<PropOrSpread> {
match property {
PropOrSpread::Spread(ref mut prop) => {
// handle spread variables and such
if let Expr::Object(arg) = &mut *prop.expr {
arg.props = arg
.props
.take()
.into_iter()
.fold(vec![], |acc, p| self.reduce_object_properties(acc, p));
} else {
self.replace_object_with_prop_function = true;
let identifier = get_local_identifier(self.identifier_idx, &prop.expr);
self.extra_attrs.push(JSXAttrOrSpread::JSXAttr(JSXAttr {
span: DUMMY_SP,
name: JSXAttrName::Ident(identifier.clone()),
value: Some(JSXAttrValue::JSXExprContainer(JSXExprContainer {
span: DUMMY_SP,
expr: JSXExpr::Expr(prop.expr.take()),
})),
}));
prop.expr = Box::new(self.p.clone().make_member(identifier));
}
acc.push(property);
}
PropOrSpread::Prop(ref mut prop) => {
let key = get_prop_key_as_expr(prop);
let key_pn = get_prop_name(prop);
if key.is_member()
|| key.is_call()
|| (key.is_ident()
&& key_pn.is_some()
&& key_pn.unwrap().is_computed()
&& !matches!(&**prop, Prop::Shorthand(..)))
{
self.replace_object_with_prop_function = true;
let identifier = get_local_identifier(self.identifier_idx, &key);
self.extra_attrs.push(JSXAttrOrSpread::JSXAttr(JSXAttr {
span: DUMMY_SP,
name: identifier.clone().into(),
value: Some(JSXAttrValue::JSXExprContainer(JSXExprContainer {
span: DUMMY_SP,
// TODO: Perf
expr: JSXExpr::Expr(Box::new(key.clone().into_owned())),
})),
}));
set_key_of_prop(prop, Box::new(self.p.clone().make_member(identifier)));
}
let mut value = take_prop_value(prop);
if let Expr::Object(value_obj) = &mut *value {
value_obj.props = value_obj
.props
.take()
.into_iter()
.fold(vec![], |acc, p| self.reduce_object_properties(acc, p));
set_value_of_prop(prop, value);
acc.push(property);
} else if !matches!(&*value, Expr::Lit(..)) {
// if a non-primitive value we have to interpolate it
self.replace_object_with_prop_function = true;
let identifier = get_local_identifier(self.identifier_idx, &value);
self.extra_attrs.push(JSXAttrOrSpread::JSXAttr(JSXAttr {
span: DUMMY_SP,
name: JSXAttrName::Ident(identifier.clone()),
value: Some(JSXAttrValue::JSXExprContainer(JSXExprContainer {
span: DUMMY_SP,
expr: JSXExpr::Expr(value.take()),
})),
}));
let key = get_prop_name2(prop);
acc.push(PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
key,
value: Box::new(self.p.clone().make_member(identifier)),
}))));
} else {
set_value_of_prop(prop, value);
acc.push(property);
}
}
}
acc
}
}
fn set_value_of_prop(prop: &mut Prop, value: Box<Expr>) {
match prop {
Prop::Shorthand(p) => {
*prop = Prop::KeyValue(KeyValueProp {
key: PropName::Ident(p.clone()),
value,
});
}
Prop::KeyValue(p) => {
p.value = value;
}
Prop::Assign(..) => unreachable!("assign property is not allowed for object literals"),
Prop::Getter(_p) => todo!(),
Prop::Setter(_p) => todo!(),
Prop::Method(_p) => todo!(),
}
}
fn take_prop_value(prop: &mut Prop) -> Box<Expr> {
match prop {
Prop::Shorthand(p) => Box::new(Expr::Ident(p.clone())),
Prop::KeyValue(p) => p.value.take(),
Prop::Assign(..) => unreachable!("assign property is not allowed for object literals"),
Prop::Getter(_p) => todo!(),
Prop::Setter(_p) => todo!(),
Prop::Method(_p) => todo!(),
}
}
fn set_key_of_prop(prop: &mut Prop, key: Box<Expr>) {
let value = take_prop_value(prop);
*prop = Prop::KeyValue(KeyValueProp {
key: PropName::Computed(ComputedPropName {
span: DUMMY_SP,
expr: key,
}),
value,
});
}
fn get_local_identifier(idx: &mut usize, expr: &Expr) -> Ident {
*idx += 1;
let identifier = quote_ident!(expr.span(), append_if_gt_one("$_css", *idx));
// TODO: Unique identifier
identifier
}
fn append_if_gt_one(s: &str, suffix: usize) -> Cow<str> {
if suffix > 1 {
Cow::Owned(format!("{}{}", s, suffix))
} else {
Cow::Borrowed(s)
}
}
fn get_name_ident(el: &JSXElementName) -> Ident {
match el {
JSXElementName::Ident(v) => v.clone(),
JSXElementName::JSXMemberExpr(e) => Ident {
sym: format!("{}_{}", get_name_of_jsx_obj(&e.obj), e.prop.sym).into(),
span: e.prop.span,
optional: false,
},
_ => {
unimplemented!("get_name_ident for namespaced jsx element")
}
}
}
fn get_name_of_jsx_obj(el: &JSXObject) -> JsWord {
match el {
JSXObject::Ident(v) => v.sym.clone(),
JSXObject::JSXMemberExpr(e) => {
format!("{}{}", get_name_of_jsx_obj(&e.obj), e.prop.sym).into()
}
}
}
fn trace_root_value(e: &mut Expr) -> Option<&mut Expr> {
match e {
Expr::Member(e) => trace_root_value(&mut e.obj),
Expr::Call(e) => match &mut e.callee {
Callee::Expr(e) => trace_root_value(e),
_ => None,
},
Expr::Ident(_) => Some(e),
Expr::Lit(_) => Some(e),
_ => None,
}
}

View file

@ -1,37 +0,0 @@
#![deny(unused)]
use std::{fs::read_to_string, path::PathBuf};
use styled_components::{styled_components, Config};
use swc_core::{
common::{chain, Mark},
ecma::parser::{EsConfig, Syntax},
ecma::transforms::base::resolver,
ecma::transforms::testing::test_fixture,
};
#[testing::fixture("tests/fixtures/**/code.js")]
fn fixture(input: PathBuf) {
let dir = input.parent().unwrap();
let config = read_to_string(dir.join("config.json")).expect("failed to read config.json");
println!("---- Config -----\n{}", config);
let config: Config = serde_json::from_str(&config).unwrap();
test_fixture(
Syntax::Es(EsConfig {
jsx: true,
..Default::default()
}),
&|t| {
//
let fm = t.cm.load_file(&input).unwrap();
chain!(
resolver(Mark::new(), Mark::new(), false),
styled_components(fm.name.clone(), fm.src_hash, config.clone())
)
},
&input,
&dir.join("output.js"),
Default::default(),
)
}

View file

@ -1,12 +0,0 @@
{
"plugins": [
[
"../../../src",
{
"ssr": false,
"fileName": false,
"transpileTemplateLiterals": false
}
]
]
}

View file

@ -1,4 +0,0 @@
import styled from 'styled-components'
const WithAttrs = styled.div.attrs({ some: 'value' })``
const WithAttrsWrapped = styled(Inner).attrs({ some: 'value' })``

View file

@ -1,11 +0,0 @@
import styled from 'styled-components';
const WithAttrs = styled.div.attrs({
some: 'value'
}).withConfig({
displayName: "WithAttrs"
})``;
const WithAttrsWrapped = styled(Inner).attrs({
some: 'value'
}).withConfig({
displayName: "WithAttrsWrapped"
})``;

View file

@ -1,10 +0,0 @@
{
"plugins": [
[
"../../../src",
{
"pure": true
}
]
]
}

View file

@ -1,7 +0,0 @@
import { createGlobalStyle } from 'styled-components'
const GlobalStyle = createGlobalStyle`
body {
color: red;
}
`

View file

@ -1,2 +0,0 @@
import { createGlobalStyle } from 'styled-components';
const GlobalStyle = /*#__PURE__*/createGlobalStyle(["body{color:red;}"]);

View file

@ -1,10 +0,0 @@
{
"plugins": [
[
"../../../src",
{
"pure": true
}
]
]
}

View file

@ -1,10 +0,0 @@
import styled, { css } from 'styled-components'
const partial = css`
color: red;
`
const Component = styled.div`
${partial};
background: blue;
`

View file

@ -1,6 +0,0 @@
import styled, { css } from 'styled-components';
const partial = /*#__PURE__*/css(["color:red;"]);
const Component = /*#__PURE__*/styled.div.withConfig({
displayName: "code__Component",
componentId: "sc-4wpzk3-0"
})(["", ";background:blue;"], partial);

View file

@ -1,10 +0,0 @@
{
"plugins": [
[
"../../../src",
{
"pure": true
}
]
]
}

View file

@ -1,11 +0,0 @@
import { keyframes } from 'styled-components'
const Animation = keyframes`
0% {
opacity: 0;
}
100% {
opacity: 1;
}
`

View file

@ -1,2 +0,0 @@
import { keyframes } from 'styled-components';
const Animation = /*#__PURE__*/keyframes(["0%{opacity:0;}100%{opacity:1;}"]);

View file

@ -1,10 +0,0 @@
{
"plugins": [
[
"../../../src",
{
"pure": true
}
]
]
}

View file

@ -1,14 +0,0 @@
import styled from 'styled-components'
const Test = styled.div`
width: 100%;
`
const Test2 = styled('div')``
const Test3 = true ? styled.div`` : styled.div``
const styles = { One: styled.div`` }
let Component
Component = styled.div``
const WrappedComponent = styled(Inner)``
const StyledObjectForm = styled.div({ color: red })
const StyledFunctionForm = styled.div(p => ({ color: p.color || 'red' }))
const normalFunc = add(5, 3)

View file

@ -1,44 +0,0 @@
import styled from 'styled-components';
const Test = /*#__PURE__*/styled.div.withConfig({
displayName: "code__Test",
componentId: "sc-u20i28-0"
})(["width:100%;"]);
const Test2 = /*#__PURE__*/styled('div').withConfig({
displayName: "code__Test2",
componentId: "sc-u20i28-1"
})([""]);
const Test3 = true ? styled.div.withConfig({
displayName: "code__Test3",
componentId: "sc-u20i28-2"
})([""]) : styled.div.withConfig({
displayName: "code__Test3",
componentId: "sc-u20i28-3"
})([""]);
const styles = {
One: styled.div.withConfig({
displayName: "code__One",
componentId: "sc-u20i28-4"
})([""])
};
let Component;
Component = styled.div.withConfig({
displayName: "code__Component",
componentId: "sc-u20i28-5"
})([""]);
const WrappedComponent = /*#__PURE__*/styled(Inner).withConfig({
displayName: "code__WrappedComponent",
componentId: "sc-u20i28-6"
})([""]);
const StyledObjectForm = /*#__PURE__*/styled.div.withConfig({
displayName: "code__StyledObjectForm",
componentId: "sc-u20i28-7"
})({
color: red
});
const StyledFunctionForm = /*#__PURE__*/styled.div.withConfig({
displayName: "code__StyledFunctionForm",
componentId: "sc-u20i28-8"
})(p => ({
color: p.color || 'red'
}));
const normalFunc = add(5, 3);

View file

@ -1,10 +0,0 @@
{
"plugins": [
[
"../../../src",
{
"pure": true
}
]
]
}

View file

@ -1,3 +0,0 @@
import { withTheme } from 'styled-components'
const ThemedComponent = withTheme(() => null)

View file

@ -1,2 +0,0 @@
import { withTheme } from 'styled-components';
const ThemedComponent = /*#__PURE__*/withTheme(() => null);

View file

@ -1,7 +0,0 @@
{
"plugins": [
[
"../../../src"
]
]
}

View file

@ -1,10 +0,0 @@
import React from "react"
import { css } from "styled-components"
export default function Example() {
return <div css={someCss}>oops</div>
}
const someCss = css`
color: red;
`

View file

@ -1,12 +0,0 @@
import _styled from "styled-components";
import React from "react";
import { css } from "styled-components";
export default function Example() {
return <_StyledDiv>oops</_StyledDiv>;
}
const someCss = css(["color:red;"]);
var _StyledDiv = _styled("div").withConfig({
displayName: "code___StyledDiv",
componentId: "sc-7mydya-0"
})(["", ""], someCss);

View file

@ -1,12 +0,0 @@
{
"plugins": [
[
"../../../src",
{
"ssr": false,
"displayName": false,
"transpileTemplateLiterals": false
}
]
]
}

View file

@ -1,17 +0,0 @@
import { createGlobalStyle, css, keyframes } from 'styled-components'
const key = keyframes`
to {
transform: rotate(360deg);
}
`
const color = css`
color: ${theColor};
`
const GlobalStyles = createGlobalStyle`
html {
color: red;
}
`

View file

@ -1,4 +0,0 @@
import { createGlobalStyle, css, keyframes } from 'styled-components';
const key = keyframes`to{transform:rotate(360deg);}`;
const color = css`color:${theColor};`;
const GlobalStyles = createGlobalStyle`html{color:red;}`;

View file

@ -1,15 +0,0 @@
{
"presets": [
"@babel/preset-env"
],
"plugins": [
[
"../../../src",
{
"ssr": false,
"displayName": false,
"transpileTemplateLiterals": false
}
]
]
}

View file

@ -1,24 +0,0 @@
import styled from 'styled-components';
const Simple = styled.div`
width: 100%;
`;
const Interpolation = styled.div`
content: " ${props => props.text} ";
`;
const SpecialCharacters = styled.div`
content: " ${props => props.text} ";\n color: red;
`;
const Comment = styled.div`
// comment
color: red;
`
const Parens = styled.div`
&:hover {
color: blue;
}
`;

View file

@ -1,23 +0,0 @@
"use strict";
var _styledComponents = _interopRequireDefault(require("styled-components"));
var _templateObject, _templateObject2, _templateObject3, _templateObject4, _templateObject5;
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
function _taggedTemplateLiteral(strings, raw) { if (!raw) { raw = strings.slice(0); } return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); }
var Simple = _styledComponents["default"].div(_templateObject || (_templateObject = _taggedTemplateLiteral(["width:100%;"])));
var Interpolation = _styledComponents["default"].div(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["content:\" ", " \";"])), function (props) {
return props.text;
});
var SpecialCharacters = _styledComponents["default"].div(_templateObject3 || (_templateObject3 = _taggedTemplateLiteral(["content:\" ", " \";color:red;"])), function (props) {
return props.text;
});
var Comment = _styledComponents["default"].div(_templateObject4 || (_templateObject4 = _taggedTemplateLiteral(["color:red;"])));
var Parens = _styledComponents["default"].div(_templateObject5 || (_templateObject5 = _taggedTemplateLiteral(["&:hover{color:blue;}"])));

View file

@ -1,12 +0,0 @@
{
"plugins": [
[
"../../../src",
{
"ssr": false,
"displayName": false,
"transpileTemplateLiterals": false
}
]
]
}

View file

@ -1,36 +0,0 @@
import styled from 'styled-components';
const Simple = styled.div`
width: 100%;
`;
const Interpolation = styled.div`
content: "https://test.com/${props => props.endpoint}";
`;
const SpecialCharacters = styled.div`
content: " ${props => props.text} ";\n color: red;
`;
const Comment = styled.div`
width: 100%;
// comment
color: red;
`;
const Parens = styled.div`
&:hover {
color: blue;
}
color: red;
`;
const UrlComments = styled.div`
color: red;
/* // */
background: red;
/* comment 1 */
/* comment 2 */
// comment 3
border: 1px solid green;
`;

View file

@ -1,7 +0,0 @@
import styled from 'styled-components';
const Simple = styled.div`width:100%;`;
const Interpolation = styled.div`content:"https://test.com/${props => props.endpoint}";`;
const SpecialCharacters = styled.div`content:" ${props => props.text} ";color:red;`;
const Comment = styled.div`width:100%;color:red;`;
const Parens = styled.div`&:hover{color:blue;}color:red;`;
const UrlComments = styled.div`color:red;background:red;border:1px solid green;`;

View file

@ -1,10 +0,0 @@
{
"plugins": [
[
"../../../src",
{
"minify": true
}
]
]
}

View file

@ -1,40 +0,0 @@
import styled from 'styled-components'
const Test1 = styled.div`
width: 100%;
// color: ${'red'};
`
const Test2 = styled.div`
width: 100%;
// color: pale${'red'};
`
const Test3 = styled.div`
width: 100%;
// color
${'red'};
`
const Test4 = styled.div`
width: 100%;
// color: ${'red'}-blue;
`
const Test5 = styled.div`
width: 100%;
// color: ${'red'}${'blue'};
`
const Test6 = styled.div`
background: url("https://google.com");
width: 100%;
${'green'} // color: ${'red'}${'blue'};
`
const Test7 = styled.div`
background: url("https://google.com");
width: ${p => p.props.width};
${'green'} // color: ${'red'}${'blue'};
height: ${p => p.props.height};
`

View file

@ -1,29 +0,0 @@
import styled from 'styled-components';
const Test1 = styled.div.withConfig({
displayName: "code__Test1",
componentId: "sc-kc0mjf-0"
})(["width:100%;"]);
const Test2 = styled.div.withConfig({
displayName: "code__Test2",
componentId: "sc-kc0mjf-1"
})(["width:100%;"]);
const Test3 = styled.div.withConfig({
displayName: "code__Test3",
componentId: "sc-kc0mjf-2"
})(["width:100%;", ";"], 'red');
const Test4 = styled.div.withConfig({
displayName: "code__Test4",
componentId: "sc-kc0mjf-3"
})(["width:100%;"]);
const Test5 = styled.div.withConfig({
displayName: "code__Test5",
componentId: "sc-kc0mjf-4"
})(["width:100%;"]);
const Test6 = styled.div.withConfig({
displayName: "code__Test6",
componentId: "sc-kc0mjf-5"
})(["background:url(\"https://google.com\");width:100%;", " "], 'green');
const Test7 = styled.div.withConfig({
displayName: "code__Test7",
componentId: "sc-kc0mjf-6"
})(["background:url(\"https://google.com\");width:", ";", " height:", ";"], p => p.props.width, 'green', p => p.props.height);

View file

@ -1,8 +0,0 @@
{
"plugins": [
["@babel/plugin-transform-modules-commonjs"],
[
"../../../src"
]
]
}

View file

@ -1,11 +0,0 @@
import React from "react";
import { css } from "styled-components";
import Icons from "./icons";
const someCss = css` background: purple;`;
const App1 = () => { return <Icons css={someCss} />; };
const App2 = () => { return <Icons.Foo css={someCss} />; };
const App3 = () => { return <Icons.Foo.Bar css={someCss} />; };

View file

@ -1,42 +0,0 @@
"use strict";
var _styledComponents = _interopRequireWildcard(require("styled-components"));
var _react = _interopRequireDefault(require("react"));
var _icons = _interopRequireDefault(require("./icons"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
const someCss = (0, _styledComponents.css)([" background:purple;"]);
const App1 = () => {
return <_StyledIcons />;
};
const App2 = () => {
return <_StyledIconsFoo />;
};
const App3 = () => {
return <_StyledIconsFooBar />;
};
var _StyledIcons = (0, _styledComponents.default)(_icons.default).withConfig({
displayName: "code___StyledIcons",
componentId: "sc-1wxehft-0"
})(["", ""], someCss);
var _StyledIconsFoo = (0, _styledComponents.default)(_icons.default.Foo).withConfig({
displayName: "code___StyledIconsFoo",
componentId: "sc-1wxehft-1"
})(["", ""], someCss);
var _StyledIconsFooBar = (0, _styledComponents.default)(_icons.default.Foo.Bar).withConfig({
displayName: "code___StyledIconsFooBar",
componentId: "sc-1wxehft-2"
})(["", ""], someCss);

View file

@ -1,14 +0,0 @@
{
"presets": ["@babel/preset-env"],
"plugins": [
[
"../../../src",
{
"ssr": false,
"displayName": true,
"transpileTemplateLiterals": true,
"minify": false
}
]
]
}

View file

@ -1,27 +0,0 @@
import styled, { css, createGlobalStyle } from 'styled-components'
const Named = styled.div`
width: 100%;
`
const NamedWithInterpolation = styled.div`
color: ${color => props.color};
`
const Wrapped = styled(Inner)`
color: red;
`
const Foo = styled.div({
color: 'green',
})
const style = css`
background: green;
`
const GlobalStyle = createGlobalStyle`
html {
background: silver;
}
`

View file

@ -1,32 +0,0 @@
"use strict";
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
var _styledComponents = _interopRequireWildcard(require("styled-components"));
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
var Named = _styledComponents["default"].div.withConfig({
displayName: "code__Named"
})(["\n width: 100%;\n"]);
var NamedWithInterpolation = _styledComponents["default"].div.withConfig({
displayName: "code__NamedWithInterpolation"
})(["\n color: ", ";\n"], function (color) {
return props.color;
});
var Wrapped = (0, _styledComponents["default"])(Inner).withConfig({
displayName: "code__Wrapped"
})(["\n color: red;\n"]);
var Foo = _styledComponents["default"].div.withConfig({
displayName: "code__Foo"
})({
color: 'green'
});
var style = (0, _styledComponents.css)(["\n background: green;\n"]);
var GlobalStyle = (0, _styledComponents.createGlobalStyle)(["\n html {\n background: silver;\n }\n"]);

View file

@ -1,16 +0,0 @@
{
"presets": [
"@babel/preset-env"
],
"plugins": [
[
"../../../src",
{
"ssr": false,
"displayName": false,
"transpileTemplateLiterals": true,
"minify": false
}
]
]
}

View file

@ -1,11 +0,0 @@
import styled from 'styled-components';
const Named = styled.div`
width: 100%;
`;
const NamedWithInterpolation = styled.div`
color: ${color => props.color};
`;
const Wrapped = styled(Inner)`color: red;`;

View file

@ -1,13 +0,0 @@
"use strict";
var _styledComponents = _interopRequireDefault(require("styled-components"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
var Named = _styledComponents["default"].div(["\n width: 100%;\n"]);
var NamedWithInterpolation = _styledComponents["default"].div(["\n color: ", ";\n"], function (color) {
return props.color;
});
var Wrapped = (0, _styledComponents["default"])(Inner)(["color: red;"]);

View file

@ -1,11 +0,0 @@
{
"plugins": [
[
"../../../src",
{
"transpileTemplateLiterals": false,
"ssr": true
}
]
]
}

View file

@ -1,2 +0,0 @@
const Test = s.div`width: 100%;`;
import { default as s, css } from 'styled-components';

View file

@ -1,5 +0,0 @@
const Test = s.div.withConfig({
displayName: "code__Test",
componentId: "sc-1dds9bl-0"
})`width:100%;`;
import { default as s, css } from 'styled-components';

View file

@ -1,15 +0,0 @@
import styled from 'styled-components'
const Test = styled.div`
width: 100%;
`
const Test2 = styled('div')``
const Test3 = true ? styled.div`` : styled.div``
const styles = { One: styled.div`` }
let Component
Component = styled.div``
const WrappedComponent = styled(Inner)``
class ClassComponent {
static Child = styled.div``
}
var GoodName = BadName = styled.div``;

View file

@ -1,5 +0,0 @@
{
"ssr": false,
"fileName": false,
"transpileTemplateLiterals": false
}

View file

@ -1,34 +0,0 @@
import styled from 'styled-components';
const Test = styled.div.withConfig({
displayName: "Test"
})`
width: 100%;
`;
const Test2 = styled('div').withConfig({
displayName: "Test2"
})``;
const Test3 = true ? styled.div.withConfig({
displayName: "Test3"
})`` : styled.div.withConfig({
displayName: "Test3"
})``;
const styles = {
One: styled.div.withConfig({
displayName: "One"
})``
};
let Component;
Component = styled.div.withConfig({
displayName: "Component"
})``;
const WrappedComponent = styled(Inner).withConfig({
displayName: "WrappedComponent"
})``;
class ClassComponent {
static Child = styled.div.withConfig({
displayName: "Child"
})``;
}
var GoodName = BadName = styled.div.withConfig({
displayName: "GoodName"
})``;

View file

@ -1,30 +0,0 @@
import styled from 'styled-components'
const Test = styled.div`
width: 100%;
`
const Test2 = true ? styled.div`` : styled.div``
const styles = { One: styled.div`` }
let Component
Component = styled.div``
const WrappedComponent = styled(Inner)``
const WrappedComponent2 = styled.div({})
const WrappedComponent3 = styled(Inner)({})
const WrappedComponent4 = styled(Inner).attrs(() => ({ something: 'else' }))({})
const WrappedComponent5 = styled.div.attrs(() => ({ something: 'else' }))({})
const WrappedComponent6 = styled.div.attrs(() => ({ something: 'else' }))``
const WrappedComponent7 = styled.div.withConfig({
shouldForwardProp: () => {},
})({})
const WrappedComponent8 = styled.div
.withConfig({
shouldForwardProp: () => {},
})
.attrs(() => ({ something: 'else' }))({})
const WrappedComponent9 = styled.div
.attrs(() => ({ something: 'else' }))
.withConfig({
shouldForwardProp: () => {},
})({})

View file

@ -1,5 +0,0 @@
{
"fileName": false,
"transpileTemplateLiterals": false,
"ssr": true
}

Some files were not shown because too many files have changed in this diff Show more