stylex_transform/shared/transformers/
stylex_keyframes.rs1use std::rc::Rc;
2
3use indexmap::IndexMap;
4use stylex_macros::stylex_panic;
5use swc_core::ecma::ast::Expr;
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, FunctionMap, FunctionType};
10use crate::shared::structures::pre_rule::PreRuleValue;
11use crate::shared::structures::state_manager::StateManager;
12use crate::shared::structures::types::FlatCompiledStyles;
13use crate::shared::utils::ast::convertors::{
14 convert_expr_to_str, convert_key_value_to_str, create_string_expr,
15};
16use crate::shared::utils::common::{create_hash, dashify};
17use crate::shared::utils::core::flat_map_expanded_shorthands::flat_map_expanded_shorthands;
18use crate::shared::utils::css::common::transform_value_cached;
19use crate::shared::utils::object::{
20 Pipe, obj_entries, obj_from_entries, obj_map, obj_map_keys_string,
21};
22use crate::shared::{
23 enums::data_structures::evaluate_result_value::EvaluateResultValue,
24 utils::common::downcast_style_options_to_state_manager,
25};
26use stylex_constants::constants::messages::{
27 ENTRY_MUST_BE_TUPLE, VALUE_MUST_BE_STRING, VALUES_MUST_BE_OBJECT,
28};
29use stylex_css::css::generate_ltr::generate_ltr;
30use stylex_css::css::generate_rtl::generate_rtl;
31use stylex_structures::order_pair::OrderPair;
32use stylex_structures::pair::Pair;
33use stylex_types::enums::data_structures::injectable_style::InjectableStyleKind;
34use stylex_types::structures::injectable_style::InjectableStyle;
35
36pub(crate) fn stylex_keyframes(
37 frames: &EvaluateResultValue,
38 state: &mut StateManager,
39) -> (String, InjectableStyleKind) {
40 let mut class_name_prefix = state.options.class_name_prefix.clone();
41
42 if class_name_prefix.is_empty() {
43 class_name_prefix = "x".to_string();
44 }
45
46 let Some(frames) = frames.as_expr().and_then(|expr| expr.as_object()) else {
47 stylex_panic!("{}", VALUES_MUST_BE_OBJECT)
48 };
49
50 let expanded_object = obj_map(ObjMapType::Object(frames.clone()), state, |frame, state| {
51 let Some((_, frame, _)) = frame.as_tuple() else {
52 stylex_panic!("{}", VALUES_MUST_BE_OBJECT)
53 };
54
55 let pipe_result = Pipe::create(frame)
56 .pipe(|frame| expand_frame_shorthands(frame, state))
57 .pipe(|entries| obj_map_keys_string(&entries, dashify))
58 .pipe(|entries| {
59 obj_map(
60 ObjMapType::Map(entries),
61 state,
62 |entry, state| match entry.as_ref() {
63 FlatCompiledStylesValue::KeyValue(pair) => {
64 Rc::new(FlatCompiledStylesValue::KeyValue(Pair::new(
65 pair.key.clone(),
66 transform_value_cached(pair.key.as_str(), pair.value.as_str(), state),
67 )))
68 },
69 _ => stylex_panic!("{}", ENTRY_MUST_BE_TUPLE),
70 },
71 )
72 })
73 .done();
74
75 let pairs = pipe_result
76 .into_iter()
77 .filter_map(|(_, value)| value.as_key_value().cloned())
78 .collect::<Vec<Pair>>();
79
80 Rc::new(FlatCompiledStylesValue::KeyValues(pairs))
81 });
82
83 let options = state.options.clone();
84
85 let ltr_styles = obj_map(
86 ObjMapType::Map(expanded_object.clone()),
87 state,
88 |frame, _| {
89 let Some(pairs) = frame.as_key_values() else {
90 stylex_panic!("{}", VALUES_MUST_BE_OBJECT)
91 };
92
93 let ltr_values = pairs
94 .iter()
95 .map(|pair| generate_ltr(pair, &options))
96 .collect();
97
98 Rc::new(FlatCompiledStylesValue::KeyValues(ltr_values))
99 },
100 );
101
102 let stable_styles = obj_map(
103 ObjMapType::Map(expanded_object.clone()),
104 state,
105 |frame, _| {
106 let Some(pairs) = frame.as_key_values() else {
107 stylex_panic!("{}", VALUES_MUST_BE_OBJECT)
108 };
109
110 let ltr_values = pairs
111 .iter()
112 .map(|pair| generate_ltr(pair, &Default::default()))
113 .collect();
114
115 Rc::new(FlatCompiledStylesValue::KeyValues(ltr_values))
116 },
117 );
118
119 let options = state.options.clone();
120
121 let rtl_styles = obj_map(
122 ObjMapType::Map(expanded_object.clone()),
123 state,
124 |frame, _| {
125 let Some(pairs) = frame.as_key_values() else {
126 stylex_panic!("{}", VALUES_MUST_BE_OBJECT)
127 };
128
129 let rtl_values = pairs
130 .iter()
131 .map(|pair| generate_rtl(pair, &options).unwrap_or_else(|| pair.clone()))
132 .collect();
133
134 Rc::new(FlatCompiledStylesValue::KeyValues(rtl_values))
135 },
136 );
137
138 let ltr_string = construct_keyframes_obj(<r_styles);
139 let rtl_string = construct_keyframes_obj(&rtl_styles);
140 let stable_string = construct_keyframes_obj(&stable_styles);
141
142 let animation_name = format!(
146 "{}{}-B",
147 class_name_prefix,
148 create_hash(&format!("<>{}", stable_string))
149 );
150
151 let ltr = format!("@keyframes {}{{{}}}", animation_name, ltr_string);
152 let rtl = if ltr_string == rtl_string {
153 None
154 } else {
155 Some(format!("@keyframes {}{{{}}}", animation_name, rtl_string))
156 };
157
158 (
159 animation_name,
160 InjectableStyleKind::Regular(InjectableStyle {
161 ltr,
162 rtl,
163 priority: Some(0.0),
164 }),
165 )
166}
167
168fn construct_keyframes_obj(frames: &FlatCompiledStyles) -> String {
169 frames
170 .into_iter()
171 .map(|(key, value)| {
172 let value = match value.as_ref() {
173 FlatCompiledStylesValue::KeyValues(pairs) => pairs
174 .iter()
175 .map(|pair| {
176 if pair.key.is_empty() || pair.value.is_empty() {
177 String::default()
178 } else {
179 format!("{}:{};", pair.key, pair.value)
180 }
181 })
182 .collect::<Vec<String>>()
183 .join(""),
184 _ => stylex_panic!("Value must be a key value pair array"),
185 };
186
187 format!("{}{{{}}}", key, value)
188 })
189 .collect::<Vec<String>>()
190 .join("")
191}
192
193fn expand_frame_shorthands(frame: &Expr, state: &mut StateManager) -> IndexMap<String, String> {
194 let res: Vec<_> = obj_entries(&frame.clone())
195 .iter()
196 .flat_map(|pair| {
197 let key = convert_key_value_to_str(pair);
198 let value = match convert_expr_to_str(pair.value.as_ref(), state, &FunctionMap::default()) {
199 Some(v) => v,
200 None => stylex_panic!("{}", VALUE_MUST_BE_STRING),
201 };
202
203 flat_map_expanded_shorthands((key, PreRuleValue::String(value)), &state.options)
204 .into_iter()
205 .filter_map(|pair| {
206 pair.1.as_ref()?;
207
208 Some(pair)
209 })
210 .collect::<Vec<OrderPair>>()
211 })
212 .filter(|item| item.1.is_some())
213 .collect::<Vec<OrderPair>>();
214
215 obj_from_entries(&res)
216}
217
218pub(crate) fn get_keyframes_fn() -> FunctionConfig {
219 FunctionConfig {
220 fn_ptr: FunctionType::StylexExprFn(
221 |expr: Expr, local_state: &mut dyn stylex_types::traits::StyleOptions| -> Expr {
222 let state = downcast_style_options_to_state_manager(local_state);
223
224 let (animation_name, injected_style) =
225 stylex_keyframes(&EvaluateResultValue::Expr(expr), state);
226
227 state
228 .other_injected_css_rules
229 .insert(animation_name.clone(), Rc::new(injected_style));
230
231 create_string_expr(animation_name.as_str())
232 },
233 ),
234 takes_path: false,
235 }
236}