Skip to main content

stylex_transform/shared/utils/core/
make_string_expression.rs

1use stylex_macros::stylex_panic;
2use swc_core::{
3  common::DUMMY_SP,
4  ecma::{
5    ast::{
6      BinExpr, BinaryOp, ComputedPropName, Expr, KeyValueProp, MemberExpr, MemberProp, Prop,
7      PropName, PropOrSpread, UnaryExpr, UnaryOp,
8    },
9    utils::quote_ident,
10  },
11};
12
13use crate::shared::enums::data_structures::fn_result::FnResult;
14use crate::shared::utils::ast::convertors::{create_number_expr, create_string_expr};
15use stylex_ast::ast::factories::create_object_expression;
16
17use super::{js_to_expr::convert_object_to_ast, parse_nullable_style::ResolvedArg};
18
19fn fn_result_to_expression(fn_result: FnResult) -> Option<Expr> {
20  match fn_result {
21    FnResult::Stylex(string_object) => Some(string_object),
22    FnResult::Props(string_object) | FnResult::Attrs(string_object) => {
23      Some(convert_object_to_ast(&string_object))
24    },
25  }
26}
27
28pub(crate) fn make_string_expression(
29  values: &[ResolvedArg],
30  props_like_fn: fn(&[ResolvedArg]) -> Option<FnResult>,
31) -> Option<Expr> {
32  let conditions = values
33    .iter()
34    .filter_map(|value| match value {
35      ResolvedArg::ConditionalStyle(expr, _, _, _, _) => Some(expr),
36      _ => None,
37    })
38    .collect::<Vec<_>>();
39
40  if conditions.is_empty() {
41    match props_like_fn(values) {
42      Some(value) => {
43        return fn_result_to_expression(value);
44      },
45      _ => {
46        return Some(create_string_expr(""));
47      },
48    }
49  }
50
51  let condition_permutations = gen_condition_permutations(conditions.len());
52
53  let obj_entries = condition_permutations
54    .iter()
55    .filter_map(|permutation| {
56      let mut i = 0;
57
58      let args = values
59        .iter()
60        .filter_map(|arg| match arg {
61          ResolvedArg::StyleObject(_, _, _) => Some(arg.clone()),
62          ResolvedArg::ConditionalStyle(_test, primary, fallback, idents, member) => {
63            let result = if permutation.get(i).unwrap_or(&false) == &true {
64              primary
65            } else {
66              fallback
67            };
68
69            i += 1;
70
71            result.as_ref().map(|result| {
72              ResolvedArg::StyleObject(result.clone(), idents.clone(), member.clone())
73            })
74          },
75        })
76        .collect::<Vec<ResolvedArg>>();
77
78      let key = permutation
79        .iter()
80        .fold(0, |so_far, &b| (so_far << 1) | if b { 1 } else { 0 });
81
82      if let Some(result) = fn_result_to_expression(match props_like_fn(&args) {
83        Some(r) => r,
84        None => stylex_panic!(
85          "Style transformation returned no result for the given condition permutation."
86        ),
87      }) {
88        let prop = PropOrSpread::Prop(Box::new(Prop::from(KeyValueProp {
89          key: PropName::Ident(quote_ident!(key.to_string())),
90          value: Box::new(result),
91        })));
92        return Some(prop);
93      }
94
95      None
96    })
97    .collect::<Vec<PropOrSpread>>();
98
99  let obj_expressions = create_object_expression(obj_entries);
100  let conditions_to_key =
101    gen_bitwise_or_of_conditions(&conditions.into_iter().cloned().collect::<Vec<_>>());
102
103  Some(Expr::from(MemberExpr {
104    span: DUMMY_SP,
105    obj: Box::new(obj_expressions),
106    prop: MemberProp::Computed(ComputedPropName {
107      span: DUMMY_SP,
108      expr: conditions_to_key,
109    }),
110  }))
111}
112
113fn gen_bitwise_or_of_conditions(conditions: &[Expr]) -> Box<Expr> {
114  let binary_expressions = conditions
115    .iter()
116    .enumerate()
117    .map(|(i, condition)| {
118      let shift = conditions.len() - i - 1;
119
120      Expr::from(BinExpr {
121        left: Box::new(Expr::from(UnaryExpr {
122          span: DUMMY_SP,
123          op: UnaryOp::Bang,
124          arg: Box::new(Expr::from(UnaryExpr {
125            span: DUMMY_SP,
126            op: UnaryOp::Bang,
127            arg: Box::new(condition.clone()),
128          })),
129        })),
130        op: BinaryOp::LShift,
131        right: Box::new(create_number_expr(shift as f64)),
132        span: DUMMY_SP,
133      })
134    })
135    .collect::<Vec<Expr>>();
136
137  Box::new(
138    match binary_expressions.into_iter().reduce(|acc, expr| {
139      Expr::from(BinExpr {
140        span: DUMMY_SP,
141        op: BinaryOp::BitOr,
142        left: Box::new(acc),
143        right: Box::new(expr),
144      })
145    }) {
146      Some(expr) => expr,
147      None => stylex_panic!("Cannot generate condition mask from an empty conditions list."),
148    },
149  )
150}
151
152fn gen_condition_permutations(count: usize) -> Vec<Vec<bool>> {
153  (0..2usize.pow(count as u32))
154    .map(|i| (0..count).map(|j| i & (1 << j) != 0).collect())
155    .collect()
156}