turbopack_ecmascript_plugins/transform/
styled_components.rs1use anyhow::Result;
2use async_trait::async_trait;
3use serde::Deserialize;
4use swc_core::{atoms::Wtf8Atom, common::comments::NoopComments, ecma::ast::Program};
5use turbo_tasks::{ValueDefault, Vc};
6use turbopack_ecmascript::{CustomTransformer, TransformContext};
7
8#[turbo_tasks::value(shared, operation)]
9#[derive(Clone, Debug, Deserialize)]
10#[serde(default, rename_all = "camelCase")]
11pub struct StyledComponentsTransformConfig {
12 pub display_name: bool,
13 pub ssr: bool,
14 pub file_name: bool,
15 pub top_level_import_paths: Vec<String>,
16 pub meaningless_file_names: Vec<String>,
17 pub css_prop: bool,
18 pub namespace: Option<String>,
19}
20
21impl Default for StyledComponentsTransformConfig {
22 fn default() -> Self {
23 StyledComponentsTransformConfig {
24 display_name: true,
25 ssr: true,
26 file_name: true,
27 top_level_import_paths: vec![],
28 meaningless_file_names: vec!["index".to_string()],
29 css_prop: true,
30 namespace: None,
31 }
32 }
33}
34
35#[turbo_tasks::value_impl]
36impl StyledComponentsTransformConfig {
37 #[turbo_tasks::function]
38 fn default_private() -> Vc<Self> {
39 Self::cell(Default::default())
40 }
41}
42
43impl ValueDefault for StyledComponentsTransformConfig {
44 fn value_default() -> Vc<Self> {
45 StyledComponentsTransformConfig::default_private()
46 }
47}
48
49#[derive(Debug)]
50pub struct StyledComponentsTransformer {
51 config: styled_components::Config,
52}
53
54impl StyledComponentsTransformer {
55 pub fn new(config: &StyledComponentsTransformConfig) -> Self {
56 let mut options = styled_components::Config {
57 display_name: config.display_name,
58 ssr: config.ssr,
59 file_name: config.file_name,
60 css_prop: config.css_prop,
61 ..Default::default()
62 };
63
64 if let Some(namespace) = &config.namespace {
65 options.namespace.clone_from(namespace);
66 }
67
68 let top_level_import_paths = &config.top_level_import_paths;
69 if !top_level_import_paths.is_empty() {
70 options.top_level_import_paths = top_level_import_paths
71 .iter()
72 .map(|s| Wtf8Atom::from(s.clone()))
73 .collect();
74 }
75 let meaningless_file_names = &config.meaningless_file_names;
76 if !meaningless_file_names.is_empty() {
77 options
78 .meaningless_file_names
79 .clone_from(meaningless_file_names);
80 }
81
82 Self { config: options }
83 }
84}
85
86#[async_trait]
87impl CustomTransformer for StyledComponentsTransformer {
88 #[tracing::instrument(level = tracing::Level::TRACE, name = "styled_components", skip_all)]
89 async fn transform(&self, program: &mut Program, ctx: &TransformContext<'_>) -> Result<()> {
90 program.mutate(styled_components::styled_components(
91 Some(ctx.file_path_str),
92 ctx.file_name_hash,
93 &self.config,
94 NoopComments,
95 ));
96
97 Ok(())
98 }
99}