swc_ecma_minifier/pass/mangle_names/
preserver.rs

1use 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
16/// Returns `(preserved, unresolved)`
17pub(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    // Force rename synthesized names
35    // See https://github.com/swc-project/swc/issues/9468
36    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}