Skip to main content

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