stylex_transform/shared/utils/core/
parse_nullable_style.rs1use 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 #[inline]
49 pub(crate) fn style_object(style_obj: StyleObject) -> Self {
50 ResolvedArg::StyleObject(style_obj, Vec::default(), Vec::default())
51 }
52
53 #[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 #[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 #[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}