turbopack_ecmascript_plugins/transform/
styled_components.rs

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