swc_ecma_usage_analyzer/
util.rs

1use swc_ecma_ast::*;
2
3pub fn is_global_var_with_pure_property_access(s: &str) -> bool {
4    match s {
5        "JSON" | "Array" | "Set" | "Map" | "String" | "Object" | "Number" | "Date" | "BigInt"
6        | "Boolean" | "Math" | "Error" | "Reflect" => return true,
7        _ => {}
8    }
9
10    matches!(
11        s,
12        "console"
13            | "clearInterval"
14            | "clearTimeout"
15            | "setInterval"
16            | "setTimeout"
17            | "setImmediate"
18            | "btoa"
19            | "decodeURI"
20            | "decodeURIComponent"
21            | "encodeURI"
22            | "encodeURIComponent"
23            | "escape"
24            | "eval"
25            | "EvalError"
26            | "Function"
27            | "isFinite"
28            | "isNaN"
29            | "parseFloat"
30            | "parseInt"
31            | "RegExp"
32            | "RangeError"
33            | "ReferenceError"
34            | "SyntaxError"
35            | "TypeError"
36            | "unescape"
37            | "URIError"
38            | "atob"
39            | "globalThis"
40            | "NaN"
41            | "Symbol"
42            | "Promise"
43            | "WeakRef"
44            | "ArrayBuffer"
45    )
46}
47
48pub fn can_end_conditionally(s: &Stmt) -> bool {
49    ///
50    ///`ignore_always`: If true, [Stmt::Return] will be ignored.
51    fn can_end(s: &Stmt, ignore_always: bool) -> bool {
52        match s {
53            Stmt::If(s) => {
54                can_end(&s.cons, false)
55                    || s.alt
56                        .as_deref()
57                        .map(|s| can_end(s, false))
58                        .unwrap_or_default()
59            }
60
61            Stmt::Switch(s) => s
62                .cases
63                .iter()
64                .any(|case| case.cons.iter().any(|s| can_end(s, false))),
65
66            Stmt::Try(s) => {
67                s.block.stmts.iter().any(|s| can_end(s, ignore_always))
68                    || s.handler
69                        .as_ref()
70                        .map(|h| h.body.stmts.iter().any(|s| can_end(s, ignore_always)))
71                        .unwrap_or_default()
72                    || s.finalizer
73                        .as_ref()
74                        .map(|f| f.stmts.iter().any(|s| can_end(s, ignore_always)))
75                        .unwrap_or_default()
76            }
77
78            Stmt::DoWhile(s) => can_end(&s.body, ignore_always),
79
80            Stmt::While(s) => can_end(&s.body, ignore_always),
81
82            Stmt::For(s) => can_end(&s.body, ignore_always),
83
84            Stmt::ForOf(s) => can_end(&s.body, ignore_always),
85
86            Stmt::ForIn(s) => can_end(&s.body, ignore_always),
87
88            Stmt::Return(..) | Stmt::Break(..) | Stmt::Continue(..) => !ignore_always,
89
90            Stmt::Block(s) => s.stmts.iter().any(|s| can_end(s, ignore_always)),
91
92            _ => false,
93        }
94    }
95
96    can_end(s, true)
97}
98
99/// Find `Object.defineProperty()`
100fn is_object_property_call(call: &CallExpr) -> bool {
101    if let Callee::Expr(callee) = &call.callee {
102        match &**callee {
103            Expr::Member(MemberExpr {
104                obj,
105                prop: MemberProp::Ident(IdentName { sym, .. }),
106                ..
107            }) if *sym == *"defineProperty" && obj.is_ident_ref_to("Object") => {
108                return true;
109            }
110
111            _ => {}
112        }
113    };
114
115    false
116}
117
118pub fn get_mut_object_define_property_name_arg(call: &mut CallExpr) -> Option<&mut Str> {
119    if is_object_property_call(call) {
120        let second_arg: &mut Expr = call.args.get_mut(1).map(|arg| &mut arg.expr)?;
121        second_arg.as_mut_lit().and_then(|lit| lit.as_mut_str())
122    } else {
123        None
124    }
125}
126
127pub fn get_object_define_property_name_arg(call: &CallExpr) -> Option<&Str> {
128    if is_object_property_call(call) {
129        let second_arg: &Expr = call.args.get(1).map(|arg| &arg.expr)?;
130        second_arg.as_lit().and_then(|lit| lit.as_str())
131    } else {
132        None
133    }
134}