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_visit::VisitMutWith;
48use swc_timer::timer;
49
50pub use crate::pass::global_defs::globals_defs;
51use crate::{
52 compress::{compressor, pure_optimizer, PureOptimizerConfig},
53 metadata::info_marker,
54 mode::{Minification, Mode},
55 option::{CompressOptions, ExtraOptions, MinifyOptions},
56 pass::{
57 global_defs, mangle_names::idents_to_preserve, mangle_props::mangle_properties,
58 merge_exports::merge_exports, postcompress::postcompress_optimizer,
59 },
60 timing::Timings,
62 util::base54::CharFreq,
63};
64
65#[macro_use]
66mod macros;
67mod compress;
68mod debug;
69pub mod eval;
70#[doc(hidden)]
71pub mod js;
72mod metadata;
73mod mode;
74pub mod option;
75mod pass;
76mod program_data;
77mod size_hint;
78pub mod timing;
79mod util;
80
81pub mod marks {
82 pub use swc_ecma_usage_analyzer::marks::Marks;
83}
84
85const DISABLE_BUGGY_PASSES: bool = true;
86
87pub(crate) static CPU_COUNT: Lazy<usize> = Lazy::new(num_cpus::get);
88pub(crate) static HEAVY_TASK_PARALLELS: Lazy<usize> = Lazy::new(|| *CPU_COUNT * 8);
89pub(crate) static LIGHT_TASK_PARALLELS: Lazy<usize> = Lazy::new(|| *CPU_COUNT * 100);
90
91pub fn optimize(
92 mut n: Program,
93 _cm: Lrc<SourceMap>,
94 comments: Option<&dyn Comments>,
95 mut timings: Option<&mut Timings>,
96 options: &MinifyOptions,
97 extra: &ExtraOptions,
98) -> Program {
99 let _timer = timer!("minify");
100
101 let mut marks = Marks::new();
102 marks.top_level_ctxt = SyntaxContext::empty().apply_mark(extra.top_level_mark);
103 marks.unresolved_mark = extra.unresolved_mark;
104
105 debug_assert_valid(&n);
106
107 if let Some(defs) = options.compress.as_ref().map(|c| &c.global_defs) {
108 let _timer = timer!("inline global defs");
109 if !defs.is_empty() {
116 let defs = defs.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
117 n.visit_mut_with(&mut global_defs::globals_defs(
118 defs,
119 extra.unresolved_mark,
120 extra.top_level_mark,
121 ));
122 }
123 }
124
125 if options.compress.is_some() {
126 n.visit_mut_with(&mut info_marker(
127 options.compress.as_ref(),
128 comments,
129 marks,
130 ));
132 debug_assert_valid(&n);
133 }
134
135 if options.wrap {
136 }
139
140 if options.enclose {
141 }
144
145 if options.rename && DISABLE_BUGGY_PASSES {
148 }
152
153 if let Some(ref mut t) = timings {
154 t.section("compress");
155 }
156 if let Some(c) = &options.compress {
157 {
158 let _timer = timer!("compress ast");
159
160 perform_dce(&mut n, c, marks);
161
162 n.mutate(&mut compressor(
163 marks,
164 c,
165 options.mangle.as_ref(),
166 &Minification,
167 ));
168
169 perform_dce(&mut n, c, marks);
170 }
171
172 let _timer = timer!("postcompress");
175
176 postcompress_optimizer(&mut n, c);
177
178 n.visit_mut_with(&mut pure_optimizer(
179 c,
180 marks,
181 PureOptimizerConfig {
182 force_str_for_tpl: Minification.force_str_for_tpl(),
183 enable_join_vars: true,
184 },
185 ));
186 }
187
188 if let Some(ref mut _t) = timings {
189 }
191 if options.mangle.is_some() {
192 }
194
195 if let Some(ref mut t) = timings {
196 t.section("mangle");
197 }
198
199 if let Some(mangle) = &options.mangle {
200 let _timer = timer!("mangle names");
201 let preserved = idents_to_preserve(mangle, marks, &n);
204
205 let chars = if !mangle.disable_char_freq {
206 debug_assert!(preserved.idents.is_some());
207 CharFreq::compute(&n, preserved.idents.as_ref().unwrap()).compile()
208 } else {
209 debug_assert!(preserved.idents.is_none());
210 CharFreq::default().compile()
211 };
212
213 mangle_names(
214 &mut n,
215 mangle,
216 preserved.preserved,
217 chars,
218 extra.top_level_mark,
219 extra.mangle_name_cache.clone(),
220 );
221
222 if let Some(property_mangle_options) = &mangle.props {
223 mangle_properties(&mut n, property_mangle_options, chars);
224 }
225 }
226
227 n.visit_mut_with(&mut merge_exports());
228
229 if let Some(ref mut t) = timings {
230 t.section("hygiene");
231 t.end_section();
232 }
233
234 n
235}
236
237fn perform_dce(m: &mut Program, options: &CompressOptions, extra: Marks) {
238 if !options.unused && !options.dead_code {
239 return;
240 }
241
242 let _timer = timer!("remove dead code");
243
244 let mut visitor = swc_ecma_transforms_optimization::simplify::dce::dce(
245 swc_ecma_transforms_optimization::simplify::dce::Config {
246 module_mark: None,
247 top_level: options.top_level(),
248 top_retain: options.top_retain.clone(),
249 preserve_imports_with_side_effects: true,
250 },
251 extra.unresolved_mark,
252 );
253
254 loop {
255 #[cfg(feature = "debug")]
256 let start = crate::debug::dump(&*m, false);
257
258 m.visit_mut_with(&mut visitor);
259
260 #[cfg(feature = "debug")]
261 if visitor.changed() {
262 let src = crate::debug::dump(&*m, false);
263 tracing::debug!(
264 "===== Before DCE =====\n{}\n===== After DCE =====\n{}",
265 start,
266 src
267 );
268 }
269
270 if !visitor.changed() {
271 break;
272 }
273
274 visitor.reset();
275 }
276
277 debug_assert_valid(&*m);
278}