diff --git a/crates/oxc_minifier/src/ast_passes/peephole_minimize_conditions.rs b/crates/oxc_minifier/src/ast_passes/peephole_minimize_conditions.rs index 32c2fe76afebf..ae644636c32db 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_minimize_conditions.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_minimize_conditions.rs @@ -515,7 +515,7 @@ impl<'a> PeepholeMinimizeConditions { } } - // `a ? c : b && c` -> `(a || b) && c`` + // `a ? c : b && c` -> `(a || b) && c` if let Expression::LogicalExpression(logical_expr) = &mut expr.alternate { if logical_expr.operator == LogicalOperator::And && logical_expr.right.content_eq(&expr.consequent) 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 700102da0e3f0..9a08e7b54ff77 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 @@ -149,7 +149,8 @@ impl<'a> Traverse<'a> for PeepholeSubstituteAlternateSyntax { Self::try_compress_assignment_to_update_expression(e, ctx) } Expression::LogicalExpression(e) => Self::try_compress_is_null_or_undefined(e, ctx) - .or_else(|| self.try_compress_logical_expression_to_assignment_expression(e, ctx)), + .or_else(|| self.try_compress_logical_expression_to_assignment_expression(e, ctx)) + .or_else(|| Self::try_rotate_logical_expression(e, ctx)), Expression::TemplateLiteral(t) => Self::try_fold_template_literal(t, ctx), Expression::BinaryExpression(e) => Self::try_fold_loose_equals_undefined(e, ctx) .or_else(|| Self::try_compress_typeof_undefined(e, ctx)), @@ -503,6 +504,38 @@ impl<'a, 'b> PeepholeSubstituteAlternateSyntax { Some(ctx.ast.move_expression(&mut expr.right)) } + /// `a || (b || c);` -> `(a || b) || c;` + fn try_rotate_logical_expression( + expr: &mut LogicalExpression<'a>, + ctx: Ctx<'a, 'b>, + ) -> Option> { + let Expression::LogicalExpression(right) = &mut expr.right else { return None }; + if right.operator != expr.operator { + return None; + } + + let mut new_left = ctx.ast.expression_logical( + expr.span, + ctx.ast.move_expression(&mut expr.left), + expr.operator, + ctx.ast.move_expression(&mut right.left), + ); + + { + let Expression::LogicalExpression(new_left2) = &mut new_left else { unreachable!() }; + if let Some(expr) = Self::try_rotate_logical_expression(new_left2, ctx) { + new_left = expr; + } + } + + Some(ctx.ast.expression_logical( + expr.span, + new_left, + expr.operator, + ctx.ast.move_expression(&mut right.right), + )) + } + /// Returns `true` if the assignment target and expression have no side effect for *evaluation* and points to the same reference. /// /// Evaluation here means `Evaluation` in the spec. diff --git a/crates/oxc_minifier/tests/ast_passes/mod.rs b/crates/oxc_minifier/tests/ast_passes/mod.rs index 7deb1896faa30..7ab884fe41d8b 100644 --- a/crates/oxc_minifier/tests/ast_passes/mod.rs +++ b/crates/oxc_minifier/tests/ast_passes/mod.rs @@ -50,6 +50,17 @@ fn integration() { return console.log(e || JSON.stringify(os)); });"#, ); + + test_idempotent( + "if (!(foo instanceof Var) || open) { + arg0 = null; + } else if (que || !(foo && bar)) { + if (baz()) arg0 = null; + }", + "(!(foo instanceof Var) || open || (que || !(foo && bar)) && baz()) && (arg0 = null);", + ); + + test_same("a && (b && (c && (d && (e && (f && (g && (h && i && j && k && l && m && n && o && p && q && r && s && t && u && v && w && x && y && z)))))))"); } #[test] // https://github.com/oxc-project/oxc/issues/4341