Skip to main content

stylex_transform/shared/transformers/
stylex_position_try.rs

1use std::fmt::Write;
2use std::rc::Rc;
3
4use stylex_macros::stylex_panic;
5use swc_core::ecma::ast::{Expr, PropOrSpread};
6
7use crate::shared::enums::data_structures::flat_compiled_styles_value::FlatCompiledStylesValue;
8use crate::shared::enums::data_structures::obj_map_type::ObjMapType;
9use crate::shared::structures::functions::{FunctionConfig, FunctionType};
10use crate::shared::structures::state_manager::StateManager;
11use crate::shared::structures::types::FlatCompiledStyles;
12use crate::shared::utils::ast::convertors::{convert_lit_to_string, create_string_expr};
13use crate::shared::utils::common::{create_hash, dashify};
14use crate::shared::utils::css::common::transform_value_cached;
15use crate::shared::utils::object::{
16  Pipe, obj_map, obj_map_keys_string, preprocess_object_properties,
17};
18use crate::shared::{
19  enums::data_structures::evaluate_result_value::EvaluateResultValue,
20  utils::common::downcast_style_options_to_state_manager,
21};
22use stylex_ast::ast::factories::{create_object_lit, create_string_key_value_prop};
23use stylex_constants::constants::messages::{
24  ENTRY_MUST_BE_TUPLE, THEME_VAR_TUPLE, VALUE_MUST_BE_STRING, VALUES_MUST_BE_OBJECT,
25};
26use stylex_css::css::generate_ltr::generate_ltr;
27use stylex_css::css::generate_rtl::generate_rtl;
28use stylex_structures::pair::Pair;
29use stylex_types::enums::data_structures::injectable_style::InjectableStyleKind;
30use stylex_types::structures::injectable_style::InjectableStyle;
31
32pub(crate) fn stylex_position_try(
33  styles: &EvaluateResultValue,
34  state: &mut StateManager,
35) -> (String, InjectableStyleKind) {
36  let mut class_name_prefix = state.options.class_name_prefix.clone();
37
38  if class_name_prefix.is_empty() {
39    class_name_prefix = "x".to_string();
40  }
41
42  let Some(styles) = styles.as_expr().and_then(|expr| expr.as_object()) else {
43    stylex_panic!("{}", VALUES_MUST_BE_OBJECT)
44  };
45
46  let extended_object = {
47    let pipe_result = Pipe::create(styles.clone())
48      .pipe(|styles| preprocess_object_properties(&Expr::Object(styles), state))
49      .pipe(|entries| obj_map_keys_string(&entries, dashify))
50      .pipe(|entries| {
51        obj_map(
52          ObjMapType::Map(entries),
53          state,
54          |entry, state| match entry.as_ref() {
55            FlatCompiledStylesValue::KeyValue(pair) => Rc::new(FlatCompiledStylesValue::String(
56              transform_value_cached(pair.key.as_str(), pair.value.as_str(), state),
57            )),
58            _ => stylex_panic!("{}", ENTRY_MUST_BE_TUPLE),
59          },
60        )
61      })
62      .done();
63
64    create_object_lit(
65      pipe_result
66        .into_iter()
67        .map(|(key, value)| {
68          let value = match value.as_string() {
69            Some(s) => s.clone(),
70            None => stylex_panic!("{}", VALUE_MUST_BE_STRING),
71          };
72
73          create_string_key_value_prop(&key, &value)
74        })
75        .collect::<Vec<PropOrSpread>>(),
76    )
77  };
78
79  let options = state.options.clone();
80
81  let ltr_styles = obj_map(
82    ObjMapType::Object(extended_object.clone()),
83    state,
84    |style, _| {
85      let Some(tuple) = style.as_tuple() else {
86        stylex_panic!("{}", THEME_VAR_TUPLE)
87      };
88
89      let ltr_values = generate_ltr(
90        &Pair {
91          key: tuple.0.clone(),
92          value: convert_lit_to_string(match tuple.1.clone().as_lit() {
93            Some(lit) => lit,
94            None => stylex_panic!("{}", VALUE_MUST_BE_STRING),
95          })
96          .unwrap_or_default(),
97        },
98        &options,
99      );
100
101      Rc::new(FlatCompiledStylesValue::KeyValue(ltr_values))
102    },
103  );
104
105  let rtl_styles = obj_map(
106    ObjMapType::Object(extended_object.clone()),
107    state,
108    |style, _| {
109      let Some(tuple) = style.as_tuple() else {
110        stylex_panic!("{}", THEME_VAR_TUPLE)
111      };
112
113      let value = convert_lit_to_string(match tuple.1.clone().as_lit() {
114        Some(lit) => lit,
115        None => stylex_panic!("{}", VALUE_MUST_BE_STRING),
116      })
117      .unwrap_or_default();
118
119      let key = tuple.0.clone();
120
121      let rtl_values = generate_rtl(
122        &Pair {
123          key: key.clone(),
124          value: value.clone(),
125        },
126        &options,
127      );
128
129      match rtl_values {
130        Some(rtl_value) => Rc::new(FlatCompiledStylesValue::KeyValue(rtl_value)),
131        None => Rc::new(FlatCompiledStylesValue::KeyValue(Pair { key, value })),
132      }
133    },
134  );
135
136  let ltr_string = construct_position_try_obj(ltr_styles);
137  let rtl_string = construct_position_try_obj(rtl_styles);
138
139  let position_try_name = format!("--{}{}", class_name_prefix, create_hash(&ltr_string));
140
141  let ltr = format!("@position-try {}{{{}}}", position_try_name, ltr_string);
142  let rtl = if ltr_string == rtl_string {
143    None
144  } else {
145    Some(format!(
146      "@position-try {}{{{}}}",
147      position_try_name, rtl_string
148    ))
149  };
150
151  (
152    position_try_name,
153    InjectableStyleKind::Regular(InjectableStyle {
154      ltr,
155      rtl,
156      priority: Some(0.0),
157    }),
158  )
159}
160
161pub(crate) fn get_position_try_fn() -> FunctionConfig {
162  FunctionConfig {
163    fn_ptr: FunctionType::StylexExprFn(
164      |expr: Expr, local_state: &mut dyn stylex_types::traits::StyleOptions| -> Expr {
165        let state = downcast_style_options_to_state_manager(local_state);
166
167        let (position_try_name, injected_style) =
168          stylex_position_try(&EvaluateResultValue::Expr(expr), state);
169
170        state
171          .other_injected_css_rules
172          .insert(position_try_name.clone(), Rc::new(injected_style));
173
174        create_string_expr(position_try_name.as_str())
175      },
176    ),
177    takes_path: false,
178  }
179}
180
181fn construct_position_try_obj(styles: FlatCompiledStyles) -> String {
182  // Collect and sort keys for deterministic output
183  let mut sorted_keys = styles.keys().cloned().collect::<Vec<String>>();
184  sorted_keys.sort();
185
186  sorted_keys
187    .into_iter()
188    .map(|k| {
189      let v = match styles.get(&k) {
190        Some(v) => v,
191        None => stylex_panic!("Expected property key to exist in compiled styles."),
192      };
193      match v.as_ref() {
194        FlatCompiledStylesValue::String(val) => format!("{}:{};", k, val),
195        FlatCompiledStylesValue::KeyValue(pair) => {
196          format!("{}:{};{}:{};", k, k, pair.key, pair.value)
197        },
198        FlatCompiledStylesValue::KeyValues(pairs) => {
199          let mut strng = String::new();
200          for pair in pairs {
201            let _ = write!(strng, "{}:{};{}:{};", k, k, pair.key, pair.value);
202          }
203          strng
204        },
205        _ => String::default(),
206      }
207    })
208    .collect::<Vec<String>>()
209    .join("")
210}