Skip to content

Commit

Permalink
feat: add support for emotion (#694)
Browse files Browse the repository at this point in the history
* feat: add support to emotion

* feat: judge emtion config before applying swc emotion transform
  • Loading branch information
zhangpanweb authored Nov 22, 2023
1 parent 71f7c47 commit f8603e2
Show file tree
Hide file tree
Showing 10 changed files with 317 additions and 52 deletions.
34 changes: 34 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ puffin_egui = { version = "0.22.0", optional = true }
lazy_static = "1.4.0"
convert_case = "0.6.0"
path-clean = "1.0.1"
swc_emotion = "0.51.0"

[features]
profile = ["dep:eframe", "dep:puffin", "dep:puffin_egui"]
6 changes: 3 additions & 3 deletions crates/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ pub use {
serde_xml_rs, serde_yaml, svgr_rs, swc_atoms, swc_common, swc_css_ast, swc_css_codegen,
swc_css_compat, swc_css_minifier, swc_css_modules, swc_css_parser, swc_css_prefixer,
swc_css_visit, swc_ecma_ast, swc_ecma_codegen, swc_ecma_minifier, swc_ecma_parser,
swc_ecma_preset_env, swc_ecma_transforms, swc_ecma_utils, swc_ecma_visit, swc_error_reporters,
swc_node_comments, thiserror, tokio, tokio_tungstenite, toml, tracing, tracing_subscriber,
tungstenite, twox_hash,
swc_ecma_preset_env, swc_ecma_transforms, swc_ecma_utils, swc_ecma_visit, swc_emotion,
swc_error_reporters, swc_node_comments, thiserror, tokio, tokio_tungstenite, toml, tracing,
tracing_subscriber, tungstenite, twox_hash,
};

#[macro_export]
Expand Down
4 changes: 3 additions & 1 deletion crates/mako/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ pub struct Config {
pub _minifish: Option<MinifishConfig>,
#[serde(rename = "optimizePackageImports")]
pub optimize_package_imports: bool,
pub emotion: bool,
}

pub(crate) fn hash_config(c: &Config) -> u64 {
Expand Down Expand Up @@ -350,7 +351,8 @@ const DEFAULT_CONFIG: &str = r#"
"nodePolyfill": true,
"ignores": [],
"_minifish": null,
"optimizePackageImports": false
"optimizePackageImports": false,
"emotion": false,
}
"#;

Expand Down
93 changes: 72 additions & 21 deletions crates/mako/src/transformers/transform_react.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use std::path::Path;
use std::sync::Arc;

use mako_core::swc_common::comments::NoopComments;
use mako_core::swc_common::sync::Lrc;
use mako_core::swc_common::{chain, Mark, SourceMap};
use mako_core::swc_ecma_ast::Module;
use mako_core::swc_ecma_transforms::react::{react, Options, RefreshOptions, Runtime};
use mako_core::swc_ecma_visit::{VisitMut, VisitMutWith};
use mako_core::swc_ecma_visit::{Fold, VisitMut, VisitMutWith};
use mako_core::swc_emotion::{emotion, EmotionOptions};

use crate::ast::build_js_ast;
use crate::build::Task;
Expand Down Expand Up @@ -42,27 +44,46 @@ pub fn mako_react(
};
}

let visit = react(
cm,
Some(NoopComments),
Options {
import_source: Some("react".to_string()),
pragma: Some("React.createElement".into()),
pragma_frag: Some("React.Fragment".into()),
// support react 17 + only
runtime: Some(Runtime::Automatic),
development: Some(is_dev),
// to avoid throw error for svg namespace element
throw_if_namespace: Some(false),
refresh: if use_refresh {
Some(RefreshOptions::default())
} else {
None
let (import_source, pragma) = if context.config.emotion {
("@emotion/react", "jsx")
} else {
("react", "React.createElement")
};

let emotion = if context.config.emotion {
Box::new(Emotion {
mode: context.config.mode.clone(),
cm: cm.clone(),
path: task.path.clone(),
})
} else {
noop()
};

let visit = chain!(
emotion,
react(
cm,
Some(NoopComments),
Options {
import_source: Some(import_source.to_string()),
pragma: Some(pragma.into()),
pragma_frag: Some("React.Fragment".into()),
// support react 17 + only
runtime: Some(Runtime::Automatic),
development: Some(is_dev),
// to avoid throw error for svg namespace element
throw_if_namespace: Some(false),
refresh: if use_refresh {
Some(RefreshOptions::default())
} else {
None
},
..Default::default()
},
..Default::default()
},
*top_level_mark,
*unresolved_mark,
*top_level_mark,
*unresolved_mark,
)
);
if use_refresh {
Box::new(if task.is_entry {
Expand All @@ -83,6 +104,36 @@ pub fn mako_react(
}
}

struct Emotion {
cm: Lrc<SourceMap>,
path: String,
mode: Mode,
}

impl VisitMut for Emotion {
fn visit_mut_module(&mut self, module: &mut Module) {
let is_dev = matches!(self.mode, Mode::Development);
let pos = self.cm.lookup_char_pos(module.span.lo);
let hash = pos.file.src_hash as u32;
let mut folder = emotion(
EmotionOptions {
enabled: Some(true),
sourcemap: Some(true),
auto_label: Some(is_dev),
import_map: None,
..Default::default()
},
Path::new(&self.path),
hash,
self.cm.clone(),
NoopComments,
);
module.body = folder.fold_module(module.clone()).body;

module.visit_mut_children_with(self);
}
}

impl VisitMut for PrefixCode {
fn visit_mut_module(&mut self, module: &mut Module) {
let post_code_snippet_module =
Expand Down
40 changes: 40 additions & 0 deletions examples/with-emotion/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { css } from '@emotion/react';
import styled from '@emotion/styled';
import ReactDOM from 'react-dom/client';
import React from 'react';

const style = css`
color: hotpink;
`;

const DivContainer = styled.div({
background: 'red',
});

const SpanContainer = styled('span')({
background: 'yellow',
});

const Child = styled.div`
color: red;
`;

const Parent = styled.div`
${Child} {
color: green;
}
`;

const App = () => {
return [
<div css={style}>Hello emotion</div>,
<DivContainer>red div</DivContainer>,
<SpanContainer>yellow span</SpanContainer>,
<Parent>
<Child>Green because I am inside a Parent</Child>
</Parent>,
<Child>Red because I am not inside a Parent</Child>,
];
};

ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
3 changes: 3 additions & 0 deletions examples/with-emotion/mako.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"emotion": true
}
8 changes: 8 additions & 0 deletions examples/with-emotion/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"dependencies": {
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"react": "18.2.0",
"react-dom": "18.2.0"
}
}
12 changes: 12 additions & 0 deletions examples/with-emotion/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">

<head>
</head>

<body>
<div id="root"></div>
<script src="index.js"></script>
</body>

</html>
Loading

0 comments on commit f8603e2

Please sign in to comment.