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
15macro_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
28pub struct ExtraOptions {
30 pub unresolved_mark: Mark,
32
33 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 #[serde(default)]
94 pub eval: bool,
95
96 #[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#[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 #[cfg_attr(feature = "extra-serde", serde(default))]
221 #[cfg_attr(feature = "extra-serde", serde(alias = "expression"))]
222 pub expr: bool,
223
224 #[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 #[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 #[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 #[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 #[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 #[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 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}