swc_ecma_minifier/pass/
global_defs.rs1use std::borrow::Cow;
2
3use swc_common::{pass::CompilerPass, EqIgnoreSpan, Mark, SyntaxContext};
4use swc_ecma_ast::*;
5use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
6
7pub fn globals_defs(
8 defs: Vec<(Box<Expr>, Box<Expr>)>,
9 unresolved_mark: Mark,
10 top_level_mark: Mark,
11) -> impl VisitMut {
12 GlobalDefs {
13 defs,
14 unresolved_ctxt: SyntaxContext::empty().apply_mark(unresolved_mark),
15 top_level_ctxt: SyntaxContext::empty().apply_mark(top_level_mark),
16 ..Default::default()
17 }
18}
19
20#[derive(Default)]
21struct GlobalDefs {
22 defs: Vec<(Box<Expr>, Box<Expr>)>,
23
24 unresolved_ctxt: SyntaxContext,
25 top_level_ctxt: SyntaxContext,
26
27 in_lhs_of_assign: bool,
28}
29
30impl CompilerPass for GlobalDefs {
31 fn name(&self) -> Cow<'static, str> {
32 Cow::Borrowed("global-defs")
33 }
34}
35
36impl VisitMut for GlobalDefs {
38 noop_visit_mut_type!();
39
40 fn visit_mut_assign_expr(&mut self, n: &mut AssignExpr) {
41 let old = self.in_lhs_of_assign;
42 self.in_lhs_of_assign = true;
43 n.left.visit_mut_with(self);
44 self.in_lhs_of_assign = false;
45 n.right.visit_mut_with(self);
46 self.in_lhs_of_assign = old;
47 }
48
49 fn visit_mut_expr(&mut self, n: &mut Expr) {
50 if self.in_lhs_of_assign {
51 return;
52 }
53
54 match n {
55 Expr::Ident(i) => {
56 if i.ctxt != self.unresolved_ctxt && i.ctxt != self.top_level_ctxt {
57 return;
58 }
59 }
60 Expr::Member(MemberExpr { obj, .. }) => {
61 if let Expr::Ident(i) = &**obj {
62 if i.ctxt != self.unresolved_ctxt && i.ctxt != self.top_level_ctxt {
63 return;
64 }
65 }
66 }
67 _ => {}
68 }
69
70 if let Some((_, new)) = self
71 .defs
72 .iter()
73 .find(|(pred, _)| Ident::within_ignored_ctxt(|| should_replace(pred, n)))
74 {
75 *n = *new.clone();
76 return;
77 }
78
79 n.visit_mut_children_with(self);
80 }
81
82 fn visit_mut_update_expr(&mut self, e: &mut UpdateExpr) {
83 match &mut *e.arg {
84 Expr::Ident(..) => {}
85
86 Expr::Member(MemberExpr { prop, .. }) if !prop.is_computed() => {
87 }
89
90 _ => {
91 e.arg.visit_mut_with(self);
92 }
93 }
94 }
95}
96
97fn should_replace(pred: &Expr, node: &Expr) -> bool {
100 if pred.eq_ignore_span(node) {
101 return true;
102 }
103
104 fn match_node(node: &Expr) -> Option<(&Expr, &MemberProp)> {
105 match node {
106 Expr::Member(MemberExpr {
107 obj: node_obj,
108 prop: nodes,
109 ..
110 }) => Some((node_obj, nodes)),
111
112 Expr::OptChain(OptChainExpr { base, .. }) => {
113 let base = base.as_member()?;
114 Some((&base.obj, &base.prop))
115 }
116
117 _ => None,
118 }
119 }
120
121 match (pred, match_node(node)) {
122 (
124 Expr::Member(MemberExpr {
125 obj: pred_obj,
126 prop: pred,
127 ..
128 }),
129 Some((node_obj, nodes)),
130 ) if !(pred.is_computed() || nodes.is_computed()) => {
131 if !pred.eq_ignore_span(nodes) {
132 return false;
133 }
134
135 return should_replace(pred_obj, node_obj);
136 }
137 _ => {}
138 }
139
140 false
141}