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