swc_ecma_minifier/pass/mangle_names/
preserver.rs1use rustc_hash::FxHashSet;
2use swc_atoms::Atom;
3use swc_common::SyntaxContext;
4use swc_ecma_ast::*;
5use swc_ecma_usage_analyzer::marks::Marks;
6use swc_ecma_utils::find_pat_ids;
7use swc_ecma_visit::{noop_visit_type, visit_obj_and_computed, Visit, VisitWith};
8
9use crate::option::MangleOptions;
10
11pub(crate) struct PreversedIdents {
12 pub preserved: FxHashSet<Id>,
13 pub idents: Option<Vec<Atom>>,
14}
15
16pub(crate) fn idents_to_preserve<N>(options: &MangleOptions, marks: Marks, n: &N) -> PreversedIdents
18where
19 N: for<'a> VisitWith<Preserver<'a>>,
20{
21 let mut v = Preserver {
22 options,
23 preserved: Default::default(),
24 should_preserve: false,
25 in_top_level: false,
26
27 idents: Vec::new(),
28 unresolved_ctx: SyntaxContext::empty().apply_mark(marks.unresolved_mark),
29 };
30 n.visit_with(&mut v);
31
32 let top_level_mark = marks.top_level_ctxt.outer();
33
34 v.preserved.retain(|id| {
37 options.reserved.contains(&id.0) || id.1.outer().is_descendant_of(top_level_mark)
38 });
39
40 let idents = v
41 .idents
42 .into_iter()
43 .filter_map(|id| {
44 if v.preserved.contains(&id) {
45 None
46 } else {
47 Some(id.0)
48 }
49 })
50 .collect();
51 PreversedIdents {
52 preserved: v.preserved,
53 idents: if options.disable_char_freq {
54 None
55 } else {
56 Some(idents)
57 },
58 }
59}
60
61pub(crate) struct Preserver<'a> {
62 options: &'a MangleOptions,
63
64 preserved: FxHashSet<Id>,
65
66 should_preserve: bool,
67 in_top_level: bool,
68
69 idents: Vec<Id>,
70 unresolved_ctx: SyntaxContext,
71}
72
73impl Preserver<'_> {
74 fn is_reserved(&self, ident: &Ident) -> bool {
75 self.options.reserved.contains(&ident.sym)
76 }
77
78 fn append_ident(&mut self, ident: &Ident) {
79 if self.options.disable_char_freq
80 || (ident.ctxt == self.unresolved_ctx && ident.sym != "arguments")
81 {
82 return;
83 }
84
85 self.idents.push(ident.to_id());
86 }
87}
88
89impl Visit for Preserver<'_> {
90 noop_visit_type!(fail);
91
92 visit_obj_and_computed!();
93
94 fn visit_block_stmt(&mut self, n: &BlockStmt) {
95 let old_top_level = self.in_top_level;
96 for n in n.stmts.iter() {
97 self.in_top_level = false;
98 n.visit_with(self);
99 }
100 self.in_top_level = old_top_level;
101 }
102
103 fn visit_catch_clause(&mut self, n: &CatchClause) {
104 let old = self.should_preserve;
105
106 if self.options.ie8 && !self.options.top_level.unwrap_or_default() {
107 self.should_preserve = true;
108 n.param.visit_with(self);
109 }
110
111 self.should_preserve = old;
112 n.body.visit_with(self);
113 }
114
115 fn visit_class_decl(&mut self, n: &ClassDecl) {
116 n.visit_children_with(self);
117
118 if (self.in_top_level && !self.options.top_level.unwrap_or_default())
119 || self.options.keep_class_names
120 || self.is_reserved(&n.ident)
121 {
122 self.preserved.insert(n.ident.to_id());
123 }
124 }
125
126 fn visit_class_expr(&mut self, n: &ClassExpr) {
127 n.visit_children_with(self);
128
129 if self.options.keep_class_names {
130 if let Some(i) = &n.ident {
131 self.preserved.insert(i.to_id());
132 }
133 }
134 }
135
136 fn visit_export_decl(&mut self, n: &ExportDecl) {
137 n.visit_children_with(self);
138
139 match &n.decl {
140 Decl::Class(c) => {
141 self.preserved.insert(c.ident.to_id());
142 }
143 Decl::Fn(f) => {
144 self.preserved.insert(f.ident.to_id());
145 }
146 Decl::Var(v) => {
147 let ids: Vec<Id> = find_pat_ids(&v.decls);
148 self.preserved.extend(ids);
149 }
150 _ => {}
151 }
152 }
153
154 fn visit_expr(&mut self, n: &Expr) {
155 n.visit_children_with(self);
156
157 if let Expr::Ident(i) = n {
158 if self.should_preserve || self.is_reserved(i) {
159 self.preserved.insert(i.to_id());
160 }
161 }
162 }
163
164 fn visit_fn_decl(&mut self, n: &FnDecl) {
165 n.visit_children_with(self);
166
167 if (self.in_top_level && !self.options.top_level.unwrap_or_default())
168 || self.is_reserved(&n.ident)
169 || self.options.keep_fn_names
170 {
171 self.preserved.insert(n.ident.to_id());
172 }
173 }
174
175 fn visit_fn_expr(&mut self, n: &FnExpr) {
176 n.visit_children_with(self);
177
178 if self.options.keep_fn_names {
179 if let Some(i) = &n.ident {
180 self.preserved.insert(i.to_id());
181 }
182 }
183 }
184
185 fn visit_ident(&mut self, i: &Ident) {
186 self.append_ident(i);
187 }
188
189 fn visit_module_items(&mut self, n: &[ModuleItem]) {
190 for n in n {
191 self.in_top_level = true;
192 n.visit_with(self);
193 }
194 }
195
196 fn visit_module_export_name(&mut self, _: &ModuleExportName) {}
197
198 fn visit_pat(&mut self, n: &Pat) {
199 n.visit_children_with(self);
200
201 if let Pat::Ident(i) = n {
202 if self.should_preserve || self.is_reserved(&i.id) {
203 self.preserved.insert(i.to_id());
204 }
205 }
206 }
207
208 fn visit_script(&mut self, n: &Script) {
209 for n in n.body.iter() {
210 self.in_top_level = true;
211 n.visit_with(self);
212 }
213 }
214
215 fn visit_var_declarator(&mut self, n: &VarDeclarator) {
216 n.visit_children_with(self);
217
218 if self.in_top_level && !self.options.top_level.unwrap_or_default() {
219 let old = self.should_preserve;
220 self.should_preserve = true;
221 n.name.visit_with(self);
222 self.should_preserve = old;
223 return;
224 }
225
226 if self.options.keep_fn_names {
227 match n.init.as_deref() {
228 Some(Expr::Fn(..)) | Some(Expr::Arrow(..)) => {
229 let old = self.should_preserve;
230 self.should_preserve = true;
231 n.name.visit_with(self);
232 self.should_preserve = old;
233 }
234 _ => {}
235 }
236 }
237 }
238}