Skip to main content

stylex_transform/transform/stylex/
transform_stylex_create_theme_call.rs

1use indexmap::IndexMap;
2use rustc_hash::FxHashMap;
3use stylex_constants::constants::messages::{ONLY_OVERRIDE_DEFINE_VARS, SPREAD_NOT_SUPPORTED};
4use stylex_macros::{stylex_panic, stylex_unimplemented};
5use swc_core::{
6  common::comments::Comments,
7  ecma::ast::{CallExpr, Expr},
8};
9
10use crate::shared::structures::functions::FunctionConfigType;
11use crate::shared::structures::functions::FunctionMap;
12use crate::shared::structures::types::FunctionMapIdentifiers;
13use crate::shared::structures::types::FunctionMapMemberExpression;
14use crate::shared::transformers::stylex_create_theme::stylex_create_theme;
15use crate::shared::transformers::stylex_keyframes::get_keyframes_fn;
16use crate::shared::transformers::stylex_types::get_types_fn;
17use crate::shared::utils::core::dev_class_name::convert_theme_to_dev_styles;
18use crate::shared::utils::core::dev_class_name::convert_theme_to_test_styles;
19use crate::shared::utils::core::js_to_expr::{NestedStringObject, convert_object_to_ast};
20use crate::shared::utils::js::evaluate::evaluate;
21use crate::shared::utils::log::build_code_frame_error::build_code_frame_error;
22use crate::shared::utils::validators::{
23  is_create_theme_call, validate_stylex_create_theme_indent, validate_theme_variables,
24};
25use crate::{StyleXTransform, shared::transformers::stylex_position_try::get_position_try_fn};
26use stylex_constants::constants::messages::{non_static_value, non_style_object};
27
28impl<C> StyleXTransform<C>
29where
30  C: Comments,
31{
32  pub(crate) fn transform_stylex_create_theme_call(&mut self, call: &CallExpr) -> Option<Expr> {
33    let is_create_theme_call = is_create_theme_call(call, &self.state);
34
35    if is_create_theme_call {
36      let (_, parent_var_decl) = &self.get_call_var_name(call);
37
38      validate_stylex_create_theme_indent(parent_var_decl, call, &mut self.state);
39
40      let first_arg = call.args.first().map(|first_arg| match &first_arg.spread {
41        Some(_) => stylex_unimplemented!("{}", SPREAD_NOT_SUPPORTED),
42        None => first_arg.expr.clone(),
43      })?;
44
45      let second_arg = call
46        .args
47        .get(1)
48        .map(|second_arg| match &second_arg.spread {
49          Some(_) => stylex_unimplemented!("{}", SPREAD_NOT_SUPPORTED),
50          None => second_arg.expr.clone(),
51        })?;
52
53      let mut identifiers: FunctionMapIdentifiers = FxHashMap::default();
54      let mut member_expressions: FunctionMapMemberExpression = FxHashMap::default();
55
56      let keyframes_fn = get_keyframes_fn();
57      let types_fn = get_types_fn();
58      let position_try_fn = get_position_try_fn();
59
60      for name in &self.state.stylex_position_try_import {
61        identifiers.insert(
62          name.clone(),
63          Box::new(FunctionConfigType::Regular(keyframes_fn.clone())),
64        );
65      }
66
67      for name in &self.state.stylex_position_try_import {
68        identifiers.insert(
69          name.clone(),
70          Box::new(FunctionConfigType::Regular(position_try_fn.clone())),
71        );
72      }
73
74      for name in &self.state.stylex_types_import {
75        identifiers.insert(
76          name.clone(),
77          Box::new(FunctionConfigType::Regular(types_fn.clone())),
78        );
79      }
80
81      for name in &self.state.stylex_import {
82        let member_expression = member_expressions.entry(name.clone()).or_default();
83
84        member_expression.insert(
85          "keyframes".into(),
86          Box::new(FunctionConfigType::Regular(keyframes_fn.clone())),
87        );
88
89        let identifier = identifiers
90          .entry(name.get_import_str().into())
91          .or_insert_with(|| Box::new(FunctionConfigType::Map(FxHashMap::default())));
92
93        if let Some(identifier_map) = identifier.as_map_mut() {
94          identifier_map.insert("types".into(), types_fn.clone());
95        }
96      }
97
98      self
99        .state
100        .apply_stylex_env(&mut identifiers, &mut member_expressions);
101
102      let function_map: Box<FunctionMap> = Box::new(FunctionMap {
103        identifiers,
104        member_expressions,
105        disable_imports: false,
106      });
107
108      let evaluated_arg1 = evaluate(&first_arg, &mut self.state, &function_map);
109
110      assert!(
111        evaluated_arg1.confident,
112        "{}",
113        build_code_frame_error(
114          &Expr::Call(call.clone()),
115          &evaluated_arg1
116            .deopt
117            .unwrap_or_else(|| *first_arg.to_owned()),
118          &non_static_value("createTheme"),
119          &mut self.state,
120        )
121      );
122
123      let evaluated_arg2 = evaluate(&second_arg, &mut self.state, &function_map);
124
125      assert!(
126        evaluated_arg2.confident,
127        "{}",
128        build_code_frame_error(
129          &Expr::Call(call.clone()),
130          &evaluated_arg2
131            .deopt
132            .unwrap_or_else(|| *second_arg.to_owned()),
133          &non_static_value("createTheme"),
134          &mut self.state,
135        )
136      );
137
138      let mut variables = match evaluated_arg1.value {
139        Some(ref value) => {
140          validate_theme_variables(value, &self.state);
141          value.clone()
142        },
143        None => stylex_panic!(
144          "{}",
145          build_code_frame_error(
146            &Expr::Call(call.clone()),
147            &evaluated_arg1
148              .deopt
149              .unwrap_or_else(|| *first_arg.to_owned()),
150            ONLY_OVERRIDE_DEFINE_VARS,
151            &mut self.state,
152          )
153        ),
154      };
155
156      let overrides = match evaluated_arg2.value {
157        Some(ref value) => {
158          assert!(
159            value
160              .as_expr()
161              .map(|expr| expr.is_object())
162              .unwrap_or(false),
163            "{}",
164            build_code_frame_error(
165              &Expr::Call(call.clone()),
166              &evaluated_arg2
167                .deopt
168                .unwrap_or_else(|| *second_arg.to_owned()),
169              &non_style_object("createTheme"),
170              &mut self.state,
171            )
172          );
173          value.clone()
174        },
175        None => stylex_panic!(
176          "{}",
177          build_code_frame_error(
178            &Expr::Call(call.clone()),
179            &evaluated_arg2
180              .deopt
181              .unwrap_or_else(|| *second_arg.to_owned()),
182            &non_style_object("createTheme"),
183            &mut self.state,
184          )
185        ),
186      };
187
188      let (mut overrides_obj, inject_styles) = stylex_create_theme(
189        &mut variables,
190        &overrides,
191        &mut self.state,
192        &mut IndexMap::default(),
193      );
194
195      let (var_name, _) = self.get_call_var_name(call);
196
197      if self.state.is_test() {
198        overrides_obj =
199          convert_theme_to_test_styles(&var_name, &overrides_obj, self.state.get_filename());
200      } else if self.state.is_dev() {
201        overrides_obj =
202          convert_theme_to_dev_styles(&var_name, &overrides_obj, self.state.get_filename());
203      }
204
205      let result_ast =
206        convert_object_to_ast(&NestedStringObject::FlatCompiledStylesValues(overrides_obj));
207
208      self
209        .state
210        .register_styles(call, &inject_styles, &result_ast, None);
211
212      Some(result_ast)
213    } else {
214      None
215    }
216  }
217}