1#![deny(clippy::all)]
30#![allow(clippy::blocks_in_conditions)]
31#![allow(clippy::collapsible_else_if)]
32#![allow(clippy::collapsible_if)]
33#![allow(clippy::ptr_arg)]
34#![allow(clippy::vec_box)]
35#![allow(clippy::overly_complex_bool_expr)]
36#![allow(clippy::mutable_key_type)]
37#![allow(clippy::only_used_in_recursion)]
38#![allow(unstable_name_collisions)]
39#![allow(clippy::match_like_matches_macro)]
40
41use once_cell::sync::Lazy;
42use pass::mangle_names::mangle_names;
43use swc_common::{comments::Comments, pass::Repeated, sync::Lrc, SourceMap, SyntaxContext};
44use swc_ecma_ast::*;
45use swc_ecma_transforms_optimization::debug_assert_valid;
46use swc_ecma_usage_analyzer::marks::Marks;
47use swc_ecma_utils::ExprCtx;
48use swc_ecma_visit::VisitMutWith;
49use swc_timer::timer;
50
51pub use crate::pass::global_defs::globals_defs;
52use crate::{
53 compress::{compressor, pure_optimizer, PureOptimizerConfig},
54 metadata::info_marker,
55 mode::{Minification, Mode},
56 option::{CompressOptions, ExtraOptions, MinifyOptions},
57 pass::{
58 global_defs, mangle_names::idents_to_preserve, mangle_props::mangle_properties,
59 merge_exports::merge_exports, postcompress::postcompress_optimizer,
60 precompress::precompress_optimizer,
61 },
62 timing::Timings,
64 util::base54::CharFreq,
65};
66
67#[macro_use]
68mod macros;
69mod compress;
70mod debug;
71pub mod eval;
72#[doc(hidden)]
73pub mod js;
74mod metadata;
75mod mode;
76pub mod option;
77mod pass;
78mod program_data;
79mod size_hint;
80pub mod timing;
81mod util;
82
83pub mod marks {
84 pub use swc_ecma_usage_analyzer::marks::Marks;
85}
86
87const DISABLE_BUGGY_PASSES: bool = true;
88
89pub(crate) static CPU_COUNT: Lazy<usize> = Lazy::new(num_cpus::get);
90pub(crate) static HEAVY_TASK_PARALLELS: Lazy<usize> = Lazy::new(|| *CPU_COUNT * 8);
91pub(crate) static LIGHT_TASK_PARALLELS: Lazy<usize> = Lazy::new(|| *CPU_COUNT * 100);
92
93pub fn optimize(
94 mut n: Program,
95 _cm: Lrc<SourceMap>,
96 comments: Option<&dyn Comments>,
97 mut timings: Option<&mut Timings>,
98 options: &MinifyOptions,
99 extra: &ExtraOptions,
100) -> Program {
101 let _timer = timer!("minify");
102
103 let mut marks = Marks::new();
104 marks.top_level_ctxt = SyntaxContext::empty().apply_mark(extra.top_level_mark);
105 marks.unresolved_mark = extra.unresolved_mark;
106
107 debug_assert_valid(&n);
108
109 if let Some(defs) = options.compress.as_ref().map(|c| &c.global_defs) {
110 let _timer = timer!("inline global defs");
111 if !defs.is_empty() {
118 let defs = defs.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
119 n.visit_mut_with(&mut global_defs::globals_defs(
120 defs,
121 extra.unresolved_mark,
122 extra.top_level_mark,
123 ));
124 }
125 }
126
127 if let Some(_options) = &options.compress {
128 let _timer = timer!("precompress");
129
130 n.visit_mut_with(&mut precompress_optimizer(ExprCtx {
131 unresolved_ctxt: SyntaxContext::empty().apply_mark(marks.unresolved_mark),
132 is_unresolved_ref_safe: false,
133 in_strict: false,
134 remaining_depth: 6,
135 }));
136 debug_assert_valid(&n);
137 }
138
139 if options.compress.is_some() {
140 n.visit_mut_with(&mut info_marker(
141 options.compress.as_ref(),
142 comments,
143 marks,
144 ));
146 debug_assert_valid(&n);
147 }
148
149 if options.wrap {
150 }
153
154 if options.enclose {
155 }
158 if let Some(ref mut t) = timings {
159 t.section("compress");
160 }
161 if let Some(options) = &options.compress {
162 if options.unused {
163 perform_dce(&mut n, options, extra);
164 debug_assert_valid(&n);
165 }
166 }
167
168 if let Some(ref mut _t) = timings {
171 }
173
174 if options.rename && DISABLE_BUGGY_PASSES {
177 }
181
182 if let Some(ref mut t) = timings {
183 t.section("compress");
184 }
185 if let Some(c) = &options.compress {
186 {
187 let _timer = timer!("compress ast");
188
189 n.mutate(&mut compressor(
190 marks,
191 c,
192 options.mangle.as_ref(),
193 &Minification,
194 ))
195 }
196
197 let _timer = timer!("postcompress");
200
201 n.visit_mut_with(&mut postcompress_optimizer(c));
202
203 n.visit_mut_with(&mut pure_optimizer(
204 c,
205 marks,
206 PureOptimizerConfig {
207 force_str_for_tpl: Minification.force_str_for_tpl(),
208 enable_join_vars: true,
209 },
210 ));
211 }
212
213 if let Some(ref mut _t) = timings {
214 }
216 if options.mangle.is_some() {
217 }
219
220 if let Some(ref mut t) = timings {
221 t.section("mangle");
222 }
223
224 if let Some(mangle) = &options.mangle {
225 let _timer = timer!("mangle names");
226 let preserved = idents_to_preserve(mangle, marks, &n);
229
230 let chars = if !mangle.disable_char_freq {
231 CharFreq::compute(
232 &n,
233 &preserved,
234 SyntaxContext::empty().apply_mark(marks.unresolved_mark),
235 )
236 .compile()
237 } else {
238 CharFreq::default().compile()
239 };
240
241 mangle_names(
242 &mut n,
243 mangle,
244 preserved,
245 chars,
246 extra.top_level_mark,
247 extra.mangle_name_cache.clone(),
248 );
249
250 if let Some(property_mangle_options) = &mangle.props {
251 mangle_properties(&mut n, property_mangle_options.clone(), chars);
252 }
253 }
254
255 n.visit_mut_with(&mut merge_exports());
256
257 if let Some(ref mut t) = timings {
258 t.section("hygiene");
259 t.end_section();
260 }
261
262 n
263}
264
265fn perform_dce(m: &mut Program, options: &CompressOptions, extra: &ExtraOptions) {
266 let _timer = timer!("remove dead code");
267
268 let mut visitor = swc_ecma_transforms_optimization::simplify::dce::dce(
269 swc_ecma_transforms_optimization::simplify::dce::Config {
270 module_mark: None,
271 top_level: options.top_level(),
272 top_retain: options.top_retain.clone(),
273 preserve_imports_with_side_effects: true,
274 },
275 extra.unresolved_mark,
276 );
277
278 loop {
279 #[cfg(feature = "debug")]
280 let start = crate::debug::dump(&*m, false);
281
282 m.visit_mut_with(&mut visitor);
283
284 #[cfg(feature = "debug")]
285 if visitor.changed() {
286 let src = crate::debug::dump(&*m, false);
287 tracing::debug!(
288 "===== Before DCE =====\n{}\n===== After DCE =====\n{}",
289 start,
290 src
291 );
292 }
293
294 if !visitor.changed() {
295 break;
296 }
297
298 visitor.reset();
299 }
300
301 debug_assert_valid(&*m);
302}