Add/styled jsx swc (#29005)

Co-authored-by: Tim Neutkens <tim@timneutkens.nl>
Co-authored-by: Tim Neutkens <timneutkens@me.com>
This commit is contained in:
Maia Teegarden 2021-09-15 00:24:31 -07:00 committed by GitHub
parent 2cfdc68650
commit 4a2d5a1b49
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
85 changed files with 3514 additions and 244 deletions

View file

@ -37,7 +37,7 @@ jobs:
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
profile: minimal profile: minimal
toolchain: nightly-2021-03-25 toolchain: nightly-2021-08-12
target: ${{ matrix.target }} target: ${{ matrix.target }}
- name: Cache cargo registry - name: Cache cargo registry
uses: actions/cache@v1 uses: actions/cache@v1
@ -54,7 +54,7 @@ jobs:
uses: actions/cache@v2 uses: actions/cache@v2
with: with:
path: packages/next/native/** path: packages/next/native/**
key: next-swc-nightly-2021-03-25-${{ matrix.target }}-${{ hashFiles('.github/workflows/build_native.yml', 'packages/next/build/swc/**') }} key: next-swc-nightly-2021-08-12-${{ matrix.target }}-${{ hashFiles('.github/workflows/build_native.yml', 'packages/next/build/swc/**') }}
- name: Cross build aarch64 setup - name: Cross build aarch64 setup
if: ${{ matrix.target == 'aarch64-apple-darwin' && steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} if: ${{ matrix.target == 'aarch64-apple-darwin' && steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }}
run: | run: |

View file

@ -471,7 +471,7 @@ jobs:
if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }}
with: with:
profile: minimal profile: minimal
toolchain: nightly-2021-03-25 toolchain: nightly-2021-08-12
target: ${{ matrix.target }} target: ${{ matrix.target }}
- name: Cache cargo registry - name: Cache cargo registry
uses: actions/cache@v1 uses: actions/cache@v1
@ -491,7 +491,7 @@ jobs:
if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }}
with: with:
path: packages/next/native/next-swc.*.node path: packages/next/native/next-swc.*.node
key: next-swc-nightly-2021-03-25-${{ matrix.target }}-${{ hashFiles('.github/workflows/build_test_deploy.yml', 'packages/next/build/swc/**') }} key: next-swc-nightly-2021-08-12-${{ matrix.target }}-${{ hashFiles('.github/workflows/build_test_deploy.yml', 'packages/next/build/swc/**') }}
- name: Cross build aarch64 setup - name: Cross build aarch64 setup
if: ${{ matrix.target == 'aarch64-apple-darwin' && steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} if: ${{ matrix.target == 'aarch64-apple-darwin' && steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }}
run: | run: |
@ -531,7 +531,7 @@ jobs:
if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }}
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
toolchain: nightly-2021-03-25 toolchain: nightly-2021-08-12
profile: minimal profile: minimal
- run: cd packages/next/build/swc && cargo test - run: cd packages/next/build/swc && cargo test
if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }}

View file

@ -10,6 +10,7 @@ packages/react-dev-overlay/lib/**
**/__tmp__/** **/__tmp__/**
lerna.json lerna.json
.github/actions/next-stats-action/.work .github/actions/next-stats-action/.work
packages/next/build/swc/tests/fixture/**/*
packages/next-codemod/transforms/__testfixtures__/**/* packages/next-codemod/transforms/__testfixtures__/**/*
packages/next-codemod/transforms/__tests__/**/* packages/next-codemod/transforms/__tests__/**/*
packages/next-codemod/**/*.js packages/next-codemod/**/*.js

View file

@ -1,6 +1,7 @@
**/.next/** **/.next/**
**/_next/** **/_next/**
**/dist/** **/dist/**
packages/next/build/swc/tests/**
packages/next/compiled/**/* packages/next/compiled/**/*
packages/next/bundles/webpack/packages/*.runtime.js packages/next/bundles/webpack/packages/*.runtime.js
lerna.json lerna.json

View file

@ -93,6 +93,18 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "auto_impl"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42cbf586c80ada5e5ccdecae80d3ef0854f224e2dd74435f8d87e6831b8d0a38"
dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "0.1.7" version = "0.1.7"
@ -134,9 +146,9 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.2.1" version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "block-buffer" name = "block-buffer"
@ -330,6 +342,12 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "easy-error"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04cc9717c61d2908f50d16ebb5677c7e82ea2bdf7cb52f66b30fe079f3212e16"
[[package]] [[package]]
name = "either" name = "either"
version = "1.6.1" version = "1.6.1"
@ -700,6 +718,7 @@ version = "0.0.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"backtrace", "backtrace",
"easy-error",
"fxhash", "fxhash",
"log", "log",
"napi", "napi",
@ -714,10 +733,13 @@ dependencies = [
"swc", "swc",
"swc_atoms", "swc_atoms",
"swc_common", "swc_common",
"swc_css",
"swc_css_codegen",
"swc_ecma_preset_env", "swc_ecma_preset_env",
"swc_ecma_transforms_testing", "swc_ecma_transforms_testing",
"swc_ecmascript", "swc_ecmascript",
"swc_node_base", "swc_node_base",
"swc_stylis",
"testing", "testing",
] ]
@ -949,6 +971,30 @@ dependencies = [
"output_vt100", "output_vt100",
] ]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]] [[package]]
name = "proc-macro-hack" name = "proc-macro-hack"
version = "0.5.19" version = "0.5.19"
@ -1603,6 +1649,97 @@ dependencies = [
"url", "url",
] ]
[[package]]
name = "swc_css"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30b6bedb0b7ea4ccccb288d7f2a9300fe1405d16564f49c1c7f809a1e3f0c742"
dependencies = [
"swc_css_ast",
"swc_css_codegen",
"swc_css_parser",
"swc_css_utils",
"swc_css_visit",
]
[[package]]
name = "swc_css_ast"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "483dfb43c4014dec1e77b188b449668c8e4a1d997b17ff68879de192b6443a0c"
dependencies = [
"is-macro",
"serde",
"string_enum",
"swc_atoms",
"swc_common",
]
[[package]]
name = "swc_css_codegen"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf323b2d07f777735cc3d34e31f2b6115d2d2f6ccb211afa519c99764a02e0c1"
dependencies = [
"auto_impl",
"bitflags",
"swc_atoms",
"swc_common",
"swc_css_ast",
"swc_css_codegen_macros",
]
[[package]]
name = "swc_css_codegen_macros"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe27425548d11afee43ddbe1d0cd882cb5e042f61b1503651dae2219c92333f5"
dependencies = [
"pmutil",
"proc-macro2",
"quote",
"swc_macros_common",
"syn",
]
[[package]]
name = "swc_css_parser"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbbb971b273235ad4fc02698f92ab1a242bc12f3dde79a7191d6c522d583b835"
dependencies = [
"bitflags",
"lexical",
"swc_atoms",
"swc_common",
"swc_css_ast",
"unicode-xid",
]
[[package]]
name = "swc_css_utils"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acca437ca271d610fca62849723513130a8967414d10c404a5233796c01aca8a"
dependencies = [
"swc_atoms",
"swc_common",
"swc_css_ast",
"swc_css_visit",
]
[[package]]
name = "swc_css_visit"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b6166ea9b15898554d518ca6bdb49ea21e64bf07f2b1f42613bed78011a4242"
dependencies = [
"swc_atoms",
"swc_common",
"swc_css_ast",
"swc_visit",
]
[[package]] [[package]]
name = "swc_ecma_ast" name = "swc_ecma_ast"
version = "0.52.0" version = "0.52.0"
@ -2009,6 +2146,7 @@ checksum = "ba53c5582d6e5881b093ece9aaa4b561465afab0560abb19948f2c4bbff1bdb9"
dependencies = [ dependencies = [
"swc_ecma_ast", "swc_ecma_ast",
"swc_ecma_codegen", "swc_ecma_codegen",
"swc_ecma_minifier",
"swc_ecma_parser", "swc_ecma_parser",
"swc_ecma_transforms", "swc_ecma_transforms",
"swc_ecma_utils", "swc_ecma_utils",
@ -2050,6 +2188,19 @@ dependencies = [
"swc_common", "swc_common",
] ]
[[package]]
name = "swc_stylis"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eee38c781219cf66533d27debde66f7054e07123e6e5fa860610135ed8a5e904"
dependencies = [
"swc_atoms",
"swc_common",
"swc_css_ast",
"swc_css_utils",
"swc_css_visit",
]
[[package]] [[package]]
name = "swc_visit" name = "swc_visit"
version = "0.2.6" version = "0.2.6"

View file

@ -9,6 +9,7 @@ crate-type = ["cdylib"]
[dependencies] [dependencies]
anyhow = "1.0" anyhow = "1.0"
backtrace = "0.3" backtrace = "0.3"
easy-error = "1.0.0"
napi = { version = "1", features = ["serde-json"] } napi = { version = "1", features = ["serde-json"] }
napi-derive = "1" napi-derive = "1"
path-clean = "0.1" path-clean = "0.1"
@ -18,9 +19,12 @@ serde_json = "1"
swc = "0.51" swc = "0.51"
swc_atoms = "0.2" swc_atoms = "0.2"
swc_common = { version = "0.12", features = ["tty-emitter", "sourcemap"] } swc_common = { version = "0.12", features = ["tty-emitter", "sourcemap"] }
swc_node_base = "0.3" swc_css = "0.6"
swc_ecmascript = { version = "0.63", features = ["codegen", "optimization", "parser", "react", "transforms", "typescript", "utils", "visit"] } swc_css_codegen = "0.4"
swc_ecmascript = { version = "0.63", features = ["codegen", "minifier", "optimization", "parser", "react", "transforms", "typescript", "utils", "visit"] }
swc_ecma_preset_env = "0.42" swc_ecma_preset_env = "0.42"
swc_node_base = "0.3"
swc_stylis = "0.3"
fxhash = "0.2.1" fxhash = "0.2.1"
retain_mut = "0.1.3" retain_mut = "0.1.3"
log = "0.4.14" log = "0.4.14"

View file

@ -1 +1 @@
nightly-2021-03-25 nightly-2021-08-12

View file

@ -45,6 +45,7 @@ mod hook_optimizer;
mod minify; mod minify;
mod next_dynamic; mod next_dynamic;
pub mod next_ssg; pub mod next_ssg;
mod styled_jsx;
mod transform; mod transform;
mod util; mod util;

View file

@ -4,258 +4,251 @@ use pathdiff::diff_paths;
use swc_atoms::js_word; use swc_atoms::js_word;
use swc_common::{FileName, DUMMY_SP}; use swc_common::{FileName, DUMMY_SP};
use swc_ecmascript::ast::{ use swc_ecmascript::ast::{
ArrayLit, ArrowExpr, BinExpr, BinaryOp, BlockStmtOrExpr, CallExpr, Expr, ExprOrSpread, ArrayLit, ArrowExpr, BinExpr, BinaryOp, BlockStmtOrExpr, CallExpr, Expr, ExprOrSpread,
ExprOrSuper, Ident, ImportDecl, ImportSpecifier, KeyValueProp, Lit, MemberExpr, ObjectLit, ExprOrSuper, Ident, ImportDecl, ImportSpecifier, KeyValueProp, Lit, MemberExpr, ObjectLit, Prop,
Prop, PropName, PropOrSpread, Str, StrKind, PropName, PropOrSpread, Str, StrKind,
}; };
use swc_ecmascript::utils::{ use swc_ecmascript::utils::{
ident::{Id, IdentLike}, ident::{Id, IdentLike},
HANDLER, HANDLER,
}; };
use swc_ecmascript::visit::{Fold, FoldWith}; use swc_ecmascript::visit::{Fold, FoldWith};
pub fn next_dynamic(filename: FileName, pages_dir: Option<PathBuf>) -> impl Fold { pub fn next_dynamic(filename: FileName, pages_dir: Option<PathBuf>) -> impl Fold {
NextDynamicPatcher { NextDynamicPatcher {
pages_dir, pages_dir,
filename, filename,
dynamic_bindings: vec![], dynamic_bindings: vec![],
} is_next_dynamic_first_arg: false,
dynamically_imported_specifier: None,
}
} }
#[derive(Debug)] #[derive(Debug)]
struct NextDynamicPatcher { struct NextDynamicPatcher {
pages_dir: Option<PathBuf>, pages_dir: Option<PathBuf>,
filename: FileName, filename: FileName,
dynamic_bindings: Vec<Id>, dynamic_bindings: Vec<Id>,
is_next_dynamic_first_arg: bool,
dynamically_imported_specifier: Option<String>,
} }
impl Fold for NextDynamicPatcher { impl Fold for NextDynamicPatcher {
fn fold_import_decl(&mut self, decl: ImportDecl) -> ImportDecl { fn fold_import_decl(&mut self, decl: ImportDecl) -> ImportDecl {
let ImportDecl { let ImportDecl {
ref src, ref src,
ref specifiers, ref specifiers,
.. ..
} = decl; } = decl;
if &src.value == "next/dynamic" { if &src.value == "next/dynamic" {
for specifier in specifiers { for specifier in specifiers {
if let ImportSpecifier::Default(default_specifier) = specifier { if let ImportSpecifier::Default(default_specifier) = specifier {
self.dynamic_bindings.push(default_specifier.local.to_id()); self.dynamic_bindings.push(default_specifier.local.to_id());
}
}
} }
}
decl
} }
fn fold_call_expr(&mut self, expr: CallExpr) -> CallExpr { decl
let mut expr = expr.fold_children_with(self); }
if let ExprOrSuper::Expr(i) = &expr.callee {
if let Expr::Ident(identifier) = &**i {
if self.dynamic_bindings.contains(&identifier.to_id()) {
if expr.args.len() == 0 {
HANDLER.with(|handler| {
handler
.struct_span_err(
identifier.span,
"next/dynamic requires at least one argument",
)
.emit()
});
} else if expr.args.len() > 2 {
HANDLER.with(|handler| {
handler
.struct_span_err(
identifier.span,
"next/dynamic only accepts 2 arguments",
)
.emit()
});
}
let mut import_specifier = None; fn fold_call_expr(&mut self, expr: CallExpr) -> CallExpr {
if let Expr::Arrow(ArrowExpr { if self.is_next_dynamic_first_arg {
body: BlockStmtOrExpr::Expr(e), if let ExprOrSuper::Expr(e) = &expr.callee {
.. if let Expr::Ident(Ident { sym, .. }) = &**e {
}) = &*expr.args[0].expr if sym == "import" {
{ if let Expr::Lit(Lit::Str(Str { value, .. })) = &*expr.args[0].expr {
if let Expr::Call(CallExpr { self.dynamically_imported_specifier = Some(value.to_string());
args: a, callee, .. }
}) = &**e }
{ }
if let ExprOrSuper::Expr(e) = callee { }
if let Expr::Ident(Ident { sym, .. }) = &**e { return expr.fold_children_with(self);
if sym == "import" { }
if a.len() == 0 { let mut expr = expr.fold_children_with(self);
// Do nothing, import_specifier will if let ExprOrSuper::Expr(i) = &expr.callee {
// remain None if let Expr::Ident(identifier) = &**i {
// triggering error below if self.dynamic_bindings.contains(&identifier.to_id()) {
} else if let Expr::Lit(Lit::Str(Str { value, .. })) = if expr.args.len() == 0 {
&*a[0].expr HANDLER.with(|handler| {
{ handler
import_specifier = Some(value.clone()); .struct_span_err(
} identifier.span,
} "next/dynamic requires at least one argument",
} )
} .emit()
} });
} return expr;
} else if expr.args.len() > 2 {
HANDLER.with(|handler| {
handler
.struct_span_err(identifier.span, "next/dynamic only accepts 2 arguments")
.emit()
});
return expr;
}
if let None = import_specifier { self.is_next_dynamic_first_arg = true;
HANDLER.with(|handler| { expr.args[0].expr = expr.args[0].expr.clone().fold_with(self);
handler self.is_next_dynamic_first_arg = false;
.struct_span_err(
identifier.span,
"First argument for next/dynamic must be an arrow function \
returning a valid dynamic import call e.g. `dynamic(() => \
import('../some-component'))`",
)
.emit()
});
}
// loadableGenerated: { if let None = self.dynamically_imported_specifier {
// webpack: () => [require.resolveWeak('../components/hello')], HANDLER.with(|handler| {
// modules: handler
// ["/project/src/file-being-transformed.js -> " + '../components/hello'] } .struct_span_err(
let generated = Box::new(Expr::Object(ObjectLit { identifier.span,
span: DUMMY_SP, "First argument for next/dynamic must be an arrow function returning a valid \
props: vec![ dynamic import call e.g. `dynamic(() => import('../some-component'))`",
PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp { )
key: PropName::Ident(Ident::new("webpack".into(), DUMMY_SP)), .emit()
value: Box::new(Expr::Arrow(ArrowExpr { });
params: vec![], return expr;
body: BlockStmtOrExpr::Expr(Box::new(Expr::Array(ArrayLit { }
elems: vec![Some(ExprOrSpread {
expr: Box::new(Expr::Call(CallExpr {
callee: ExprOrSuper::Expr(Box::new(Expr::Member(
MemberExpr {
obj: ExprOrSuper::Expr(Box::new(
Expr::Ident(Ident {
sym: js_word!("require"),
span: DUMMY_SP,
optional: false,
}),
)),
prop: Box::new(Expr::Ident(Ident {
sym: "resolveWeak".into(),
span: DUMMY_SP,
optional: false,
})),
computed: false,
span: DUMMY_SP,
},
))),
args: vec![ExprOrSpread {
expr: Box::new(Expr::Lit(Lit::Str(Str {
value: self.filename.to_string().into(),
span: DUMMY_SP,
kind: StrKind::Synthesized {},
has_escape: false,
}))),
spread: None,
}],
span: DUMMY_SP,
type_args: None,
})),
spread: None,
})],
span: DUMMY_SP,
}))),
is_async: false,
is_generator: false,
span: DUMMY_SP,
return_type: None,
type_params: None,
})),
}))),
PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
key: PropName::Ident(Ident::new("modules".into(), DUMMY_SP)),
value: Box::new(Expr::Array(ArrayLit {
elems: vec![Some(ExprOrSpread {
expr: Box::new(Expr::Bin(BinExpr {
span: DUMMY_SP,
op: BinaryOp::Add,
left: Box::new(Expr::Lit(Lit::Str(Str {
value: format!(
"{} -> ",
rel_filename(
self.pages_dir.as_deref(),
&self.filename
)
)
.into(),
span: DUMMY_SP,
kind: StrKind::Synthesized {},
has_escape: false,
}))),
right: Box::new(Expr::Lit(Lit::Str(Str {
value: import_specifier.unwrap(),
span: DUMMY_SP,
kind: StrKind::Normal {
contains_quote: false,
},
has_escape: false,
}))),
})),
spread: None,
})],
span: DUMMY_SP,
})),
}))),
],
}));
let mut props = // loadableGenerated: {
vec![PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp { // webpack: () => [require.resolveWeak('../components/hello')],
key: PropName::Ident(Ident::new("loadableGenerated".into(), DUMMY_SP)), // modules:
value: generated, // ["/project/src/file-being-transformed.js -> " + '../components/hello'] }
})))]; let generated = Box::new(Expr::Object(ObjectLit {
span: DUMMY_SP,
if expr.args.len() == 2 { props: vec![
if let Expr::Object(ObjectLit { PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
props: options_props, key: PropName::Ident(Ident::new("webpack".into(), DUMMY_SP)),
.. value: Box::new(Expr::Arrow(ArrowExpr {
}) = &*expr.args[1].expr params: vec![],
{ body: BlockStmtOrExpr::Expr(Box::new(Expr::Array(ArrayLit {
props.extend(options_props.iter().cloned()); elems: vec![Some(ExprOrSpread {
} expr: Box::new(Expr::Call(CallExpr {
} callee: ExprOrSuper::Expr(Box::new(Expr::Member(MemberExpr {
obj: ExprOrSuper::Expr(Box::new(Expr::Ident(Ident {
let second_arg = ExprOrSpread { sym: js_word!("require"),
spread: None,
expr: Box::new(Expr::Object(ObjectLit {
span: DUMMY_SP, span: DUMMY_SP,
props, optional: false,
})), }))),
}; prop: Box::new(Expr::Ident(Ident {
sym: "resolveWeak".into(),
span: DUMMY_SP,
optional: false,
})),
computed: false,
span: DUMMY_SP,
}))),
args: vec![ExprOrSpread {
expr: Box::new(Expr::Lit(Lit::Str(Str {
value: self.filename.to_string().into(),
span: DUMMY_SP,
kind: StrKind::Synthesized {},
has_escape: false,
}))),
spread: None,
}],
span: DUMMY_SP,
type_args: None,
})),
spread: None,
})],
span: DUMMY_SP,
}))),
is_async: false,
is_generator: false,
span: DUMMY_SP,
return_type: None,
type_params: None,
})),
}))),
PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
key: PropName::Ident(Ident::new("modules".into(), DUMMY_SP)),
value: Box::new(Expr::Array(ArrayLit {
elems: vec![Some(ExprOrSpread {
expr: Box::new(Expr::Bin(BinExpr {
span: DUMMY_SP,
op: BinaryOp::Add,
left: Box::new(Expr::Lit(Lit::Str(Str {
value: format!(
"{} -> ",
rel_filename(self.pages_dir.as_deref(), &self.filename)
)
.into(),
span: DUMMY_SP,
kind: StrKind::Synthesized {},
has_escape: false,
}))),
right: Box::new(Expr::Lit(Lit::Str(Str {
value: self
.dynamically_imported_specifier
.as_ref()
.unwrap()
.clone()
.into(),
span: DUMMY_SP,
kind: StrKind::Normal {
contains_quote: false,
},
has_escape: false,
}))),
})),
spread: None,
})],
span: DUMMY_SP,
})),
}))),
],
}));
expr.args.push(second_arg); let mut props = vec![PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
} key: PropName::Ident(Ident::new("loadableGenerated".into(), DUMMY_SP)),
value: generated,
})))];
if expr.args.len() == 2 {
if let Expr::Object(ObjectLit {
props: options_props,
..
}) = &*expr.args[1].expr
{
props.extend(options_props.iter().cloned());
} }
}
let second_arg = ExprOrSpread {
spread: None,
expr: Box::new(Expr::Object(ObjectLit {
span: DUMMY_SP,
props,
})),
};
if expr.args.len() == 2 {
expr.args[1] = second_arg;
} else {
expr.args.push(second_arg)
}
self.dynamically_imported_specifier = None;
} }
expr }
} }
expr
}
} }
fn rel_filename(base: Option<&Path>, file: &FileName) -> String { fn rel_filename(base: Option<&Path>, file: &FileName) -> String {
let base = match base { let base = match base {
Some(v) => v, Some(v) => v,
None => return file.to_string(), None => return file.to_string(),
}; };
let file = match file { let file = match file {
FileName::Real(v) => v, FileName::Real(v) => v,
_ => { _ => {
return file.to_string(); return file.to_string();
} }
}; };
let rel_path = diff_paths(&file, base); let rel_path = diff_paths(&file, base);
let rel_path = match rel_path { let rel_path = match rel_path {
Some(v) => v, Some(v) => v,
None => return file.display().to_string(), None => return file.display().to_string(),
}; };
rel_path.display().to_string() rel_path.display().to_string()
} }

View file

@ -0,0 +1,774 @@
use easy_error::{bail, Error};
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use swc_common::{collections::AHashSet, Span, DUMMY_SP};
use swc_ecmascript::ast::*;
use swc_ecmascript::minifier::{
eval::{EvalResult, Evaluator},
marks::Marks,
};
use swc_ecmascript::utils::{
collect_decls,
ident::{Id, IdentLike},
prepend, HANDLER,
};
use swc_ecmascript::visit::{Fold, FoldWith};
//use external::external_styles;
use transform_css::transform_css;
use utils::*;
mod transform_css;
mod utils;
pub fn styled_jsx() -> impl Fold {
StyledJSXTransformer::default()
}
#[derive(Default)]
struct StyledJSXTransformer {
styles: Vec<JSXStyle>,
static_class_name: Option<String>,
class_name: Option<Expr>,
file_has_styled_jsx: bool,
has_styled_jsx: bool,
bindings: AHashSet<Id>,
nearest_scope_bindings: AHashSet<Id>,
style_import_name: Option<String>,
external_bindings: Vec<Id>,
file_has_css_resolve: bool,
external_hash: Option<String>,
add_hash: Option<(String, String)>,
add_default_decl: Option<(String, Expr)>,
evaluator: Option<Evaluator>,
}
pub struct LocalStyle {
hash: String,
css: String,
css_span: Span,
is_dynamic: bool,
expressions: Vec<Box<Expr>>,
}
pub struct ExternalStyle {
expr: Expr,
identifier: Ident,
is_global: bool,
}
pub enum JSXStyle {
Local(LocalStyle),
External(ExternalStyle),
}
impl Fold for StyledJSXTransformer {
fn fold_jsx_element(&mut self, el: JSXElement) -> JSXElement {
if self.has_styled_jsx && is_styled_jsx(&el) {
match self.replace_jsx_style(&el) {
Ok(el) => return el,
Err(_) => return el,
}
} else if self.has_styled_jsx {
return el.fold_children_with(self);
}
self.check_children_for_jsx_styles(&el.children);
let el = el.fold_children_with(self);
self.reset_styles_state();
el
}
fn fold_jsx_fragment(&mut self, fragment: JSXFragment) -> JSXFragment {
if self.has_styled_jsx {
return fragment.fold_children_with(self);
}
self.check_children_for_jsx_styles(&fragment.children);
let fragment = fragment.fold_children_with(self);
self.reset_styles_state();
fragment
}
fn fold_jsx_opening_element(&mut self, mut el: JSXOpeningElement) -> JSXOpeningElement {
if !self.has_styled_jsx {
return el;
}
if let JSXElementName::Ident(Ident { sym, span, .. }) = &el.name {
if sym != "style"
&& sym != self.style_import_name.as_ref().unwrap()
&& (!is_capitalized(&*sym)
|| self
.nearest_scope_bindings
.contains(&(sym.clone(), span.ctxt)))
{
let (existing_class_name, existing_index, existing_spread_index) =
get_existing_class_name(&el);
let new_class_name = match (existing_class_name, &self.class_name) {
(Some(existing_class_name), Some(class_name)) => Some(add(
add(class_name.clone(), string_literal_expr(" ")),
existing_class_name,
)),
(Some(existing_class_name), None) => Some(existing_class_name),
(None, Some(class_name)) => Some(class_name.clone()),
_ => None,
};
if let Some(new_class_name) = new_class_name {
let class_name_attr = JSXAttrOrSpread::JSXAttr(JSXAttr {
span: DUMMY_SP,
name: JSXAttrName::Ident(ident("className")),
value: Some(JSXAttrValue::JSXExprContainer(JSXExprContainer {
expr: JSXExpr::Expr(Box::new(new_class_name)),
span: DUMMY_SP,
})),
});
el.attrs.push(class_name_attr);
}
if let Some(existing_spread_index) = existing_spread_index {
el.attrs.remove(existing_spread_index);
}
if let Some(existing_index) = existing_index {
el.attrs.remove(existing_index);
}
}
}
el
}
fn fold_import_decl(&mut self, decl: ImportDecl) -> ImportDecl {
let ImportDecl {
ref src,
ref specifiers,
..
} = decl;
if &src.value == "styled-jsx/css" {
for specifier in specifiers {
match specifier {
ImportSpecifier::Default(default_specifier) => {
self.external_bindings.push(default_specifier.local.to_id())
}
ImportSpecifier::Named(named_specifier) => {
self.external_bindings.push(named_specifier.local.to_id())
}
_ => {}
}
}
}
decl
}
fn fold_expr(&mut self, expr: Expr) -> Expr {
let expr = expr.fold_children_with(self);
match expr {
Expr::TaggedTpl(tagged_tpl) => match &*tagged_tpl.tag {
Expr::Ident(identifier) => {
if self.external_bindings.contains(&identifier.to_id()) {
match self.process_tagged_template_expr(&tagged_tpl) {
Ok(expr) => expr,
Err(_) => Expr::TaggedTpl(tagged_tpl),
}
} else {
Expr::TaggedTpl(tagged_tpl)
}
}
Expr::Member(MemberExpr {
obj: ExprOrSuper::Expr(boxed_ident),
..
}) => {
if let Expr::Ident(identifier) = &**boxed_ident {
if self.external_bindings.contains(&identifier.to_id()) {
match self.process_tagged_template_expr(&tagged_tpl) {
Ok(expr) => expr,
Err(_) => Expr::TaggedTpl(tagged_tpl),
}
} else {
Expr::TaggedTpl(tagged_tpl)
}
} else {
Expr::TaggedTpl(tagged_tpl)
}
}
_ => Expr::TaggedTpl(tagged_tpl),
},
expr => expr,
}
}
fn fold_var_declarator(&mut self, declarator: VarDeclarator) -> VarDeclarator {
let declarator = declarator.fold_children_with(self);
if let Some(external_hash) = &self.external_hash.take() {
match &declarator.name {
Pat::Ident(BindingIdent {
id: Ident { sym, .. },
..
}) => {
self.add_hash = Some((sym.to_string(), external_hash.clone()));
}
_ => {}
}
}
declarator
}
fn fold_export_default_expr(&mut self, default_expr: ExportDefaultExpr) -> ExportDefaultExpr {
let default_expr = default_expr.fold_children_with(self);
if let Some(external_hash) = &self.external_hash.take() {
let default_ident = "_defaultExport";
self.add_hash = Some((String::from(default_ident), external_hash.clone()));
self.add_default_decl = Some((String::from(default_ident), *default_expr.expr));
return ExportDefaultExpr {
expr: Box::new(Expr::Ident(Ident {
sym: default_ident.into(),
span: DUMMY_SP,
optional: false,
})),
span: DUMMY_SP,
};
}
default_expr
}
fn fold_block_stmt(&mut self, mut block: BlockStmt) -> BlockStmt {
let mut new_stmts = vec![];
for stmt in block.stmts {
new_stmts.push(stmt.fold_children_with(self));
if let Some(add_hash) = self.add_hash.take() {
new_stmts.push(add_hash_statment(add_hash));
}
}
block.stmts = new_stmts;
block
}
fn fold_module_items(&mut self, items: Vec<ModuleItem>) -> Vec<ModuleItem> {
let mut new_items = vec![];
for item in items {
let new_item = item.fold_children_with(self);
if let Some((default_ident, default_expr)) = &self.add_default_decl {
new_items.push(ModuleItem::Stmt(Stmt::Decl(Decl::Var(VarDecl {
kind: VarDeclKind::Const,
declare: false,
decls: vec![VarDeclarator {
name: Pat::Ident(BindingIdent {
id: Ident {
sym: default_ident.clone().into(),
span: DUMMY_SP,
optional: false,
},
type_ann: None,
}),
init: Some(Box::new(default_expr.clone())),
definite: false,
span: DUMMY_SP,
}],
span: DUMMY_SP,
}))));
self.add_default_decl = None;
if let Some(add_hash) = self.add_hash.take() {
new_items.push(ModuleItem::Stmt(add_hash_statment(add_hash)));
}
}
if !is_styled_css_import(&new_item) {
new_items.push(new_item);
}
if let Some(add_hash) = self.add_hash.take() {
new_items.push(ModuleItem::Stmt(add_hash_statment(add_hash)));
}
}
if self.file_has_styled_jsx || self.file_has_css_resolve {
prepend(
&mut new_items,
styled_jsx_import_decl(&self.style_import_name.as_ref().unwrap()),
);
}
new_items
}
fn fold_function(&mut self, func: Function) -> Function {
let nearest_scope_bindings = self.nearest_scope_bindings.clone();
self.nearest_scope_bindings = collect_decls(&func);
let func = func.fold_children_with(self);
self.nearest_scope_bindings = nearest_scope_bindings;
func
}
fn fold_arrow_expr(&mut self, func: ArrowExpr) -> ArrowExpr {
let current_bindings = self.nearest_scope_bindings.clone();
self.nearest_scope_bindings = collect_decls(&func);
let func = func.fold_children_with(self);
self.nearest_scope_bindings = current_bindings;
func
}
fn fold_module(&mut self, module: Module) -> Module {
self.bindings = collect_decls(&module);
self.evaluator = Some(Evaluator::new(module.clone(), Marks::new()));
self.style_import_name = Some(get_usable_import_specifier(&module.body));
module.fold_children_with(self)
}
}
impl StyledJSXTransformer {
fn check_children_for_jsx_styles(&mut self, children: &Vec<JSXElementChild>) {
let mut styles = vec![];
for i in 0..children.len() {
if let JSXElementChild::JSXElement(child_el) = &children[i] {
if is_styled_jsx(&child_el) {
self.file_has_styled_jsx = true;
self.has_styled_jsx = true;
let expr = get_style_expr(&child_el);
let style_info = self.get_jsx_style(expr, is_global(&child_el));
styles.insert(0, style_info);
}
}
}
if self.has_styled_jsx {
let (static_class_name, class_name) =
compute_class_names(&styles, self.style_import_name.as_ref().unwrap());
self.styles = styles;
self.static_class_name = static_class_name;
self.class_name = class_name;
}
}
fn get_jsx_style(&mut self, expr: &Expr, is_global_jsx_element: bool) -> JSXStyle {
let mut hasher = DefaultHasher::new();
let css: String;
let css_span: Span;
let is_dynamic;
let mut expressions = vec![];
match expr {
Expr::Lit(Lit::Str(Str { value, span, .. })) => {
hasher.write(value.as_ref().as_bytes());
css = value.to_string().clone();
css_span = span.clone();
is_dynamic = false;
}
Expr::Tpl(Tpl {
exprs,
quasis,
span,
}) => {
if exprs.is_empty() {
hasher.write(quasis[0].raw.value.as_bytes());
css = quasis[0].raw.value.to_string();
css_span = span.clone();
is_dynamic = false;
} else {
expr.clone().hash(&mut hasher);
let mut s = String::new();
for i in 0..quasis.len() {
let placeholder = if i == quasis.len() - 1 {
String::new()
} else {
format!("__styled-jsx-placeholder__{}", i)
};
s = format!("{}{}{}", s, quasis[i].raw.value, placeholder)
}
css = String::from(s);
css_span = *span;
let res = self.evaluator.as_mut().unwrap().eval(&expr);
is_dynamic = if let Some(EvalResult::Lit(_)) = res {
false
} else {
true
};
expressions = exprs.clone();
}
}
Expr::Ident(ident) => {
return JSXStyle::External(ExternalStyle {
expr: Expr::Member(MemberExpr {
obj: ExprOrSuper::Expr(Box::new(Expr::Ident(ident.clone()))),
prop: Box::new(Expr::Ident(Ident {
sym: "__hash".into(),
span: DUMMY_SP,
optional: false,
})),
computed: false,
span: DUMMY_SP,
}),
identifier: ident.clone(),
is_global: is_global_jsx_element,
});
}
_ => panic!("Not implemented"), // TODO: handle bad style input
}
return JSXStyle::Local(LocalStyle {
hash: format!("{:x}", hasher.finish()),
css,
css_span,
is_dynamic,
expressions,
});
}
fn replace_jsx_style(&mut self, el: &JSXElement) -> Result<JSXElement, Error> {
let style_info = self.styles.pop().unwrap();
let is_global = el.opening.attrs.iter().any(|attr| {
if let JSXAttrOrSpread::JSXAttr(JSXAttr {
name: JSXAttrName::Ident(Ident { sym, .. }),
..
}) = &attr
{
if sym == "global" {
return true;
}
}
false
});
match &style_info {
JSXStyle::Local(style_info) => {
let css = transform_css(&style_info, is_global, &self.static_class_name)?;
Ok(make_local_styled_jsx_el(
&style_info,
css,
self.style_import_name.as_ref().unwrap(),
))
}
JSXStyle::External(style) => Ok(make_external_styled_jsx_el(
style,
self.style_import_name.as_ref().unwrap(),
)),
}
}
fn process_tagged_template_expr(&mut self, tagged_tpl: &TaggedTpl) -> Result<Expr, Error> {
let style = self.get_jsx_style(&Expr::Tpl(tagged_tpl.tpl.clone()), false);
let styles = vec![style];
let (static_class_name, class_name) =
compute_class_names(&styles, &self.style_import_name.as_ref().unwrap());
let tag = match &*tagged_tpl.tag {
Expr::Ident(Ident { sym, .. }) => sym.to_string(),
Expr::Member(MemberExpr { prop, .. }) => {
if let Expr::Ident(Ident { sym, .. }) = &**prop {
sym.to_string()
} else {
String::from("not_styled_jsx_tag")
}
}
_ => String::from("not_styled_jsx_tag"),
};
let style = if let JSXStyle::Local(style) = &styles[0] {
if tag != "resolve" {
self.external_hash = Some(hash_string(&style.hash.clone()));
}
style
} else {
bail!("This shouldn't happen, we already know that this is a template literal");
};
let css = transform_css(&style, tag == "global", &static_class_name)?;
if tag == "resolve" {
self.file_has_css_resolve = true;
return Ok(Expr::Object(ObjectLit {
props: vec![
PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
key: PropName::Ident(Ident {
sym: "styles".into(),
span: DUMMY_SP,
optional: false,
}),
value: Box::new(Expr::JSXElement(Box::new(make_local_styled_jsx_el(
&style,
css,
&self.style_import_name.as_ref().unwrap(),
)))),
}))),
PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
key: PropName::Ident(Ident {
sym: "className".into(),
span: DUMMY_SP,
optional: false,
}),
value: Box::new(class_name.unwrap()),
}))),
],
span: DUMMY_SP,
}));
}
Ok(Expr::New(NewExpr {
callee: Box::new(Expr::Ident(Ident {
sym: "String".into(),
span: DUMMY_SP,
optional: false,
})),
args: Some(vec![ExprOrSpread {
expr: Box::new(css),
spread: None,
}]),
span: DUMMY_SP,
type_args: None,
}))
}
fn reset_styles_state(&mut self) {
self.has_styled_jsx = false;
self.static_class_name = None;
self.class_name = None;
}
}
fn is_styled_jsx(el: &JSXElement) -> bool {
if let JSXElementName::Ident(Ident { sym, .. }) = &el.opening.name {
if sym != "style" {
return false;
}
}
el.opening.attrs.iter().any(|attr| {
if let JSXAttrOrSpread::JSXAttr(JSXAttr {
name: JSXAttrName::Ident(Ident { sym, .. }),
..
}) = &attr
{
if sym == "jsx" {
return true;
}
}
false
})
}
fn is_global(el: &JSXElement) -> bool {
if let JSXElementName::Ident(Ident { sym, .. }) = &el.opening.name {
if sym != "style" {
return false;
}
}
el.opening.attrs.iter().any(|attr| {
if let JSXAttrOrSpread::JSXAttr(JSXAttr {
name: JSXAttrName::Ident(Ident { sym, .. }),
..
}) = &attr
{
if sym == "global" {
return true;
}
}
false
})
}
fn get_style_expr(el: &JSXElement) -> &Expr {
let non_whitespace_children: &Vec<&JSXElementChild> = &el
.children
.iter()
.filter(|child| {
if let JSXElementChild::JSXText(txt) = child {
if txt.value.chars().all(char::is_whitespace) {
return false;
}
}
true
})
.collect();
if non_whitespace_children.len() != 1 {
HANDLER.with(|handler| {
handler
.struct_span_err(
el.span,
&format!(
"Expected one child under JSX style tag, but got {} (eg: <style jsx>{{`hi`}}</style>)",
non_whitespace_children.len()
),
)
.emit()
});
panic!("styled-jsx style error");
}
if let JSXElementChild::JSXExprContainer(JSXExprContainer {
expr: JSXExpr::Expr(expr),
..
}) = non_whitespace_children[0]
{
return &**expr;
}
HANDLER.with(|handler| {
handler
.struct_span_err(
el.span,
"Expected a single child of type JSXExpressionContainer under JSX Style tag (eg: <style \
jsx>{{`hi`}}</style>)",
)
.emit()
});
panic!("next-swc compilation error");
}
fn get_existing_class_name(el: &JSXOpeningElement) -> (Option<Expr>, Option<usize>, Option<usize>) {
let mut spreads = vec![];
let mut class_name_expr = None;
let mut existing_index = None;
let mut existing_spread_index = None;
for i in (0..el.attrs.len()).rev() {
match &el.attrs[i] {
JSXAttrOrSpread::JSXAttr(JSXAttr {
name: JSXAttrName::Ident(Ident { sym, .. }),
value,
..
}) => {
if sym == "className" {
existing_index = Some(i);
class_name_expr = match value {
Some(JSXAttrValue::Lit(str_lit)) => Some(Expr::Lit(str_lit.clone())),
Some(JSXAttrValue::JSXExprContainer(JSXExprContainer {
expr: JSXExpr::Expr(expr),
..
})) => Some(*expr.clone()),
None => None,
_ => None,
};
break;
}
}
JSXAttrOrSpread::SpreadElement(SpreadElement { expr, .. }) => {
if let Expr::Object(ObjectLit { props, .. }) = &**expr {
let mut has_spread = false;
let mut has_class_name = false;
for j in 0..props.len() {
if let PropOrSpread::Prop(prop) = &props[j] {
if let Prop::KeyValue(KeyValueProp { key, value }) = &**prop {
if let PropName::Ident(Ident { sym, .. }) = key {
if sym == "className" {
has_class_name = true;
class_name_expr = Some(*value.clone());
if props.len() == 1 {
existing_spread_index = Some(i);
}
}
}
}
} else {
has_spread = true;
}
}
if has_class_name {
break;
}
if !has_spread {
continue;
}
}
let valid_spread = match &**expr {
Expr::Member(_) => true,
Expr::Ident(_) => true,
_ => false,
};
if valid_spread {
let member_dot_name = Expr::Member(MemberExpr {
obj: ExprOrSuper::Expr(Box::new(*expr.clone())),
prop: Box::new(Expr::Ident(ident("className"))),
span: DUMMY_SP,
computed: false,
});
// `${name} && ${name}.className != null && ${name}.className`
spreads.push(and(
and(
*expr.clone(),
not_eq(
member_dot_name.clone(),
Expr::Lit(Lit::Null(Null { span: DUMMY_SP })),
),
),
member_dot_name.clone(),
));
}
}
_ => {}
};
}
let spread_expr = match spreads.len() {
0 => None,
_ => Some(join_spreads(spreads)),
};
let class_name_expr = match class_name_expr {
Some(e @ Expr::Tpl(_) | e @ Expr::Lit(Lit::Str(_))) => Some(e),
None => None,
_ => Some(or(class_name_expr.unwrap(), string_literal_expr(""))),
};
let existing_class_name_expr = match (spread_expr, class_name_expr) {
(Some(spread_expr), Some(class_name_expr)) => Some(or(spread_expr, class_name_expr)),
(Some(spread_expr), None) => Some(or(spread_expr, string_literal_expr(""))),
(None, Some(class_name_expr)) => Some(class_name_expr),
_ => None,
};
(
existing_class_name_expr,
existing_index,
existing_spread_index,
)
}
fn join_spreads(spreads: Vec<Expr>) -> Expr {
let mut new_expr = spreads[0].clone();
for i in 1..spreads.len() {
new_expr = Expr::Bin(BinExpr {
op: op!("||"),
left: Box::new(new_expr.clone()),
right: Box::new(spreads[i].clone()),
span: DUMMY_SP,
})
}
new_expr
}
fn add_hash_statment((ident, hash): (String, String)) -> Stmt {
Stmt::Expr(ExprStmt {
expr: Box::new(Expr::Assign(AssignExpr {
left: PatOrExpr::Expr(Box::new(Expr::Member(MemberExpr {
obj: ExprOrSuper::Expr(Box::new(Expr::Ident(Ident {
sym: ident.into(),
span: DUMMY_SP,
optional: false,
}))),
prop: Box::new(Expr::Ident(Ident {
sym: "__hash".into(),
span: DUMMY_SP,
optional: false,
})),
span: DUMMY_SP,
computed: false,
}))),
right: Box::new(string_literal_expr(&hash)),
op: op!("="),
span: DUMMY_SP,
})),
span: DUMMY_SP,
})
}
fn is_styled_css_import(item: &ModuleItem) -> bool {
if let ModuleItem::ModuleDecl(ModuleDecl::Import(ImportDecl {
src: Str { value, .. },
..
})) = item
{
if value == "styled-jsx/css" {
return true;
}
}
false
}

View file

@ -0,0 +1,255 @@
use easy_error::{bail, Error};
use swc_common::{source_map::Pos, BytePos, Span, SyntaxContext, DUMMY_SP};
use swc_css::ast::*;
use swc_css::parser::{parse_str, parse_tokens, parser::ParserConfig};
use swc_css::visit::{VisitMut, VisitMutWith};
use swc_css_codegen::{
writer::basic::{BasicCssWriter, BasicCssWriterConfig},
CodegenConfig, Emit,
};
use swc_ecmascript::ast::{Expr, Str, StrKind, Tpl, TplElement};
use swc_ecmascript::utils::HANDLER;
use swc_stylis::prefixer::prefixer;
use super::{hash_string, string_literal_expr, LocalStyle};
pub fn transform_css(
style_info: &LocalStyle,
is_global: bool,
class_name: &Option<String>,
) -> Result<Expr, Error> {
let result: Result<Stylesheet, _> = parse_str(
&style_info.css,
style_info.css_span.lo,
style_info.css_span.hi,
ParserConfig {
parse_values: false,
},
);
let mut ss = match result {
Ok(ss) => ss,
Err(_) => {
HANDLER.with(|handler| {
handler
.struct_span_err(
style_info.css_span,
"Failed to parse css in styled jsx component",
)
.emit()
});
bail!("Failed to parse css");
}
};
// ? Do we need to support optionally prefixing?
ss.visit_mut_with(&mut prefixer());
ss.visit_mut_with(&mut Namespacer {
class_name: match class_name {
Some(s) => s.clone(),
None => format!("jsx-{}", &hash_string(&style_info.hash)),
},
is_global,
is_dynamic: style_info.is_dynamic,
});
let mut s = String::new();
{
let mut wr = BasicCssWriter::new(&mut s, BasicCssWriterConfig { indent: " " });
let mut gen = swc_css_codegen::CodeGenerator::new(&mut wr, CodegenConfig { minify: true });
gen.emit(&ss).unwrap();
}
if style_info.expressions.len() == 0 {
return Ok(string_literal_expr(&s));
}
let mut parts: Vec<&str> = s.split("__styled-jsx-placeholder__").collect();
let mut final_expressions = vec![];
for i in 1..parts.len() {
let expression_index = parts[i].chars().nth(0).unwrap().to_digit(10).unwrap() as usize;
final_expressions.push(style_info.expressions[expression_index].clone());
let substr = &parts[i][1..];
parts[i] = substr;
}
Ok(Expr::Tpl(Tpl {
quasis: parts
.iter()
.map(|quasi| TplElement {
cooked: None, // ? Do we need cooked as well
raw: Str {
value: (*quasi).into(),
span: DUMMY_SP,
has_escape: false,
kind: StrKind::Synthesized {},
},
span: DUMMY_SP,
tail: false,
})
.collect(),
exprs: final_expressions,
span: DUMMY_SP,
}))
}
struct Namespacer {
class_name: String,
is_global: bool,
is_dynamic: bool,
}
impl VisitMut for Namespacer {
fn visit_mut_complex_selector(&mut self, node: &mut ComplexSelector) {
let mut new_selectors = vec![];
for selector in &node.selectors {
let transformed_selectors = self.get_transformed_selectors(selector.clone());
new_selectors.extend(transformed_selectors);
}
node.selectors = new_selectors;
}
}
impl Namespacer {
fn get_transformed_selectors(&mut self, mut node: CompoundSelector) -> Vec<CompoundSelector> {
if self.is_global {
return vec![node];
}
let mut pseudo_index = None;
for (i, selector) in node.subclass_selectors.iter().enumerate() {
if let SubclassSelector::Pseudo(PseudoSelector { name, args, .. }) = selector {
// One off global selector
if &name.value == "global" {
let block_tokens = get_block_tokens(&args);
let mut args = args.clone();
args.tokens.extend(block_tokens);
let complex_selectors: Vec<ComplexSelector> = parse_tokens(
&args,
ParserConfig {
parse_values: false,
},
)
.unwrap();
return complex_selectors[0].selectors.clone();
} else if pseudo_index.is_none() {
pseudo_index = Some(i);
}
}
}
let subclass_selector = match self.is_dynamic {
true => "__jsx-style-dynamic-selector",
false => &self.class_name,
};
let insert_index = match pseudo_index {
None => node.subclass_selectors.len(),
Some(i) => i,
};
node.subclass_selectors.insert(
insert_index,
SubclassSelector::Class(ClassSelector {
span: DUMMY_SP,
text: Text {
value: subclass_selector.into(),
span: DUMMY_SP,
},
}),
);
vec![node]
}
}
fn get_block_tokens(selector_tokens: &Tokens) -> Vec<TokenAndSpan> {
let start_pos = selector_tokens.span.hi.to_u32();
vec![
TokenAndSpan {
span: Span {
lo: BytePos(start_pos + 0),
hi: BytePos(start_pos + 1),
ctxt: SyntaxContext::empty(),
},
token: Token::WhiteSpace,
},
TokenAndSpan {
span: Span {
lo: BytePos(start_pos + 1),
hi: BytePos(start_pos + 2),
ctxt: SyntaxContext::empty(),
},
token: Token::LBrace,
},
TokenAndSpan {
span: Span {
lo: BytePos(start_pos + 2),
hi: BytePos(start_pos + 3),
ctxt: SyntaxContext::empty(),
},
token: Token::WhiteSpace,
},
TokenAndSpan {
span: Span {
lo: BytePos(start_pos + 3),
hi: BytePos(start_pos + 8),
ctxt: SyntaxContext::empty(),
},
token: Token::Ident("color".into()),
},
TokenAndSpan {
span: Span {
lo: BytePos(start_pos + 8),
hi: BytePos(start_pos + 9),
ctxt: SyntaxContext::empty(),
},
token: Token::Colon,
},
TokenAndSpan {
span: Span {
lo: BytePos(start_pos + 9),
hi: BytePos(start_pos + 10),
ctxt: SyntaxContext::empty(),
},
token: Token::WhiteSpace,
},
TokenAndSpan {
span: Span {
lo: BytePos(start_pos + 10),
hi: BytePos(start_pos + 13),
ctxt: SyntaxContext::empty(),
},
token: Token::Ident("red".into()),
},
TokenAndSpan {
span: Span {
lo: BytePos(start_pos + 13),
hi: BytePos(start_pos + 14),
ctxt: SyntaxContext::empty(),
},
token: Token::Semi,
},
TokenAndSpan {
span: Span {
lo: BytePos(start_pos + 14),
hi: BytePos(start_pos + 15),
ctxt: SyntaxContext::empty(),
},
token: Token::WhiteSpace,
},
TokenAndSpan {
span: Span {
lo: BytePos(start_pos + 15),
hi: BytePos(start_pos + 16),
ctxt: SyntaxContext::empty(),
},
token: Token::RBrace,
},
TokenAndSpan {
span: Span {
lo: BytePos(start_pos + 16),
hi: BytePos(start_pos + 17),
ctxt: SyntaxContext::empty(),
},
token: Token::WhiteSpace,
},
]
}

View file

@ -0,0 +1,365 @@
use std::collections::hash_map::DefaultHasher;
use std::hash::Hasher;
use swc_common::DUMMY_SP;
use swc_ecmascript::ast::*;
use super::{ExternalStyle, JSXStyle, LocalStyle};
fn tpl_element(value: &str) -> TplElement {
TplElement {
raw: Str {
value: value.into(),
span: DUMMY_SP,
kind: StrKind::Synthesized,
has_escape: false,
},
cooked: None,
span: DUMMY_SP,
tail: false,
}
}
pub fn compute_class_names(
styles: &Vec<JSXStyle>,
style_import_name: &str,
) -> (Option<String>, Option<Expr>) {
let mut static_class_name = None;
let mut external_jsx_id = None;
let mut static_hashes = vec![];
let mut dynamic_styles = vec![];
let mut external_styles = vec![];
for style_info in styles {
match &style_info {
JSXStyle::Local(style_info) => {
if !style_info.is_dynamic {
static_hashes.push(style_info.hash.clone());
} else {
dynamic_styles.push(style_info.clone());
}
}
JSXStyle::External(external) => {
if !external.is_global {
external_styles.push(external.expr.clone());
}
}
}
}
if external_styles.len() > 0 {
let mut quasis = vec![tpl_element("jsx-")];
for _i in 1..external_styles.len() {
quasis.push(tpl_element(" jsx-"))
}
quasis.push(tpl_element(""));
external_jsx_id = Some(Expr::Tpl(Tpl {
quasis,
exprs: external_styles
.iter()
.map(|external| Box::new(external.clone()))
.collect(),
span: DUMMY_SP,
}));
}
if static_hashes.len() > 0 {
static_class_name = Some(format!("jsx-{}", hash_string(&static_hashes.join(","))));
}
let dynamic_class_name = match dynamic_styles.len() {
0 => None,
_ => Some(Expr::Call(CallExpr {
callee: ExprOrSuper::Expr(Box::new(Expr::Member(MemberExpr {
obj: ExprOrSuper::Expr(Box::new(Expr::Ident(Ident {
sym: style_import_name.into(),
span: DUMMY_SP,
optional: false,
}))),
prop: Box::new(Expr::Ident(Ident {
sym: "dynamic".into(),
span: DUMMY_SP,
optional: false,
})),
span: DUMMY_SP,
computed: false,
}))),
args: dynamic_styles
.iter()
.map(|style_info| {
let hash_input = match &static_class_name {
Some(class_name) => format!("{}{}", style_info.hash, class_name),
None => style_info.hash.clone(),
};
ExprOrSpread {
expr: Box::new(Expr::Array(ArrayLit {
elems: vec![
Some(ExprOrSpread {
expr: Box::new(string_literal_expr(&hash_string(&hash_input))),
spread: None,
}),
Some(ExprOrSpread {
expr: Box::new(Expr::Array(ArrayLit {
elems: style_info
.expressions
.iter()
.map(|expression| {
Some(ExprOrSpread {
expr: expression.clone(),
spread: None,
})
})
.collect(),
span: DUMMY_SP,
})),
spread: None,
}),
],
span: DUMMY_SP,
})),
spread: None,
}
})
.collect(),
span: DUMMY_SP,
type_args: None,
})),
};
let class_name_expr = match (
static_class_name.clone(),
dynamic_class_name,
external_jsx_id,
) {
(Some(static_class_name), Some(dynamic_class_name), Some(external_jsx_id)) => Some(add(
add(
external_jsx_id,
string_literal_expr(&format!(" {} ", static_class_name)),
),
dynamic_class_name,
)),
(Some(static_class_name), Some(dynamic_class_name), None) => Some(add(
string_literal_expr(&format!("{} ", static_class_name)),
dynamic_class_name,
)),
(Some(static_class_name), None, Some(external_jsx_id)) => Some(add(
string_literal_expr(&format!("{} ", static_class_name)),
external_jsx_id,
)),
(None, Some(dynamic_class_name), Some(external_jsx_id)) => Some(add(
add(external_jsx_id, string_literal_expr(" ")),
dynamic_class_name,
)),
(Some(static_class_name), None, None) => Some(string_literal_expr(&static_class_name)),
(None, Some(dynamic_class_name), None) => Some(dynamic_class_name),
(None, None, Some(external_jsx_id)) => Some(external_jsx_id),
_ => None,
};
(static_class_name, class_name_expr)
}
pub fn make_external_styled_jsx_el(style: &ExternalStyle, style_import_name: &str) -> JSXElement {
let attrs = vec![JSXAttrOrSpread::JSXAttr(JSXAttr {
name: JSXAttrName::Ident(Ident {
sym: "id".into(),
span: DUMMY_SP,
optional: false,
}),
value: Some(JSXAttrValue::JSXExprContainer(JSXExprContainer {
expr: JSXExpr::Expr(Box::new(style.expr.clone())),
span: DUMMY_SP,
})),
span: DUMMY_SP,
})];
let opening = JSXOpeningElement {
name: JSXElementName::Ident(Ident {
sym: style_import_name.into(),
span: DUMMY_SP,
optional: false,
}),
attrs,
span: DUMMY_SP,
self_closing: false,
type_args: None,
};
let closing = Some(JSXClosingElement {
name: JSXElementName::Ident(Ident {
sym: style_import_name.into(),
span: DUMMY_SP,
optional: false,
}),
span: DUMMY_SP,
});
let children = vec![JSXElementChild::JSXExprContainer(JSXExprContainer {
expr: JSXExpr::Expr(Box::new(Expr::Ident(style.identifier.clone()))),
span: DUMMY_SP,
})];
JSXElement {
opening,
closing,
children,
span: DUMMY_SP,
}
}
pub fn make_local_styled_jsx_el(
style_info: &LocalStyle,
css_expr: Expr,
style_import_name: &str,
) -> JSXElement {
let mut attrs = vec![JSXAttrOrSpread::JSXAttr(JSXAttr {
name: JSXAttrName::Ident(Ident {
sym: "id".into(),
span: DUMMY_SP,
optional: false,
}),
value: Some(JSXAttrValue::JSXExprContainer(JSXExprContainer {
expr: JSXExpr::Expr(Box::new(string_literal_expr(
hash_string(&style_info.hash).as_str(),
))),
span: DUMMY_SP,
})),
span: DUMMY_SP,
})];
if style_info.is_dynamic {
attrs.push(JSXAttrOrSpread::JSXAttr(JSXAttr {
name: JSXAttrName::Ident(Ident {
sym: "dynamic".into(),
span: DUMMY_SP,
optional: false,
}),
value: Some(JSXAttrValue::JSXExprContainer(JSXExprContainer {
expr: JSXExpr::Expr(Box::new(Expr::Array(ArrayLit {
elems: style_info
.expressions
.iter()
.map(|expression| {
Some(ExprOrSpread {
expr: expression.clone(),
spread: None,
})
})
.collect(),
span: DUMMY_SP,
}))),
span: DUMMY_SP,
})),
span: DUMMY_SP,
}));
}
let opening = JSXOpeningElement {
name: JSXElementName::Ident(Ident {
sym: style_import_name.into(),
span: DUMMY_SP,
optional: false,
}),
attrs,
span: DUMMY_SP,
self_closing: false,
type_args: None,
};
let closing = Some(JSXClosingElement {
name: JSXElementName::Ident(Ident {
sym: style_import_name.into(),
span: DUMMY_SP,
optional: false,
}),
span: DUMMY_SP,
});
let children = vec![JSXElementChild::JSXExprContainer(JSXExprContainer {
expr: JSXExpr::Expr(Box::new(css_expr)),
span: DUMMY_SP,
})];
JSXElement {
opening,
closing,
children,
span: DUMMY_SP,
}
}
pub fn get_usable_import_specifier(_items: &Vec<ModuleItem>) -> String {
// TODO
String::from("_JSXStyle")
}
pub fn styled_jsx_import_decl(style_import_name: &str) -> ModuleItem {
ModuleItem::ModuleDecl(ModuleDecl::Import(ImportDecl {
asserts: None,
span: DUMMY_SP,
type_only: false,
specifiers: vec![ImportSpecifier::Default(ImportDefaultSpecifier {
local: Ident {
sym: style_import_name.into(),
span: DUMMY_SP,
optional: false,
},
span: DUMMY_SP,
})],
src: Str {
has_escape: false,
kind: StrKind::Synthesized {},
span: DUMMY_SP,
value: "styled-jsx/style".into(),
},
}))
}
// TODO: maybe use DJBHasher (need to implement)
pub fn hash_string(str: &String) -> String {
let mut hasher = DefaultHasher::new();
hasher.write(str.as_bytes());
let hash_result = hasher.finish();
format!("{:x}", hash_result)
}
pub fn string_literal_expr(str: &str) -> Expr {
Expr::Lit(Lit::Str(Str {
value: str.into(),
span: DUMMY_SP,
has_escape: false,
kind: StrKind::Synthesized {},
}))
}
pub fn ident(str: &str) -> Ident {
Ident {
sym: str.into(),
span: DUMMY_SP,
optional: false,
}
}
pub fn is_capitalized(word: &str) -> bool {
word.chars().next().unwrap().is_uppercase()
}
pub fn add(left: Expr, right: Expr) -> Expr {
binary_expr(BinaryOp::Add, left, right)
}
pub fn and(left: Expr, right: Expr) -> Expr {
binary_expr(BinaryOp::LogicalAnd, left, right)
}
pub fn or(left: Expr, right: Expr) -> Expr {
binary_expr(BinaryOp::LogicalOr, left, right)
}
pub fn not_eq(left: Expr, right: Expr) -> Expr {
binary_expr(BinaryOp::NotEq, left, right)
}
pub fn binary_expr(op: BinaryOp, left: Expr, right: Expr) -> Expr {
Expr::Bin(BinExpr {
op,
left: Box::new(left),
right: Box::new(right),
span: DUMMY_SP,
})
}

View file

@ -32,6 +32,7 @@ use crate::{
hook_optimizer::hook_optimizer, hook_optimizer::hook_optimizer,
next_dynamic::next_dynamic, next_dynamic::next_dynamic,
next_ssg::next_ssg, next_ssg::next_ssg,
styled_jsx::styled_jsx,
util::{CtxtExt, MapErr}, util::{CtxtExt, MapErr},
}; };
use anyhow::{Context as _, Error}; use anyhow::{Context as _, Error};
@ -82,6 +83,7 @@ impl Task for TransformTask {
Optional::new(next_ssg(), !self.options.disable_next_ssg), Optional::new(next_ssg(), !self.options.disable_next_ssg),
amp_attributes(), amp_attributes(),
next_dynamic(s.name.clone(), self.options.pages_dir.clone()), next_dynamic(s.name.clone(), self.options.pages_dir.clone()),
styled_jsx()
); );
self.c.process_js_with_custom_pass( self.c.process_js_with_custom_pass(
s.clone(), s.clone(),

View file

@ -1,6 +1,7 @@
use self::amp_attributes::amp_attributes; use self::amp_attributes::amp_attributes;
use self::next_dynamic::next_dynamic; use self::next_dynamic::next_dynamic;
use self::next_ssg::next_ssg; use self::next_ssg::next_ssg;
use self::styled_jsx::styled_jsx;
use std::path::PathBuf; use std::path::PathBuf;
use swc_common::{chain, comments::SingleThreadedComments, FileName}; use swc_common::{chain, comments::SingleThreadedComments, FileName};
use swc_ecma_transforms_testing::{test, test_fixture}; use swc_ecma_transforms_testing::{test, test_fixture};
@ -16,6 +17,8 @@ mod amp_attributes;
mod next_dynamic; mod next_dynamic;
#[path = "../src/next_ssg.rs"] #[path = "../src/next_ssg.rs"]
mod next_ssg; mod next_ssg;
#[path = "../src/styled_jsx/mod.rs"]
mod styled_jsx;
fn syntax() -> Syntax { fn syntax() -> Syntax {
Syntax::Es(EsConfig { Syntax::Es(EsConfig {
@ -75,3 +78,9 @@ fn next_ssg_fixture(input: PathBuf) {
&output, &output,
); );
} }
#[fixture("tests/fixture/styled-jsx/**/input.js")]
fn styled_jsx_fixture(input: PathBuf) {
let output = input.parent().unwrap().join("output.js");
test_fixture(syntax(), &|_tr| styled_jsx(), &input, &output);
}

View file

@ -1,14 +1,14 @@
import dynamic from 'next/dynamic' import dynamic from "next/dynamic";
const DynamicComponentWithCustomLoading = dynamic( const DynamicComponentWithCustomLoading = dynamic(()=>import("../components/hello")
() => import('../components/hello'), , {
{
loading: () => <p>...</p>,
},
{
loadableGenerated: { loadableGenerated: {
webpack: () => [require.resolveWeak('/some-project/src/some-file.js')], webpack: ()=>[
modules: ['some-file.js -> ' + '../components/hello'], require.resolveWeak("/some-project/src/some-file.js")
]
,
modules: [
"some-file.js -> " + "../components/hello"
]
}, },
loading: () => <p>...</p>, loading: ()=><p >...</p>
} });
)

View file

@ -0,0 +1,5 @@
import dynamic from 'next/dynamic'
const DynamicComponent = dynamic(() => handleImport(import('./components/hello')), {
loading: () => null,
ssr: false,
});

View file

@ -0,0 +1,16 @@
import dynamic from "next/dynamic";
const DynamicComponent = dynamic(()=>handleImport(import("./components/hello"))
, {
loadableGenerated: {
webpack: ()=>[
require.resolveWeak("/some-project/src/some-file.js")
]
,
modules: [
"some-file.js -> " + "./components/hello"
]
},
loading: ()=>null
,
ssr: false
});

View file

@ -0,0 +1,6 @@
const a = () => (
<div>
<p>hi</p>
<style>{'woot'}</style>
</div>
)

View file

@ -0,0 +1,5 @@
const a = ()=><div >
<p >hi</p>
<style >{"woot"}</style>
</div>
;

View file

@ -0,0 +1,55 @@
export default () => {
const Element = 'div'
return (
<div>
<div className="test" {...test.test} />
<div className="test" {...test.test.test} />
<div className="test" {...this.test.test} />
<div data-test="test" />
<div className={'test'} />
<div className={`test`} />
<div className={`test${true ? ' test2' : ''}`} />
<div className={'test ' + test} />
<div className={['test', 'test2'].join(' ')} />
<div className={true && 'test'} />
<div className={test ? 'test' : null} />
<div className={test} />
<div className={test && 'test'} />
<div className={test && test('test')} />
<div className={undefined} />
<div className={null} />
<div className={false} />
<div className={'test'} data-test />
<div data-test className={'test'} />
<div className={'test'} data-test="test" />
<div className={'test'} {...props} />
<div className={'test'} {...props} {...rest} />
<div className={`test ${test ? 'test' : ''}`} {...props} />
<div className={test && test('test')} {...props} />
<div className={test && test('test') && 'test'} {...props} />
<div className={test && test('test') && test2('test')} {...props} />
<div {...props} className={'test'} />
<div {...props} {...rest} className={'test'} />
<div {...props} className={'test'} {...rest} />
<div {...props} />
<div {...props} {...rest} />
<div {...props} data-foo {...rest} />
<div {...props} className={'test'} data-foo {...rest} />
<div {...{ id: 'foo' }} />
<div {...{ className: 'foo' }} />
<div {...{ className: 'foo' }} className="test" />
<div className="test" {...{ className: 'foo' }} />
<div {...{ className: 'foo' }} {...bar} />
<div {...{ className: 'foo' }} {...bar} className="test" />
<div className="test" {...{ className: 'foo' }} {...bar} />
<div className="test" {...{ className: props.className }} />
<div className="test" {...{ className: props.className }} {...bar} />
<div className="test" {...bar} {...{ className: props.className }} />
<div className="test" {...bar()} />
<Element />
<Element className="test" />
<Element {...props} />
<style jsx>{'div { color: red }'}</style>
</div>
)
}

View file

@ -0,0 +1,63 @@
import _JSXStyle from "styled-jsx/style";
export default (()=>{
const Element = "div";
return <div className={"jsx-abb4c2202db1a207"}>
<div {...test.test} className={"jsx-abb4c2202db1a207" + " " + (test.test && test.test.className != null && test.test.className || "test")}/>
<div {...test.test.test} className={"jsx-abb4c2202db1a207" + " " + (test.test.test && test.test.test.className != null && test.test.test.className || "test")}/>
<div {...this.test.test} className={"jsx-abb4c2202db1a207" + " " + (this.test.test && this.test.test.className != null && this.test.test.className || "test")}/>
<div data-test="test" className={"jsx-abb4c2202db1a207"}/>
<div className={"jsx-abb4c2202db1a207" + " " + "test"}/>
<div className={"jsx-abb4c2202db1a207" + " " + `test`}/>
<div className={"jsx-abb4c2202db1a207" + " " + `test${true ? " test2" : ""}`}/>
<div className={"jsx-abb4c2202db1a207" + " " + ("test " + test || "")}/>
<div className={"jsx-abb4c2202db1a207" + " " + ([
"test",
"test2"
].join(" ") || "")}/>
<div className={"jsx-abb4c2202db1a207" + " " + (true && "test" || "")}/>
<div className={"jsx-abb4c2202db1a207" + " " + ((test ? "test" : null) || "")}/>
<div className={"jsx-abb4c2202db1a207" + " " + (test || "")}/>
<div className={"jsx-abb4c2202db1a207" + " " + (test && "test" || "")}/>
<div className={"jsx-abb4c2202db1a207" + " " + (test && test("test") || "")}/>
<div className={"jsx-abb4c2202db1a207" + " " + (undefined || "")}/>
<div className={"jsx-abb4c2202db1a207" + " " + (null || "")}/>
<div className={"jsx-abb4c2202db1a207" + " " + (false || "")}/>
<div data-test className={"jsx-abb4c2202db1a207" + " " + "test"}/>
<div data-test className={"jsx-abb4c2202db1a207" + " " + "test"}/>
<div data-test="test" className={"jsx-abb4c2202db1a207" + " " + "test"}/>
<div {...props} className={"jsx-abb4c2202db1a207" + " " + (props && props.className != null && props.className || "test")}/>
<div {...props} {...rest} className={"jsx-abb4c2202db1a207" + " " + (rest && rest.className != null && rest.className || props && props.className != null && props.className || "test")}/>
<div {...props} className={"jsx-abb4c2202db1a207" + " " + (props && props.className != null && props.className || `test ${test ? "test" : ""}`)}/>
<div {...props} className={"jsx-abb4c2202db1a207" + " " + (props && props.className != null && props.className || test && test("test") || "")}/>
<div {...props} className={"jsx-abb4c2202db1a207" + " " + (props && props.className != null && props.className || test && test("test") && "test" || "")}/>
<div {...props} className={"jsx-abb4c2202db1a207" + " " + (props && props.className != null && props.className || test && test("test") && test2("test") || "")}/>
<div {...props} className={"jsx-abb4c2202db1a207" + " " + "test"}/>
<div {...props} {...rest} className={"jsx-abb4c2202db1a207" + " " + "test"}/>
<div {...props} {...rest} className={"jsx-abb4c2202db1a207" + " " + (rest && rest.className != null && rest.className || "test")}/>
<div {...props} className={"jsx-abb4c2202db1a207" + " " + (props && props.className != null && props.className || "")}/>
<div {...props} {...rest} className={"jsx-abb4c2202db1a207" + " " + (rest && rest.className != null && rest.className || props && props.className != null && props.className || "")}/>
<div {...props} data-foo {...rest} className={"jsx-abb4c2202db1a207" + " " + (rest && rest.className != null && rest.className || props && props.className != null && props.className || "")}/>
<div {...props} data-foo {...rest} className={"jsx-abb4c2202db1a207" + " " + (rest && rest.className != null && rest.className || "test")}/>
<div {...{
id: "foo"
}} className={"jsx-abb4c2202db1a207"}/>
<div className={"jsx-abb4c2202db1a207" + " " + "foo"}/>
<div {...{
className: "foo"
}} className={"jsx-abb4c2202db1a207" + " " + "test"}/>
<div className="test" className={"jsx-abb4c2202db1a207" + " " + "foo"}/>
<div {...bar} className={"jsx-abb4c2202db1a207" + " " + (bar && bar.className != null && bar.className || "foo")}/>
<div {...{
className: "foo"
}} {...bar} className={"jsx-abb4c2202db1a207" + " " + "test"}/>
<div className="test" {...bar} className={"jsx-abb4c2202db1a207" + " " + (bar && bar.className != null && bar.className || "foo")}/>
<div className="test" className={"jsx-abb4c2202db1a207" + " " + (props.className || "")}/>
<div className="test" {...bar} className={"jsx-abb4c2202db1a207" + " " + (bar && bar.className != null && bar.className || props.className || "")}/>
<div className="test" {...bar} className={"jsx-abb4c2202db1a207" + " " + (props.className || "")}/>
<div {...bar()} className={"jsx-abb4c2202db1a207" + " " + "test"}/>
<Element className={"jsx-abb4c2202db1a207"}/>
<Element className={"jsx-abb4c2202db1a207" + " " + "test"}/>
<Element {...props} className={"jsx-abb4c2202db1a207" + " " + (props && props.className != null && props.className || "")}/>
<_JSXStyle id={"abb4c2202db1a207"}>{"div.jsx-abb4c2202db1a207 {color:red}"}</_JSXStyle>
</div>;
});

View file

@ -0,0 +1,166 @@
import styles from './styles'
const styles2 = require('./styles2')
// external only
export const Test1 = () => (
<div>
<p>external only</p>
<style jsx>{styles}</style>
<style jsx>{styles2}</style>
</div>
)
// external and static
export const Test2 = () => (
<div>
<p>external and static</p>
<style jsx>{`
p {
color: red;
}
`}</style>
<style jsx>{styles}</style>
</div>
)
// external and dynamic
export const Test3 = ({ color }) => (
<div>
<p>external and dynamic</p>
<style jsx>{`
p {
color: ${color};
}
`}</style>
<style jsx>{styles}</style>
</div>
)
// external, static and dynamic
export const Test4 = ({ color }) => (
<div>
<p>external, static and dynamic</p>
<style jsx>{`
p {
display: inline-block;
}
`}</style>
<style jsx>{`
p {
color: ${color};
}
`}</style>
<style jsx>{styles}</style>
</div>
)
// static only
export const Test5 = () => (
<div>
<p>static only</p>
<style jsx>{`
p {
display: inline-block;
}
`}</style>
<style jsx>{`
p {
color: red;
}
`}</style>
</div>
)
// static and dynamic
export const Test6 = ({ color }) => (
<div>
<p>static and dynamic</p>
<style jsx>{`
p {
display: inline-block;
}
`}</style>
<style jsx>{`
p {
color: ${color};
}
`}</style>
</div>
)
// dynamic only
export const Test7 = ({ color }) => (
<div>
<p>dynamic only</p>
<style jsx>{`
p {
color: ${color};
}
`}</style>
</div>
)
// dynamic with scoped compound variable
export const Test8 = ({ color }) => {
if (color) {
const innerProps = { color }
return (
<div>
<p>dynamic with scoped compound variable</p>
<style jsx>{`
p {
color: ${innerProps.color};
}
`}</style>
</div>
)
}
}
// dynamic with compound variable
export const Test9 = ({ color }) => {
const innerProps = { color }
return (
<div>
<p>dynamic with compound variable</p>
<style jsx>{`
p {
color: ${innerProps.color};
}
`}</style>
</div>
)
}
const foo = 'red'
// dynamic with constant variable
export const Test10 = () => (
<div>
<p>dynamic with constant variable</p>
<style jsx>{`
p {
color: ${foo};
}
`}</style>
</div>
)
// dynamic with complex scope
export const Test11 = ({ color }) => {
const items = Array.from({ length: 5 }).map((item, i) => (
<li className="item" key={i}>
<style jsx>{`
.item {
color: ${color};
}
`}</style>
Item #{i + 1}
</li>
))
return <ul className="items">{items}</ul>
}

View file

@ -0,0 +1,160 @@
import _JSXStyle from "styled-jsx/style";
import styles from "./styles";
const styles2 = require("./styles2");
export const Test1 = ()=><div className={`jsx-${styles2.__hash} jsx-${styles.__hash}`}>
<p className={`jsx-${styles2.__hash} jsx-${styles.__hash}`}>external only</p>
<_JSXStyle id={styles.__hash}>{styles}</_JSXStyle>
<_JSXStyle id={styles2.__hash}>{styles2}</_JSXStyle>
</div>
;
export const Test2 = ()=><div className={"jsx-81a68341e430a972 " + `jsx-${styles.__hash}`}>
<p className={"jsx-81a68341e430a972 " + `jsx-${styles.__hash}`}>external and static</p>
<_JSXStyle id={"81a68341e430a972"}>{"p.jsx-81a68341e430a972 {color:red}"}</_JSXStyle>
<_JSXStyle id={styles.__hash}>{styles}</_JSXStyle>
</div>
;
export const Test3 = ({ color })=><div className={`jsx-${styles.__hash}` + " " + _JSXStyle.dynamic([
"26b457d3bc475235",
[
color
]
])}>
<p className={`jsx-${styles.__hash}` + " " + _JSXStyle.dynamic([
"26b457d3bc475235",
[
color
]
])}>external and dynamic</p>
<_JSXStyle id={"26b457d3bc475235"} dynamic={[
color
]}>{`p.__jsx-style-dynamic-selector {color:${color}}`}</_JSXStyle>
<_JSXStyle id={styles.__hash}>{styles}</_JSXStyle>
</div>
;
export const Test4 = ({ color })=><div className={`jsx-${styles.__hash}` + " jsx-ceba8c9ce34e3d0c " + _JSXStyle.dynamic([
"e6cca97eb9ca1a15",
[
color
]
])}>
<p className={`jsx-${styles.__hash}` + " jsx-ceba8c9ce34e3d0c " + _JSXStyle.dynamic([
"e6cca97eb9ca1a15",
[
color
]
])}>external, static and dynamic</p>
<_JSXStyle id={"ceba8c9ce34e3d0c"}>{"p.jsx-ceba8c9ce34e3d0c {display:inline-block}"}</_JSXStyle>
<_JSXStyle id={"c86b0d7e7328204f"} dynamic={[
color
]}>{`p.__jsx-style-dynamic-selector {color:${color}}`}</_JSXStyle>
<_JSXStyle id={styles.__hash}>{styles}</_JSXStyle>
</div>
;
export const Test5 = ()=><div className={"jsx-df0159ebd3f9fb6f"}>
<p className={"jsx-df0159ebd3f9fb6f"}>static only</p>
<_JSXStyle id={"ceba8c9ce34e3d0c"}>{"p.jsx-df0159ebd3f9fb6f {display:inline-block}"}</_JSXStyle>
<_JSXStyle id={"81a68341e430a972"}>{"p.jsx-df0159ebd3f9fb6f {color:red}"}</_JSXStyle>
</div>
;
export const Test6 = ({ color })=><div className={"jsx-ceba8c9ce34e3d0c " + _JSXStyle.dynamic([
"a55f0611581d64ca",
[
color
]
])}>
<p className={"jsx-ceba8c9ce34e3d0c " + _JSXStyle.dynamic([
"a55f0611581d64ca",
[
color
]
])}>static and dynamic</p>
<_JSXStyle id={"ceba8c9ce34e3d0c"}>{"p.jsx-ceba8c9ce34e3d0c {display:inline-block}"}</_JSXStyle>
<_JSXStyle id={"f0677f918e6b5a01"} dynamic={[
color
]}>{`p.__jsx-style-dynamic-selector {color:${color}}`}</_JSXStyle>
</div>
;
export const Test7 = ({ color })=><div className={_JSXStyle.dynamic([
"4adc07771eb3723",
[
color
]
])}>
<p className={_JSXStyle.dynamic([
"4adc07771eb3723",
[
color
]
])}>dynamic only</p>
<_JSXStyle id={"4adc07771eb3723"} dynamic={[
color
]}>{`p.__jsx-style-dynamic-selector {color:${color}}`}</_JSXStyle>
</div>
;
export const Test8 = ({ color })=>{
if (color) {
const innerProps = {
color
};
return <div className={_JSXStyle.dynamic([
"a552a4a765a9ca4e",
[
innerProps.color
]
])}>
<p className={_JSXStyle.dynamic([
"a552a4a765a9ca4e",
[
innerProps.color
]
])}>dynamic with scoped compound variable</p>
<_JSXStyle id={"a552a4a765a9ca4e"} dynamic={[
innerProps.color
]}>{`p.__jsx-style-dynamic-selector {color:${innerProps.color}}`}</_JSXStyle>
</div>;
}
};
export const Test9 = ({ color })=>{
const innerProps = {
color
};
return <div className={_JSXStyle.dynamic([
"7ef39e3312c80ead",
[
innerProps.color
]
])}>
<p className={_JSXStyle.dynamic([
"7ef39e3312c80ead",
[
innerProps.color
]
])}>dynamic with compound variable</p>
<_JSXStyle id={"7ef39e3312c80ead"} dynamic={[
innerProps.color
]}>{`p.__jsx-style-dynamic-selector {color:${innerProps.color}}`}</_JSXStyle>
</div>;
};
const foo = "red";
export const Test10 = ()=><div className={"jsx-688a08f9e73a5653"}>
<p className={"jsx-688a08f9e73a5653"}>dynamic with constant variable</p>
<_JSXStyle id={"688a08f9e73a5653"}>{`p.jsx-688a08f9e73a5653 {color:${foo}}`}</_JSXStyle>
</div>
;
export const Test11 = ({ color })=>{
const items = Array.from({
length: 5
}).map((item, i)=><li key={i} className={_JSXStyle.dynamic([
"ebe03db0369a2939",
[
color
]
]) + " " + "item"}>
<_JSXStyle id={"ebe03db0369a2939"} dynamic={[
color
]}>{`.item.__jsx-style-dynamic-selector {color:${color}}`}</_JSXStyle>
Item #{i + 1}
</li>
);
return <ul className="items">{items}</ul>;
};

View file

@ -0,0 +1,14 @@
export default class {
render() {
return (
<div>
<p>test</p>
<style jsx>{`
p {
color: red;
}
`}</style>
</div>
)
}
}

View file

@ -0,0 +1,9 @@
import _JSXStyle from "styled-jsx/style";
export default class {
render() {
return <div className={"jsx-b2b86d63f35d25ee"}>
<p className={"jsx-b2b86d63f35d25ee"}>test</p>
<_JSXStyle id={"b2b86d63f35d25ee"}>{"p.jsx-b2b86d63f35d25ee {color:red}"}</_JSXStyle>
</div>;
}
}

View file

@ -0,0 +1,11 @@
const Test = () => (
<div>
<span>test</span>
<Component />
<style jsx>{`
span {
color: red;
}
`}</style>
</div>
)

View file

@ -0,0 +1,7 @@
import _JSXStyle from "styled-jsx/style";
const Test = ()=><div className={"jsx-a9535d7d5f32c3c4"}>
<span className={"jsx-a9535d7d5f32c3c4"}>test</span>
<Component />
<_JSXStyle id={"a9535d7d5f32c3c4"}>{"span.jsx-a9535d7d5f32c3c4 {color:red}"}</_JSXStyle>
</div>
;

View file

@ -0,0 +1,16 @@
// TODO
// export const _JSXStyle = '_JSXStyle-literal'
// export default function() {
// return (
// <div>
// <p>test</p>
// <style jsx>{`
// p {
// color: red;
// }
// `}</style>
// </div>
// )
// }

View file

@ -0,0 +1,25 @@
import css from 'styled-jsx/css'
export default ({ children }) => (
<div>
<p>{children}</p>
<style jsx>{styles}</style>
</div>
)
const styles = css`
p {
color: red;
}
`
class Test extends React.Component {
render() {
return (
<div>
<p>{this.props.children}</p>
<style jsx>{styles}</style>
</div>
)
}
}

View file

@ -0,0 +1,16 @@
import _JSXStyle from "styled-jsx/style";
export default (({ children })=><div className={`jsx-${styles.__hash}`}>
<p className={`jsx-${styles.__hash}`}>{children}</p>
<_JSXStyle id={styles.__hash}>{styles}</_JSXStyle>
</div>
);
const styles = new String("p.jsx-556239d258b6d66a {color:red}");
styles.__hash = "556239d258b6d66a";
class Test extends React.Component {
render() {
return <div className={`jsx-${styles.__hash}`}>
<p className={`jsx-${styles.__hash}`}>{this.props.children}</p>
<_JSXStyle id={styles.__hash}>{styles}</_JSXStyle>
</div>;
}
}

View file

@ -0,0 +1,23 @@
const color = 'red'
const otherColor = 'green'
const A = () => (
<div>
<p>test</p>
<style jsx>{`p { color: ${color} }`}</style>
</div>
)
const B = () => (
<div>
<p>test</p>
<style jsx>{`p { color: ${otherColor} }`}</style>
</div>
)
export default () => (
<div>
<A />
<B />
</div>
)

View file

@ -0,0 +1,18 @@
import _JSXStyle from "styled-jsx/style";
const color = "red";
const otherColor = "green";
const A = ()=><div className={"jsx-706d62168f5e52dc"}>
<p className={"jsx-706d62168f5e52dc"}>test</p>
<_JSXStyle id={"706d62168f5e52dc"}>{`p.jsx-706d62168f5e52dc {color:${color}}`}</_JSXStyle>
</div>
;
const B = ()=><div className={"jsx-d698fce2ff7d98bb"}>
<p className={"jsx-d698fce2ff7d98bb"}>test</p>
<_JSXStyle id={"d698fce2ff7d98bb"}>{`p.jsx-d698fce2ff7d98bb {color:${otherColor}}`}</_JSXStyle>
</div>
;
export default (()=><div >
<A />
<B />
</div>
);

View file

@ -0,0 +1,32 @@
export default class {
render() {
const Element = 'div'
return (
<Element className="root">
<p>dynamic element</p>
<style jsx>{`
.root {
background: red;
}
`}</style>
</Element>
)
}
}
const Element2 = 'div'
export const Test2 = class {
render() {
return (
<Element2 className="root">
<p>dynamic element</p>
<style jsx>{`
.root {
background: red;
}
`}</style>
</Element2>
)
}
}

View file

@ -0,0 +1,19 @@
import _JSXStyle from "styled-jsx/style";
export default class {
render() {
const Element = "div";
return <Element className={"jsx-f825b24bbab5b83b" + " " + "root"}>
<p className={"jsx-f825b24bbab5b83b"}>dynamic element</p>
<_JSXStyle id={"f825b24bbab5b83b"}>{".root.jsx-f825b24bbab5b83b {background:red}"}</_JSXStyle>
</Element>;
}
}
const Element2 = "div";
export const Test2 = class {
render() {
return <Element2 className="root">
<p className={"jsx-f825b24bbab5b83b"}>dynamic element</p>
<_JSXStyle id={"f825b24bbab5b83b"}>{".root.jsx-f825b24bbab5b83b {background:red}"}</_JSXStyle>
</Element2>;
}
}

View file

@ -0,0 +1,12 @@
import styles from './styles2'
export default ({ level = 1 }) => {
const Element = `h${level}`
return (
<Element className="root">
<p>dynamic element</p>
<style jsx>{styles}</style>
</Element>
)
}

View file

@ -0,0 +1,9 @@
import _JSXStyle from "styled-jsx/style";
import styles from "./styles2";
export default (({ level =1 })=>{
const Element = `h${level}`;
return <Element className={`jsx-${styles.__hash}` + " " + "root"}>
<p className={`jsx-${styles.__hash}`}>dynamic element</p>
<_JSXStyle id={styles.__hash}>{styles}</_JSXStyle>
</Element>;
});

View file

@ -0,0 +1,43 @@
export default ({ level = 1 }) => {
const Element = `h${level}`
return (
<Element className="root">
<p>dynamic element</p>
<style jsx>{`
.root {
background: red;
}
`}</style>
</Element>
)
}
export const TestLowerCase = ({ level = 1 }) => {
const element = `h${level}`
return (
<element className="root">
<p>dynamic element</p>
<style jsx>{`
.root {
background: red;
}
`}</style>
</element>
)
}
const Element2 = 'div'
export const Test2 = () => {
return (
<Element2 className="root">
<p>dynamic element</p>
<style jsx>{`
.root {
background: red;
}
`}</style>
</Element2>
)
}

View file

@ -0,0 +1,22 @@
import _JSXStyle from "styled-jsx/style";
export default (({ level =1 })=>{
const Element = `h${level}`;
return <Element className={"jsx-fca64cc3f069b519" + " " + "root"}>
<p className={"jsx-fca64cc3f069b519"}>dynamic element</p>
<_JSXStyle id={"fca64cc3f069b519"}>{".root.jsx-fca64cc3f069b519 {background:red}"}</_JSXStyle>
</Element>;
});
export const TestLowerCase = ({ level =1 })=>{
const element = `h${level}`;
return <element className={"jsx-fca64cc3f069b519" + " " + "root"}>
<p className={"jsx-fca64cc3f069b519"}>dynamic element</p>
<_JSXStyle id={"fca64cc3f069b519"}>{".root.jsx-fca64cc3f069b519 {background:red}"}</_JSXStyle>
</element>;
};
const Element2 = "div";
export const Test2 = ()=>{
return <Element2 className="root">
<p className={"jsx-fca64cc3f069b519"}>dynamic element</p>
<_JSXStyle id={"fca64cc3f069b519"}>{".root.jsx-fca64cc3f069b519 {background:red}"}</_JSXStyle>
</Element2>;
};

View file

@ -0,0 +1,41 @@
// TODO
// import React, { Component } from 'react'
// export default class Index extends Component {
// static getInitialProps() {
// return { color: 'aquamarine' }
// }
// render() {
// return (
// <div>
// {[1, 2].map(idx => (
// <div key={idx}>
// {[3, 4].map(idx2 => (
// <div key={idx2}>{this.props.color}</div>
// ))}
// </div>
// ))}
// {[1, 2].map(idx => (
// <div key={idx}>
// <div>
// {this.props.color}
// <div className="something">
// <React.Fragment>
// <div>
// <div>{this.props.color} hello there</div>
// </div>
// </React.Fragment>
// </div>
// </div>
// </div>
// ))}
// <style jsx>{`
// div {
// background: ${this.props.color};
// }
// `}</style>
// </div>
// )
// }
// }

View file

@ -0,0 +1 @@
// module.exports = () => <p><style jsx>{`p { color:red; }`}</style></p>

View file

@ -0,0 +1 @@
// module.exports = () => <p><style>{`p { color:red; }`}</style></p>

View file

@ -0,0 +1,90 @@
const darken = c => c
const color = 'red'
const otherColor = 'green'
const mediumScreen = '680px'
const animationDuration = '200ms'
const animationName = 'my-cool-animation'
const obj = { display: 'block' }
export default ({ display }) => (
<div>
<p>test</p>
<style jsx>{`
p.${color} {
color: ${otherColor};
display: ${obj.display};
}
`}</style>
<style jsx>{'p { color: red }'}</style>
<style jsx global>{`
body {
background: ${color};
}
`}</style>
<style jsx global>{`
body {
background: ${color};
}
`}</style>
// TODO: the next two should have the same hash
<style jsx>{`
p {
color: ${color};
}
`}</style>
<style jsx>{`
p {
color: ${color};
}
`}</style>
<style jsx>{`
p {
color: ${darken(color)};
}
`}</style>
<style jsx>{`
p {
color: ${darken(color) + 2};
}
`}</style>
<style jsx>{`
@media (min-width: ${mediumScreen}) {
p {
color: green;
}
p {
color: ${`red`};
}
}
p {
color: red;
}
`}</style>
<style jsx>{`
p {
animation-duration: ${animationDuration};
}
`}</style>
<style jsx>{`
p {
animation: ${animationDuration} forwards ${animationName};
}
div {
background: ${color};
}
`}</style>
<style jsx>{`
span {
display: ${display ? 'block' : 'none'};
}
`}</style>
// TODO: causes bad syntax
{/* <style jsx>{`
span:before {
display: ${display ? 'block' : 'none'};
content: '\`';
}
`}</style> */}
</div>
)

View file

@ -0,0 +1,73 @@
import _JSXStyle from "styled-jsx/style";
const darken = (c)=>c
;
const color = "red";
const otherColor = "green";
const mediumScreen = "680px";
const animationDuration = "200ms";
const animationName = "my-cool-animation";
const obj = {
display: "block"
};
export default (({ display })=><div className={"jsx-802e359ee0ab20c4 " + _JSXStyle.dynamic([
"76aa3eae4e21e4ca",
[
display ? "block" : "none"
]
], [
"5ba905f763f40220",
[
darken(color) + 2
]
], [
"c4bb394aae9bd00b",
[
darken(color)
]
])}>
<p className={"jsx-802e359ee0ab20c4 " + _JSXStyle.dynamic([
"76aa3eae4e21e4ca",
[
display ? "block" : "none"
]
], [
"5ba905f763f40220",
[
darken(color) + 2
]
], [
"c4bb394aae9bd00b",
[
darken(color)
]
])}>test</p>
<_JSXStyle id={"f3d3af42197b4734"}>{`p.${color}.jsx-802e359ee0ab20c4 {color:${otherColor};
display:${obj.display}}`}</_JSXStyle>
<_JSXStyle id={"94239b6d6b42c9b5"}>{"p.jsx-802e359ee0ab20c4 {color:red}"}</_JSXStyle>
<_JSXStyle id={"32d670fe91cc2fa1"}>{`body {background:${color}}`}</_JSXStyle>
<_JSXStyle id={"3134af19d5bf663e"}>{`body {background:${color}}`}</_JSXStyle>
// TODO: the next two should have the same hash
<_JSXStyle id={"8e512e75f8a7c096"}>{`p.jsx-802e359ee0ab20c4 {color:${color}}`}</_JSXStyle>
<_JSXStyle id={"c25992945d0883cd"}>{`p.jsx-802e359ee0ab20c4 {color:${color}}`}</_JSXStyle>
<_JSXStyle id={"2f77c12736da5e9e"} dynamic={[
darken(color)
]}>{`p.__jsx-style-dynamic-selector {color:${darken(color)}}`}</_JSXStyle>
<_JSXStyle id={"10454d61c818c6eb"} dynamic={[
darken(color) + 2
]}>{`p.__jsx-style-dynamic-selector {color:${darken(color) + 2}}`}</_JSXStyle>
<_JSXStyle id={"281f7ed4d9150bac"}>{`@media (min-width:${mediumScreen}) {p.jsx-802e359ee0ab20c4 {color:green}
p.jsx-802e359ee0ab20c4 {color:${`red`}}}
p.jsx-802e359ee0ab20c4 {color:red}`}</_JSXStyle>
<_JSXStyle id={"d8950943ae4cf3dc"}>{`p.jsx-802e359ee0ab20c4 {-webkit-animation-duration:${animationDuration};
animation-duration:${animationDuration}}`}</_JSXStyle>
<_JSXStyle id={"1ee00865f92bdbe7"}>{`p.jsx-802e359ee0ab20c4 {-webkit-animation:${animationDuration} forwards ${animationName};
animation:${animationDuration} forwards ${animationName}}
div.jsx-802e359ee0ab20c4 {background:${color}}`}</_JSXStyle>
<_JSXStyle id={"4925b8e0a7ab752a"} dynamic={[
display ? "block" : "none"
]}>{`span.__jsx-style-dynamic-selector {display:${display ? "block" : "none"}}`}</_JSXStyle>
// TODO: causes bad syntax
{}
</div>
);

View file

@ -0,0 +1,5 @@
import css from 'styled-jsx/css'
function test() {
css.resolve`div { color: red }`
}

View file

@ -0,0 +1,7 @@
import _JSXStyle from "styled-jsx/style";
function test() {
({
styles: <_JSXStyle id={"abb4c2202db1a207"}>{"div.jsx-abb4c2202db1a207 {color:red}"}</_JSXStyle>,
className: "jsx-abb4c2202db1a207"
});
}

View file

@ -0,0 +1,19 @@
import styles, { foo as styles3 } from './styles'
const styles2 = require('./styles2')
export default () =>
<div>
<p>test</p>
<div>woot</div>
<p>woot</p>
<style jsx global>
{styles2}
</style>
<style jsx global>
{styles3}
</style>
<style jsx global>
{styles}
</style>
</div>

View file

@ -0,0 +1,12 @@
import _JSXStyle from "styled-jsx/style";
import styles, { foo as styles3 } from "./styles";
const styles2 = require("./styles2");
export default (()=><div >
<p >test</p>
<div >woot</div>
<p >woot</p>
<_JSXStyle id={styles2.__hash}>{styles2}</_JSXStyle>
<_JSXStyle id={styles3.__hash}>{styles3}</_JSXStyle>
<_JSXStyle id={styles.__hash}>{styles}</_JSXStyle>
</div>
);

View file

@ -0,0 +1,9 @@
import styles from './styles'
export default () =>
<div>
<p>test</p>
<style jsx>
{styles}
</style>
</div>

View file

@ -0,0 +1,7 @@
import _JSXStyle from "styled-jsx/style";
import styles from "./styles";
export default (()=><div className={`jsx-${styles.__hash}`}>
<p className={`jsx-${styles.__hash}`}>test</p>
<_JSXStyle id={styles.__hash}>{styles}</_JSXStyle>
</div>
);

View file

@ -0,0 +1,41 @@
import styles from './styles'
const styles2 = require('./styles2')
import { foo as styles3 } from './styles'
export default () => (
<div>
<p className="foo">test</p>
<p>woot</p>
<style jsx global>
{styles2}
</style>
<style jsx>{styles3}</style>
<div>woot</div>
<style jsx>{`
p {
color: red;
}
div {
color: green;
}
`}</style>
<style jsx>{styles}</style>
</div>
)
export const Test = () => (
<div>
<p className="foo">test</p>
<p>woot</p>
<style jsx>{styles3}</style>
<div>woot</div>
<style jsx>{`
p {
color: red;
}
div {
color: green;
}
`}</style>
</div>
)

View file

@ -0,0 +1,22 @@
import _JSXStyle from "styled-jsx/style";
import styles from "./styles";
const styles2 = require("./styles2");
import { foo as styles3 } from "./styles";
export default (()=><div className={"jsx-bee92b62eadf8a14 " + `jsx-${styles.__hash} jsx-${styles3.__hash}`}>
<p className={"jsx-bee92b62eadf8a14 " + `jsx-${styles.__hash} jsx-${styles3.__hash}` + " " + "foo"}>test</p>
<p className={"jsx-bee92b62eadf8a14 " + `jsx-${styles.__hash} jsx-${styles3.__hash}`}>woot</p>
<_JSXStyle id={styles2.__hash}>{styles2}</_JSXStyle>
<_JSXStyle id={styles3.__hash}>{styles3}</_JSXStyle>
<div className={"jsx-bee92b62eadf8a14 " + `jsx-${styles.__hash} jsx-${styles3.__hash}`}>woot</div>
<_JSXStyle id={"bee92b62eadf8a14"}>{"p.jsx-bee92b62eadf8a14 {color:red}\ndiv.jsx-bee92b62eadf8a14 {color:green}"}</_JSXStyle>
<_JSXStyle id={styles.__hash}>{styles}</_JSXStyle>
</div>
);
export const Test = ()=><div className={"jsx-bee92b62eadf8a14 " + `jsx-${styles3.__hash}`}>
<p className={"jsx-bee92b62eadf8a14 " + `jsx-${styles3.__hash}` + " " + "foo"}>test</p>
<p className={"jsx-bee92b62eadf8a14 " + `jsx-${styles3.__hash}`}>woot</p>
<_JSXStyle id={styles3.__hash}>{styles3}</_JSXStyle>
<div className={"jsx-bee92b62eadf8a14 " + `jsx-${styles3.__hash}`}>woot</div>
<_JSXStyle id={"bee92b62eadf8a14"}>{"p.jsx-bee92b62eadf8a14 {color:red}\ndiv.jsx-bee92b62eadf8a14 {color:green}"}</_JSXStyle>
</div>
;

View file

@ -0,0 +1,51 @@
import React from 'react'
export default () => (
<>
<p>Testing!!!</p>
<p className="foo">Bar</p>
<>
<h3 id="head">Title...</h3>
<React.Fragment>
<p>hello</p>
<>
<p>foo</p>
<p>bar</p>
</>
<p>world</p>
</React.Fragment>
</>
<style jsx>{`
p {
color: cyan;
}
.foo {
font-size: 18px;
color: hotpink;
}
#head {
text-decoration: underline;
}
`}</style>
</>
)
function Component1() {
return (
<>
<div>test</div>
</>
)
}
function Component2() {
return (
<div>
<style jsx>{`
div {
color: red;
}
`}</style>
</div>
)
}

View file

@ -0,0 +1,29 @@
import _JSXStyle from "styled-jsx/style";
import React from "react";
export default (()=><>
<p className={"jsx-6dd5f97e085c0297"}>Testing!!!</p>
<p className={"jsx-6dd5f97e085c0297" + " " + "foo"}>Bar</p>
<>
<h3 id="head" className={"jsx-6dd5f97e085c0297"}>Title...</h3>
<React.Fragment >
<p className={"jsx-6dd5f97e085c0297"}>hello</p>
<>
<p className={"jsx-6dd5f97e085c0297"}>foo</p>
<p className={"jsx-6dd5f97e085c0297"}>bar</p>
</>
<p className={"jsx-6dd5f97e085c0297"}>world</p>
</React.Fragment>
</>
<_JSXStyle id={"6dd5f97e085c0297"}>{"p.jsx-6dd5f97e085c0297 {color:cyan}\n.foo.jsx-6dd5f97e085c0297 {font-size:18px;\ncolor:hotpink}\n#head.jsx-6dd5f97e085c0297 {text-decoration:underline}"}</_JSXStyle>
</>
);
function Component1() {
return <>
<div >test</div>
</>;
}
function Component2() {
return <div className={"jsx-678f41ca6d3b294b"}>
<_JSXStyle id={"678f41ca6d3b294b"}>{"div.jsx-678f41ca6d3b294b {color:red}"}</_JSXStyle>
</div>;
}

View file

@ -0,0 +1,22 @@
const Test = () => (
<div>
<style jsx global>{`
body {
color: red
}
:hover { color: red; display: flex;
animation: foo 1s ease-out }
div a {
display: none;
}
[data-test] > div {
color: red;
}
`}</style>
</div>
)
const Test2 = () => <style global jsx>{'p { color: red }'}</style>

View file

@ -0,0 +1,7 @@
import _JSXStyle from "styled-jsx/style";
const Test = ()=><div className={"jsx-d47d6adadf14e957"}>
<_JSXStyle id={"d47d6adadf14e957"}>{"body {color:red}\n:hover {color:red;\ndisplay:-webkit-box;\ndisplay:-webkit-flex;\ndisplay:-ms-flexbox;\ndisplay:flex;\n-webkit-animation:foo 1s ease-out;\nanimation:foo 1s ease-out}\ndiv a {display:none}\n[data-test] >div {color:red}"}</_JSXStyle>
</div>
;
const Test2 = ()=><style global jsx>{"p { color: red }"}</style>
;

View file

@ -0,0 +1,9 @@
const Test = () => <style global jsx>{'p { color: red }'}</style>
export default () => (
<div>
<p>test</p>
<style jsx global>{`body { background: red }`}</style>
<style jsx>{'p { color: red }'}</style>
</div>
)

View file

@ -0,0 +1,9 @@
import _JSXStyle from "styled-jsx/style";
const Test = ()=><style global jsx>{"p { color: red }"}</style>
;
export default (()=><div className={"jsx-3822e6e1fb9fa41a"}>
<p className={"jsx-3822e6e1fb9fa41a"}>test</p>
<_JSXStyle id={"b7efb453c85593c1"}>{"body {background:red}"}</_JSXStyle>
<_JSXStyle id={"94239b6d6b42c9b5"}>{"p.jsx-3822e6e1fb9fa41a {color:red}"}</_JSXStyle>
</div>
);

View file

@ -0,0 +1,43 @@
const attrs = {
id: 'test'
}
const Test1 = () => (
<div>
<span {...attrs} data-test="test">test</span>
<Component />
<style jsx>{`
span {
color: red;
}
`}</style>
</div>
)
const Test2 = () => <span>test</span>
const Test3 = () => (
<div>
<span>test</span>
<style jsx>{`
span {
color: red;
}
`}</style>
</div>
)
export default class {
render () {
return (
<div>
<p>test</p>
<style jsx>{`
p {
color: red;
}
`}</style>
</div>
)
}
}

View file

@ -0,0 +1,25 @@
import _JSXStyle from "styled-jsx/style";
const attrs = {
id: "test"
};
const Test1 = ()=><div className={"jsx-a9535d7d5f32c3c4"}>
<span {...attrs} data-test="test" className={"jsx-a9535d7d5f32c3c4" + " " + (attrs && attrs.className != null && attrs.className || "")}>test</span>
<Component />
<_JSXStyle id={"a9535d7d5f32c3c4"}>{"span.jsx-a9535d7d5f32c3c4 {color:red}"}</_JSXStyle>
</div>
;
const Test2 = ()=><span >test</span>
;
const Test3 = ()=><div className={"jsx-a9535d7d5f32c3c4"}>
<span className={"jsx-a9535d7d5f32c3c4"}>test</span>
<_JSXStyle id={"a9535d7d5f32c3c4"}>{"span.jsx-a9535d7d5f32c3c4 {color:red}"}</_JSXStyle>
</div>
;
export default class {
render() {
return <div className={"jsx-b2b86d63f35d25ee"}>
<p className={"jsx-b2b86d63f35d25ee"}>test</p>
<_JSXStyle id={"b2b86d63f35d25ee"}>{"p.jsx-b2b86d63f35d25ee {color:red}"}</_JSXStyle>
</div>;
}
}

View file

@ -0,0 +1,7 @@
export default () => (
<div>
<p>woot</p>
<style dangerouslySetInnerHTML={{ __html: `body { margin: 0; }` }}></style>
<style jsx>{'p { color: red }'}</style>
</div>
)

View file

@ -0,0 +1,9 @@
import _JSXStyle from "styled-jsx/style";
export default (()=><div className={"jsx-94239b6d6b42c9b5"}>
<p className={"jsx-94239b6d6b42c9b5"}>woot</p>
<style dangerouslySetInnerHTML={{
__html: `body { margin: 0; }`
}}></style>
<_JSXStyle id={"94239b6d6b42c9b5"}>{"p.jsx-94239b6d6b42c9b5 {color:red}"}</_JSXStyle>
</div>
);

View file

@ -0,0 +1,30 @@
import css from 'hell'
const color = 'red'
const bar = css`
div {
font-size: 3em;
}
`
export const uh = bar
export const foo = css`div { color: ${color}}`
export default css`
div {
font-size: 3em;
}
p {
color: ${color};
}
`
const Title = styled.h1`
color: red;
font-size: 50px;
`
const AnotherTitle = Title.extend`color: blue;`
export const Component = () => <AnotherTitle>My page</AnotherTitle>

View file

@ -0,0 +1,24 @@
import css from "hell";
const color = "red";
const bar = css`
div {
font-size: 3em;
}
`;
export const uh = bar;
export const foo = css`div { color: ${color}}`;
export default css`
div {
font-size: 3em;
}
p {
color: ${color};
}
`;
const Title = styled.h1`
color: red;
font-size: 50px;
`;
const AnotherTitle = Title.extend`color: blue;`;
export const Component = ()=><AnotherTitle >My page</AnotherTitle>
;

View file

@ -0,0 +1,8 @@
// TODO: needs sourcemaps
// export default () => (
// <div>
// <p>test</p>
// <p>woot</p>
// <style jsx>{'p { color: red }'}</style>
// </div>
// )

View file

@ -0,0 +1,8 @@
export default () => (
<div>
<p>test</p>
<p>woot</p>
<p>woot</p>
<style jsx>{'p { color: red }'}</style>
</div>
)

View file

@ -0,0 +1,8 @@
import _JSXStyle from "styled-jsx/style";
export default (()=><div className={"jsx-94239b6d6b42c9b5"}>
<p className={"jsx-94239b6d6b42c9b5"}>test</p>
<p className={"jsx-94239b6d6b42c9b5"}>woot</p>
<p className={"jsx-94239b6d6b42c9b5"}>woot</p>
<_JSXStyle id={"94239b6d6b42c9b5"}>{"p.jsx-94239b6d6b42c9b5 {color:red}"}</_JSXStyle>
</div>
);

View file

@ -0,0 +1,13 @@
// TODO
// import css from 'styled-jsx/css'
// const color = 'red'
// export const foo = css`div { color: ${color}}`
// const props = { color: 'red ' }
// export default css`
// div { font-size: 3em; color: ${props.color} }
// p { color: ${this.props.color};}
// `

View file

@ -0,0 +1,11 @@
// TODO
// import css from 'styled-jsx/css'
// const color = 'red'
// export const foo = css`div { color: ${color}}`
// export default css`
// div { font-size: 3em }
// p { color: ${props.color};}
// `

View file

@ -0,0 +1,58 @@
import css, { resolve, global } from 'styled-jsx/css'
import colors, { size } from './constants'
const color = 'red'
const bar = css`
div {
font-size: 3em;
}
`
const baz = css.global`
div {
font-size: 3em;
}
`
const a = global`
div {
font-size: ${size}em;
}
`
export const uh = bar
export const foo = css`div { color: ${color}}`
// TODO: the next 3 should not transformed as dynamic
css.resolve`
div {
color: ${colors.green.light};
}
a { color: red }
`
const b = resolve`
div {
color: ${colors.green.light};
}
a { color: red }
`
const dynamic = colors => {
const b = resolve`
div {
color: ${colors.green.light};
}
a { color: red }
`
}
export default css.resolve`
div {
font-size: 3em;
}
p {
color: ${color};
}
`

View file

@ -0,0 +1,55 @@
import _JSXStyle from "styled-jsx/style";
import colors, { size } from "./constants";
const color = "red";
const bar = new String("div.jsx-aaed0341accea8f {font-size:3em}");
bar.__hash = "aaed0341accea8f";
const baz = new String("div {font-size:3em}");
baz.__hash = "aaed0341accea8f";
const a = new String(`div {font-size:${size}em}`);
a.__hash = "611c3773b76f8ea3";
export const uh = bar;
export const foo = new String(`div.jsx-e46da79e05d23fff {color:${color}}`);
foo.__hash = "e46da79e05d23fff";
({
styles: <_JSXStyle id={"12e2c41a8d37fe78"} dynamic={[
colors.green.light
]}>{`div.__jsx-style-dynamic-selector {color:${colors.green.light}}
a.__jsx-style-dynamic-selector {color:red}`}</_JSXStyle>,
className: _JSXStyle.dynamic([
"12e2c41a8d37fe78",
[
colors.green.light
]
])
});
const b = {
styles: <_JSXStyle id={"d5617758481f82cd"} dynamic={[
colors.green.light
]}>{`div.__jsx-style-dynamic-selector {color:${colors.green.light}}
a.__jsx-style-dynamic-selector {color:red}`}</_JSXStyle>,
className: _JSXStyle.dynamic([
"d5617758481f82cd",
[
colors.green.light
]
])
};
const dynamic = (colors)=>{
const b = {
styles: <_JSXStyle id={"a7ac579c753e9d90"} dynamic={[
colors.green.light
]}>{`div.__jsx-style-dynamic-selector {color:${colors.green.light}}
a.__jsx-style-dynamic-selector {color:red}`}</_JSXStyle>,
className: _JSXStyle.dynamic([
"a7ac579c753e9d90",
[
colors.green.light
]
])
};
};
export default {
styles: <_JSXStyle id={"e4642e4164827181"}>{`div.jsx-e4642e4164827181 {font-size:3em}
p.jsx-e4642e4164827181 {color:${color}}`}</_JSXStyle>,
className: "jsx-e4642e4164827181"
};

View file

@ -0,0 +1,6 @@
// TODO: support common js
// import css from 'styled-jsx/css'
// module.exports = css`
// div { font-size: 3em }
// `

View file

@ -0,0 +1,176 @@
export default () => (
<div>
<p>test</p>
<style jsx>{`
html {
background-image:
linear-gradient(0deg, rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.8)),
url(/static/background.svg);
}
:global(p) {
color: blue
}
:global(p){
color: blue;
}
:global(p), a {
color: blue;
}
:global(.foo + a) {
color: red;
}
:global(body) {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
}
p {
color: red;
}
p{
color: red
}
* {
color: blue;
}
[href="woot"] {
color: red;
}
p a span {
color: red;
}
p :global(span) {
background: blue
}
p a[title="'w ' ' t'"] {
margin: auto
}
p :global(span:not(.test)) {
color: green
}
p, h1 {
color: blue;
animation: hahaha 3s ease forwards infinite;
animation-name: hahaha;
animation-delay: 100ms;
}
p {
animation: hahaha 1s, hehehe 2s;
}
p:hover {
color: red;
}
p::before {
color: red;
}
:hover {
color: red;
}
::before {
color: red;
}
:hover p {
color: red;
}
p + a {
color: red;
}
p ~ a {
color: red;
}
p > a {
color: red;
}
@keyframes hahaha {
from { top: 0 }
to { top: 100 }
}
@keyframes hehehe {
from { left: 0 }
to { left: 100 }
}
@media (min-width: 500px) {
.test {
color: red;
}
}
.test {
/* test, test */
display: block;
/*
test
*/
}
.inline-flex {
display: inline-flex;
}
.flex {
display: flex;
}
.test {
box-shadow: 0 0 10px black, inset 0 0 5px black
}
.test[title=","] {
display: inline-block;
}
.test.is-status .test {
color: red;
}
.a-selector:hover,
.a-selector:focus
{
outline: none;
}
@media (min-width: 1px) and (max-width: 768px) {
[class*='grid__col--'] {
margin-top: 12px;
margin-bottom: 12px;
}
}
@media (max-width: 64em) {
.test {
margin-bottom: 1em;
}
@supports (-moz-appearance: none) and (display: contents) {
.test {
margin-bottom: 2rem;
}
}
}
`}</style>
</div>
)

View file

@ -0,0 +1,6 @@
import _JSXStyle from "styled-jsx/style";
export default (()=><div className={"jsx-768337a97aceabd1"}>
<p className={"jsx-768337a97aceabd1"}>test</p>
<_JSXStyle id={"768337a97aceabd1"}>{"html.jsx-768337a97aceabd1 {background-image:linear-gradient(0deg, rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.8)), url(/static/background.svg)}\np {color:blue}\np {color:blue}\np, a.jsx-768337a97aceabd1 {color:blue}\n.foo +a {color:red}\nbody {font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif}\np.jsx-768337a97aceabd1 {color:red}\np.jsx-768337a97aceabd1 {color:red}\n*.jsx-768337a97aceabd1 {color:blue}\n[href = 'woot'].jsx-768337a97aceabd1 {color:red}\np.jsx-768337a97aceabd1 a.jsx-768337a97aceabd1 span.jsx-768337a97aceabd1 {color:red}\np.jsx-768337a97aceabd1 span {background:blue}\np.jsx-768337a97aceabd1 a[title = ''w ' ' t''].jsx-768337a97aceabd1 {margin:auto}\np.jsx-768337a97aceabd1 span:not(.test) {color:green}\np.jsx-768337a97aceabd1, h1.jsx-768337a97aceabd1 {color:blue;\n-webkit-animation:hahaha 3s ease forwards infinite;\nanimation:hahaha 3s ease forwards infinite;\n-webkit-animation-name:hahaha;\nanimation-name:hahaha;\nanimation-delay:100ms}\np.jsx-768337a97aceabd1 {-webkit-animation:hahaha 1s, hehehe 2s;\nanimation:hahaha 1s, hehehe 2s}\np.jsx-768337a97aceabd1:hover {color:red}\np.jsx-768337a97aceabd1::before {color:red}\n.jsx-768337a97aceabd1:hover {color:red}\n.jsx-768337a97aceabd1::before {color:red}\n.jsx-768337a97aceabd1:hover p.jsx-768337a97aceabd1 {color:red}\np.jsx-768337a97aceabd1 +a.jsx-768337a97aceabd1 {color:red}\np.jsx-768337a97aceabd1 ~a.jsx-768337a97aceabd1 {color:red}\np.jsx-768337a97aceabd1 >a.jsx-768337a97aceabd1 {color:red}\n@keyframes hahaha {from {top:0}to {top:100}}\n@keyframes hehehe {from {left:0}to {left:100}}\n@media (min-width:500px) {.test.jsx-768337a97aceabd1 {color:red}}\n.test.jsx-768337a97aceabd1 {display:block}\n.inline-flex.jsx-768337a97aceabd1 {display:-webkit-inline-box;\ndisplay:-webkit-inline-flex;\ndisplay:-ms-inline-flexbox;\ndisplay:inline-flex}\n.flex.jsx-768337a97aceabd1 {display:-webkit-box;\ndisplay:-webkit-flex;\ndisplay:-ms-flexbox;\ndisplay:flex}\n.test.jsx-768337a97aceabd1 {box-shadow:0 0 10px black, inset 0 0 5px black}\n.test[title = ','].jsx-768337a97aceabd1 {display:inline-block}\n.test.is-status.jsx-768337a97aceabd1 .test.jsx-768337a97aceabd1 {color:red}\n.a-selector.jsx-768337a97aceabd1:hover, .a-selector.jsx-768337a97aceabd1:focus {outline:none}\n@media (min-width:1px) and (max-width:768px) {[class *= 'grid__col--'].jsx-768337a97aceabd1 {margin-top:12px;\nmargin-bottom:12px}}\n@media (max-width:64em) {.test.jsx-768337a97aceabd1 {margin-bottom:1em}\n@supports (-moz-appearance:none) and (display:contents) {.test.jsx-768337a97aceabd1 {margin-bottom:2rem}}}"}</_JSXStyle>
</div>
);

View file

@ -0,0 +1,12 @@
export default () => (
<div>
<p>test</p>
<p>woot</p>
<p>woot</p>
<style jsx>
{
'p { color: red }'
}
</style>
</div>
)

View file

@ -0,0 +1,8 @@
import _JSXStyle from "styled-jsx/style";
export default (()=><div className={"jsx-94239b6d6b42c9b5"}>
<p className={"jsx-94239b6d6b42c9b5"}>test</p>
<p className={"jsx-94239b6d6b42c9b5"}>woot</p>
<p className={"jsx-94239b6d6b42c9b5"}>woot</p>
<_JSXStyle id={"94239b6d6b42c9b5"}>{"p.jsx-94239b6d6b42c9b5 {color:red}"}</_JSXStyle>
</div>
);