Skip to main content

stylex_ast/ast/
factories.rs

1use stylex_macros::stylex_panic;
2use swc_core::{
3  common::SyntaxContext,
4  ecma::ast::{
5    ArrowExpr, BigInt, BindingIdent, BlockStmtOrExpr, CallExpr, Callee, Ident, IdentName, JSXAttr,
6    JSXAttrName, JSXAttrOrSpread, JSXAttrValue, KeyValueProp, Lit, MemberExpr, Null, ParenExpr,
7    Pat, Prop, PropName, SpreadElement, VarDeclarator,
8  },
9};
10use swc_core::{
11  common::{DUMMY_SP, Span},
12  ecma::ast::{ArrayLit, Expr, ExprOrSpread, ObjectLit, PropOrSpread},
13};
14
15use super::convertors::{
16  convert_string_to_prop_name, create_bool_expr, create_null_expr, create_number_expr,
17  create_string_expr,
18};
19
20/// Wraps an owned expression in a ParenExpr with DUMMY_SP span.
21/// This is commonly used when creating error contexts.
22///
23/// # Example
24/// ```ignore
25/// let wrapped = wrap_in_paren(some_expr);
26/// ```
27#[inline]
28pub fn wrap_in_paren(expr: Expr) -> Expr {
29  Expr::Paren(ParenExpr {
30    span: DUMMY_SP,
31    expr: Box::new(expr),
32  })
33}
34
35/// Wraps a reference to an expression in a ParenExpr with DUMMY_SP span.
36/// This clones the expression and is commonly used when creating error contexts.
37///
38/// # Example
39/// ```ignore
40/// let wrapped = wrap_in_paren_ref(&path);
41/// ```
42#[inline]
43pub fn wrap_in_paren_ref(expr: &Expr) -> Expr {
44  wrap_in_paren(expr.clone())
45}
46
47/// Creates an `ObjectLit` from a vector of `PropOrSpread`.
48///
49/// # Arguments
50/// * `props` - The properties to include in the object literal
51///
52/// # Example
53/// ```ignore
54/// let object_lit = create_object_lit(vec![prop_or_spread_factory(key, value)]);
55/// ```
56pub fn create_object_lit(props: Vec<PropOrSpread>) -> ObjectLit {
57  ObjectLit {
58    span: DUMMY_SP,
59    props,
60  }
61}
62
63/// Creates an `ArrayLit` from a vector of `ExprOrSpread`.
64///
65/// # Arguments
66/// * `elems` - The elements to include in the array literal
67///
68/// # Example
69/// ```ignore
70/// let array_lit = create_array_lit(vec![create_expr_or_spread(value)]);
71/// ```
72pub fn create_array_lit(elems: Vec<Option<ExprOrSpread>>) -> ArrayLit {
73  ArrayLit {
74    span: DUMMY_SP,
75    elems,
76  }
77}
78
79/// Creates an `Expr::Object` from a vector of `PropOrSpread`.
80///
81/// # Arguments
82/// * `props` - The properties to include in the object literal
83///
84/// # Example
85/// ```ignore
86/// let object_expression = create_object_expression(vec![prop_or_spread_factory(key, value)]);
87/// ```
88pub fn create_object_expression(props: Vec<PropOrSpread>) -> Expr {
89  Expr::from(create_object_lit(props))
90}
91
92pub fn create_array_expression(elems: Vec<Option<ExprOrSpread>>) -> Expr {
93  Expr::from(create_array_lit(elems))
94}
95
96/// Creates a `PropOrSpread` from a key and value.
97///
98/// # Arguments
99/// * `key` - The key of the property
100/// * `value` - The value of the property
101///
102/// # Example
103/// ```ignore
104/// let prop_or_spread = create_key_value_prop("key", value);
105pub fn create_key_value_prop(key: &str, value: Expr) -> PropOrSpread {
106  PropOrSpread::from(Prop::from(KeyValueProp {
107    key: match convert_string_to_prop_name(key) {
108      Some(k) => k,
109      None => stylex_panic!("Failed to create prop name from key: {}", key),
110    },
111    value: Box::new(value),
112  }))
113}
114
115/// Creates a `BindingIdent` from an `Ident`.
116///
117/// # Arguments
118/// * `ident` - The identifier to create a binding for
119///
120/// # Example
121/// ```ignore
122/// let binding_ident = create_binding_ident(ident);
123/// ```
124pub fn create_binding_ident(ident: Ident) -> BindingIdent {
125  BindingIdent::from(ident)
126}
127
128/// Creates a `Lit::Str` from a string.
129///
130/// # Arguments
131/// * `value` - The string to create a literal for
132///
133/// # Example
134/// ```ignore
135/// let lit_str = create_string_lit("value");
136/// ```
137pub fn create_string_lit(value: &str) -> Lit {
138  Lit::from(value)
139}
140
141/// Creates a `Lit::Number` from a number.
142///
143/// # Arguments
144/// * `value` - The number to create a literal for
145pub fn create_number_lit(value: f64) -> Lit {
146  Lit::from(value)
147}
148
149/// Creates a `Lit::BigInt` from a `BigInt`.
150///
151/// # Arguments
152/// * `value` - The big integer to create a literal for
153///
154/// # Example
155/// ```ignore
156/// let lit_big_int = create_big_int_lit(BigInt::from(123));
157/// ```
158pub fn create_big_int_lit(value: BigInt) -> Lit {
159  Lit::from(value)
160}
161
162/// Creates a `Lit::Boolean` from a boolean.
163///
164/// # Arguments
165/// * `value` - The boolean to create a literal for
166///
167/// # Example
168/// ```ignore
169/// let lit_boolean = create_boolean_lit(true);
170/// ```
171pub fn create_boolean_lit(value: bool) -> Lit {
172  Lit::from(value)
173}
174
175/// Creates a `Lit::Null` from a `Null`.
176///
177/// # Arguments
178/// * `value` - The null to create a literal for
179///
180/// # Example
181/// ```ignore
182/// let lit_null = create_null_lit();
183/// ```
184pub fn create_null_lit() -> Lit {
185  Lit::Null(Null { span: DUMMY_SP })
186}
187
188/// Creates an `Ident` from a string.
189///
190/// # Arguments
191/// * `name` - The identifier name
192///
193/// # Example
194/// ```ignore
195/// let ident = create_ident("props");
196/// ```
197pub fn create_ident(name: &str) -> Ident {
198  Ident::from(name)
199}
200
201pub fn create_nested_object_prop(key: &str, values: Vec<PropOrSpread>) -> PropOrSpread {
202  let object = ObjectLit {
203    span: DUMMY_SP,
204    props: values,
205  };
206
207  create_key_value_prop(key, Expr::Object(object))
208}
209
210/// Creates a `PropOrSpread` from an already-constructed `PropName` and an expression value.
211///
212/// Use this when the key is an existing `PropName` (e.g. cloned from another prop),
213/// avoiding the need to re-stringify it.
214pub fn create_prop_from_name(key: PropName, value: Expr) -> PropOrSpread {
215  PropOrSpread::from(Prop::from(KeyValueProp {
216    key,
217    value: Box::new(value),
218  }))
219}
220
221/// Creates a `KeyValueProp` with an `IdentName` key.
222///
223/// # Arguments
224/// * `key` - The identifier name
225/// * `value` - The value of the property
226///
227/// # Example
228/// ```ignore
229/// let key_value = create_key_value_prop_ident("props", value);
230pub fn create_key_value_prop_ident(key: &str, value: Expr) -> KeyValueProp {
231  KeyValueProp {
232    key: PropName::Ident(IdentName::new(key.into(), DUMMY_SP)),
233    value: Box::new(value),
234  }
235}
236
237/// Creates a `PropOrSpread` with an unconditional `PropName::Ident` key.
238///
239/// Unlike `create_key_value_prop`, this bypasses identifier validation,
240/// preserving keys that contain special characters (e.g. `@media …`) as ident nodes.
241/// Use this wherever downstream code calls `.as_ident()` on the resulting key.
242pub fn create_ident_key_value_prop(key: &str, value: Expr) -> PropOrSpread {
243  PropOrSpread::from(Prop::from(KeyValueProp {
244    key: PropName::Ident(IdentName::new(key.into(), DUMMY_SP)),
245    value: Box::new(value),
246  }))
247}
248
249/// Creates a `PropOrSpread` with a string key and value.
250///
251/// # Arguments
252/// * `key` - The string key
253/// * `value` - The value of the property
254///
255/// # Example
256/// ```ignore
257/// let prop_or_spread = create_string_key_value_prop("key", "value");
258pub fn create_string_key_value_prop(key: &str, value: &str) -> PropOrSpread {
259  let value = create_string_expr(value);
260
261  create_key_value_prop(key, value)
262}
263
264// NOTE: Tests only using this function
265#[allow(dead_code)]
266pub fn create_string_array_prop(key: &str, value: &[&str]) -> PropOrSpread {
267  let array = ArrayLit {
268    span: DUMMY_SP,
269    elems: value
270      .iter()
271      .map(|v| Some(create_string_expr_or_spread(v)))
272      .collect::<Vec<Option<ExprOrSpread>>>(),
273  };
274
275  create_key_value_prop(key, Expr::from(array))
276}
277
278/// Creates a `PropOrSpread` with a boolean key and value.
279///
280/// # Arguments
281/// * `key` - The boolean key
282/// * `value` - The value of the property
283///
284/// # Example
285/// ```ignore
286/// let prop_or_spread = prop_or_spread_boolean_factory("key", true);
287pub fn create_boolean_prop(key: &str, value: Option<bool>) -> PropOrSpread {
288  match value {
289    Some(value) => create_key_value_prop(key, create_bool_expr(value)),
290    None => stylex_panic!("Value is not a boolean"),
291  }
292}
293
294/// Wraps an arbitrary expression in `ExprOrSpread` with no spread.
295/// This is the generic counterpart to the typed `expr_or_spread_*_factory` helpers
296/// and eliminates the common boilerplate `ExprOrSpread { spread: None, expr: Box::new(e) }`.
297pub fn create_expr_or_spread(expr: Expr) -> ExprOrSpread {
298  ExprOrSpread {
299    expr: Box::new(expr),
300    spread: None,
301  }
302}
303
304/// Creates an `ExprOrSpread` with a string value.
305///
306/// # Arguments
307/// * `value` - The string value
308///
309/// # Example
310/// ```ignore
311/// let expr_or_spread = create_string_expr_or_spread("value");
312/// ```
313pub fn create_string_expr_or_spread(value: &str) -> ExprOrSpread {
314  create_expr_or_spread(create_string_expr(value))
315}
316
317pub fn create_number_expr_or_spread(value: f64) -> ExprOrSpread {
318  create_expr_or_spread(create_number_expr(value))
319}
320
321// NOTE: Tests only using this function
322#[allow(dead_code)]
323pub fn create_array(values: &[Expr]) -> ArrayLit {
324  array_fabric(values, None)
325}
326
327/// Creates an `ArrayLit` with a spread.
328///
329/// # Arguments
330/// * `values` - The values to include in the array literal
331///
332/// # Example
333/// ```ignore
334/// let array_lit = _create_spreaded_array(vec![expr1, expr2, expr3]);
335/// ```
336pub fn create_spreaded_array(values: &[Expr]) -> ArrayLit {
337  array_fabric(values, Some(DUMMY_SP))
338}
339
340/// Creates an `ArrayLit` with a spread.
341///
342/// # Arguments
343/// * `values` - The values to include in the array literal
344/// * `spread` - The span of the spread
345///
346/// # Example
347/// ```ignore
348/// let array_lit = array_fabric(vec![expr1, expr2, expr3], Some(DUMMY_SP));
349// NOTE: Tests only using this function
350#[allow(dead_code)]
351fn array_fabric(values: &[Expr], spread: Option<Span>) -> ArrayLit {
352  ArrayLit {
353    span: DUMMY_SP,
354    elems: values
355      .iter()
356      .map(|value| {
357        Some(ExprOrSpread {
358          spread,
359          expr: Box::new(value.clone()),
360        })
361      })
362      .collect(),
363  }
364}
365
366/// Creates an `IdentName` from a string.
367///
368/// # Arguments
369/// * `name` - The identifier name
370///
371/// # Example
372/// ```ignore
373/// let ident_name = create_ident_name("props");
374/// ```
375#[inline]
376pub fn create_ident_name(name: &str) -> IdentName {
377  IdentName::new(name.into(), DUMMY_SP)
378}
379
380/// Creates a `SpreadElement` for spreading an expression.
381///
382/// # Arguments
383/// * `expr` - The expression to spread
384///
385/// # Example
386/// ```ignore
387/// let spread = create_spread_element(obj_expr);
388/// ```
389#[inline]
390pub fn create_spread_element(expr: Expr) -> SpreadElement {
391  SpreadElement {
392    dot3_token: DUMMY_SP,
393    expr: Box::new(expr),
394  }
395}
396
397/// Creates a `PropOrSpread::Spread` for spreading properties in an object literal.
398///
399/// # Arguments
400/// * `expr` - The expression to spread
401///
402/// # Example
403/// ```ignore
404/// let spread = create_spread_prop(call_expr);
405/// ```
406#[inline]
407pub fn create_spread_prop(expr: Expr) -> PropOrSpread {
408  PropOrSpread::Spread(create_spread_element(expr))
409}
410
411/// Creates a `CallExpr` with a member expression callee (e.g., `obj.method(...args)`).
412///
413/// # Arguments
414/// * `callee_member` - The member expression to call
415/// * `args` - The call arguments
416///
417/// # Example
418/// ```ignore
419/// let member = member_expr_factory("stylex", "props");
420/// let call = create_member_call_expr(member, vec![arg1, arg2]);
421/// ```
422#[inline]
423pub fn create_member_call_expr(callee_member: MemberExpr, args: Vec<ExprOrSpread>) -> CallExpr {
424  CallExpr {
425    span: DUMMY_SP,
426    callee: Callee::Expr(Box::new(Expr::Member(callee_member))),
427    args,
428    type_args: None,
429    ctxt: SyntaxContext::empty(),
430  }
431}
432
433/// Creates a `CallExpr` with an identifier callee (e.g., `func(...args)`).
434///
435/// # Arguments
436/// * `callee_ident` - The identifier to call
437/// * `args` - The call arguments
438///
439/// # Example
440/// ```ignore
441/// let call = create_ident_call_expr("merge", vec![arg1]);
442/// ```
443#[inline]
444pub fn create_ident_call_expr(callee_ident: &str, args: Vec<ExprOrSpread>) -> CallExpr {
445  CallExpr {
446    span: DUMMY_SP,
447    callee: Callee::Expr(Box::new(Expr::Ident(create_ident(callee_ident)))),
448    args,
449    type_args: None,
450    ctxt: SyntaxContext::empty(),
451  }
452}
453
454/// Creates an arrow expression `() => expr`.
455///
456/// # Arguments
457/// * `body_expr` - The expression to return
458///
459/// # Example
460/// ```ignore
461/// let arrow = create_arrow_expression(call_expr);
462/// ```
463#[inline]
464pub fn create_arrow_expression(body_expr: Expr) -> Expr {
465  use ArrowExpr;
466  Expr::Arrow(ArrowExpr {
467    span: DUMMY_SP,
468    params: vec![],
469    body: Box::new(BlockStmtOrExpr::Expr(Box::new(body_expr))),
470    is_async: false,
471    is_generator: false,
472    type_params: None,
473    return_type: None,
474    ctxt: SyntaxContext::empty(),
475  })
476}
477
478/// Creates a `JSXAttrOrSpread::SpreadElement` for JSX attributes.
479///
480/// # Arguments
481/// * `expr` - The expression to spread
482///
483/// # Example
484/// ```ignore
485/// let jsx_spread = create_jsx_spread_attr(props_call);
486/// ```
487#[inline]
488pub fn create_jsx_spread_attr(expr: Expr) -> JSXAttrOrSpread {
489  JSXAttrOrSpread::SpreadElement(create_spread_element(expr))
490}
491
492/// Creates a `JSXAttrOrSpread::JSXAttr` wrapper.
493///
494/// # Arguments
495/// * `attr` - The JSX attribute
496///
497/// # Example
498/// ```ignore
499/// let jsx_attr = create_jsx_attr_or_spread(attr);
500/// ```
501#[inline]
502pub fn create_jsx_attr_or_spread(attr: JSXAttr) -> JSXAttrOrSpread {
503  use JSXAttrOrSpread;
504  JSXAttrOrSpread::JSXAttr(attr)
505}
506
507/// Creates a `JSXAttr` from a name and value.
508///
509/// # Arguments
510/// * `name` - The name of the attribute
511/// * `value` - The value of the attribute
512///
513/// # Example
514/// ```ignore
515/// let jsx_attr = create_jsx_attr("name", value);
516/// ```
517#[allow(dead_code)]
518pub fn create_jsx_attr(name: &str, value: JSXAttrValue) -> JSXAttr {
519  JSXAttr {
520    span: DUMMY_SP,
521    name: JSXAttrName::Ident(IdentName::from(name)),
522    value: Some(value),
523  }
524}
525
526/// Creates a `VarDeclarator` with an identifier name and an expression initializer.
527///
528/// # Arguments
529/// * `ident` - The identifier for the variable name
530/// * `init` - The initializer expression
531///
532/// # Example
533/// ```ignore
534/// let decl = create_var_declarator(my_ident, some_expr);
535/// ```
536pub fn create_var_declarator(ident: Ident, init: Expr) -> VarDeclarator {
537  VarDeclarator {
538    span: DUMMY_SP,
539    name: Pat::Ident(create_binding_ident(ident)),
540    init: Some(Box::new(init)),
541    definite: false,
542  }
543}
544
545/// Creates a `VarDeclarator` initialized to `null`.
546///
547/// Useful when hoisting a variable declaration ahead of its actual assignment,
548/// e.g. `var x = null;` before the value is set later.
549///
550/// # Arguments
551/// * `ident` - The identifier for the variable name
552///
553/// # Example
554/// ```ignore
555/// let decl = var_declarator_null_init_factory(my_ident);
556/// ```
557pub fn create_null_var_declarator(ident: Ident) -> VarDeclarator {
558  create_var_declarator(ident, create_null_expr())
559}
560
561/// Creates a `VarDeclarator` initialized to a string.
562///
563/// # Arguments
564/// * `ident` - The identifier for the variable name
565/// * `value` - The string value
566///
567/// # Example
568/// ```ignore
569/// let decl = create_string_var_declarator(my_ident, "value");
570/// ```
571pub fn create_string_var_declarator(ident: Ident, value: &str) -> VarDeclarator {
572  create_var_declarator(ident, create_string_expr(value))
573}