swc_ecma_transforms_react/refresh/
util.rs

1use 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}