turbopack_ecmascript_plugins/transform/
styled_components.rs1use 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}