stylex_transform/shared/transformers/
stylex_first_that_works.rs1use 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}