swc/
builder.rs

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}