Skip to main content

stylex_js/
helpers.rs

1use stylex_constants::constants::{
2  common::{INVALID_METHODS, MUTATING_ARRAY_METHODS, MUTATING_OBJECT_METHODS, VALID_CALLEES},
3  messages::INVALID_UTF8,
4};
5use stylex_macros::stylex_panic;
6use swc_core::{
7  atoms::Atom,
8  ecma::ast::{AssignTarget, Expr, Lit, MemberProp, SimpleAssignTarget, UnaryOp},
9};
10
11pub fn is_valid_callee(callee: &Expr) -> bool {
12  if let Expr::Ident(ident) = callee {
13    VALID_CALLEES.contains(ident.sym.as_ref())
14  } else {
15    false
16  }
17}
18
19pub fn get_callee_name(callee: &Expr) -> &str {
20  match callee {
21    Expr::Ident(ident) => &ident.sym,
22    _ => stylex_panic!("The function being called must be a static identifier."),
23  }
24}
25
26pub fn is_invalid_method(prop: &MemberProp) -> bool {
27  match prop {
28    MemberProp::Ident(ident_prop) => INVALID_METHODS.contains(&*ident_prop.sym),
29    _ => false,
30  }
31}
32
33/// Checks if a member property represents a mutating object method (Object.assign, etc.)
34pub fn is_mutating_object_method(prop: &MemberProp) -> bool {
35  if let MemberProp::Ident(ident_prop) = prop {
36    MUTATING_OBJECT_METHODS.contains(&*ident_prop.sym)
37  } else {
38    false
39  }
40}
41
42/// Checks if a member property represents a mutating array method (push, pop, splice, etc.)
43pub fn is_mutating_array_method(prop: &MemberProp) -> bool {
44  if let MemberProp::Ident(ident_prop) = prop {
45    MUTATING_ARRAY_METHODS.contains(&*ident_prop.sym)
46  } else {
47    false
48  }
49}
50
51/// Checks if an expression represents a mutation operation
52/// Returns true if any of the following conditions are met:
53/// - Assignment to a member expression (e.g., `a.x = 1` or `a[0] = 1`)
54/// - Update expression on a member (e.g., `++a.x` or `a[0]++`)
55/// - Delete operation on a member (e.g., `delete a.x`)
56pub fn is_mutation_expr(expr: &Expr) -> bool {
57  match expr {
58    // Check for assignment to member: a.x = 1 or a[0] = 1
59    Expr::Assign(assign)
60      if matches!(
61        &assign.left,
62        AssignTarget::Simple(SimpleAssignTarget::Member(member)) if member.obj.is_ident()
63      ) =>
64    {
65      true
66    },
67
68    // Check for update on member: ++a.x or a[0]++
69    Expr::Update(update) if matches!(&*update.arg, Expr::Member(member) if member.obj.is_ident()) => {
70      true
71    },
72
73    // Check for delete on member: delete a.x
74    Expr::Unary(unary)
75      if unary.op == UnaryOp::Delete
76        && matches!(&*unary.arg, Expr::Member(member) if member.obj.is_ident()) =>
77    {
78      true
79    },
80
81    _ => false,
82  }
83}
84
85pub fn get_method_name(prop: &MemberProp) -> &str {
86  match prop {
87    MemberProp::Ident(ident_prop) => &ident_prop.sym,
88    _ => stylex_panic!("The method name in a call expression must be a static identifier."),
89  }
90}
91
92pub fn is_id_prop(prop: &MemberProp) -> Option<&Atom> {
93  if let MemberProp::Computed(comp_prop) = prop
94    && let Expr::Lit(Lit::Str(strng)) = comp_prop.expr.as_ref()
95  {
96    return Some(match strng.value.as_atom() {
97      Some(a) => a,
98      None => stylex_panic!("{}", INVALID_UTF8),
99    });
100  }
101
102  None
103}