#![deny(clippy::all)]
#![allow(clippy::blocks_in_conditions)]
#![allow(clippy::collapsible_else_if)]
#![allow(clippy::collapsible_if)]
#![allow(clippy::ptr_arg)]
#![allow(clippy::vec_box)]
#![allow(clippy::overly_complex_bool_expr)]
#![allow(clippy::mutable_key_type)]
#![allow(clippy::only_used_in_recursion)]
#![allow(unstable_name_collisions)]
#![allow(clippy::match_like_matches_macro)]
use once_cell::sync::Lazy;
use pass::mangle_names::mangle_names;
use swc_common::{comments::Comments, pass::Repeated, sync::Lrc, SourceMap, SyntaxContext};
use swc_ecma_ast::*;
use swc_ecma_transforms_optimization::debug_assert_valid;
use swc_ecma_usage_analyzer::marks::Marks;
use swc_ecma_visit::VisitMutWith;
use swc_timer::timer;
pub use crate::pass::global_defs::globals_defs;
use crate::{
compress::{compressor, pure_optimizer, PureOptimizerConfig},
metadata::info_marker,
mode::{Minification, Mode},
option::{CompressOptions, ExtraOptions, MinifyOptions},
pass::{
global_defs, mangle_names::idents_to_preserve, mangle_props::mangle_properties,
merge_exports::merge_exports, postcompress::postcompress_optimizer,
precompress::precompress_optimizer,
},
timing::Timings,
util::base54::CharFreq,
};
#[macro_use]
mod macros;
mod compress;
mod debug;
pub mod eval;
#[doc(hidden)]
pub mod js;
mod metadata;
mod mode;
pub mod option;
mod pass;
mod program_data;
mod size_hint;
pub mod timing;
mod util;
pub mod marks {
pub use swc_ecma_usage_analyzer::marks::Marks;
}
const DISABLE_BUGGY_PASSES: bool = true;
pub(crate) static CPU_COUNT: Lazy<usize> = Lazy::new(num_cpus::get);
pub(crate) static HEAVY_TASK_PARALLELS: Lazy<usize> = Lazy::new(|| *CPU_COUNT * 8);
pub(crate) static LIGHT_TASK_PARALLELS: Lazy<usize> = Lazy::new(|| *CPU_COUNT * 100);
pub fn optimize(
mut n: Program,
_cm: Lrc<SourceMap>,
comments: Option<&dyn Comments>,
mut timings: Option<&mut Timings>,
options: &MinifyOptions,
extra: &ExtraOptions,
) -> Program {
let _timer = timer!("minify");
let mut marks = Marks::new();
marks.top_level_ctxt = SyntaxContext::empty().apply_mark(extra.top_level_mark);
marks.unresolved_mark = extra.unresolved_mark;
debug_assert_valid(&n);
if let Some(defs) = options.compress.as_ref().map(|c| &c.global_defs) {
let _timer = timer!("inline global defs");
if !defs.is_empty() {
let defs = defs.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
n.visit_mut_with(&mut global_defs::globals_defs(
defs,
extra.unresolved_mark,
extra.top_level_mark,
));
}
}
if let Some(_options) = &options.compress {
let _timer = timer!("precompress");
n.visit_mut_with(&mut precompress_optimizer());
debug_assert_valid(&n);
}
if options.compress.is_some() {
n.visit_mut_with(&mut info_marker(
options.compress.as_ref(),
comments,
marks,
));
debug_assert_valid(&n);
}
if options.wrap {
}
if options.enclose {
}
if let Some(ref mut t) = timings {
t.section("compress");
}
if let Some(options) = &options.compress {
if options.unused {
perform_dce(&mut n, options, extra);
debug_assert_valid(&n);
}
}
if let Some(ref mut _t) = timings {
}
if options.rename && DISABLE_BUGGY_PASSES {
}
if let Some(ref mut t) = timings {
t.section("compress");
}
if let Some(c) = &options.compress {
{
let _timer = timer!("compress ast");
n.visit_mut_with(&mut compressor(
marks,
c,
options.mangle.as_ref(),
&Minification,
))
}
let _timer = timer!("postcompress");
n.visit_mut_with(&mut postcompress_optimizer(c));
let mut pass = 0;
loop {
pass += 1;
let mut v = pure_optimizer(
c,
marks,
PureOptimizerConfig {
force_str_for_tpl: Minification.force_str_for_tpl(),
enable_join_vars: true,
#[cfg(feature = "debug")]
debug_infinite_loop: false,
},
);
n.visit_mut_with(&mut v);
if !v.changed() || c.passes <= pass {
break;
}
}
}
if let Some(ref mut _t) = timings {
}
if options.mangle.is_some() {
}
if let Some(ref mut t) = timings {
t.section("mangle");
}
if let Some(mangle) = &options.mangle {
let _timer = timer!("mangle names");
let preserved = idents_to_preserve(mangle, marks, &n);
let chars = CharFreq::compute(
&n,
&preserved,
SyntaxContext::empty().apply_mark(marks.unresolved_mark),
)
.compile();
mangle_names(
&mut n,
mangle,
preserved,
chars,
extra.top_level_mark,
extra.mangle_name_cache.clone(),
);
if let Some(property_mangle_options) = &mangle.props {
mangle_properties(&mut n, property_mangle_options.clone(), chars);
}
}
n.visit_mut_with(&mut merge_exports());
if let Some(ref mut t) = timings {
t.section("hygiene");
t.end_section();
}
n
}
fn perform_dce(m: &mut Program, options: &CompressOptions, extra: &ExtraOptions) {
let _timer = timer!("remove dead code");
let mut visitor = swc_ecma_transforms_optimization::simplify::dce::dce(
swc_ecma_transforms_optimization::simplify::dce::Config {
module_mark: None,
top_level: options.top_level(),
top_retain: options.top_retain.clone(),
preserve_imports_with_side_effects: true,
},
extra.unresolved_mark,
);
loop {
#[cfg(feature = "debug")]
let start = crate::debug::dump(&*m, false);
m.visit_mut_with(&mut visitor);
#[cfg(feature = "debug")]
if visitor.changed() {
let src = crate::debug::dump(&*m, false);
tracing::debug!(
"===== Before DCE =====\n{}\n===== After DCE =====\n{}",
start,
src
);
}
if !visitor.changed() {
break;
}
visitor.reset();
}
debug_assert_valid(&*m);
}