stylex_transform/shared/utils/core/
make_string_expression.rs1use 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}