turbopack_ecmascript_plugins/transform/
emotion.rs1#![allow(unused)]
2use std::{
3 hash::{Hash, Hasher},
4 path::Path,
5};
6
7use anyhow::Result;
8use async_trait::async_trait;
9use bincode::{Decode, Encode};
10use indexmap::IndexMap;
11use rustc_hash::{FxBuildHasher, FxHasher};
12use serde::{Deserialize, Serialize};
13use swc_core::{
14 atoms::Atom,
15 common::util::take::Take,
16 ecma::{
17 ast::{Module, Program},
18 visit::FoldWith,
19 },
20};
21use swc_emotion::ImportMap;
22use turbo_rcstr::RcStr;
23use turbo_tasks::{NonLocalValue, OperationValue, ValueDefault, Vc, trace::TraceRawVcs};
24use turbopack_ecmascript::{CustomTransformer, TransformContext};
25
26#[derive(
27 Clone,
28 PartialEq,
29 Eq,
30 Debug,
31 TraceRawVcs,
32 Deserialize,
33 NonLocalValue,
34 OperationValue,
35 Encode,
36 Decode,
37)]
38#[serde(rename_all = "kebab-case")]
39pub enum EmotionLabelKind {
40 DevOnly,
41 Always,
42 Never,
43}
44
45#[derive(
46 Clone, PartialEq, Eq, Debug, TraceRawVcs, Serialize, Deserialize, NonLocalValue, OperationValue,
47)]
48#[serde(rename_all = "camelCase")]
49pub struct EmotionImportItemConfig {
50 pub canonical_import: EmotionItemSpecifier,
51 pub styled_base_import: Option<EmotionItemSpecifier>,
52}
53
54impl From<&EmotionImportItemConfig> for swc_emotion::ImportItemConfig {
55 fn from(value: &EmotionImportItemConfig) -> Self {
56 swc_emotion::ImportItemConfig {
57 canonical_import: From::from(&value.canonical_import),
58 styled_base_import: value.styled_base_import.as_ref().map(From::from),
59 }
60 }
61}
62
63#[derive(
64 Clone, PartialEq, Eq, Debug, TraceRawVcs, Serialize, Deserialize, NonLocalValue, OperationValue,
65)]
66pub struct EmotionItemSpecifier(pub RcStr, pub RcStr);
67
68impl From<&EmotionItemSpecifier> for swc_emotion::ItemSpecifier {
69 fn from(value: &EmotionItemSpecifier) -> Self {
70 swc_emotion::ItemSpecifier(value.0.as_str().into(), value.1.as_str().into())
71 }
72}
73
74pub type EmotionImportMapValue = IndexMap<RcStr, EmotionImportItemConfig, FxBuildHasher>;
75
76#[turbo_tasks::value(shared, operation)]
77#[derive(Default, Clone, Debug, Deserialize)]
78#[serde(rename_all = "camelCase")]
79pub struct EmotionTransformConfig {
80 pub sourcemap: Option<bool>,
81 pub label_format: Option<String>,
82 pub auto_label: Option<EmotionLabelKind>,
83 #[bincode(with_serde)]
84 pub import_map: Option<IndexMap<RcStr, EmotionImportMapValue, FxBuildHasher>>,
85}
86
87#[derive(Debug)]
88pub struct EmotionTransformer {
89 #[cfg(feature = "transform_emotion")]
90 config: swc_emotion::EmotionOptions,
91}
92
93#[cfg(feature = "transform_emotion")]
94impl EmotionTransformer {
95 pub fn new(config: &EmotionTransformConfig) -> Option<Self> {
96 let config = swc_emotion::EmotionOptions {
97 enabled: Some(true),
100 sourcemap: config.sourcemap,
101 label_format: config.label_format.as_deref().map(From::from),
102 auto_label: match config.auto_label.as_ref() {
103 Some(EmotionLabelKind::Always) => Some(true),
104 Some(EmotionLabelKind::Never) => Some(false),
105 Some(EmotionLabelKind::DevOnly) => None,
107 None => None,
108 },
109 import_map: config.import_map.as_ref().map(|map| {
110 map.iter()
111 .map(|(k, v)| {
112 (
113 k.as_str().into(),
114 swc_emotion::ImportMapValue::from_iter(v.iter().map(|(k, v)| {
115 (k.as_str().into(), swc_emotion::ImportItemConfig::from(v))
116 })),
117 )
118 })
119 .collect()
120 }),
121 };
122
123 Some(EmotionTransformer { config })
124 }
125}
126
127#[cfg(not(feature = "transform_emotion"))]
128impl EmotionTransformer {
129 pub fn new(_config: &EmotionTransformConfig) -> Option<Self> {
130 None
131 }
132}
133
134#[async_trait]
135impl CustomTransformer for EmotionTransformer {
136 #[tracing::instrument(level = tracing::Level::TRACE, name = "emotion", skip_all)]
137 async fn transform(&self, program: &mut Program, ctx: &TransformContext<'_>) -> Result<()> {
138 #[cfg(feature = "transform_emotion")]
139 {
140 let hash = {
141 let mut hasher = FxHasher::default();
142 program.hash(&mut hasher);
143 hasher.finish()
144 };
145 program.mutate(swc_emotion::emotion(
146 &self.config,
147 Path::new(ctx.file_name_str),
148 hash as u32,
149 ctx.source_map.clone(),
150 ctx.comments.clone(),
151 ));
152 }
153
154 Ok(())
155 }
156}