Skip to main content

stylex_transform/shared/utils/core/
parse_nullable_style.rs

1use std::rc::Rc;
2
3use indexmap::IndexMap;
4use stylex_macros::{stylex_panic, stylex_unimplemented};
5use swc_core::ecma::ast::{Expr, Ident, Lit, MemberExpr, MemberProp, ObjectLit};
6
7use crate::shared::enums::data_structures::evaluate_result_value::EvaluateResultValue;
8use crate::shared::enums::data_structures::flat_compiled_styles_value::FlatCompiledStylesValue;
9use crate::shared::structures::functions::FunctionMap;
10use crate::shared::structures::state_manager::StateManager;
11use crate::shared::structures::types::FlatCompiledStyles;
12use crate::shared::utils::ast::convertors::{
13  convert_expr_to_bool, convert_expr_to_str, convert_key_value_to_str, convert_lit_to_string,
14};
15use crate::shared::utils::common::reduce_ident_count;
16use crate::shared::utils::js::evaluate::evaluate;
17
18#[derive(Debug, PartialEq, Clone)]
19pub(crate) enum StyleObject {
20  Style(FlatCompiledStyles),
21  Nullable,
22  Other,
23  Unreachable,
24}
25
26#[derive(Debug, PartialEq, Clone)]
27pub(crate) enum ResolvedArg {
28  StyleObject(StyleObject, Vec<Ident>, Vec<MemberExpr>),
29  ConditionalStyle(
30    Expr,
31    Option<StyleObject>,
32    Option<StyleObject>,
33    Vec<Ident>,
34    Vec<MemberExpr>,
35  ),
36}
37
38impl ResolvedArg {
39  /// Creates a `ResolvedArg::StyleObject` with empty ident and member vectors.
40  ///
41  /// # Arguments
42  /// * `style_obj` - The resolved style object
43  ///
44  /// # Example
45  /// ```ignore
46  /// let arg = ResolvedArg::style_object(StyleObject::Style(...));
47  /// ```
48  #[inline]
49  pub(crate) fn style_object(style_obj: StyleObject) -> Self {
50    ResolvedArg::StyleObject(style_obj, Vec::default(), Vec::default())
51  }
52
53  /// Creates a `ResolvedArg::StyleObject` with identifier tracking.
54  ///
55  /// # Arguments
56  /// * `style_obj` - The resolved style object
57  /// * `idents` - Vector of identifiers
58  ///
59  /// # Example
60  /// ```ignore
61  /// let arg = ResolvedArg::style_object_with_ident(StyleObject::Style(...), vec![ident]);
62  /// ```
63  #[inline]
64  pub(crate) fn style_object_with_ident(style_obj: StyleObject, idents: Vec<Ident>) -> Self {
65    ResolvedArg::StyleObject(style_obj, idents, Vec::default())
66  }
67
68  /// Creates a `ResolvedArg::StyleObject` with both identifier and member tracking.
69  ///
70  /// # Arguments
71  /// * `style_obj` - The resolved style object
72  /// * `idents` - Vector of identifiers
73  /// * `members` - Vector of member expressions
74  ///
75  /// # Example
76  /// ```ignore
77  /// let arg = ResolvedArg::style_object_full(StyleObject::Style(...), vec![ident], vec![member]);
78  /// ```
79  #[inline]
80  pub(crate) fn style_object_full(
81    style_obj: StyleObject,
82    idents: Vec<Ident>,
83    members: Vec<MemberExpr>,
84  ) -> Self {
85    ResolvedArg::StyleObject(style_obj, idents, members)
86  }
87
88  /// Creates a `ResolvedArg::ConditionalStyle` with full parameters.
89  ///
90  /// # Arguments
91  /// * `test` - The test expression
92  /// * `primary` - Primary style object
93  /// * `fallback` - Fallback style object
94  /// * `idents` - Vector of identifiers
95  /// * `members` - Vector of member expressions
96  ///
97  /// # Example
98  /// ```ignore
99  /// let arg = ResolvedArg::conditional(test, Some(primary), Some(fallback), idents, members);
100  /// ```
101  #[inline]
102  pub(crate) fn conditional(
103    test: Expr,
104    primary: Option<StyleObject>,
105    fallback: Option<StyleObject>,
106    idents: Vec<Ident>,
107    members: Vec<MemberExpr>,
108  ) -> Self {
109    ResolvedArg::ConditionalStyle(test, primary, fallback, idents, members)
110  }
111}
112
113pub(crate) fn parse_nullable_style(
114  path: &Expr,
115  state: &mut StateManager,
116  evaluate_path_fn_config: &FunctionMap,
117  should_reduce_count: bool,
118) -> StyleObject {
119  let result = match path {
120    Expr::Lit(lit) => {
121      if let Lit::Null(_) = lit {
122        StyleObject::Nullable
123      } else {
124        StyleObject::Other
125      }
126    },
127    Expr::Ident(ident) => {
128      if ident.sym == "undefined" {
129        StyleObject::Nullable
130      } else {
131        if should_reduce_count {
132          reduce_ident_count(state, ident);
133        }
134        StyleObject::Other
135      }
136    },
137    Expr::Member(member) => {
138      let mut obj_name: Option<String> = None;
139      let mut prop_name: Option<String> = None;
140
141      if let Some(obj_ident) = member.obj.as_ident()
142        && state.style_map.contains_key(obj_ident.sym.as_str())
143      {
144        if should_reduce_count && let Some(member_ident) = member.obj.as_ident() {
145          reduce_ident_count(state, member_ident);
146        }
147
148        match &member.prop {
149          MemberProp::Ident(prop_ident) => {
150            obj_name = Some(obj_ident.sym.as_str().to_string());
151            prop_name = Some(prop_ident.sym.as_str().to_string());
152          },
153          MemberProp::Computed(computed) => {
154            if let Some(lit) = computed.expr.as_lit() {
155              obj_name = Some(obj_ident.sym.as_str().to_string());
156              prop_name = convert_lit_to_string(lit);
157            }
158          },
159          MemberProp::PrivateName(_) => {},
160        }
161      }
162
163      if let Some(obj_name) = obj_name
164        && let Some(prop_name) = prop_name
165      {
166        let style = state.style_map.get(&obj_name);
167
168        if let Some(style) = style {
169          let style_value = style.get(&prop_name);
170
171          if let Some(style_value) = style_value {
172            return StyleObject::Style((**style_value).clone());
173          }
174        }
175      }
176
177      StyleObject::Other
178    },
179    _ => StyleObject::Other,
180  };
181
182  if result == StyleObject::Other {
183    let parsed_obj = evaluate(path, state, evaluate_path_fn_config);
184
185    if parsed_obj.confident
186      && let Some(result) = parsed_obj.value.as_ref()
187    {
188      let mut compiled_styles: IndexMap<String, Rc<FlatCompiledStylesValue>> = IndexMap::new();
189
190      if let Some(value) = parse_compiled_styles(&mut compiled_styles, result) {
191        return value;
192      }
193    }
194  }
195
196  result
197}
198
199fn parse_compiled_styles(
200  compiled_styles: &mut IndexMap<String, Rc<FlatCompiledStylesValue>>,
201  result: &EvaluateResultValue,
202) -> Option<StyleObject> {
203  match result {
204    EvaluateResultValue::Vec(arr) => {
205      for item in arr.iter() {
206        match item.as_ref() {
207          Some(EvaluateResultValue::Expr(expr)) => parse_nullable_object(compiled_styles, expr),
208          Some(EvaluateResultValue::Vec(arr)) => {
209            parse_compiled_styles(compiled_styles, &EvaluateResultValue::Vec(arr.clone()));
210          },
211          _ => {
212            stylex_unimplemented!(
213              "Encountered an unsupported evaluation result while parsing a nullable style array."
214            );
215          },
216        };
217      }
218      if compiled_styles.is_empty() {
219        return Some(StyleObject::Other);
220      }
221      return Some(StyleObject::Style(compiled_styles.clone()));
222    },
223    EvaluateResultValue::Expr(expr) => {
224      if expr.is_object() {
225        parse_nullable_object(compiled_styles, expr);
226        return Some(StyleObject::Style(compiled_styles.clone()));
227      }
228    },
229    EvaluateResultValue::ThemeRef(_) => {
230      return Some(StyleObject::Other);
231    },
232    _ => {
233      stylex_unimplemented!(
234        "Encountered an unsupported evaluation result while parsing a nullable style."
235      );
236    },
237  }
238  None
239}
240
241fn parse_nullable_object(
242  compiled_styles: &mut IndexMap<String, Rc<FlatCompiledStylesValue>>,
243  expr: &Expr,
244) {
245  match expr {
246    Expr::Object(ObjectLit { props, .. }) => {
247      for prop in props.iter() {
248        if let Some(key_value) = prop.as_prop().and_then(|p| p.as_key_value()) {
249          let key = convert_key_value_to_str(key_value);
250          match key_value.value.as_ref() {
251            Expr::Lit(lit) => parse_nullable_key_value(compiled_styles, key, lit),
252
253            _ => {
254              stylex_unimplemented!(
255                "Encountered an unsupported expression type while parsing a nullable style array."
256              );
257            },
258          };
259        }
260      }
261    },
262    _ => {
263      stylex_unimplemented!(
264        "Encountered an unsupported expression type while parsing a nullable style array."
265      );
266    },
267  }
268}
269
270fn parse_nullable_key_value(
271  compiled_styles: &mut IndexMap<String, Rc<FlatCompiledStylesValue>>,
272  key: String,
273  lit: &Lit,
274) {
275  match lit {
276    Lit::Str(_) => {
277      let value = match convert_lit_to_string(lit) {
278        Some(s) => s,
279        None => stylex_panic!("Failed to convert literal value to string in style parsing."),
280      };
281
282      compiled_styles.insert(key, Rc::new(FlatCompiledStylesValue::String(value)));
283    },
284    Lit::Bool(bool_lit) => {
285      let value = bool_lit.value;
286      compiled_styles.insert(key, Rc::new(FlatCompiledStylesValue::Bool(value)));
287    },
288    Lit::Null(_) => {
289      compiled_styles.insert(key, Rc::new(FlatCompiledStylesValue::Null));
290    },
291    _ => {
292      stylex_panic!("Unhandled literal type in nullable style parsing array");
293    },
294  }
295}
296
297fn _evaluate_style_object(
298  path: &Expr,
299  state: &mut StateManager,
300  functions: &FunctionMap,
301) -> Option<StyleObject> {
302  let parsed_obj = evaluate(path, state, functions);
303  if parsed_obj.confident
304    && let Some(EvaluateResultValue::Expr(Expr::Object(obj))) = parsed_obj.value.as_ref()
305  {
306    let style_value: FlatCompiledStyles = obj
307      .props
308      .iter()
309      .filter_map(|prop| {
310        prop
311          .as_prop()
312          .and_then(|prop| prop.as_key_value())
313          .map(|key_value| {
314            let key = convert_key_value_to_str(key_value);
315            let value = if let Some(strng) =
316              convert_expr_to_str(key_value.value.as_ref(), state, functions)
317            {
318              FlatCompiledStylesValue::String(strng)
319            } else {
320              FlatCompiledStylesValue::Bool(convert_expr_to_bool(
321                key_value.value.as_ref(),
322                state,
323                functions,
324              ))
325            };
326
327            (key, Rc::new(value))
328          })
329      })
330      .collect();
331
332    return Some(StyleObject::Style(style_value));
333  };
334
335  None
336}