stylex_transform/transform/stylex/
transform_stylex_define_vars_call.rs1use rustc_hash::FxHashMap;
2use stylex_constants::constants::messages::SPREAD_NOT_SUPPORTED;
3use stylex_macros::{stylex_panic, stylex_unimplemented};
4use swc_core::{
5 common::comments::Comments,
6 ecma::ast::{CallExpr, Expr},
7};
8
9use crate::shared::structures::functions::FunctionConfigType;
10use crate::shared::structures::functions::FunctionMap;
11use crate::shared::structures::types::{FunctionMapIdentifiers, FunctionMapMemberExpression};
12use crate::shared::transformers::stylex_define_vars::stylex_define_vars;
13use crate::shared::transformers::stylex_keyframes::get_keyframes_fn;
14use crate::shared::transformers::stylex_position_try::get_position_try_fn;
15use crate::shared::transformers::stylex_types::get_types_fn;
16use crate::shared::utils::common::gen_file_based_identifier;
17use crate::shared::utils::core::js_to_expr::{NestedStringObject, convert_object_to_ast};
18use crate::shared::utils::js::evaluate::evaluate;
19use crate::shared::utils::log::build_code_frame_error::build_code_frame_error;
20use crate::shared::utils::validators::{find_and_validate_stylex_define_vars, is_define_vars_call};
21use stylex_constants::constants::messages::{
22 cannot_generate_hash, non_static_value, non_style_object,
23};
24
25use crate::StyleXTransform;
26use stylex_structures::top_level_expression::TopLevelExpression;
27
28impl<C> StyleXTransform<C>
29where
30 C: Comments,
31{
32 pub(crate) fn transform_stylex_define_vars(&mut self, call: &CallExpr) -> Option<Expr> {
33 let is_define_vars = is_define_vars_call(call, &self.state);
34
35 if is_define_vars {
36 let stylex_create_theme_top_level_expr =
37 match find_and_validate_stylex_define_vars(call, &mut self.state) {
38 Some(expr) => expr,
39 None => stylex_panic!("defineVars(): Could not find the top-level variable declaration."),
40 };
41
42 let TopLevelExpression(_, _, var_id) = stylex_create_theme_top_level_expr;
43
44 let first_arg = call.args.first().map(|first_arg| match &first_arg.spread {
45 Some(_) => stylex_unimplemented!("{}", SPREAD_NOT_SUPPORTED),
46 None => first_arg.expr.clone(),
47 })?;
48
49 let mut identifiers: FunctionMapIdentifiers = FxHashMap::default();
50 let mut member_expressions: FunctionMapMemberExpression = FxHashMap::default();
51
52 let keyframes_fn = get_keyframes_fn();
53 let types_fn = get_types_fn();
54 let position_try_fn = get_position_try_fn();
55
56 for name in &self.state.stylex_keyframes_import {
57 identifiers.insert(
58 name.clone(),
59 Box::new(FunctionConfigType::Regular(keyframes_fn.clone())),
60 );
61 }
62
63 for name in &self.state.stylex_types_import {
64 identifiers.insert(
65 name.clone(),
66 Box::new(FunctionConfigType::Regular(types_fn.clone())),
67 );
68 }
69
70 for name in &self.state.stylex_position_try_import {
71 identifiers.insert(
72 name.clone(),
73 Box::new(FunctionConfigType::Regular(position_try_fn.clone())),
74 );
75 }
76
77 for name in &self.state.stylex_import {
78 let member_expression = member_expressions.entry(name.clone()).or_default();
79
80 member_expression.insert(
81 "keyframes".into(),
82 Box::new(FunctionConfigType::Regular(keyframes_fn.clone())),
83 );
84
85 member_expression.insert(
86 "positionTry".into(),
87 Box::new(FunctionConfigType::Regular(position_try_fn.clone())),
88 );
89
90 let identifier = identifiers
91 .entry(name.get_import_str().into())
92 .or_insert_with(|| Box::new(FunctionConfigType::Map(FxHashMap::default())));
93
94 if let Some(identifier_map) = identifier.as_map_mut() {
95 identifier_map.insert("types".into(), types_fn.clone());
96 }
97 }
98
99 self
100 .state
101 .apply_stylex_env(&mut identifiers, &mut member_expressions);
102
103 let function_map: Box<FunctionMap> = Box::new(FunctionMap {
104 identifiers,
105 member_expressions,
106 disable_imports: false,
107 });
108
109 let evaluated_arg = evaluate(&first_arg, &mut self.state, &function_map);
110
111 assert!(
112 evaluated_arg.confident,
113 "{}",
114 build_code_frame_error(
115 &Expr::Call(call.clone()),
116 &evaluated_arg.deopt.unwrap_or_else(|| *first_arg.to_owned()),
117 &non_static_value("defineVars"),
118 &mut self.state,
119 )
120 );
121
122 let value = match evaluated_arg.value {
123 Some(value) => {
124 assert!(
125 value
126 .as_expr()
127 .map(|expr| expr.is_object())
128 .unwrap_or(false),
129 "{}",
130 build_code_frame_error(
131 &Expr::Call(call.clone()),
132 &evaluated_arg.deopt.unwrap_or_else(|| *first_arg.to_owned()),
133 &non_style_object("defineVars"),
134 &mut self.state,
135 )
136 );
137 value
138 },
139 None => stylex_panic!("{}", non_static_value("defineVars")),
140 };
141
142 let file_name = match self
143 .state
144 .get_filename_for_hashing(&mut FxHashMap::default())
145 {
146 Some(name) => name,
147 None => stylex_panic!("{}", cannot_generate_hash("defineVars")),
148 };
149
150 let export_name = match var_id.map(|decl| decl.to_string()) {
151 Some(name) => name,
152 None => stylex_panic!(
153 "defineVars(): The export variable could not be found. Ensure the call is bound to a named export."
154 ),
155 };
156
157 self.state.export_id = Some(gen_file_based_identifier(&file_name, &export_name, None));
158
159 let (variables_obj, injected_styles_sans_keyframes) =
160 stylex_define_vars(&value, &mut self.state);
161
162 let mut injected_styles = self.state.other_injected_css_rules.clone();
163 injected_styles.extend(injected_styles_sans_keyframes);
164
165 let result_ast =
166 convert_object_to_ast(&NestedStringObject::FlatCompiledStylesValues(variables_obj));
167
168 self
169 .state
170 .register_styles(call, &injected_styles, &result_ast, None);
171
172 Some(result_ast)
173 } else {
174 None
175 }
176 }
177}