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 = "true_by_default"))]
286    #[cfg_attr(feature = "extra-serde", serde(alias = "merge_imports"))]
287    pub merge_imports: bool,
288
289    #[cfg_attr(feature = "extra-serde", serde(default))]
290    pub module: bool,
291
292    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
293    #[cfg_attr(feature = "extra-serde", serde(alias = "negate_iife"))]
294    pub negate_iife: bool,
295
296    /// If this value is zero, the minifier will repeat work until the ast node
297    /// is settled.
298    #[cfg_attr(feature = "extra-serde", serde(default = "default_passes"))]
299    #[cfg_attr(feature = "extra-serde", serde(alias = "passes"))]
300    pub passes: usize,
301
302    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
303    #[cfg_attr(feature = "extra-serde", serde(alias = "properties"))]
304    pub props: bool,
305
306    #[cfg_attr(feature = "extra-serde", serde(default))]
307    #[cfg_attr(feature = "extra-serde", serde(alias = "pure_getters"))]
308    pub pure_getters: PureGetterOption,
309
310    #[cfg_attr(feature = "extra-serde", serde(skip))]
311    #[cfg_attr(feature = "extra-serde", serde(alias = "pure_funcs"))]
312    pub pure_funcs: Vec<Box<Expr>>,
313
314    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
315    #[cfg_attr(feature = "extra-serde", serde(alias = "reduce_funcs"))]
316    pub reduce_fns: bool,
317    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
318    #[cfg_attr(feature = "extra-serde", serde(alias = "reduce_vars"))]
319    pub reduce_vars: bool,
320
321    #[cfg_attr(feature = "extra-serde", serde(default = "three_by_default"))]
322    #[cfg_attr(feature = "extra-serde", serde(alias = "sequences"))]
323    pub sequences: u8,
324
325    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
326    #[cfg_attr(feature = "extra-serde", serde(alias = "side_effects"))]
327    pub side_effects: bool,
328
329    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
330    #[cfg_attr(feature = "extra-serde", serde(alias = "switches"))]
331    pub switches: bool,
332
333    /// Top level symbols to retain.
334    #[cfg_attr(feature = "extra-serde", serde(default))]
335    #[cfg_attr(feature = "extra-serde", serde(alias = "top_retain"))]
336    pub top_retain: Vec<Atom>,
337
338    #[cfg_attr(feature = "extra-serde", serde(default))]
339    #[cfg_attr(feature = "extra-serde", serde(alias = "toplevel"))]
340    pub top_level: Option<TopLevelOptions>,
341
342    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
343    #[cfg_attr(feature = "extra-serde", serde(alias = "typeofs"))]
344    pub typeofs: bool,
345
346    #[cfg_attr(feature = "extra-serde", serde(default))]
347    #[cfg_attr(feature = "extra-serde", serde(rename = "unsafe"))]
348    pub unsafe_passes: bool,
349
350    #[cfg_attr(feature = "extra-serde", serde(default))]
351    pub unsafe_arrows: bool,
352
353    #[cfg_attr(feature = "extra-serde", serde(default))]
354    pub unsafe_comps: bool,
355
356    #[cfg_attr(feature = "extra-serde", serde(default))]
357    #[cfg_attr(feature = "extra-serde", serde(alias = "unsafe_Function"))]
358    pub unsafe_function: bool,
359
360    #[cfg_attr(feature = "extra-serde", serde(default))]
361    pub unsafe_math: bool,
362
363    #[cfg_attr(feature = "extra-serde", serde(default))]
364    pub unsafe_symbols: bool,
365
366    #[cfg_attr(feature = "extra-serde", serde(default))]
367    pub unsafe_methods: bool,
368
369    #[cfg_attr(feature = "extra-serde", serde(default))]
370    pub unsafe_proto: bool,
371
372    #[cfg_attr(feature = "extra-serde", serde(default))]
373    pub unsafe_regexp: bool,
374
375    #[cfg_attr(feature = "extra-serde", serde(default))]
376    pub unsafe_undefined: bool,
377
378    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
379    pub unused: bool,
380
381    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
382    pub const_to_let: bool,
383
384    /// If you modified globals, set this to false.
385    ///
386    /// Defaults to true.
387    #[cfg_attr(feature = "extra-serde", serde(default = "true_by_default"))]
388    pub pristine_globals: bool,
389
390    #[cfg_attr(feature = "extra-serde", serde(default))]
391    pub experimental: CompressExperimentalOptions,
392}
393
394impl CompressOptions {
395    pub(crate) fn sequences(&self) -> bool {
396        self.sequences != 0
397    }
398
399    /// Returns `true` if any of toplevel optimizer is enabled.
400    pub(crate) fn top_level(&self) -> bool {
401        if !self.top_retain.is_empty() {
402            return true;
403        }
404
405        self.top_level.map(|v| v.functions).unwrap_or(false) || self.module
406    }
407}
408
409const fn true_by_default() -> bool {
410    true
411}
412
413const fn default_passes() -> usize {
414    2
415}
416
417const fn three_by_default() -> u8 {
418    3
419}
420
421const fn default_ecma() -> EsVersion {
422    EsVersion::Es5
423}
424
425impl_default!(MangleOptions);
426
427impl Default for CompressOptions {
428    fn default() -> Self {
429        Self {
430            arguments: false,
431            arrows: true,
432            bools: true,
433            bools_as_ints: false,
434            collapse_vars: true,
435            comparisons: true,
436            computed_props: true,
437            conditionals: true,
438            dead_code: true,
439            directives: true,
440            drop_console: false,
441            drop_debugger: true,
442            ecma: default_ecma(),
443            evaluate: true,
444            expr: false,
445            global_defs: Default::default(),
446            hoist_fns: false,
447            hoist_props: true,
448            hoist_vars: false,
449            ie8: false,
450            if_return: true,
451            inline: 3,
452            join_vars: true,
453            keep_classnames: false,
454            keep_fargs: true,
455            keep_fnames: false,
456            keep_infinity: false,
457            loops: true,
458            merge_imports: true,
459            module: false,
460            negate_iife: true,
461            passes: default_passes(),
462            props: true,
463            pure_getters: Default::default(),
464            pure_funcs: Default::default(),
465            reduce_fns: true,
466            reduce_vars: false,
467            sequences: 3,
468            side_effects: true,
469            switches: true,
470            top_retain: Default::default(),
471            top_level: Default::default(),
472            typeofs: true,
473            unsafe_passes: false,
474            unsafe_arrows: false,
475            unsafe_comps: false,
476            unsafe_function: false,
477            unsafe_math: false,
478            unsafe_methods: false,
479            unsafe_proto: false,
480            unsafe_regexp: false,
481            unsafe_symbols: false,
482            unsafe_undefined: false,
483            unused: true,
484            const_to_let: true,
485            pristine_globals: true,
486            experimental: Default::default(),
487        }
488    }
489}
490
491pub trait MangleCache: Send + Sync {
492    fn vars_cache(&self, op: &mut dyn FnMut(&RenameMap));
493
494    fn props_cache(&self, op: &mut dyn FnMut(&FxHashMap<Atom, Atom>));
495
496    fn update_vars_cache(&self, new_data: &RenameMap);
497
498    fn update_props_cache(&self, new_data: &FxHashMap<Atom, Atom>);
499}
500
501#[derive(Debug, Default)]
502pub struct SimpleMangleCache {
503    pub vars: RwLock<RenameMap>,
504    pub props: RwLock<FxHashMap<Atom, Atom>>,
505}
506
507impl MangleCache for SimpleMangleCache {
508    fn vars_cache(&self, op: &mut dyn FnMut(&RenameMap)) {
509        let vars = self.vars.read();
510        op(&vars);
511    }
512
513    fn props_cache(&self, op: &mut dyn FnMut(&FxHashMap<Atom, Atom>)) {
514        let props = self.props.read();
515        op(&props);
516    }
517
518    fn update_vars_cache(&self, new_data: &RenameMap) {
519        let mut vars = self.vars.write();
520        vars.extend(new_data.iter().map(|(k, v)| (k.clone(), v.clone())));
521    }
522
523    fn update_props_cache(&self, new_data: &FxHashMap<Atom, Atom>) {
524        let mut props = self.props.write();
525        props.extend(new_data.iter().map(|(k, v)| (k.clone(), v.clone())));
526    }
527}