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 e925b0b85b4e72..af1a6cd838f85d 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,3 +1,4 @@ +use oxc_allocator::CloneIn; use oxc_ast::ast::*; use oxc_span::{GetSpan, SPAN}; use oxc_syntax::{ @@ -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,37 @@ impl<'a> PeepholeSubstituteAlternateSyntax { self.changed = true; } } + + fn try_compress_assignment_expression( + &mut self, + expr: &mut AssignmentExpression<'a>, + ctx: &mut TraverseCtx<'a>, + ) -> Option> { + let Some(target) = expr.left.as_simple_assignment_target() else { return None }; + // TODO avoid using `clone_in`, as it is performance-intensive + let target = target.clone_in(ctx.ast.allocator); + if matches!(expr.operator, AssignmentOperator::Subtraction) { + match &expr.right { + Expression::NumericLiteral(num) if num.value == 1.0 => { + 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 == 1.0).then(|| { + ctx.ast.expression_update(SPAN, UpdateOperator::Increment, true, target) + }) + } else { + None + } + } + _ => None, + } + } else { + None + } + } } /// @@ -302,4 +340,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"); + } }