mod compressor;
pub(crate) mod generated;
mod keep_var;
mod minifier_traverse;
mod options;
mod peephole;
mod state;
mod symbol_value;
mod traverse_context;
use oxc_allocator::Allocator;
use oxc_ast::ast::Program;
use oxc_index::IndexVec;
use oxc_mangler::Mangler;
use oxc_semantic::{Scoping, SemanticBuilder};
use oxc_span::CompactStr;
use oxc_syntax::class::ClassId;
use rustc_hash::FxHashMap;
pub use oxc_mangler::{MangleOptions, MangleOptionsKeepNames};
pub(crate) use crate::generated::traverse::Traverse;
#[doc(hidden)]
pub(crate) use crate::traverse_context::MinifierTraverseCtx as TraverseCtx;
pub(crate) use crate::traverse_context::ReusableMinifierTraverseCtx as ReusableTraverseCtx;
pub use crate::{compressor::Compressor, options::*};
#[derive(Debug, Clone)]
pub struct MinifierOptions {
pub mangle: Option<MangleOptions>,
pub compress: Option<CompressOptions>,
}
impl Default for MinifierOptions {
fn default() -> Self {
Self { mangle: Some(MangleOptions::default()), compress: Some(CompressOptions::default()) }
}
}
pub struct MinifierReturn {
pub scoping: Option<Scoping>,
pub class_private_mappings: Option<IndexVec<ClassId, FxHashMap<String, CompactStr>>>,
pub iterations: u8,
}
pub struct Minifier {
options: MinifierOptions,
}
impl<'a> Minifier {
pub fn new(options: MinifierOptions) -> Self {
Self { options }
}
pub fn minify(self, allocator: &'a Allocator, program: &mut Program<'a>) -> MinifierReturn {
self.build(false, allocator, program)
}
pub fn dce(self, allocator: &'a Allocator, program: &mut Program<'a>) -> MinifierReturn {
self.build(true, allocator, program)
}
fn build(
self,
dce: bool,
allocator: &'a Allocator,
program: &mut Program<'a>,
) -> MinifierReturn {
let (stats, iterations) = self
.options
.compress
.map(|options| {
let semantic = SemanticBuilder::new().build(program).semantic;
let stats = semantic.stats();
let scoping = semantic.into_scoping();
let compressor = Compressor::new(allocator);
let iterations = if dce {
let options = CompressOptions {
target: options.target,
treeshake: options.treeshake,
..CompressOptions::dce()
};
compressor.dead_code_elimination_with_scoping(program, scoping, options)
} else {
compressor.build_with_scoping(program, scoping, options)
};
(stats, iterations)
})
.unwrap_or_default();
let (scoping, class_private_mappings) = self
.options
.mangle
.map(|options| {
let mut semantic = SemanticBuilder::new().with_stats(stats).build(program).semantic;
let class_private_mappings = Mangler::default()
.with_options(options)
.build_with_semantic(&mut semantic, program);
(semantic.into_scoping(), class_private_mappings)
})
.map_or((None, None), |(scoping, mappings)| (Some(scoping), Some(mappings)));
MinifierReturn { scoping, class_private_mappings, iterations }
}
}