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))]
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 #[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 #[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 #[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 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}