swc_ecma_transforms_react/refresh/
util.rs1use rustc_hash::FxHashSet;
2use swc_common::{Spanned, SyntaxContext, DUMMY_SP};
3use swc_ecma_ast::*;
4use swc_ecma_utils::ExprFactory;
5use swc_ecma_visit::{noop_visit_type, Visit, VisitWith};
6
7pub fn is_builtin_hook(name: &str) -> bool {
8 matches!(
9 name,
10 "useState"
11 | "useReducer"
12 | "useEffect"
13 | "useLayoutEffect"
14 | "useMemo"
15 | "useCallback"
16 | "useRef"
17 | "useContext"
18 | "useImperativeHandle"
19 | "useDebugValue"
20 )
21}
22
23pub fn is_body_arrow_fn(body: &BlockStmtOrExpr) -> bool {
24 if let BlockStmtOrExpr::Expr(body) = body {
25 body.is_arrow()
26 } else {
27 false
28 }
29}
30
31fn assert_hygiene(e: &Expr) {
32 if !cfg!(debug_assertions) {
33 return;
34 }
35
36 if let Expr::Ident(i) = e {
37 if i.ctxt == SyntaxContext::empty() {
38 panic!("`{i}` should be resolved")
39 }
40 }
41}
42
43pub fn make_assign_stmt(handle: Ident, expr: Box<Expr>) -> Expr {
44 assert_hygiene(&expr);
45
46 AssignExpr {
47 span: expr.span(),
48 op: op!("="),
49 left: handle.into(),
50 right: expr,
51 }
52 .into()
53}
54
55pub fn make_call_stmt(handle: Ident) -> Stmt {
56 ExprStmt {
57 span: DUMMY_SP,
58 expr: Box::new(make_call_expr(handle)),
59 }
60 .into()
61}
62
63pub fn make_call_expr(handle: Ident) -> Expr {
64 CallExpr {
65 span: DUMMY_SP,
66 callee: handle.as_callee(),
67 args: Vec::new(),
68 ..Default::default()
69 }
70 .into()
71}
72
73pub fn is_import_or_require(expr: &Expr) -> bool {
74 match expr {
75 Expr::Call(CallExpr {
76 callee: Callee::Expr(expr),
77 ..
78 }) => {
79 if let Expr::Ident(ident) = expr.as_ref() {
80 ident.sym.contains("require")
81 } else {
82 false
83 }
84 }
85 Expr::Call(CallExpr {
86 callee: Callee::Import(_),
87 ..
88 }) => true,
89 _ => false,
90 }
91}
92
93pub struct UsedInJsx(FxHashSet<Id>);
94
95impl Visit for UsedInJsx {
96 noop_visit_type!();
97
98 fn visit_call_expr(&mut self, n: &CallExpr) {
99 n.visit_children_with(self);
100
101 if let Callee::Expr(expr) = &n.callee {
102 let ident = match expr.as_ref() {
103 Expr::Ident(ident) => ident.to_id(),
104 Expr::Member(MemberExpr {
105 prop: MemberProp::Ident(ident),
106 ..
107 }) => (ident.sym.clone(), SyntaxContext::empty()),
108 _ => return,
109 };
110 if matches!(
111 ident.0.as_ref(),
112 "createElement" | "jsx" | "jsxDEV" | "jsxs"
113 ) {
114 if let Some(ExprOrSpread { expr, .. }) = n.args.first() {
115 if let Expr::Ident(ident) = expr.as_ref() {
116 self.0.insert(ident.to_id());
117 }
118 }
119 }
120 }
121 }
122
123 fn visit_jsx_opening_element(&mut self, n: &JSXOpeningElement) {
124 if let JSXElementName::Ident(ident) = &n.name {
125 self.0.insert(ident.to_id());
126 }
127 }
128}
129
130pub fn collect_ident_in_jsx<V: VisitWith<UsedInJsx>>(item: &V) -> FxHashSet<Id> {
131 let mut visitor = UsedInJsx(FxHashSet::default());
132 item.visit_with(&mut visitor);
133 visitor.0
134}