1use swc_common::{comments::Comments, sync::Lrc, util::take::Take, Mark, SourceMap};
2use swc_ecma_ast::{Module, Script};
3use swc_ecma_minifier::option::{terser::TerserTopLevelOptions, MinifyOptions};
4use swc_ecma_transforms::{hygiene::hygiene_with_config, resolver};
5use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
6
7use crate::config::JsMinifyOptions;
8
9pub struct MinifierPass<'a> {
10 pub options: Option<JsMinifyOptions>,
11 pub cm: Lrc<SourceMap>,
12 pub comments: Option<&'a dyn Comments>,
13 pub top_level_mark: Mark,
14}
15
16impl VisitMut for MinifierPass<'_> {
17 noop_visit_mut_type!(fail);
18
19 fn visit_mut_module(&mut self, m: &mut Module) {
20 if let Some(options) = &self.options {
21 let opts = MinifyOptions {
22 compress: options
23 .compress
24 .clone()
25 .unwrap_as_option(|default| match default {
26 Some(true) => Some(Default::default()),
27 _ => None,
28 })
29 .map(|mut v| {
30 if v.const_to_let.is_none() {
31 v.const_to_let = Some(true);
32 }
33 if v.toplevel.is_none() {
34 v.toplevel = Some(TerserTopLevelOptions::Bool(true));
35 }
36
37 v.into_config(self.cm.clone())
38 }),
39 mangle: options
40 .mangle
41 .clone()
42 .unwrap_as_option(|default| match default {
43 Some(true) => Some(Default::default()),
44 _ => None,
45 }),
46 ..Default::default()
47 };
48
49 if opts.compress.is_none() && opts.mangle.is_none() {
50 return;
51 }
52
53 m.visit_mut_with(&mut hygiene_with_config(
54 swc_ecma_transforms_base::hygiene::Config {
55 top_level_mark: self.top_level_mark,
56 ..Default::default()
57 },
58 ));
59
60 let unresolved_mark = Mark::new();
61 let top_level_mark = Mark::new();
62
63 m.visit_mut_with(&mut resolver(unresolved_mark, top_level_mark, false));
64
65 m.map_with_mut(|m| {
66 swc_ecma_minifier::optimize(
67 m.into(),
68 self.cm.clone(),
69 self.comments.as_ref().map(|v| v as &dyn Comments),
70 None,
71 &opts,
72 &swc_ecma_minifier::option::ExtraOptions {
73 unresolved_mark,
74 top_level_mark,
75 mangle_name_cache: None,
76 },
77 )
78 .expect_module()
79 })
80 }
81 }
82
83 fn visit_mut_script(&mut self, m: &mut Script) {
84 if let Some(options) = &self.options {
85 let opts = MinifyOptions {
86 compress: options
87 .compress
88 .clone()
89 .unwrap_as_option(|default| match default {
90 Some(true) => Some(Default::default()),
91 _ => None,
92 })
93 .map(|mut v| {
94 if v.const_to_let.is_none() {
95 v.const_to_let = Some(true);
96 }
97
98 v.module = false;
99
100 v.into_config(self.cm.clone())
101 }),
102 mangle: options
103 .mangle
104 .clone()
105 .unwrap_as_option(|default| match default {
106 Some(true) => Some(Default::default()),
107 _ => None,
108 }),
109 ..Default::default()
110 };
111
112 if opts.compress.is_none() && opts.mangle.is_none() {
113 return;
114 }
115
116 m.visit_mut_with(&mut hygiene_with_config(
117 swc_ecma_transforms_base::hygiene::Config {
118 top_level_mark: self.top_level_mark,
119 ..Default::default()
120 },
121 ));
122
123 let unresolved_mark = Mark::new();
124 let top_level_mark = Mark::new();
125
126 m.visit_mut_with(&mut resolver(unresolved_mark, top_level_mark, false));
127
128 m.map_with_mut(|m| {
129 swc_ecma_minifier::optimize(
130 m.into(),
131 self.cm.clone(),
132 self.comments.as_ref().map(|v| v as &dyn Comments),
133 None,
134 &opts,
135 &swc_ecma_minifier::option::ExtraOptions {
136 unresolved_mark,
137 top_level_mark,
138 mangle_name_cache: None,
139 },
140 )
141 .expect_script()
142 })
143 }
144 }
145}