turbopack_ecmascript_plugins/transform/
styled_components.rs

1use 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}