stylex_transform/transform/stylex/
transform_stylex_create_theme_call.rs1use 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}