diff --git a/crates/oxc_minifier/src/peephole/remove_dead_code.rs b/crates/oxc_minifier/src/peephole/remove_dead_code.rs index 768398d0a51e5..412a65e16703b 100644 --- a/crates/oxc_minifier/src/peephole/remove_dead_code.rs +++ b/crates/oxc_minifier/src/peephole/remove_dead_code.rs @@ -28,6 +28,7 @@ impl<'a> PeepholeOptimizations { Statement::ForStatement(s) => self.try_fold_for(s, state, ctx), Statement::TryStatement(s) => Self::try_fold_try(s, ctx), Statement::LabeledStatement(s) => Self::try_fold_labeled(s, ctx), + Statement::FunctionDeclaration(f) => Self::remove_unused_function_declaration(f, ctx), _ => None, } { *stmt = new_stmt; @@ -565,6 +566,21 @@ impl<'a> PeepholeOptimizations { _ => false, } } + + fn remove_unused_function_declaration( + f: &Function<'a>, + ctx: &mut Ctx<'a, '_>, + ) -> Option> { + if ctx.state.options.unused == CompressOptionsUnused::Keep { + return None; + } + let id = f.id.as_ref()?; + let symbol_id = id.symbol_id.get()?; + if ctx.scoping().symbol_is_unused(symbol_id) { + return Some(ctx.ast.statement_empty(f.span)); + } + None + } } impl<'a> LatePeepholeOptimizations { @@ -587,7 +603,10 @@ impl<'a> LatePeepholeOptimizations { /// #[cfg(test)] mod test { - use crate::tester::{test, test_same}; + use crate::{ + CompressOptions, + tester::{test, test_options, test_same}, + }; #[test] fn test_fold_block() { @@ -794,4 +813,10 @@ mod test { fn remove_constant_value() { test("const foo = false; if (foo) { console.log('foo') }", "const foo = !1;"); } + + #[test] + fn remove_unused_function_declaration() { + let options = CompressOptions::smallest(); + test_options("function foo() {}", "", &options); + } } diff --git a/crates/oxc_minifier/src/peephole/remove_unused_expression.rs b/crates/oxc_minifier/src/peephole/remove_unused_expression.rs index a59bdce78fc67..53ee3326502b3 100644 --- a/crates/oxc_minifier/src/peephole/remove_unused_expression.rs +++ b/crates/oxc_minifier/src/peephole/remove_unused_expression.rs @@ -665,7 +665,7 @@ impl<'a> PeepholeOptimizations { mod test { use crate::{ CompressOptions, TreeShakeOptions, - tester::{test, test_options, test_same, test_same_options}, + tester::{default_options, test, test_options, test_same, test_same_options}, }; #[test] @@ -948,14 +948,14 @@ mod test { fn treeshake_options_annotations_false() { let options = CompressOptions { treeshake: TreeShakeOptions { annotations: false, ..TreeShakeOptions::default() }, - ..CompressOptions::smallest() + ..default_options() }; test_same_options("function test() {} /* @__PURE__ */ test()", &options); test_same_options("function test() {} /* @__PURE__ */ new test()", &options); let options = CompressOptions { treeshake: TreeShakeOptions { annotations: true, ..TreeShakeOptions::default() }, - ..CompressOptions::smallest() + ..default_options() }; test_options("function test() {} /* @__PURE__ */ test()", "function test() {}", &options); test_options( diff --git a/crates/oxc_minifier/tests/peephole/dead_code_elimination.rs b/crates/oxc_minifier/tests/peephole/dead_code_elimination.rs index 31ec00b7eaf64..7412f28db89e0 100644 --- a/crates/oxc_minifier/tests/peephole/dead_code_elimination.rs +++ b/crates/oxc_minifier/tests/peephole/dead_code_elimination.rs @@ -7,6 +7,8 @@ use oxc_minifier::Compressor; use oxc_parser::Parser; use oxc_span::SourceType; +use super::default_options; + #[track_caller] fn run(source_text: &str, source_type: SourceType, options: Option) -> String { let allocator = Allocator::default(); @@ -26,7 +28,7 @@ fn test(source_text: &str, expected: &str) { let source_text = source_text.cow_replace("false", f); let source_type = SourceType::default(); - let result = run(&source_text, source_type, Some(CompressOptions::default())); + let result = run(&source_text, source_type, Some(default_options())); let expected = run(expected, source_type, None); assert_eq!(result, expected, "\nfor source\n{source_text}\nexpect\n{expected}\ngot\n{result}"); } @@ -229,6 +231,7 @@ fn dce_from_terser() { "#, r#"function f() { g(); + x = 10; throw new Error("foo"); var x; } diff --git a/crates/oxc_minifier/tests/peephole/mod.rs b/crates/oxc_minifier/tests/peephole/mod.rs index f58333d4c4fe5..58b89ce740aab 100644 --- a/crates/oxc_minifier/tests/peephole/mod.rs +++ b/crates/oxc_minifier/tests/peephole/mod.rs @@ -7,15 +7,17 @@ mod statement_fusion; use oxc_minifier::{CompressOptions, CompressOptionsUnused}; -#[track_caller] -fn test(source_text: &str, expected: &str) { - let options = CompressOptions { +pub fn default_options() -> CompressOptions { + CompressOptions { drop_debugger: false, - drop_console: false, unused: CompressOptionsUnused::Keep, ..CompressOptions::smallest() - }; - crate::test(source_text, expected, options); + } +} + +#[track_caller] +fn test(source_text: &str, expected: &str) { + crate::test(source_text, expected, default_options()); } #[track_caller] diff --git a/napi/minify/test/terser.test.ts b/napi/minify/test/terser.test.ts index 2c145bbd5576c..b9910cb97f729 100644 --- a/napi/minify/test/terser.test.ts +++ b/napi/minify/test/terser.test.ts @@ -3385,19 +3385,17 @@ test('inline_script_on', () => { }); test('test_unexpected_crash', () => { - const prepend = 'x();'; const code = - 'function x(){var getsInlined=function(){var leakedVariable1=3;var leakedVariable2=1+2*leakedVariable1;console.log(leakedVariable1);console.log(leakedVariable2)};var getsDropped=getsInlined()}'; + 'x(); function x(){var getsInlined=function(){var leakedVariable1=3;var leakedVariable2=1+2*leakedVariable1;console.log(leakedVariable1);console.log(leakedVariable2)};var getsDropped=getsInlined()}'; const expected = ['3', '7']; - run(code, expected, prepend); + run(code, expected); }); test('test_unexpected_crash_2', () => { - const prepend = 'x();'; const code = - 'function x(){var getsInlined=function(){var leakedVariable1=3;var leakedVariable2=1+leakedVariable1[0];console.log(leakedVariable1);console.log(leakedVariable2)};var getsDropped=getsInlined()}'; + 'x(); function x(){var getsInlined=function(){var leakedVariable1=3;var leakedVariable2=1+leakedVariable1[0];console.log(leakedVariable1);console.log(leakedVariable2)};var getsDropped=getsInlined()}'; const expected = ['3', 'NaN']; - run(code, expected, prepend); + run(code, expected); }); test('issue_1724', () => { diff --git a/tasks/minsize/minsize.snap b/tasks/minsize/minsize.snap index 85b94202ee542..f835ace0c522e 100644 --- a/tasks/minsize/minsize.snap +++ b/tasks/minsize/minsize.snap @@ -21,7 +21,7 @@ Original | minified | minified | gzip | gzip | Fixture 3.20 MB | 1.01 MB | 1.01 MB | 324.08 kB | 331.56 kB | echarts.js -6.69 MB | 2.25 MB | 2.31 MB | 463.18 kB | 488.28 kB | antd.js +6.69 MB | 2.24 MB | 2.31 MB | 462.45 kB | 488.28 kB | antd.js 10.95 MB | 3.34 MB | 3.49 MB | 856.90 kB | 915.50 kB | typescript.js