Skip to main content

stylex_transform/transform/stylex/
transform_stylex_position_try_call.rs

1use std::rc::Rc;
2use stylex_constants::constants::messages::{SPREAD_NOT_SUPPORTED, expected_call_expression};
3
4use indexmap::IndexMap;
5use rustc_hash::FxHashMap;
6use stylex_macros::{stylex_panic, stylex_unimplemented};
7use swc_core::ecma::ast::VarDeclarator;
8use swc_core::{common::comments::Comments, ecma::ast::Expr};
9
10use crate::shared::structures::functions::FunctionConfigType;
11use crate::shared::structures::functions::{FunctionConfig, FunctionMap, FunctionType};
12use crate::shared::structures::types::{FunctionMapIdentifiers, FunctionMapMemberExpression};
13use crate::shared::transformers::stylex_first_that_works::stylex_first_that_works;
14use crate::shared::transformers::stylex_position_try::stylex_position_try;
15use crate::shared::utils::ast::convertors::create_string_expr;
16use crate::shared::utils::js::evaluate::evaluate;
17use crate::shared::utils::log::build_code_frame_error::build_code_frame_error;
18use crate::shared::utils::validators::validate_stylex_position_try_indent;
19use crate::shared::utils::validators::{assert_valid_position_try, assert_valid_properties};
20use crate::{StyleXTransform, shared::utils::validators::is_position_try_call};
21use stylex_constants::constants::common::VALID_POSITION_TRY_PROPERTIES;
22use stylex_constants::constants::messages::POSITION_TRY_INVALID_PROPERTY;
23use stylex_constants::constants::messages::{non_static_value, non_style_object};
24
25impl<C> StyleXTransform<C>
26where
27  C: Comments,
28{
29  pub(crate) fn transform_stylex_position_try_call(
30    &mut self,
31    var_decl: &VarDeclarator,
32  ) -> Option<Expr> {
33    let is_position_try_call = is_position_try_call(var_decl, &self.state);
34
35    if is_position_try_call {
36      validate_stylex_position_try_indent(var_decl, &mut self.state);
37
38      let call = match var_decl.init.as_ref().and_then(|decl| decl.as_call()) {
39        Some(call) => call,
40        None => stylex_panic!("{}", expected_call_expression("positionTry")),
41      };
42
43      let first_arg = call.args.first().map(|first_arg| match &first_arg.spread {
44        Some(_) => stylex_unimplemented!("{}", SPREAD_NOT_SUPPORTED),
45        None => first_arg.expr.clone(),
46      })?;
47
48      let mut identifiers: FunctionMapIdentifiers = FxHashMap::default();
49      let mut member_expressions: FunctionMapMemberExpression = FxHashMap::default();
50
51      let first_that_works_fn = FunctionConfig {
52        fn_ptr: FunctionType::ArrayArgs(stylex_first_that_works),
53        takes_path: false,
54      };
55
56      for name in &self.state.stylex_first_that_works_import {
57        identifiers.insert(
58          name.clone(),
59          Box::new(FunctionConfigType::Regular(first_that_works_fn.clone())),
60        );
61      }
62
63      for name in &self.state.stylex_import {
64        let member_expression = member_expressions.entry(name.clone()).or_default();
65
66        member_expression.insert(
67          "firstThatWorks".into(),
68          Box::new(FunctionConfigType::Regular(first_that_works_fn.clone())),
69        );
70      }
71
72      self
73        .state
74        .apply_stylex_env(&mut identifiers, &mut member_expressions);
75
76      let function_map: Box<FunctionMap> = Box::new(FunctionMap {
77        identifiers,
78        member_expressions,
79        disable_imports: false,
80      });
81
82      let evaluated_arg = evaluate(&first_arg, &mut self.state, &function_map);
83
84      assert!(
85        evaluated_arg.confident,
86        "{}",
87        build_code_frame_error(
88          &Expr::Call(call.clone()),
89          &evaluated_arg.deopt.unwrap_or_else(|| *first_arg.to_owned()),
90          &non_static_value("positionTry"),
91          &mut self.state,
92        )
93      );
94
95      let plain_object = match evaluated_arg.value {
96        Some(value) => {
97          assert!(
98            value
99              .as_expr()
100              .map(|expr| expr.is_object())
101              .unwrap_or(false),
102            "{}",
103            build_code_frame_error(
104              &Expr::Call(call.clone()),
105              &evaluated_arg.deopt.unwrap_or_else(|| *first_arg.to_owned()),
106              &non_style_object("positionTry"),
107              &mut self.state,
108            )
109          );
110          value
111        },
112        None => stylex_panic!(
113          "{}",
114          build_code_frame_error(
115            &Expr::Call(call.clone()),
116            &evaluated_arg.deopt.unwrap_or_else(|| *first_arg.to_owned()),
117            &non_static_value("positionTry"),
118            &mut self.state,
119          )
120        ),
121      };
122
123      assert_valid_position_try(&plain_object, &mut self.state);
124      assert_valid_properties(
125        &plain_object,
126        &*VALID_POSITION_TRY_PROPERTIES,
127        POSITION_TRY_INVALID_PROPERTY,
128        &mut self.state,
129      );
130
131      let (position_try_name, injectable_style) =
132        stylex_position_try(&plain_object, &mut self.state);
133
134      let mut injected_styles = IndexMap::new();
135
136      injected_styles.insert(position_try_name.clone(), Rc::new(injectable_style));
137
138      let result_ast = create_string_expr(position_try_name.as_str());
139
140      self
141        .state
142        .register_styles(call, &injected_styles, &result_ast, None);
143
144      Some(result_ast)
145    } else {
146      None
147    }
148  }
149}