diff --git a/examples/with-emotion-swc/.gitignore b/examples/with-emotion-swc/.gitignore new file mode 100644 index 000000000000..1437c53f70bc --- /dev/null +++ b/examples/with-emotion-swc/.gitignore @@ -0,0 +1,34 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env.local +.env.development.local +.env.test.local +.env.production.local + +# vercel +.vercel diff --git a/examples/with-emotion-swc/README.md b/examples/with-emotion-swc/README.md new file mode 100644 index 000000000000..12b4f677a8d0 --- /dev/null +++ b/examples/with-emotion-swc/README.md @@ -0,0 +1,31 @@ +# Emotion Example + +Extract and inline critical css with +[@emotion/css](https://github.com/emotion-js/emotion/tree/master/packages/css), +[@emotion/server](https://github.com/emotion-js/emotion/tree/master/packages/server), +[@emotion/react](https://github.com/emotion-js/emotion/tree/master/packages/react), +and [@emotion/styled](https://github.com/emotion-js/emotion/tree/master/packages/styled). + +## Preview + +Preview the example live on [StackBlitz](http://stackblitz.com/): + +[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/vercel/next.js/tree/canary/examples/with-emotion) + +## Deploy your own + +Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example): + +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/with-emotion&project-name=with-emotion&repository-name=with-emotion) + +## How to use + +Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the example: + +```bash +npx create-next-app --example with-emotion with-emotion-app +# or +yarn create next-app --example with-emotion with-emotion-app +``` + +Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)). diff --git a/examples/with-emotion-swc/next.config.js b/examples/with-emotion-swc/next.config.js new file mode 100644 index 000000000000..589e094972ac --- /dev/null +++ b/examples/with-emotion-swc/next.config.js @@ -0,0 +1,12 @@ +/** @type {import('next').NextConfig} */ + +const nextConfig = { + reactStrictMode: true, + compiler: { + emotion: { + enabled: true, + }, + }, +} + +module.exports = nextConfig diff --git a/examples/with-emotion-swc/package.json b/examples/with-emotion-swc/package.json new file mode 100644 index 000000000000..411e3fcb8efb --- /dev/null +++ b/examples/with-emotion-swc/package.json @@ -0,0 +1,15 @@ +{ + "private": true, + "scripts": { + "dev": "next", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "@emotion/react": "11.8.1", + "@emotion/styled": "11.8.1", + "next": "latest", + "react": "17.0.2", + "react-dom": "17.0.2" + } +} diff --git a/examples/with-emotion-swc/pages/_app.js b/examples/with-emotion-swc/pages/_app.js new file mode 100644 index 000000000000..aaa132572121 --- /dev/null +++ b/examples/with-emotion-swc/pages/_app.js @@ -0,0 +1,10 @@ +import { globalStyles } from '../shared/styles' + +const App = ({ Component, pageProps }) => ( + <> + {globalStyles} + + +) + +export default App diff --git a/examples/with-emotion-swc/pages/index.js b/examples/with-emotion-swc/pages/index.js new file mode 100644 index 000000000000..e1300eb342ad --- /dev/null +++ b/examples/with-emotion-swc/pages/index.js @@ -0,0 +1,14 @@ +import { Animated, Basic, bounce, Combined, Pink } from '../shared/styles' + +const Home = () => ( +
+ Cool Styles + Pink text + + With :hover. + + Let's bounce. +
+) + +export default Home diff --git a/examples/with-emotion-swc/shared/styles.js b/examples/with-emotion-swc/shared/styles.js new file mode 100644 index 000000000000..cb9720bbfeba --- /dev/null +++ b/examples/with-emotion-swc/shared/styles.js @@ -0,0 +1,72 @@ +import { css, Global, keyframes } from '@emotion/react' +import styled from '@emotion/styled' + +export const globalStyles = ( + +) + +export const basicStyles = css({ + backgroundColor: 'white', + color: 'cornflowerblue', + border: '1px solid lightgreen', + borderRight: 'none', + borderBottom: 'none', + boxShadow: '5px 5px 0 0 lightgreen, 10px 10px 0 0 lightyellow', + transition: 'all 0.1s linear', + margin: '3rem 0', + padding: '1rem 0.5rem', +}) + +export const hoverStyles = css` + &:hover { + color: white; + background-color: lightgray; + border-color: aqua; + box-shadow: -15px -15px 0 0 aqua, -30px -30px 0 0 cornflowerblue; + } +` +export const bounce = keyframes` + from { + transform: scale(1.01); + } + to { + transform: scale(0.99); + } +` + +export const Basic = styled.div` + ${basicStyles}; +` + +export const Combined = styled.div` + ${basicStyles}; + ${hoverStyles}; + & code { + background-color: linen; + } +` + +export const Pink = styled.div(basicStyles, { + color: 'hotpink', +}) + +export const Animated = styled.div` + ${basicStyles}; + ${hoverStyles}; + & code { + background-color: linen; + } + animation: ${({ animation }) => animation} 0.2s infinite ease-in-out alternate; +` diff --git a/package.json b/package.json index 162cce7f1e6c..962abe81bb28 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "@babel/plugin-proposal-object-rest-spread": "7.14.7", "@babel/preset-flow": "7.14.5", "@babel/preset-react": "7.14.5", + "@emotion/babel-plugin": "11.7.2", "@fullhuman/postcss-purgecss": "1.3.0", "@mdx-js/loader": "0.18.0", "@svgr/webpack": "5.5.0", @@ -148,9 +149,9 @@ "react-dom": "17.0.2", "react-dom-18": "npm:react-dom@18.0.0-rc.0", "react-ssr-prepass": "1.0.8", + "react-virtualized": "9.22.3", "relay-compiler": "13.0.2", "relay-runtime": "13.0.2", - "react-virtualized": "9.22.3", "release": "6.3.0", "request-promise-core": "1.1.2", "resolve-from": "5.0.0", diff --git a/packages/next-swc/Cargo.lock b/packages/next-swc/Cargo.lock index 69cba72cb2d9..2cc86cfcfaf2 100644 --- a/packages/next-swc/Cargo.lock +++ b/packages/next-swc/Cargo.lock @@ -805,6 +805,7 @@ checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" name = "next-swc" version = "0.0.0" dependencies = [ + "base64 0.13.0", "byteorder", "chrono", "easy-error", @@ -1355,9 +1356,9 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" [[package]] name = "relative-path" -version = "1.5.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9629de8974fd69c97684736786b807edd3da456d3e3f95341dd9d4cbd8f5ad6" +checksum = "a49a831dc1e13c9392b660b162333d4cb0033bbbdfe6a1687177e59e89037c86" [[package]] name = "remove_dir_all" diff --git a/packages/next-swc/crates/core/Cargo.toml b/packages/next-swc/crates/core/Cargo.toml index e61bcb720069..c069399b8213 100644 --- a/packages/next-swc/crates/core/Cargo.toml +++ b/packages/next-swc/crates/core/Cargo.toml @@ -7,6 +7,7 @@ version = "0.0.0" crate-type = ["cdylib", "rlib"] [dependencies] +base64 = "0.13" byteorder = "1" chrono = "0.4" easy-error = "1.0.0" diff --git a/packages/next-swc/crates/core/src/emotion/mod.rs b/packages/next-swc/crates/core/src/emotion/mod.rs index a26456c60fe1..579e5e45de96 100644 --- a/packages/next-swc/crates/core/src/emotion/mod.rs +++ b/packages/next-swc/crates/core/src/emotion/mod.rs @@ -1,10 +1,11 @@ -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::sync::Arc; use fxhash::FxHashMap; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; -use swc_common::{SourceMap, DUMMY_SP}; +use swc::sourcemap::{RawToken, SourceMap as RawSourcemap}; +use swc_common::{BytePos, SourceMap, DUMMY_SP}; use swc_ecmascript::ast::{ ExprOrSpread, Ident, KeyValueProp, Lit, MemberProp, ObjectLit, Pat, Prop, PropName, PropOrSpread, VarDeclarator, @@ -35,15 +36,13 @@ static EMOTION_OFFICIAL_LIBRARIES: Lazy> = Lazy::new(|| }); #[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] pub struct EmotionOptions { pub enabled: Option, pub sourcemap: Option, pub auto_label: Option, pub label_format: Option, - pub css_prop_optimization: Option, pub custom_modules: Option>, - pub jsx_factory: Option, - pub jsx_import_source: Option, } impl Default for EmotionOptions { @@ -53,10 +52,7 @@ impl Default for EmotionOptions { sourcemap: Some(true), auto_label: Some(true), label_format: Some("[local]".to_owned()), - css_prop_optimization: Some(true), custom_modules: None, - jsx_import_source: Some("@emotion/react".to_owned()), - jsx_factory: None, } } } @@ -102,14 +98,14 @@ struct PackageMeta { pub fn emotion( emotion_options: EmotionOptions, - file_name: &PathBuf, + path: &Path, cm: Arc, react_jsx_runtime: bool, es_module_interop: bool, ) -> impl Fold { EmotionTransformer::new( emotion_options, - file_name, + path, cm, react_jsx_runtime, es_module_interop, @@ -134,7 +130,7 @@ pub struct EmotionTransformer { impl EmotionTransformer { pub fn new( options: EmotionOptions, - path: &PathBuf, + path: &Path, cm: Arc, react_jsx_runtime: bool, es_module_interop: bool, @@ -171,13 +167,15 @@ impl EmotionTransformer { self.filepath_hash.unwrap() } - fn create_label(&self) -> String { + fn create_label(&self, with_prefix: bool) -> String { + let prefix = if with_prefix { "label:" } else { "" }; let mut label = format!( - "label:{}", + "{}{}", + prefix, self.options .label_format .clone() - .unwrap_or("[local]".to_owned()) + .unwrap_or_else(|| "[local]".to_owned()) ); if let Some(current_context) = &self.current_context { label = label.replace("[local]", current_context); @@ -191,6 +189,35 @@ impl EmotionTransformer { label } + fn create_sourcemap(&mut self, pos: BytePos) -> Option { + if self.options.sourcemap.unwrap_or(false) { + let loc = self.cm.get_code_map().lookup_char_pos(pos); + let filename = self.filepath.to_str().map(|s| s.to_owned()); + let cm = RawSourcemap::new( + filename.clone(), + vec![RawToken { + dst_line: 0, + dst_col: 0, + src_line: loc.line as u32 - 1, + src_col: loc.col_display as u32, + src_id: 0, + name_id: 0, + }], + Vec::new(), + vec![filename.unwrap_or_default()], + Some(vec![Some(loc.file.src.to_string())]), + ); + let mut writer = Vec::new(); + if cm.to_writer(&mut writer).is_ok() { + return Some(format!( + "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,{} */", + base64::encode(writer) + )); + } + } + None + } + // Find the imported name from modules // These import statements are supported: // import styled from '@emotion/styled' @@ -292,12 +319,16 @@ impl Fold for EmotionTransformer { if self.options.auto_label.unwrap_or(false) { expr.args.push(ExprOrSpread { spread: None, - expr: Box::new(Expr::Lit(Lit::Str(self.create_label().into()))), + expr: Box::new(Expr::Lit(Lit::Str( + self.create_label(true).into(), + ))), }); } - if self.options.sourcemap.unwrap_or(false) { - let _loc = self.cm.get_code_map().lookup_char_pos(expr.span.lo()); - // generate sourcemap + if let Some(cm) = self.create_sourcemap(expr.span.lo) { + expr.args.push(ExprOrSpread { + spread: None, + expr: Box::new(Expr::Lit(Lit::Str(cm.into()))), + }); } } } @@ -308,33 +339,34 @@ impl Fold for EmotionTransformer { if let Expr::Ident(i) = callee_exp.as_ref() { if let Some(package) = self.import_packages.get(i.as_ref()) { if !c.args.is_empty() && matches!(package.kind, ExprKind::Styled) { + let mut args_props = Vec::with_capacity(2); + args_props.push(self.create_target_arg_node()); if self.options.auto_label.unwrap_or(false) { - c.args.push(ExprOrSpread { + args_props.push(PropOrSpread::Prop(Box::new( + Prop::KeyValue(KeyValueProp { + key: PropName::Ident(Ident::new( + "label".into(), + DUMMY_SP, + )), + value: Box::new(Expr::Lit(Lit::Str( + self.create_label(false).into(), + ))), + }), + ))); + } + if let Some(cm) = self.create_sourcemap(expr.span.lo()) { + expr.args.push(ExprOrSpread { spread: None, - expr: Box::new(Expr::Object(ObjectLit { - span: DUMMY_SP, - props: vec![ - self.create_target_arg_node(), - PropOrSpread::Prop(Box::new(Prop::KeyValue( - KeyValueProp { - key: PropName::Ident(Ident::new( - "label".into(), - DUMMY_SP, - )), - value: Box::new(Expr::Lit(Lit::Str( - self.create_label().into(), - ))), - }, - ))), - ], - })), + expr: Box::new(Expr::Lit(Lit::Str(cm.into()))), }); } - if self.options.sourcemap.unwrap_or(false) { - let _loc = - self.cm.get_code_map().lookup_char_pos(expr.span.lo()); - // generate sourcemap - } + c.args.push(ExprOrSpread { + spread: None, + expr: Box::new(Expr::Object(ObjectLit { + span: DUMMY_SP, + props: args_props, + })), + }); } } } @@ -345,77 +377,74 @@ impl Fold for EmotionTransformer { Expr::Member(m) => { if let Expr::Ident(i) = m.obj.as_ref() { if let Some(package) = self.import_packages.get(i.as_ref()) { - if self.options.auto_label.unwrap_or(false) { - match package.kind { - ExprKind::Css => { + match package.kind { + ExprKind::Css => { + if self.options.auto_label.unwrap_or(false) { expr.args.push(ExprOrSpread { spread: None, expr: Box::new(Expr::Lit(Lit::Str( - self.create_label().into(), + self.create_label(true).into(), ))), }); } - ExprKind::Styled => { - if let MemberProp::Ident(prop) = &m.prop { - return CallExpr { - span: expr.span, - type_args: expr.type_args, - args: expr.args, - callee: Callee::Expr(Box::new(Expr::Call( - CallExpr { - span: DUMMY_SP, - type_args: None, - callee: Callee::Expr(Box::new( - Expr::Ident(Ident::new( - i.sym.clone(), - i.span, - )), - )), - args: vec![ - ExprOrSpread { - spread: None, - expr: Box::new(Expr::Lit( - Lit::Str(prop.as_ref().into()), - )), - }, - ExprOrSpread { - spread: None, - expr: Box::new(Expr::Object( - ObjectLit { - span: DUMMY_SP, - props: vec![ - self.create_target_arg_node(), - PropOrSpread::Prop(Box::new( - Prop::KeyValue(KeyValueProp { - key: PropName::Ident( - Ident::new( - "label".into(), - DUMMY_SP, - ), - ), - value: Box::new(Expr::Lit( - Lit::Str( - self.create_label() - .into(), - ), - )), - }), - )), - ], - }, - )), - }, - ], - }, - ))), - }; - } + if let Some(sm) = self.create_sourcemap(expr.span.lo()) { + expr.args.push(ExprOrSpread { + spread: None, + expr: Box::new(Expr::Lit(Lit::Str(sm.into()))), + }); } } - if self.options.sourcemap.unwrap_or(false) { - let _loc = - self.cm.get_code_map().lookup_char_pos(expr.span.lo()); - // generate sourcemap + ExprKind::Styled => { + if let MemberProp::Ident(prop) = &m.prop { + let mut args_props = Vec::with_capacity(2); + args_props.push(self.create_target_arg_node()); + if self.options.auto_label.unwrap_or(false) { + args_props.push(PropOrSpread::Prop(Box::new( + Prop::KeyValue(KeyValueProp { + key: PropName::Ident(Ident::new( + "label".into(), + DUMMY_SP, + )), + value: Box::new(Expr::Lit(Lit::Str( + self.create_label(false).into(), + ))), + }), + ))); + } + if let Some(cm) = self.create_sourcemap(expr.span.lo()) { + expr.args.push(ExprOrSpread { + spread: None, + expr: Box::new(Expr::Lit(Lit::Str(cm.into()))), + }); + } + return CallExpr { + span: expr.span, + type_args: expr.type_args, + args: expr.args, + callee: Callee::Expr(Box::new(Expr::Call(CallExpr { + span: DUMMY_SP, + type_args: None, + callee: Callee::Expr(Box::new(Expr::Ident( + Ident::new(i.sym.clone(), i.span), + ))), + args: vec![ + ExprOrSpread { + spread: None, + expr: Box::new(Expr::Lit(Lit::Str( + prop.as_ref().into(), + ))), + }, + ExprOrSpread { + spread: None, + expr: Box::new(Expr::Object(ObjectLit { + span: DUMMY_SP, + props: args_props, + })), + }, + ], + }))), + }; + } } } } diff --git a/packages/next-swc/crates/core/tests/fixture/emotion/css-in-callback/output.ts b/packages/next-swc/crates/core/tests/fixture/emotion/css-in-callback/output.ts index 0e4961b9d54f..580b6058dd29 100644 --- a/packages/next-swc/crates/core/tests/fixture/emotion/css-in-callback/output.ts +++ b/packages/next-swc/crates/core/tests/fixture/emotion/css-in-callback/output.ts @@ -10,27 +10,35 @@ const stylesInCallback = (props: any) => background: 'yellow', width: `${props.scale * 100}px`, }, - 'label:stylesInCallback' + 'label:stylesInCallback', + '/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiL1VzZXJzL2xvbmd5aW5hbi93b3Jrc3BhY2UvZ2l0aHViL25leHQuanMvcGFja2FnZXMvbmV4dC1zd2MvY3JhdGVzL2NvcmUvdGVzdHMvZml4dHVyZS9lbW90aW9uL2Nzcy1pbi1jYWxsYmFjay9pbnB1dC50c3giLCJzb3VyY2VzIjpbIi9Vc2Vycy9sb25neWluYW4vd29ya3NwYWNlL2dpdGh1Yi9uZXh0LmpzL3BhY2thZ2VzL25leHQtc3djL2NyYXRlcy9jb3JlL3Rlc3RzL2ZpeHR1cmUvZW1vdGlvbi9jc3MtaW4tY2FsbGJhY2svaW5wdXQudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGNzcyB9IGZyb20gJ0BlbW90aW9uL3JlYWN0J1xuaW1wb3J0IHN0eWxlZCBmcm9tICdAZW1vdGlvbi9zdHlsZWQnXG5pbXBvcnQgeyBQdXJlQ29tcG9uZW50IH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgUmVhY3RET00gZnJvbSAncmVhY3QtZG9tJ1xuXG5jb25zdCBzdHlsZXNJbkNhbGxiYWNrID0gKHByb3BzOiBhbnkpID0+XG4gIGNzcyh7XG4gICAgY29sb3I6ICdyZWQnLFxuICAgIGJhY2tncm91bmQ6ICd5ZWxsb3cnLFxuICAgIHdpZHRoOiBgJHtwcm9wcy5zY2FsZSAqIDEwMH1weGAsXG4gIH0pXG5cbmNvbnN0IHN0eWxlcyA9IGNzcyh7XG4gIGNvbG9yOiAncmVkJyxcbiAgd2lkdGg6ICcyMHB4Jyxcbn0pXG5cbmNvbnN0IERpY0NvbnRhaW5lciA9IHN0eWxlZC5kaXYoe1xuICBiYWNrZ3JvdW5kOiAncmVkJyxcbn0pXG5cbmNvbnN0IFNwYW5Db250YWluZXIgPSBzdHlsZWQoJ3NwYW4nKSh7XG4gIGJhY2tncm91bmQ6ICd5ZWxsb3cnLFxufSlcblxuY29uc3QgQ29udGFpbmVyID0gc3R5bGVkKCdidXR0b24nKWBcbiAgJHtzdHlsZXNJbkNhbGxiYWNrfVxuICAkeygpID0+XG4gICAgY3NzKHtcbiAgICAgIGJhY2tncm91bmQ6ICdyZWQnLFxuICAgIH0pfVxuYFxuZXhwb3J0IGNsYXNzIFNpbXBsZUNvbXBvbmVudCBleHRlbmRzIFB1cmVDb21wb25lbnQge1xuICByZW5kZXIoKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxDb250YWluZXI+XG4gICAgICAgIDxzcGFuPmhlbGxvPC9zcGFuPlxuICAgICAgPC9Db250YWluZXI+XG4gICAgKVxuICB9XG59XG5cblJlYWN0RE9NLnJlbmRlcig8U2ltcGxlQ29tcG9uZW50IC8+LCBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCcjYXBwJykpXG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBTUUifQ== */' ) const styles = css( { color: 'red', width: '20px', }, - 'label:styles' + 'label:styles', + '/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiL1VzZXJzL2xvbmd5aW5hbi93b3Jrc3BhY2UvZ2l0aHViL25leHQuanMvcGFja2FnZXMvbmV4dC1zd2MvY3JhdGVzL2NvcmUvdGVzdHMvZml4dHVyZS9lbW90aW9uL2Nzcy1pbi1jYWxsYmFjay9pbnB1dC50c3giLCJzb3VyY2VzIjpbIi9Vc2Vycy9sb25neWluYW4vd29ya3NwYWNlL2dpdGh1Yi9uZXh0LmpzL3BhY2thZ2VzL25leHQtc3djL2NyYXRlcy9jb3JlL3Rlc3RzL2ZpeHR1cmUvZW1vdGlvbi9jc3MtaW4tY2FsbGJhY2svaW5wdXQudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGNzcyB9IGZyb20gJ0BlbW90aW9uL3JlYWN0J1xuaW1wb3J0IHN0eWxlZCBmcm9tICdAZW1vdGlvbi9zdHlsZWQnXG5pbXBvcnQgeyBQdXJlQ29tcG9uZW50IH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgUmVhY3RET00gZnJvbSAncmVhY3QtZG9tJ1xuXG5jb25zdCBzdHlsZXNJbkNhbGxiYWNrID0gKHByb3BzOiBhbnkpID0+XG4gIGNzcyh7XG4gICAgY29sb3I6ICdyZWQnLFxuICAgIGJhY2tncm91bmQ6ICd5ZWxsb3cnLFxuICAgIHdpZHRoOiBgJHtwcm9wcy5zY2FsZSAqIDEwMH1weGAsXG4gIH0pXG5cbmNvbnN0IHN0eWxlcyA9IGNzcyh7XG4gIGNvbG9yOiAncmVkJyxcbiAgd2lkdGg6ICcyMHB4Jyxcbn0pXG5cbmNvbnN0IERpY0NvbnRhaW5lciA9IHN0eWxlZC5kaXYoe1xuICBiYWNrZ3JvdW5kOiAncmVkJyxcbn0pXG5cbmNvbnN0IFNwYW5Db250YWluZXIgPSBzdHlsZWQoJ3NwYW4nKSh7XG4gIGJhY2tncm91bmQ6ICd5ZWxsb3cnLFxufSlcblxuY29uc3QgQ29udGFpbmVyID0gc3R5bGVkKCdidXR0b24nKWBcbiAgJHtzdHlsZXNJbkNhbGxiYWNrfVxuICAkeygpID0+XG4gICAgY3NzKHtcbiAgICAgIGJhY2tncm91bmQ6ICdyZWQnLFxuICAgIH0pfVxuYFxuZXhwb3J0IGNsYXNzIFNpbXBsZUNvbXBvbmVudCBleHRlbmRzIFB1cmVDb21wb25lbnQge1xuICByZW5kZXIoKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxDb250YWluZXI+XG4gICAgICAgIDxzcGFuPmhlbGxvPC9zcGFuPlxuICAgICAgPC9Db250YWluZXI+XG4gICAgKVxuICB9XG59XG5cblJlYWN0RE9NLnJlbmRlcig8U2ltcGxlQ29tcG9uZW50IC8+LCBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCcjYXBwJykpXG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBWWUifQ== */' ) const DicContainer = styled('div', { target: 'ep3ww290', - label: 'label:DicContainer', -})({ - background: 'red', -}) + label: 'DicContainer', +})( + { + background: 'red', + }, + '/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiL1VzZXJzL2xvbmd5aW5hbi93b3Jrc3BhY2UvZ2l0aHViL25leHQuanMvcGFja2FnZXMvbmV4dC1zd2MvY3JhdGVzL2NvcmUvdGVzdHMvZml4dHVyZS9lbW90aW9uL2Nzcy1pbi1jYWxsYmFjay9pbnB1dC50c3giLCJzb3VyY2VzIjpbIi9Vc2Vycy9sb25neWluYW4vd29ya3NwYWNlL2dpdGh1Yi9uZXh0LmpzL3BhY2thZ2VzL25leHQtc3djL2NyYXRlcy9jb3JlL3Rlc3RzL2ZpeHR1cmUvZW1vdGlvbi9jc3MtaW4tY2FsbGJhY2svaW5wdXQudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGNzcyB9IGZyb20gJ0BlbW90aW9uL3JlYWN0J1xuaW1wb3J0IHN0eWxlZCBmcm9tICdAZW1vdGlvbi9zdHlsZWQnXG5pbXBvcnQgeyBQdXJlQ29tcG9uZW50IH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgUmVhY3RET00gZnJvbSAncmVhY3QtZG9tJ1xuXG5jb25zdCBzdHlsZXNJbkNhbGxiYWNrID0gKHByb3BzOiBhbnkpID0+XG4gIGNzcyh7XG4gICAgY29sb3I6ICdyZWQnLFxuICAgIGJhY2tncm91bmQ6ICd5ZWxsb3cnLFxuICAgIHdpZHRoOiBgJHtwcm9wcy5zY2FsZSAqIDEwMH1weGAsXG4gIH0pXG5cbmNvbnN0IHN0eWxlcyA9IGNzcyh7XG4gIGNvbG9yOiAncmVkJyxcbiAgd2lkdGg6ICcyMHB4Jyxcbn0pXG5cbmNvbnN0IERpY0NvbnRhaW5lciA9IHN0eWxlZC5kaXYoe1xuICBiYWNrZ3JvdW5kOiAncmVkJyxcbn0pXG5cbmNvbnN0IFNwYW5Db250YWluZXIgPSBzdHlsZWQoJ3NwYW4nKSh7XG4gIGJhY2tncm91bmQ6ICd5ZWxsb3cnLFxufSlcblxuY29uc3QgQ29udGFpbmVyID0gc3R5bGVkKCdidXR0b24nKWBcbiAgJHtzdHlsZXNJbkNhbGxiYWNrfVxuICAkeygpID0+XG4gICAgY3NzKHtcbiAgICAgIGJhY2tncm91bmQ6ICdyZWQnLFxuICAgIH0pfVxuYFxuZXhwb3J0IGNsYXNzIFNpbXBsZUNvbXBvbmVudCBleHRlbmRzIFB1cmVDb21wb25lbnQge1xuICByZW5kZXIoKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxDb250YWluZXI+XG4gICAgICAgIDxzcGFuPmhlbGxvPC9zcGFuPlxuICAgICAgPC9Db250YWluZXI+XG4gICAgKVxuICB9XG59XG5cblJlYWN0RE9NLnJlbmRlcig8U2ltcGxlQ29tcG9uZW50IC8+LCBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCcjYXBwJykpXG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBaUJxQiJ9 */' +) const SpanContainer = styled('span', { target: 'ep3ww291', - label: 'label:SpanContainer', -})({ - background: 'yellow', -}) + label: 'SpanContainer', +})( + { + background: 'yellow', + }, + '/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiL1VzZXJzL2xvbmd5aW5hbi93b3Jrc3BhY2UvZ2l0aHViL25leHQuanMvcGFja2FnZXMvbmV4dC1zd2MvY3JhdGVzL2NvcmUvdGVzdHMvZml4dHVyZS9lbW90aW9uL2Nzcy1pbi1jYWxsYmFjay9pbnB1dC50c3giLCJzb3VyY2VzIjpbIi9Vc2Vycy9sb25neWluYW4vd29ya3NwYWNlL2dpdGh1Yi9uZXh0LmpzL3BhY2thZ2VzL25leHQtc3djL2NyYXRlcy9jb3JlL3Rlc3RzL2ZpeHR1cmUvZW1vdGlvbi9jc3MtaW4tY2FsbGJhY2svaW5wdXQudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGNzcyB9IGZyb20gJ0BlbW90aW9uL3JlYWN0J1xuaW1wb3J0IHN0eWxlZCBmcm9tICdAZW1vdGlvbi9zdHlsZWQnXG5pbXBvcnQgeyBQdXJlQ29tcG9uZW50IH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgUmVhY3RET00gZnJvbSAncmVhY3QtZG9tJ1xuXG5jb25zdCBzdHlsZXNJbkNhbGxiYWNrID0gKHByb3BzOiBhbnkpID0+XG4gIGNzcyh7XG4gICAgY29sb3I6ICdyZWQnLFxuICAgIGJhY2tncm91bmQ6ICd5ZWxsb3cnLFxuICAgIHdpZHRoOiBgJHtwcm9wcy5zY2FsZSAqIDEwMH1weGAsXG4gIH0pXG5cbmNvbnN0IHN0eWxlcyA9IGNzcyh7XG4gIGNvbG9yOiAncmVkJyxcbiAgd2lkdGg6ICcyMHB4Jyxcbn0pXG5cbmNvbnN0IERpY0NvbnRhaW5lciA9IHN0eWxlZC5kaXYoe1xuICBiYWNrZ3JvdW5kOiAncmVkJyxcbn0pXG5cbmNvbnN0IFNwYW5Db250YWluZXIgPSBzdHlsZWQoJ3NwYW4nKSh7XG4gIGJhY2tncm91bmQ6ICd5ZWxsb3cnLFxufSlcblxuY29uc3QgQ29udGFpbmVyID0gc3R5bGVkKCdidXR0b24nKWBcbiAgJHtzdHlsZXNJbkNhbGxiYWNrfVxuICAkeygpID0+XG4gICAgY3NzKHtcbiAgICAgIGJhY2tncm91bmQ6ICdyZWQnLFxuICAgIH0pfVxuYFxuZXhwb3J0IGNsYXNzIFNpbXBsZUNvbXBvbmVudCBleHRlbmRzIFB1cmVDb21wb25lbnQge1xuICByZW5kZXIoKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxDb250YWluZXI+XG4gICAgICAgIDxzcGFuPmhlbGxvPC9zcGFuPlxuICAgICAgPC9Db250YWluZXI+XG4gICAgKVxuICB9XG59XG5cblJlYWN0RE9NLnJlbmRlcig8U2ltcGxlQ29tcG9uZW50IC8+LCBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCcjYXBwJykpXG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBcUJzQiJ9 */' +) const Container = styled('button')` ${stylesInCallback} ${() => @@ -38,7 +46,8 @@ const Container = styled('button')` { background: 'red', }, - 'label:Container' + 'label:Container', + '/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiL1VzZXJzL2xvbmd5aW5hbi93b3Jrc3BhY2UvZ2l0aHViL25leHQuanMvcGFja2FnZXMvbmV4dC1zd2MvY3JhdGVzL2NvcmUvdGVzdHMvZml4dHVyZS9lbW90aW9uL2Nzcy1pbi1jYWxsYmFjay9pbnB1dC50c3giLCJzb3VyY2VzIjpbIi9Vc2Vycy9sb25neWluYW4vd29ya3NwYWNlL2dpdGh1Yi9uZXh0LmpzL3BhY2thZ2VzL25leHQtc3djL2NyYXRlcy9jb3JlL3Rlc3RzL2ZpeHR1cmUvZW1vdGlvbi9jc3MtaW4tY2FsbGJhY2svaW5wdXQudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGNzcyB9IGZyb20gJ0BlbW90aW9uL3JlYWN0J1xuaW1wb3J0IHN0eWxlZCBmcm9tICdAZW1vdGlvbi9zdHlsZWQnXG5pbXBvcnQgeyBQdXJlQ29tcG9uZW50IH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgUmVhY3RET00gZnJvbSAncmVhY3QtZG9tJ1xuXG5jb25zdCBzdHlsZXNJbkNhbGxiYWNrID0gKHByb3BzOiBhbnkpID0+XG4gIGNzcyh7XG4gICAgY29sb3I6ICdyZWQnLFxuICAgIGJhY2tncm91bmQ6ICd5ZWxsb3cnLFxuICAgIHdpZHRoOiBgJHtwcm9wcy5zY2FsZSAqIDEwMH1weGAsXG4gIH0pXG5cbmNvbnN0IHN0eWxlcyA9IGNzcyh7XG4gIGNvbG9yOiAncmVkJyxcbiAgd2lkdGg6ICcyMHB4Jyxcbn0pXG5cbmNvbnN0IERpY0NvbnRhaW5lciA9IHN0eWxlZC5kaXYoe1xuICBiYWNrZ3JvdW5kOiAncmVkJyxcbn0pXG5cbmNvbnN0IFNwYW5Db250YWluZXIgPSBzdHlsZWQoJ3NwYW4nKSh7XG4gIGJhY2tncm91bmQ6ICd5ZWxsb3cnLFxufSlcblxuY29uc3QgQ29udGFpbmVyID0gc3R5bGVkKCdidXR0b24nKWBcbiAgJHtzdHlsZXNJbkNhbGxiYWNrfVxuICAkeygpID0+XG4gICAgY3NzKHtcbiAgICAgIGJhY2tncm91bmQ6ICdyZWQnLFxuICAgIH0pfVxuYFxuZXhwb3J0IGNsYXNzIFNpbXBsZUNvbXBvbmVudCBleHRlbmRzIFB1cmVDb21wb25lbnQge1xuICByZW5kZXIoKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxDb250YWluZXI+XG4gICAgICAgIDxzcGFuPmhlbGxvPC9zcGFuPlxuICAgICAgPC9Db250YWluZXI+XG4gICAgKVxuICB9XG59XG5cblJlYWN0RE9NLnJlbmRlcig8U2ltcGxlQ29tcG9uZW50IC8+LCBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCcjYXBwJykpXG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBNEJJIn0= */' )} ` export class SimpleComponent extends PureComponent { diff --git a/packages/next/build/swc/options.js b/packages/next/build/swc/options.js index 2d2cbee22fa8..339f550513ce 100644 --- a/packages/next/build/swc/options.js +++ b/packages/next/build/swc/options.js @@ -52,7 +52,9 @@ export function getBaseSWCOptions({ legacyDecorator: enableDecorators, decoratorMetadata: emitDecoratorMetadata, react: { - importSource: jsConfig?.compilerOptions?.jsxImportSource || 'react', + importSource: nextConfig?.emotion?.enabled + ? '@emotion/react' + : jsConfig?.compilerOptions?.jsxImportSource || 'react', runtime: 'automatic', pragma: 'React.createElement', pragmaFrag: 'React.Fragment', @@ -89,6 +91,37 @@ export function getBaseSWCOptions({ removeConsole: nextConfig?.compiler?.removeConsole, reactRemoveProperties: nextConfig?.compiler?.reactRemoveProperties, relay: nextConfig?.compiler?.relay, + emotion: getEmotionOptions(nextConfig, development), + } +} + +function getEmotionOptions(nextConfig, development) { + if ( + !nextConfig?.compiler?.emotion || + !nextConfig?.compiler?.emotion?.enabled + ) { + return null + } + let autoLabel = false + switch (nextConfig?.compiler?.emotion?.autoLabel) { + case 'never': + autoLabel = false + break + case 'always': + autoLabel = true + break + case 'dev-only': + default: + autoLabel = !!development + break + } + return { + enabled: true, + autoLabel, + labelFormat: nextConfig?.compiler?.emotion?.labelFormat, + sourcemap: development + ? nextConfig?.compiler?.emotion?.sourceMap ?? true + : false, } } diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index 82d9cb2d9e60..5dfad38af146 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -1633,6 +1633,7 @@ export default async function getBaseWebpackConfig( reactRemoveProperties: config.compiler?.reactRemoveProperties, styledComponents: config.compiler?.styledComponents, relay: config.compiler?.relay, + emotion: config.compiler?.emotion, }) const cache: any = { diff --git a/packages/next/server/config-shared.ts b/packages/next/server/config-shared.ts index b927e37ffdc8..840cb175edb7 100644 --- a/packages/next/server/config-shared.ts +++ b/packages/next/server/config-shared.ts @@ -382,6 +382,12 @@ export interface NextConfig extends Record { exclude?: string[] } styledComponents?: boolean + emotion?: { + enabled?: boolean + sourceMap?: boolean + autoLabel?: 'dev-only' | 'always' | 'never' + labelFormat?: string + } } /**