1mod compressor;
48pub(crate) mod generated;
49mod keep_var;
50mod minifier_traverse;
51mod options;
52mod peephole;
53mod state;
54mod symbol_value;
55mod traverse_context;
56
57use oxc_allocator::Allocator;
58use oxc_ast::ast::Program;
59use oxc_index::IndexVec;
60use oxc_mangler::Mangler;
61use oxc_semantic::{Scoping, SemanticBuilder};
62use oxc_str::CompactStr;
63use oxc_syntax::class::ClassId;
64use rustc_hash::FxHashMap;
65
66pub use oxc_mangler::{MangleOptions, MangleOptionsKeepNames};
67
68pub(crate) use crate::generated::traverse::Traverse;
69#[doc(hidden)]
70pub(crate) use crate::traverse_context::MinifierTraverseCtx as TraverseCtx;
71pub(crate) use crate::traverse_context::ReusableMinifierTraverseCtx as ReusableTraverseCtx;
72pub use crate::{compressor::Compressor, options::*};
73
74#[derive(Debug, Clone)]
75pub struct MinifierOptions {
76 pub mangle: Option<MangleOptions>,
77 pub compress: Option<CompressOptions>,
78}
79
80impl Default for MinifierOptions {
81 fn default() -> Self {
82 Self { mangle: Some(MangleOptions::default()), compress: Some(CompressOptions::default()) }
83 }
84}
85
86pub struct MinifierReturn {
87 pub scoping: Option<Scoping>,
88
89 pub class_private_mappings: Option<IndexVec<ClassId, FxHashMap<String, CompactStr>>>,
92
93 pub iterations: u8,
95}
96
97pub struct Minifier {
98 options: MinifierOptions,
99}
100
101impl<'a> Minifier {
102 pub fn new(options: MinifierOptions) -> Self {
103 Self { options }
104 }
105
106 pub fn minify(self, allocator: &'a Allocator, program: &mut Program<'a>) -> MinifierReturn {
107 self.build(false, allocator, program)
108 }
109
110 pub fn dce(self, allocator: &'a Allocator, program: &mut Program<'a>) -> MinifierReturn {
111 self.build(true, allocator, program)
112 }
113
114 fn build(
115 self,
116 dce: bool,
117 allocator: &'a Allocator,
118 program: &mut Program<'a>,
119 ) -> MinifierReturn {
120 let (stats, iterations) = self
121 .options
122 .compress
123 .map(|options| {
124 let semantic = SemanticBuilder::new().build(program).semantic;
125 let stats = semantic.stats();
126 let scoping = semantic.into_scoping();
127 let compressor = Compressor::new(allocator);
128 let iterations = if dce {
129 let options = CompressOptions {
130 target: options.target,
131 treeshake: options.treeshake,
132 ..CompressOptions::dce()
133 };
134 compressor.dead_code_elimination_with_scoping(program, scoping, options)
135 } else {
136 compressor.build_with_scoping(program, scoping, options)
137 };
138 (stats, iterations)
139 })
140 .unwrap_or_default();
141 let (scoping, class_private_mappings) = self
142 .options
143 .mangle
144 .map(|options| {
145 let mut semantic = SemanticBuilder::new().with_stats(stats).build(program).semantic;
146 let class_private_mappings = Mangler::default()
147 .with_options(options)
148 .build_with_semantic(&mut semantic, program);
149 (semantic.into_scoping(), class_private_mappings)
150 })
151 .map_or((None, None), |(scoping, mappings)| (Some(scoping), Some(mappings)));
152 MinifierReturn { scoping, class_private_mappings, iterations }
153 }
154}