mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
refactor(minifier): remove unused code and traits (#8632)
This commit is contained in:
parent
c1d243be46
commit
52458de00b
8 changed files with 13 additions and 397 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -1858,7 +1858,6 @@ dependencies = [
|
|||
"oxc_syntax",
|
||||
"oxc_traverse",
|
||||
"pico-args",
|
||||
"rustc-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ oxc_syntax = { workspace = true }
|
|||
oxc_traverse = { workspace = true }
|
||||
|
||||
cow-utils = { workspace = true }
|
||||
rustc-hash = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
oxc_parser = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -1,284 +0,0 @@
|
|||
use oxc_allocator::Vec;
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_traverse::TraverseCtx;
|
||||
|
||||
use super::PeepholeOptimizations;
|
||||
|
||||
impl<'a> PeepholeOptimizations {
|
||||
/// Tries to chain assignments together.
|
||||
///
|
||||
/// <https://github.com/google/closure-compiler/blob/v20240609/src/com/google/javascript/jscomp/ExploitAssigns.java>
|
||||
#[expect(clippy::unused_self)]
|
||||
pub fn exploit_assigns(
|
||||
&mut self,
|
||||
_stmts: &mut Vec<'a, Statement<'a>>,
|
||||
_ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://github.com/google/closure-compiler/blob/v20240609/test/com/google/javascript/jscomp/ExploitAssignsTest.java>
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::tester::{test, test_same};
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_expr_exploitation_types() {
|
||||
test("a = true; b = true", "b = a = true");
|
||||
test("a = !0; b = !0", "b = a = !0");
|
||||
test("a = !1; b = !1", "b = a = !1");
|
||||
test("a = void 0; b = void 0", "b = a = void 0");
|
||||
test("a = -Infinity; b = -Infinity", "b = a = -Infinity");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_expr_exploitation_types2() {
|
||||
test("a = !0; b = !0", "b = a = !0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn nullish_coalesce() {
|
||||
test("a = null; a ?? b;", "(a = null)??b");
|
||||
test("a = true; if (a ?? a) { foo(); }", "if ((a = true) ?? a) { foo() }");
|
||||
test("a = !0; if (a ?? a) { foo(); }", "if ((a = !0) ?? a) { foo() }");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_expr_exploitation() {
|
||||
test("a = null; b = null; var c = b", "var c = b = a = null");
|
||||
test("a = null; b = null", "b = a = null");
|
||||
test("a = undefined; b = undefined", "b = a = undefined");
|
||||
test("a = 0; b = 0", "b=a=0");
|
||||
test("a = 'foo'; b = 'foo'", "b = a = \"foo\"");
|
||||
test("a = c; b = c", "b=a=c");
|
||||
|
||||
test_same("a = 0; b = 1");
|
||||
test_same("a = \"foo\"; b = \"foox\"");
|
||||
|
||||
test("a = null; a && b;", "(a = null)&&b");
|
||||
test("a = null; a || b;", "(a = null)||b");
|
||||
|
||||
test("a = null; a ? b : c;", "(a = null) ? b : c");
|
||||
|
||||
test("a = null; this.foo = null;", "this.foo = a = null");
|
||||
test("function f(){ a = null; return null; }", "function f(){return a = null}");
|
||||
|
||||
test("a = true; if (a) { foo(); }", "if (a = true) { foo() }");
|
||||
test("a = true; if (a && a) { foo(); }", "if ((a = true) && a) { foo() }");
|
||||
test("a = false; if (a) { foo(); }", "if (a = false) { foo() }");
|
||||
|
||||
test("a = !0; if (a) { foo(); }", "if (a = !0) { foo() }");
|
||||
test("a = !0; if (a && a) { foo(); }", "if ((a = !0) && a) { foo() }");
|
||||
test("a = !1; if (a) { foo(); }", "if (a = !1) { foo() }");
|
||||
|
||||
test_same("a = this.foo; a();");
|
||||
test("a = b; b = a;", "b = a = b");
|
||||
test_same("a = b; a.c = a");
|
||||
test("this.foo = null; this.bar = null;", "this.bar = this.foo = null");
|
||||
test(
|
||||
"this.foo = null; this.bar = null; this.baz = this.bar",
|
||||
"this.baz = this.bar = this.foo = null",
|
||||
);
|
||||
test(
|
||||
"this.foo = null; this.bar = null; this.baz = this?.bar",
|
||||
"this.bar = this.foo = null; this.baz = this?.bar;",
|
||||
);
|
||||
test("this.foo = null; a = null;", "a = this.foo = null");
|
||||
test("this.foo = null; a = this.foo;", "a = this.foo = null");
|
||||
test_same("this.foo = null; a = this?.foo;");
|
||||
test("a.b.c=null; a=null;", "a = a.b.c = null");
|
||||
test_same("a = null; a.b.c = null");
|
||||
test("(a=b).c = null; this.b = null;", "this.b = (a=b).c = null");
|
||||
test_same("if(x) a = null; else b = a");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_let_const_assignment() {
|
||||
test("a = null; b = null; let c = b", "let c = b = a = null");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_block_scope() {
|
||||
test("{ a = null; b = null; c = b }", "{ c = b = a = null }");
|
||||
|
||||
// TODO (simranarora) What should we have as the intended behavior with block scoping?
|
||||
test(
|
||||
"a = null; b = null; { c = b; }",
|
||||
// "{ c = b = a = null; }
|
||||
"b = a = null; { c = b; }",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_exploit_in_arrow_function() {
|
||||
test("() => { a = null; return null; }", "() => { return a = null }");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_nested_expr_exploitation() {
|
||||
test(
|
||||
"this.foo = null; this.bar = null; this.baz = null;",
|
||||
"this.baz = this.bar = this.foo = null",
|
||||
);
|
||||
|
||||
test(
|
||||
"a = 3; this.foo = a; this.bar = a; this.baz = 3;",
|
||||
"this.baz = this.bar = this.foo = a = 3",
|
||||
);
|
||||
test(
|
||||
"a = 3; this.foo = a; this.bar = this.foo; this.baz = a;",
|
||||
"this.baz = this.bar = this.foo = a = 3",
|
||||
);
|
||||
// recursively optimize assigns until optional chaining on RHS
|
||||
test(
|
||||
"a = 3; this.foo = a; this.bar = this?.foo; this.baz = a;",
|
||||
"this.foo = a = 3; this.bar = this?.foo; this.baz = a;",
|
||||
);
|
||||
test(
|
||||
"a = 3; this.foo = a; this.bar = 3; this.baz = this.foo;",
|
||||
"this.baz = this.bar = this.foo = a = 3",
|
||||
);
|
||||
// recursively optimize assigns until optional chaining on RHS
|
||||
test(
|
||||
"a = 3; this.foo = a; this.bar = 3; this.baz = this?.foo;",
|
||||
"this.bar = this.foo = a = 3; this.baz = this?.foo;",
|
||||
);
|
||||
// test(
|
||||
// "a = 3; this.foo = a; a = 3; this.bar = 3; " + "a = 3; this.baz = this.foo;",
|
||||
// "this.baz = a = this.bar = a = this.foo = a = 3",
|
||||
// );
|
||||
// recursively optimize assigns until optional chaining on RHS
|
||||
// test(
|
||||
// lines("a = 3; this.foo = a; a = 3; this.bar = 3; a = 3; this.baz = this?.foo;"),
|
||||
// lines("a = this.bar = a = this.foo = a = 3; this.baz = this?.foo;"),
|
||||
// );
|
||||
|
||||
// test(
|
||||
// "a = 4; this.foo = a; a = 3; this.bar = 3; " + "a = 3; this.baz = this.foo;",
|
||||
// "this.foo = a = 4; a = this.bar = a = 3; this.baz = this.foo",
|
||||
// );
|
||||
// recursively optimize assigns until optional chaining on RHS
|
||||
// test(
|
||||
// lines("a = 4; this.foo = a; a = 3; this.bar = 3; a = 3; this.baz = this?.foo;"),
|
||||
// lines("this.foo = a = 4;", "a = this.bar = a = 3;", "this.baz = this?.foo;"),
|
||||
// );
|
||||
|
||||
// test(
|
||||
// "a = 3; this.foo = a; a = 4; this.bar = 3; " + "a = 3; this.baz = this.foo;",
|
||||
// "this.foo = a = 3; a = 4; a = this.bar = 3; this.baz = this.foo",
|
||||
// );
|
||||
// test(
|
||||
// lines("a = 3; this.foo = a; a = 4; this.bar = 3; ", "a = 3; this.baz = this?.foo;"),
|
||||
// lines("this.foo = a = 3;", "a = 4;", "a = this.bar = 3;", "this.baz = this?.foo;"),
|
||||
// );
|
||||
// test(
|
||||
// "a = 3; this.foo = a; a = 3; this.bar = 3; " + "a = 4; this.baz = this.foo;",
|
||||
// "this.bar = a = this.foo = a = 3; a = 4; this.baz = this.foo",
|
||||
// );
|
||||
// test(
|
||||
// lines("a = 3; this.foo = a; a = 3; this.bar = 3; a = 4; this.baz = this?.foo;"),
|
||||
// lines("this.bar = a = this.foo = a = 3;", "a = 4;", "this.baz = this?.foo;"),
|
||||
// );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_bug1840071() {
|
||||
// Some external properties are implemented as setters. Let's
|
||||
// make sure that we don't collapse them inappropriately.
|
||||
test("a.b = a.x; if (a.x) {}", "if (a.b = a.x) {}");
|
||||
test_same("a.b = a?.x; if (a?.x) {}");
|
||||
test_same("a.b = a.x; if (a.b) {}");
|
||||
test("a.b = a.c = a.x; if (a.x) {}", "if (a.b = a.c = a.x) {}");
|
||||
test_same("a.b = a.c = a?.x; if (a?.x) {}");
|
||||
|
||||
test_same("a.b = a.c = a.x; if (a.c) {}");
|
||||
test_same("a.b = a.c = a.x; if (a.b) {}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_bug2072343() {
|
||||
test_same("a = a.x;a = a.x");
|
||||
test_same("a = a.x;b = a.x");
|
||||
test("b = a.x;a = a.x", "a = b = a.x");
|
||||
test_same("b = a?.x;a = a?.x");
|
||||
test_same("a.x = a;a = a.x");
|
||||
test_same("a.b = a.b.x;a.b = a.b.x");
|
||||
test_same("a.y = a.y.x;b = a.y;c = a.y.x");
|
||||
test("a = a.x;b = a;c = a.x", "b = a = a.x;c = a.x");
|
||||
test("b = a.x;a = b;c = a.x", "a = b = a.x;c = a.x");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_bad_collapse_into_call() {
|
||||
// Can't collapse this, because if we did, 'foo' would be called
|
||||
// in the wrong 'this' context.
|
||||
test_same("this.foo = function() {}; this.foo();");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_bad_collapse() {
|
||||
test_same("this.$e$ = []; this.$b$ = null;");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_issue1017() {
|
||||
test_same("x = x.parentNode.parentNode; x = x.parentNode.parentNode;");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_destructuring_lhs_array_ideal_behaviours() {
|
||||
test_same("a => { ([a] = a); return a; }"); // `a` is being reassigned.
|
||||
test_same("a => { ([b] = a); return a; }"); // Evaluating `b` could side-effect `a`.
|
||||
test_same("a => { ([a = foo()] = a); return a; }"); // `foo` may be invoked.
|
||||
test_same("(a, b) => { (a = [a] = b); return b; }"); // Evaluating `a` could side-effect `b`.
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_destructuring_lhs_array_backoff_behaviours() {
|
||||
// TODO(b/123102446): We really like to collapse some of these chained assignments.
|
||||
|
||||
test_same("(a, b) => { ([a] = a = b); return b; }"); // The middle `a` is redundant.
|
||||
test_same("(a, b) => { ([a] = a = b); return a; }"); // The middle `a` is redundant.
|
||||
test(
|
||||
"(a, b) => { (a = [a] = b); return a; }", // The final `a` is redundant.
|
||||
"(a, b) => { return (a = [a] = b); }",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_destructuring_lhs_object_ideal_behaviours() {
|
||||
test_same("a => { ({a} = a); return a; }"); // `a` is being reassigned.
|
||||
test_same("a => { ({b} = a); return a; }"); // Evaluating `b` could side-effect `a`.
|
||||
test_same("a => { ({a = foo()} = a); return a; }"); // `foo` may be invoked.
|
||||
test_same("(a, b) => { (a = {a} = b); return b; }"); // Evaluating `a` could side-effect `b`.
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_destructuring_lhs_object_backoff_behaviours() {
|
||||
// TODO(b/123102446): We really like to collapse some of these chained assignments.
|
||||
|
||||
test_same("(a, b) => { ({a} = a = b); return b; }"); // The middle `a` is redundant.
|
||||
test_same("(a, b) => { ({a} = a = b); return a; }"); // The middle `a` is redundant.
|
||||
test(
|
||||
"(a, b) => { (a = {a} = b); return a; }", // The final `a` is redundant.
|
||||
"(a, b) => { return (a = {a} = b); }",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,6 @@ use oxc_traverse::{traverse_mut_with_ctx, ReusableTraverseCtx, Traverse, Travers
|
|||
|
||||
mod collapse_variable_declarations;
|
||||
mod convert_to_dotted_properties;
|
||||
mod exploit_assigns;
|
||||
mod minimize_exit_points;
|
||||
mod normalize;
|
||||
mod peephole_fold_constants;
|
||||
|
|
@ -13,16 +12,9 @@ mod peephole_minimize_conditions;
|
|||
mod peephole_remove_dead_code;
|
||||
mod peephole_replace_known_methods;
|
||||
mod peephole_substitute_alternate_syntax;
|
||||
mod remove_unused_code;
|
||||
mod statement_fusion;
|
||||
|
||||
pub use normalize::{Normalize, NormalizeOptions};
|
||||
#[expect(unused)]
|
||||
pub use remove_unused_code::RemoveUnusedCode;
|
||||
|
||||
pub trait CompressorPass<'a>: Traverse<'a> {
|
||||
fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>);
|
||||
}
|
||||
|
||||
pub struct PeepholeOptimizations {
|
||||
target: ESTarget,
|
||||
|
|
@ -32,16 +24,16 @@ pub struct PeepholeOptimizations {
|
|||
in_fixed_loop: bool,
|
||||
}
|
||||
|
||||
impl PeepholeOptimizations {
|
||||
impl<'a> PeepholeOptimizations {
|
||||
pub fn new(target: ESTarget, in_fixed_loop: bool) -> Self {
|
||||
Self { target, changed: false, in_fixed_loop }
|
||||
}
|
||||
|
||||
pub fn run_in_loop<'a>(
|
||||
&mut self,
|
||||
program: &mut Program<'a>,
|
||||
ctx: &mut ReusableTraverseCtx<'a>,
|
||||
) {
|
||||
pub fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) {
|
||||
traverse_mut_with_ctx(self, program, ctx);
|
||||
}
|
||||
|
||||
pub fn run_in_loop(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) {
|
||||
let mut i = 0;
|
||||
loop {
|
||||
self.changed = false;
|
||||
|
|
@ -58,16 +50,9 @@ impl PeepholeOptimizations {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> CompressorPass<'a> for PeepholeOptimizations {
|
||||
fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) {
|
||||
traverse_mut_with_ctx(self, program, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Traverse<'a> for PeepholeOptimizations {
|
||||
fn exit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
|
||||
self.statement_fusion_exit_statements(stmts, ctx);
|
||||
self.exploit_assigns(stmts, ctx);
|
||||
self.collapse_variable_declarations(stmts, ctx);
|
||||
self.minimize_conditions_exit_statements(stmts, ctx);
|
||||
self.remove_dead_code_exit_statements(stmts, ctx);
|
||||
|
|
@ -167,14 +152,12 @@ pub struct DeadCodeElimination {
|
|||
inner: PeepholeOptimizations,
|
||||
}
|
||||
|
||||
impl DeadCodeElimination {
|
||||
impl<'a> DeadCodeElimination {
|
||||
pub fn new() -> Self {
|
||||
Self { inner: PeepholeOptimizations::new(ESTarget::ESNext, false) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> CompressorPass<'a> for DeadCodeElimination {
|
||||
fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) {
|
||||
pub fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) {
|
||||
traverse_mut_with_ctx(self, program, ctx);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use oxc_span::GetSpan;
|
|||
use oxc_syntax::scope::ScopeFlags;
|
||||
use oxc_traverse::{traverse_mut_with_ctx, ReusableTraverseCtx, Traverse, TraverseCtx};
|
||||
|
||||
use crate::{ctx::Ctx, CompressOptions, CompressorPass};
|
||||
use crate::{ctx::Ctx, CompressOptions};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct NormalizeOptions {
|
||||
|
|
@ -33,8 +33,8 @@ pub struct Normalize {
|
|||
compress_options: CompressOptions,
|
||||
}
|
||||
|
||||
impl<'a> CompressorPass<'a> for Normalize {
|
||||
fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) {
|
||||
impl<'a> Normalize {
|
||||
pub fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) {
|
||||
traverse_mut_with_ctx(self, program, ctx);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,81 +0,0 @@
|
|||
use rustc_hash::FxHashSet;
|
||||
|
||||
use oxc_allocator::Vec as ArenaVec;
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_syntax::symbol::SymbolId;
|
||||
use oxc_traverse::{traverse_mut_with_ctx, ReusableTraverseCtx, Traverse, TraverseCtx};
|
||||
|
||||
use crate::CompressorPass;
|
||||
|
||||
/// Remove Unused Code
|
||||
///
|
||||
/// <https://github.com/google/closure-compiler/blob/v20240609/src/com/google/javascript/jscomp/RemoveUnusedCode.java>
|
||||
pub struct RemoveUnusedCode {
|
||||
pub(crate) changed: bool,
|
||||
|
||||
symbol_ids_to_remove: FxHashSet<SymbolId>,
|
||||
}
|
||||
|
||||
impl<'a> CompressorPass<'a> for RemoveUnusedCode {
|
||||
fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) {
|
||||
self.changed = false;
|
||||
traverse_mut_with_ctx(self, program, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Traverse<'a> for RemoveUnusedCode {
|
||||
fn enter_program(&mut self, _node: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
let symbols = ctx.symbols();
|
||||
for symbol_id in symbols.symbol_ids() {
|
||||
if symbols.get_resolved_references(symbol_id).count() == 0 {
|
||||
self.symbol_ids_to_remove.insert(symbol_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn exit_statements(
|
||||
&mut self,
|
||||
stmts: &mut ArenaVec<'a, Statement<'a>>,
|
||||
_ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
if self.changed {
|
||||
stmts.retain(|stmt| !matches!(stmt, Statement::EmptyStatement(_)));
|
||||
}
|
||||
}
|
||||
|
||||
fn exit_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
if let Statement::VariableDeclaration(decl) = stmt {
|
||||
decl.declarations.retain(|d| {
|
||||
if let BindingPatternKind::BindingIdentifier(ident) = &d.id.kind {
|
||||
if d.init.is_none() && self.symbol_ids_to_remove.contains(&ident.symbol_id()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
});
|
||||
if decl.declarations.is_empty() {
|
||||
self.changed = true;
|
||||
*stmt = ctx.ast.statement_empty(decl.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RemoveUnusedCode {
|
||||
#[allow(dead_code)]
|
||||
pub fn new() -> Self {
|
||||
Self { changed: false, symbol_ids_to_remove: FxHashSet::default() }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::tester::{test, test_same};
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn simple() {
|
||||
test("var x", "");
|
||||
test_same("var x = 1");
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ use oxc_traverse::ReusableTraverseCtx;
|
|||
|
||||
use crate::{
|
||||
ast_passes::{DeadCodeElimination, Normalize, NormalizeOptions, PeepholeOptimizations},
|
||||
CompressOptions, CompressorPass,
|
||||
CompressOptions,
|
||||
};
|
||||
|
||||
pub struct Compressor<'a> {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use oxc_semantic::{SemanticBuilder, Stats};
|
|||
|
||||
pub use oxc_mangler::MangleOptions;
|
||||
|
||||
pub use crate::{ast_passes::CompressorPass, compressor::Compressor, options::CompressOptions};
|
||||
pub use crate::{compressor::Compressor, options::CompressOptions};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct MinifierOptions {
|
||||
|
|
|
|||
Loading…
Reference in a new issue