swc_ecma_minifier/option/
mod.rs

1#![cfg_attr(not(feature = "extra-serde"), allow(unused))]
2
3use std::sync::Arc;
4
5use parking_lot::RwLock;
6use rustc_hash::FxHashMap;
7use serde::{Deserialize, Serialize};
8use swc_atoms::Atom;
9use swc_common::Mark;
10use swc_config::{merge::Merge, CachedRegex};
11use swc_ecma_ast::{EsVersion, Expr, Id};
12
13/// Implement default using serde.
14macro_rules! impl_default {
15    ($T:ty) => {
16        impl Default for $T {
17            fn default() -> Self {
18                serde_json::from_value(serde_json::Value::Object(Default::default())).unwrap()
19            }
20        }
21    };
22}
23
24pub mod terser;
25
26/// This is not serializable.
27pub struct ExtraOptions {
28    /// It should be the [Mark] used for `resolver`.
29    pub unresolved_mark: Mark,
30
31    /// It should be the [Mark] used for `resolver`.
32    pub top_level_mark: Mark,
33
34    pub mangle_name_cache: Option<Arc<dyn MangleCache>>,
35}
36
37#[derive(Debug, Default, Clone)]
38#[cfg_attr(feature = "extra-serde", derive(Serialize, Deserialize))]
39#[cfg_attr(feature = "extra-serde", serde(rename_all = "camelCase"))]
40#[cfg_attr(feature = "extra-serde", serde(deny_unknown_fields))]
41pub struct MinifyOptions {
42    #[cfg_attr(feature = "extra-serde", serde(default))]
43    pub rename: bool,
44    #[cfg_attr(feature = "extra-serde", serde(default))]
45    pub compress: Option<CompressOptions>,
46    #[cfg_attr(feature = "extra-serde", serde(default))]
47    pub mangle: Option<MangleOptions>,
48    #[cfg_attr(feature = "extra-serde", serde(default))]
49    pub wrap: bool,
50    #[cfg_attr(feature = "extra-serde", serde(default))]
51    pub enclose: bool,
52}
53
54#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
55#[serde(rename_all = "camelCase")]
56#[serde(deny_unknown_fields)]
57pub struct TopLevelOptions {
58    pub functions: bool,
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize)]
62#[serde(rename_all = "camelCase")]
63#[serde(deny_unknown_fields)]
64pub struct MangleOptions {
65    #[serde(default, alias = "properties")]
66    pub props: Option<ManglePropertiesOptions>,
67
68    #[serde(default, alias = "toplevel")]
69    pub top_level: Option<bool>,
70
71    #[serde(default, alias = "keep_classnames")]
72    pub keep_class_names: bool,
73
74    #[serde(default, alias = "keep_fnames")]
75    pub keep_fn_names: bool,
76
77    #[serde(default, alias = "keep_private_props")]
78    pub keep_private_props: bool,
79
80    #[serde(default, alias = "ie8")]
81    pub ie8: bool,
82
83    #[deprecated = "This field is no longer required to work around bugs in Safari 10."]
84    #[serde(default, alias = "safari10")]
85    pub safari10: bool,
86
87    #[serde(default, alias = "reserved")]
88    pub reserved: Vec<Atom>,
89
90    /// mangle names visible in scopes where eval or with are used
91    #[serde(default)]
92    pub eval: bool,
93}
94
95#[derive(Debug, Clone, Default, Serialize, Deserialize, Merge)]
96#[serde(rename_all = "camelCase")]
97pub struct ManglePropertiesOptions {
98    #[serde(default, alias = "reserved")]
99    pub reserved: Vec<Atom>,
100    #[serde(default, alias = "undeclared")]
101    pub undeclared: Option<bool>,
102    #[serde(default)]
103    pub regex: Option<CachedRegex>,
104}
105
106#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
107#[serde(deny_unknown_fields)]
108#[serde(untagged)]
109pub enum PureGetterOption {
110    Bool(bool),
111    #[serde(rename = "strict")]
112    Strict,
113    Str(Vec<Atom>),
114}
115
116impl Default for PureGetterOption {
117    fn default() -> Self {
118        Self::Strict
119    }
120}
121
122/// https://terser.org/docs/api-reference.html#compress-options
123#[derive(Debug, Clone)]
124#[cfg_attr(feature = "extra-serde", derive(Serialize, Deserialize))]
125#[cfg_attr(feature = "extra-serde", serde(rename_all = "camelCase"))]
126#[cfg_attr(feature = "extra-serde", serde(deny_unknown_fields))]
127pub struct CompressOptions {
128    #[cfg_attr(feature = "extra-serde", serde(default))]
129    #[cfg_attr(feature = "extra-serde", serde(alias = "arguments"))]
130    pub arguments: bool,
131
132    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
133    pub arrows: bool,
134
135    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
136    #[cfg_attr(feature = "extra-serde", serde(alias = "booleans"))]
137    pub bools: bool,
138
139    #[cfg_attr(feature = "extra-serde", serde(default))]
140    #[cfg_attr(feature = "extra-serde", serde(alias = "booleans_as_integers"))]
141    pub bools_as_ints: bool,
142
143    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
144    #[cfg_attr(feature = "extra-serde", serde(alias = "collapse_vars"))]
145    pub collapse_vars: bool,
146
147    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
148    #[cfg_attr(feature = "extra-serde", serde(alias = "comparisons"))]
149    pub comparisons: bool,
150
151    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
152    #[cfg_attr(feature = "extra-serde", serde(alias = "computed_props"))]
153    pub computed_props: bool,
154
155    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
156    #[cfg_attr(feature = "extra-serde", serde(alias = "conditionals"))]
157    pub conditionals: bool,
158
159    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
160    #[cfg_attr(feature = "extra-serde", serde(alias = "dead_code"))]
161    pub dead_code: bool,
162
163    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
164    #[cfg_attr(feature = "extra-serde", serde(alias = "directives"))]
165    pub directives: bool,
166
167    #[cfg_attr(feature = "extra-serde", serde(default))]
168    #[cfg_attr(feature = "extra-serde", serde(alias = "drop_console"))]
169    pub drop_console: bool,
170
171    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
172    #[cfg_attr(feature = "extra-serde", serde(alias = "drop_debugger"))]
173    pub drop_debugger: bool,
174
175    #[cfg_attr(feature = "extra-serde", serde(default = "default_ecma"))]
176    pub ecma: EsVersion,
177
178    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
179    #[cfg_attr(feature = "extra-serde", serde(alias = "evaluate"))]
180    pub evaluate: bool,
181
182    /// Should we simplify expressions?
183    #[cfg_attr(feature = "extra-serde", serde(default))]
184    #[cfg_attr(feature = "extra-serde", serde(alias = "expression"))]
185    pub expr: bool,
186
187    /// All expressions should have dummy span. Use [swc_ecma_utils::drop_span]
188    /// to remove spans.
189    #[cfg_attr(feature = "extra-serde", serde(skip))]
190    #[cfg_attr(feature = "extra-serde", serde(alias = "global_defs"))]
191    pub global_defs: FxHashMap<Box<Expr>, Box<Expr>>,
192
193    #[cfg_attr(feature = "extra-serde", serde(default))]
194    #[cfg_attr(feature = "extra-serde", serde(alias = "hoist_funs"))]
195    pub hoist_fns: bool,
196
197    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
198    #[cfg_attr(feature = "extra-serde", serde(alias = "hoist_props"))]
199    pub hoist_props: bool,
200
201    #[cfg_attr(feature = "extra-serde", serde(default))]
202    #[cfg_attr(feature = "extra-serde", serde(alias = "hoist_vars"))]
203    pub hoist_vars: bool,
204
205    /// No effect.
206    #[cfg_attr(feature = "extra-serde", serde(default))]
207    #[cfg_attr(feature = "extra-serde", serde(alias = "ie8"))]
208    pub ie8: bool,
209
210    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
211    #[cfg_attr(feature = "extra-serde", serde(alias = "if_return"))]
212    pub if_return: bool,
213
214    ///
215    /// - `0`: disabled inlining
216    /// - `1`: inline simple functions
217    /// - `2`: inline functions with arguments
218    /// - `3`: inline functions with arguments and variables
219
220    #[cfg_attr(feature = "extra-serde", serde(default = "three_by_default"))]
221    #[cfg_attr(feature = "extra-serde", serde(alias = "inline"))]
222    pub inline: u8,
223
224    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
225    #[cfg_attr(feature = "extra-serde", serde(alias = "join_vars"))]
226    pub join_vars: bool,
227
228    #[cfg_attr(feature = "extra-serde", serde(default))]
229    #[cfg_attr(feature = "extra-serde", serde(alias = "keep_classnames"))]
230    pub keep_classnames: bool,
231
232    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
233    #[cfg_attr(feature = "extra-serde", serde(alias = "keep_fargs"))]
234    pub keep_fargs: bool,
235
236    #[cfg_attr(feature = "extra-serde", serde(default))]
237    #[cfg_attr(feature = "extra-serde", serde(alias = "keep_fnames"))]
238    pub keep_fnames: bool,
239
240    #[cfg_attr(feature = "extra-serde", serde(default))]
241    #[cfg_attr(feature = "extra-serde", serde(alias = "keep_infinity"))]
242    pub keep_infinity: bool,
243
244    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
245    #[cfg_attr(feature = "extra-serde", serde(alias = "loops"))]
246    pub loops: bool,
247
248    #[cfg_attr(feature = "extra-serde", serde(default))]
249    pub module: bool,
250
251    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
252    #[cfg_attr(feature = "extra-serde", serde(alias = "negate_iife"))]
253    pub negate_iife: bool,
254
255    /// If this value is zero, the minifier will repeat work until the ast node
256    /// is settled.
257    #[cfg_attr(feature = "extra-serde", serde(default = "default_passes"))]
258    #[cfg_attr(feature = "extra-serde", serde(alias = "passes"))]
259    pub passes: usize,
260
261    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
262    #[cfg_attr(feature = "extra-serde", serde(alias = "properties"))]
263    pub props: bool,
264
265    #[cfg_attr(feature = "extra-serde", serde(default))]
266    #[cfg_attr(feature = "extra-serde", serde(alias = "pure_getters"))]
267    pub pure_getters: PureGetterOption,
268
269    #[cfg_attr(feature = "extra-serde", serde(default))]
270    #[cfg_attr(feature = "extra-serde", serde(alias = "pure_funcs"))]
271    pub pure_funcs: Vec<Box<Expr>>,
272
273    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
274    #[cfg_attr(feature = "extra-serde", serde(alias = "reduce_funcs"))]
275    pub reduce_fns: bool,
276    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
277    #[cfg_attr(feature = "extra-serde", serde(alias = "reduce_vars"))]
278    pub reduce_vars: bool,
279
280    #[cfg_attr(feature = "extra-serde", serde(default = "three_by_default"))]
281    #[cfg_attr(feature = "extra-serde", serde(alias = "sequences"))]
282    pub sequences: u8,
283
284    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
285    #[cfg_attr(feature = "extra-serde", serde(alias = "side_effects"))]
286    pub side_effects: bool,
287
288    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
289    #[cfg_attr(feature = "extra-serde", serde(alias = "switches"))]
290    pub switches: bool,
291
292    /// Top level symbols to retain.
293    #[cfg_attr(feature = "extra-serde", serde(default))]
294    #[cfg_attr(feature = "extra-serde", serde(alias = "top_retain"))]
295    pub top_retain: Vec<Atom>,
296
297    #[cfg_attr(feature = "extra-serde", serde(default))]
298    #[cfg_attr(feature = "extra-serde", serde(alias = "toplevel"))]
299    pub top_level: Option<TopLevelOptions>,
300
301    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
302    #[cfg_attr(feature = "extra-serde", serde(alias = "typeofs"))]
303    pub typeofs: bool,
304
305    #[cfg_attr(feature = "extra-serde", serde(default))]
306    #[cfg_attr(feature = "extra-serde", serde(rename = "unsafe"))]
307    pub unsafe_passes: bool,
308
309    #[cfg_attr(feature = "extra-serde", serde(default))]
310    pub unsafe_arrows: bool,
311
312    #[cfg_attr(feature = "extra-serde", serde(default))]
313    pub unsafe_comps: bool,
314
315    #[cfg_attr(feature = "extra-serde", serde(default))]
316    #[cfg_attr(feature = "extra-serde", serde(alias = "unsafe_Function"))]
317    pub unsafe_function: bool,
318
319    #[cfg_attr(feature = "extra-serde", serde(default))]
320    pub unsafe_math: bool,
321
322    #[cfg_attr(feature = "extra-serde", serde(default))]
323    pub unsafe_symbols: bool,
324
325    #[cfg_attr(feature = "extra-serde", serde(default))]
326    pub unsafe_methods: bool,
327
328    #[cfg_attr(feature = "extra-serde", serde(default))]
329    pub unsafe_proto: bool,
330
331    #[cfg_attr(feature = "extra-serde", serde(default))]
332    pub unsafe_regexp: bool,
333
334    #[cfg_attr(feature = "extra-serde", serde(default))]
335    pub unsafe_undefined: bool,
336
337    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
338    pub unused: bool,
339
340    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
341    pub const_to_let: bool,
342
343    /// If you modified globals, set this to false.
344    ///
345    /// Defaults to true.
346    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
347    pub pristine_globals: bool,
348}
349
350impl CompressOptions {
351    pub(crate) fn sequences(&self) -> bool {
352        self.sequences != 0
353    }
354
355    /// Returns `true` if any of toplevel optimizer is enabled.
356    pub(crate) fn top_level(&self) -> bool {
357        if !self.top_retain.is_empty() {
358            return true;
359        }
360
361        self.top_level.map(|v| v.functions).unwrap_or(false) || self.module
362    }
363}
364
365const fn true_by_default() -> bool {
366    true
367}
368
369const fn default_passes() -> usize {
370    2
371}
372
373const fn three_by_default() -> u8 {
374    3
375}
376
377const fn default_ecma() -> EsVersion {
378    EsVersion::Es5
379}
380
381impl_default!(MangleOptions);
382
383impl Default for CompressOptions {
384    fn default() -> Self {
385        Self {
386            arguments: false,
387            arrows: true,
388            bools: true,
389            bools_as_ints: false,
390            collapse_vars: true,
391            comparisons: true,
392            computed_props: true,
393            conditionals: true,
394            dead_code: true,
395            directives: true,
396            drop_console: false,
397            drop_debugger: true,
398            ecma: default_ecma(),
399            evaluate: true,
400            expr: false,
401            global_defs: Default::default(),
402            hoist_fns: false,
403            hoist_props: true,
404            hoist_vars: false,
405            ie8: false,
406            if_return: true,
407            inline: 3,
408            join_vars: true,
409            keep_classnames: false,
410            keep_fargs: true,
411            keep_fnames: false,
412            keep_infinity: false,
413            loops: true,
414            module: false,
415            negate_iife: true,
416            passes: default_passes(),
417            props: true,
418            pure_getters: Default::default(),
419            pure_funcs: Default::default(),
420            reduce_fns: true,
421            reduce_vars: false,
422            sequences: 3,
423            side_effects: true,
424            switches: true,
425            top_retain: Default::default(),
426            top_level: Default::default(),
427            typeofs: true,
428            unsafe_passes: false,
429            unsafe_arrows: false,
430            unsafe_comps: false,
431            unsafe_function: false,
432            unsafe_math: false,
433            unsafe_methods: false,
434            unsafe_proto: false,
435            unsafe_regexp: false,
436            unsafe_symbols: false,
437            unsafe_undefined: false,
438            unused: true,
439            const_to_let: true,
440            pristine_globals: true,
441        }
442    }
443}
444
445pub trait MangleCache: Send + Sync {
446    fn vars_cache(&self, op: &mut dyn FnMut(&FxHashMap<Id, Atom>));
447
448    fn props_cache(&self, op: &mut dyn FnMut(&FxHashMap<Atom, Atom>));
449
450    fn update_vars_cache(&self, new_data: &FxHashMap<Id, Atom>);
451
452    fn update_props_cache(&self, new_data: &FxHashMap<Atom, Atom>);
453}
454
455#[derive(Debug, Default)]
456pub struct SimpleMangleCache {
457    pub vars: RwLock<FxHashMap<Id, Atom>>,
458    pub props: RwLock<FxHashMap<Atom, Atom>>,
459}
460
461impl MangleCache for SimpleMangleCache {
462    fn vars_cache(&self, op: &mut dyn FnMut(&FxHashMap<Id, Atom>)) {
463        let vars = self.vars.read();
464        op(&vars);
465    }
466
467    fn props_cache(&self, op: &mut dyn FnMut(&FxHashMap<Atom, Atom>)) {
468        let props = self.props.read();
469        op(&props);
470    }
471
472    fn update_vars_cache(&self, new_data: &FxHashMap<Id, Atom>) {
473        let mut vars = self.vars.write();
474        vars.extend(new_data.iter().map(|(k, v)| (k.clone(), v.clone())));
475    }
476
477    fn update_props_cache(&self, new_data: &FxHashMap<Atom, Atom>) {
478        let mut props = self.props.write();
479        props.extend(new_data.iter().map(|(k, v)| (k.clone(), v.clone())));
480    }
481}