Skip to main content

stylex_transform/shared/transformers/
stylex_first_that_works.rs

1use log::warn;
2use stylex_macros::stylex_panic;
3use swc_core::ecma::ast::Expr;
4
5use stylex_types::traits::StyleOptions;
6
7use crate::shared::structures::state_manager::StateManager;
8use crate::shared::utils::ast::convertors::{convert_expr_to_str, create_string_expr};
9use crate::shared::{
10  structures::functions::FunctionMap, utils::common::downcast_style_options_to_state_manager,
11};
12use stylex_ast::ast::factories::{create_array_expression, create_expr_or_spread};
13use stylex_constants::constants::messages::EXPRESSION_IS_NOT_A_STRING;
14use stylex_regex::regex::IS_CSS_VAR;
15
16fn is_var(arg: &Expr, state: &mut StateManager, functions: &FunctionMap) -> bool {
17  let str_arg = match convert_expr_to_str(arg, state, functions) {
18    Some(s) => s,
19    None => stylex_panic!("{}", EXPRESSION_IS_NOT_A_STRING),
20  };
21
22  IS_CSS_VAR.is_match(&str_arg).unwrap_or_else(|err| {
23    warn!(
24      "Error matching IS_CSS_VAR for '{}': {}. Skipping pattern match.",
25      str_arg, err
26    );
27
28    false
29  })
30}
31
32pub(crate) fn stylex_first_that_works(
33  args: Vec<Expr>,
34  state: &mut dyn StyleOptions,
35  functions: &FunctionMap,
36) -> Expr {
37  let state = downcast_style_options_to_state_manager(state);
38  let first_var = args.iter().position(|arg| is_var(arg, state, functions));
39
40  match first_var {
41    None => {
42      let elems = args
43        .into_iter()
44        .rev()
45        .map(|arg| Some(create_expr_or_spread(arg)))
46        .collect();
47
48      create_array_expression(elems)
49    },
50    Some(first_var) => {
51      let priorities = args[..first_var].iter().rev().collect::<Vec<_>>();
52      let rest = &args[first_var..];
53      let first_non_var = rest.iter().position(|arg| !is_var(arg, state, functions));
54      let var_parts = if let Some(first_non_var) = first_non_var {
55        rest[..=first_non_var].iter().rev().collect::<Vec<_>>()
56      } else {
57        rest.iter().rev().collect::<Vec<_>>()
58      };
59
60      let vars = var_parts
61        .into_iter()
62        .map(|arg| {
63          if is_var(arg, state, functions) {
64            let str_arg = match convert_expr_to_str(arg, state, functions) {
65              Some(s) => s,
66              None => stylex_panic!("Argument is not a string"),
67            };
68            let cleared_str_arg = &str_arg[4..str_arg.len() - 1];
69            create_string_expr(cleared_str_arg)
70          } else {
71            arg.clone()
72          }
73        })
74        .collect::<Vec<_>>();
75
76      let return_value = {
77        let mut so_far = String::new();
78        for var_name in vars.iter() {
79          let var_name_str = match convert_expr_to_str(var_name, state, functions) {
80            Some(s) => s,
81            None => stylex_panic!("{}", EXPRESSION_IS_NOT_A_STRING),
82          };
83
84          so_far = if !so_far.is_empty() {
85            format!("var({}, {})", var_name_str, so_far)
86          } else if var_name_str.starts_with("--") {
87            format!("var({})", var_name_str)
88          } else {
89            var_name_str
90          };
91        }
92
93        let mut result = vec![create_string_expr(&so_far)];
94        result.extend(priorities.iter().map(|&expr| expr.clone()));
95        result
96      };
97
98      if return_value.len() == 1 {
99        return return_value[0].clone();
100      }
101
102      let return_value = return_value
103        .into_iter()
104        .map(|expr| Some(create_expr_or_spread(expr)))
105        .collect();
106
107      create_array_expression(return_value)
108    },
109  }
110}