From 7f19211736b727d1591f7165f9503bfec53669f1 Mon Sep 17 00:00:00 2001 From: camc314 <18101008+camc314@users.noreply.github.com> Date: Mon, 6 Jan 2025 02:21:24 +0000 Subject: [PATCH] feat(minifier): minimize unary expression statements (#8256) `delete` cannot be minified as it may have side effect. --- .../ast_passes/peephole_remove_dead_code.rs | 35 +++++++++++++++++++ crates/oxc_syntax/src/operator.rs | 5 +++ tasks/minsize/minsize.snap | 4 +-- 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/crates/oxc_minifier/src/ast_passes/peephole_remove_dead_code.rs b/crates/oxc_minifier/src/ast_passes/peephole_remove_dead_code.rs index 34f35b33d083c..6e65a5fa6130f 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_remove_dead_code.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_remove_dead_code.rs @@ -310,6 +310,20 @@ impl<'a, 'b> PeepholeRemoveDeadCode { Expression::FunctionExpression(function_expr) if function_expr.id.is_none() => { Some(ctx.ast.statement_empty(SPAN)) } + // `typeof x` -> `` + Expression::UnaryExpression(unary_expr) + if unary_expr.operator.is_typeof() + && unary_expr.argument.is_identifier_reference() => + { + Some(ctx.ast.statement_empty(SPAN)) + } + // `typeof x.y` -> `x`, `!x` -> `x`, `void x` -> `x`... + Expression::UnaryExpression(unary_expr) if !unary_expr.operator.is_delete() => { + Some(ctx.ast.statement_expression( + unary_expr.span, + ctx.ast.move_expression(&mut unary_expr.argument), + )) + } _ => None, }) } @@ -548,4 +562,25 @@ mod test { fold("([...a, b, ...c])", "([...a, ...c])"); fold_same("([...b, ...c])"); // It would also be fine if the spreads were split apart. } + + #[test] + fn test_fold_unary_expression_statement() { + fold("typeof x", ""); + fold("typeof x?.y", "x?.y"); + fold("typeof x.y", "x.y"); + fold("typeof x.y.z()", "x.y.z()"); + fold("void x", "x"); + fold("void x?.y", "x?.y"); + fold("void x.y", "x.y"); + fold("void x.y.z()", "x.y.z()"); + fold("!x", "x"); + fold("!x?.y", "x?.y"); + fold("!x.y", "x.y"); + fold("!x.y.z()", "x.y.z()"); + fold("-x.y.z()", "x.y.z()"); + + fold_same("delete x"); + fold_same("delete x.y"); + fold_same("delete x.y.z()"); + } } diff --git a/crates/oxc_syntax/src/operator.rs b/crates/oxc_syntax/src/operator.rs index 76ac35d9e8bfc..0d295d9204bf0 100644 --- a/crates/oxc_syntax/src/operator.rs +++ b/crates/oxc_syntax/src/operator.rs @@ -498,6 +498,11 @@ impl UnaryOperator { self == Self::Void } + /// Returns `true` if this is the [`delete`](UnaryOperator::Delete) operator. + pub fn is_delete(self) -> bool { + self == Self::Delete + } + /// Returns `true` if this operator is a keyword instead of punctuation. pub fn is_keyword(self) -> bool { matches!(self, Self::Typeof | Self::Void | Self::Delete) diff --git a/tasks/minsize/minsize.snap b/tasks/minsize/minsize.snap index 36875636bc121..cb8dbf88029b2 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 | 331.90 kB | 331.56 kB | echarts.js -6.69 MB | 2.32 MB | 2.31 MB | 492.86 kB | 488.28 kB | antd.js +6.69 MB | 2.32 MB | 2.31 MB | 492.87 kB | 488.28 kB | antd.js -10.95 MB | 3.50 MB | 3.49 MB | 909.32 kB | 915.50 kB | typescript.js +10.95 MB | 3.50 MB | 3.49 MB | 909.30 kB | 915.50 kB | typescript.js