diff --git a/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs b/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs index 71cbb291ddb12..354a92126195c 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs @@ -1,3 +1,14 @@ +use std::cmp::Ordering; + +use num_bigint::BigInt; +use oxc_ast::ast::*; +use oxc_span::{GetSpan, Span, SPAN}; +use oxc_syntax::{ + number::{NumberBase, ToJsInt32}, + operator::{BinaryOperator, LogicalOperator, UnaryOperator}, +}; +use oxc_traverse::{Ancestor, Traverse, TraverseCtx}; + use crate::{ node_util::{ is_exact_int64, IsLiteralValue, MayHaveSideEffects, NodeUtil, NumberValue, ValueType, @@ -6,16 +17,6 @@ use crate::{ ty::Ty, CompressorPass, }; -use num_bigint::BigInt; -use oxc_ast::ast::*; -use oxc_span::{GetSpan, Span, SPAN}; -use oxc_syntax::number::ToJsInt32; -use oxc_syntax::{ - number::NumberBase, - operator::{BinaryOperator, LogicalOperator, UnaryOperator}, -}; -use oxc_traverse::{Ancestor, Traverse, TraverseCtx}; -use std::cmp::Ordering; /// Constant Folding /// diff --git a/crates/oxc_minifier/src/ast_passes/peephole_substitute_alternate_syntax.rs b/crates/oxc_minifier/src/ast_passes/peephole_substitute_alternate_syntax.rs index e925b0b85b4e7..31de4e35240b8 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_substitute_alternate_syntax.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_substitute_alternate_syntax.rs @@ -1,5 +1,6 @@ use oxc_ast::ast::*; use oxc_span::{GetSpan, SPAN}; +use oxc_syntax::number::ToJsInt32; use oxc_syntax::{ number::NumberBase, operator::{BinaryOperator, UnaryOperator}, @@ -77,6 +78,12 @@ impl<'a> Traverse<'a> for PeepholeSubstituteAlternateSyntax { } fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { + if let Expression::AssignmentExpression(assignment_expr) = expr { + if let Some(new_expr) = self.try_compress_assignment_expression(assignment_expr, ctx) { + *expr = new_expr; + self.changed = true; + } + } if !self.compress_undefined(expr, ctx) { self.compress_boolean(expr, ctx); } @@ -260,6 +267,45 @@ impl<'a> PeepholeSubstituteAlternateSyntax { self.changed = true; } } + + fn try_compress_assignment_expression( + &mut self, + expr: &mut AssignmentExpression<'a>, + ctx: &mut TraverseCtx<'a>, + ) -> Option> { + let target = expr.left.as_simple_assignment_target_mut()?; + if matches!(expr.operator, AssignmentOperator::Subtraction) { + match &expr.right { + Expression::NumericLiteral(num) if num.value.to_js_int_32() == 1 => { + // The `_` will not be placed to the target code. + let target = std::mem::replace( + target, + ctx.ast.simple_assignment_target_identifier_reference(SPAN, "_"), + ); + Some(ctx.ast.expression_update(SPAN, UpdateOperator::Decrement, true, target)) + } + Expression::UnaryExpression(un) + if matches!(un.operator, UnaryOperator::UnaryNegation) => + { + if let Expression::NumericLiteral(num) = &un.argument { + (num.value.to_js_int_32() == 1).then(|| { + // The `_` will not be placed to the target code. + let target = std::mem::replace( + target, + ctx.ast.simple_assignment_target_identifier_reference(SPAN, "_"), + ); + ctx.ast.expression_update(SPAN, UpdateOperator::Increment, true, target) + }) + } else { + None + } + } + _ => None, + } + } else { + None + } + } } /// @@ -302,4 +348,25 @@ mod test { // shadowd test_same("(function(undefined) { let x = typeof undefined; })()"); } + + #[test] + #[ignore] + fn fold_true_false_comparison() { + test("x == true", "x == 1"); + test("x == false", "x == 0"); + test("x != true", "x != 1"); + test("x < true", "x < 1"); + test("x <= true", "x <= 1"); + test("x > true", "x > 1"); + test("x >= true", "x >= 1"); + } + + #[test] + fn test_fold_subtraction_assignment() { + test("x -= 1", "--x"); + test("x -= -1", "++x"); + test_same("x -= 2"); + test_same("x += 1"); // The string concatenation may be triggered, so we don't fold this. + test_same("x += -1"); + } } diff --git a/tasks/minsize/minsize.snap b/tasks/minsize/minsize.snap index 1105bbf87d170..16599d056f3d6 100644 --- a/tasks/minsize/minsize.snap +++ b/tasks/minsize/minsize.snap @@ -2,7 +2,7 @@ Original | Minified | esbuild | Gzip | esbuild 72.14 kB | 24.47 kB | 23.70 kB | 8.65 kB | 8.54 kB | react.development.js -173.90 kB | 61.70 kB | 59.82 kB | 19.54 kB | 19.33 kB | moment.js +173.90 kB | 61.69 kB | 59.82 kB | 19.54 kB | 19.33 kB | moment.js 287.63 kB | 92.83 kB | 90.07 kB | 32.29 kB | 31.95 kB | jquery.js @@ -14,13 +14,13 @@ Original | Minified | esbuild | Gzip | esbuild 1.01 MB | 470.11 kB | 458.89 kB | 126.97 kB | 126.71 kB | bundle.min.js -1.25 MB | 671 kB | 646.76 kB | 164.72 kB | 163.73 kB | three.js +1.25 MB | 671.00 kB | 646.76 kB | 164.72 kB | 163.73 kB | three.js 2.14 MB | 756.69 kB | 724.14 kB | 182.87 kB | 181.07 kB | victory.js -3.20 MB | 1.05 MB | 1.01 MB | 334.11 kB | 331.56 kB | echarts.js +3.20 MB | 1.05 MB | 1.01 MB | 334.10 kB | 331.56 kB | echarts.js -6.69 MB | 2.44 MB | 2.31 MB | 498.90 kB | 488.28 kB | antd.js +6.69 MB | 2.44 MB | 2.31 MB | 498.93 kB | 488.28 kB | antd.js -10.95 MB | 3.59 MB | 3.49 MB | 913.94 kB | 915.50 kB | typescript.js +10.95 MB | 3.59 MB | 3.49 MB | 913.96 kB | 915.50 kB | typescript.js