From 4c2059af2968e46aec8725cae060850f626bd5b0 Mon Sep 17 00:00:00 2001 From: camc314 <18101008+camc314@users.noreply.github.com> Date: Wed, 1 Jan 2025 02:31:17 +0000 Subject: [PATCH] feat(minifier): reverse negated conditional exprs (#8205) --- .../peephole_minimize_conditions.rs | 41 ++++++++++++------- .../ast_passes/peephole_remove_dead_code.rs | 19 ++++----- crates/oxc_minifier/tests/ast_passes/mod.rs | 4 +- tasks/minsize/minsize.snap | 22 +++++----- 4 files changed, 49 insertions(+), 37 deletions(-) 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 088de64dddbbf..b49257e7e5838 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_minimize_conditions.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_minimize_conditions.rs @@ -257,26 +257,39 @@ impl<'a> PeepholeMinimizeConditions { } } - /// `a ? a : b` -> `a || b` fn try_minimize_conditional( expr: &mut ConditionalExpression<'a>, ctx: &mut TraverseCtx<'a>, ) -> Option> { - let Expression::Identifier(test_ident) = &expr.test else { return None }; - let Expression::Identifier(consequent_ident) = &expr.consequent else { return None }; - - if test_ident.name != consequent_ident.name { - return None; + // `a ? a : b` -> `a || b` + if let (Expression::Identifier(test_ident), Expression::Identifier(consequent_ident)) = + (&expr.test, &expr.consequent) + { + if test_ident.name == consequent_ident.name { + let ident = ctx.ast.move_expression(&mut expr.test); + + return Some(ctx.ast.expression_logical( + expr.span, + ident, + LogicalOperator::Or, + ctx.ast.move_expression(&mut expr.alternate), + )); + } } - let ident = ctx.ast.move_expression(&mut expr.test); + // `!x ? foo() : bar()` -> `x ? bar() : foo()` + if let Expression::UnaryExpression(test_expr) = &mut expr.test { + if test_expr.operator.is_not() { + let test = ctx.ast.move_expression(&mut test_expr.argument); + let consequent = ctx.ast.move_expression(&mut expr.consequent); + let alternate = ctx.ast.move_expression(&mut expr.alternate); + return Some( + ctx.ast.expression_conditional(expr.span, test, alternate, consequent), + ); + } + } - Some(ctx.ast.expression_logical( - expr.span, - ident, - LogicalOperator::Or, - ctx.ast.move_expression(&mut expr.alternate), - )) + None } } @@ -696,7 +709,7 @@ mod test { fold_same("x() ? x() : y()"); fold_same("x?.() ? x?.() : y()"); - // fold("!x ? foo() : bar()", "x ? bar() : foo()"); + fold("!x ? foo() : bar()", "x ? bar() : foo()"); // fold("while(!(x ? y : z)) foo();", "while(x ? !y : !z) foo();"); // fold("(x ? !y : !z) ? foo() : bar()", "(x ? y : z) ? bar() : foo()"); } 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 18d23ed4b9353..34f35b33d083c 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 @@ -394,17 +394,16 @@ impl<'a, 'b> PeepholeRemoveDeadCode { expr: &mut ConditionalExpression<'a>, ctx: Ctx<'a, 'b>, ) -> Option> { + // Bail `let o = { f() { assert.ok(this !== o); } }; (true ? o.f : false)(); (true ? o.f : false)``;` + let parent = ctx.ancestry.parent(); + if parent.is_tagged_template_expression() + || matches!(parent, Ancestor::CallExpressionCallee(_)) + { + return None; + } + match ctx.get_boolean_value(&expr.test) { - Some(true) => { - // Bail `let o = { f() { assert.ok(this !== o); } }; (true ? o.f : false)(); (true ? o.f : false)``;` - let parent = ctx.ancestry.parent(); - if parent.is_tagged_template_expression() - || matches!(parent, Ancestor::CallExpressionCallee(_)) - { - return None; - } - Some(ctx.ast.move_expression(&mut expr.consequent)) - } + Some(true) => Some(ctx.ast.move_expression(&mut expr.consequent)), Some(false) => Some(ctx.ast.move_expression(&mut expr.alternate)), None => None, } diff --git a/crates/oxc_minifier/tests/ast_passes/mod.rs b/crates/oxc_minifier/tests/ast_passes/mod.rs index 88d302d7e20b6..31758ac4dfd35 100644 --- a/crates/oxc_minifier/tests/ast_passes/mod.rs +++ b/crates/oxc_minifier/tests/ast_passes/mod.rs @@ -51,8 +51,8 @@ fn tagged_template() { test_same("(1, o.f)``"); test_same("(!0 && o.f)()"); test_same("(!0 && o.f)``"); - test_same("(!0 ? o.f : !1)()"); - test_same("(!0 ? o.f : !1)``"); + test("(!0 ? o.f : !1)()", "(0 ? !1: o.f)()"); + test("(!0 ? o.f : !1)``", "(0 ? !1: o.f)``"); test("foo(true && o.f)", "foo(o.f)"); test("foo(true ? o.f : false)", "foo(o.f)"); diff --git a/tasks/minsize/minsize.snap b/tasks/minsize/minsize.snap index 934fbc3765a1a..1a46d112e3dc7 100644 --- a/tasks/minsize/minsize.snap +++ b/tasks/minsize/minsize.snap @@ -3,25 +3,25 @@ Original | minified | minified | gzip | gzip | Fixture ------------------------------------------------------------------------------------- 72.14 kB | 23.71 kB | 23.70 kB | 8.62 kB | 8.54 kB | react.development.js -173.90 kB | 59.91 kB | 59.82 kB | 19.45 kB | 19.33 kB | moment.js +173.90 kB | 59.90 kB | 59.82 kB | 19.43 kB | 19.33 kB | moment.js -287.63 kB | 90.40 kB | 90.07 kB | 32.12 kB | 31.95 kB | jquery.js +287.63 kB | 90.39 kB | 90.07 kB | 32.12 kB | 31.95 kB | jquery.js -342.15 kB | 118.50 kB | 118.14 kB | 44.56 kB | 44.37 kB | vue.js +342.15 kB | 118.48 kB | 118.14 kB | 44.54 kB | 44.37 kB | vue.js -544.10 kB | 71.85 kB | 72.48 kB | 26.19 kB | 26.20 kB | lodash.js +544.10 kB | 71.84 kB | 72.48 kB | 26.19 kB | 26.20 kB | lodash.js -555.77 kB | 273.49 kB | 270.13 kB | 90.96 kB | 90.80 kB | d3.js +555.77 kB | 273.48 kB | 270.13 kB | 90.94 kB | 90.80 kB | d3.js -1.01 MB | 460.80 kB | 458.89 kB | 126.93 kB | 126.71 kB | bundle.min.js +1.01 MB | 460.76 kB | 458.89 kB | 126.88 kB | 126.71 kB | bundle.min.js -1.25 MB | 653.19 kB | 646.76 kB | 163.58 kB | 163.73 kB | three.js +1.25 MB | 653.18 kB | 646.76 kB | 163.57 kB | 163.73 kB | three.js -2.14 MB | 726.75 kB | 724.14 kB | 180.29 kB | 181.07 kB | victory.js +2.14 MB | 726.72 kB | 724.14 kB | 180.25 kB | 181.07 kB | victory.js -3.20 MB | 1.01 MB | 1.01 MB | 332.21 kB | 331.56 kB | echarts.js +3.20 MB | 1.01 MB | 1.01 MB | 332.13 kB | 331.56 kB | echarts.js -6.69 MB | 2.32 MB | 2.31 MB | 493.07 kB | 488.28 kB | antd.js +6.69 MB | 2.32 MB | 2.31 MB | 493.04 kB | 488.28 kB | antd.js -10.95 MB | 3.51 MB | 3.49 MB | 910.37 kB | 915.50 kB | typescript.js +10.95 MB | 3.51 MB | 3.49 MB | 910.11 kB | 915.50 kB | typescript.js