Skip to main content

stylex_transform/transform/stylex/
transform_stylex_define_vars_call.rs

1use 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}