From ef721fcebc62914324b4935dfbbd89a92dddaac5 Mon Sep 17 00:00:00 2001 From: Ethan Goh <7086cmd@gmail.com> Date: Sun, 10 Nov 2024 16:55:29 +0800 Subject: [PATCH] feat(minifier): minify alternated one child if block --- .../peephole_minimize_conditions.rs | 92 +++++++++++-------- tasks/minsize/minsize.snap | 24 ++--- 2 files changed, 68 insertions(+), 48 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 a25ceaa422878e..61ae1412294c12 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_minimize_conditions.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_minimize_conditions.rs @@ -91,36 +91,57 @@ impl<'a> PeepholeMinimizeConditions { ) -> Option> { if let Statement::ExpressionStatement(expr) = &mut if_stmt.consequent { // The rest of things for known boolean are tasks for dce instead of here. - (if_stmt.alternate.is_none() && if_stmt.test.to_boolean().is_none()).then(|| { - // Make if (x) y; => x && y; - let (operator, mut test) = match &mut if_stmt.test { - Expression::UnaryExpression(unary) if unary.operator.is_not() => { - let arg = ctx.ast.move_expression(&mut unary.argument); - (LogicalOperator::Or, arg) + if_stmt + .test + .to_boolean() + .is_none() + .then(|| { + if !matches!(if_stmt.alternate, None | Some(Statement::ExpressionStatement(_))) { + return None } - _ => (LogicalOperator::And, ctx.ast.move_expression(&mut if_stmt.test)), - }; - match &mut test { - Expression::BinaryExpression(bin) if bin.operator.is_equality() => { - if !bin.left.is_literal() && bin.right.is_literal() { - test = ctx.ast.expression_binary( - SPAN, - ctx.ast.move_expression(&mut bin.right), - bin.operator, - ctx.ast.move_expression(&mut bin.left), - ); + // Make if (x) y; => x && y; + let (reverse, mut test) = match &mut if_stmt.test { + Expression::UnaryExpression(unary) if unary.operator.is_not() => { + let arg = ctx.ast.move_expression(&mut unary.argument); + (true, arg) } + _ => (false, ctx.ast.move_expression(&mut if_stmt.test)), + }; + match &mut test { + Expression::BinaryExpression(bin) if bin.operator.is_equality() => { + if !bin.left.is_literal() && bin.right.is_literal() { + test = ctx.ast.expression_binary( + SPAN, + ctx.ast.move_expression(&mut bin.right), + bin.operator, + ctx.ast.move_expression(&mut bin.left), + ); + } + } + _ => {} + } + if let Some(Statement::ExpressionStatement(alt)) = &mut if_stmt.alternate { + let left = ctx.ast.move_expression(&mut expr.expression); + let right = ctx.ast.move_expression(&mut alt.expression); + let cond = if reverse { + ctx.ast.expression_conditional(SPAN, test, right, left) + } else { + ctx.ast.expression_conditional(SPAN, test, left, right) + }; + Some(ctx.ast.statement_expression(SPAN, cond)) + } else if if_stmt.alternate.is_none() { + let new_expr = ctx.ast.expression_logical( + SPAN, + test, + if reverse { LogicalOperator::Or } else { LogicalOperator::And }, + ctx.ast.move_expression(&mut expr.expression), + ); + Some(ctx.ast.statement_expression(SPAN, new_expr)) + } else { + None } - _ => {} - } - let new_expr = ctx.ast.expression_logical( - SPAN, - test, - operator, - ctx.ast.move_expression(&mut expr.expression), - ); - ctx.ast.statement_expression(SPAN, new_expr) - }) + }) + .unwrap_or(None) } else { None } @@ -168,7 +189,7 @@ mod test { // Try it out with functions fold("function f(){if(x){foo()}}", "function f(){x&&foo()}"); - // fold("function f(){if(x){foo()}else{bar()}}", "function f(){x?foo():bar()}"); + fold("function f(){if(x){foo()}else{bar()}}", "function f(){x?foo():bar()}"); // Try it out with properties and methods fold("function f(){if(x){a.b=1}}", "function f(){x&&(a.b=1)}"); @@ -191,12 +212,12 @@ mod test { // Play with nested IFs fold("function f(){if(x){if(y)foo()}}", "function f(){x && (y && foo())}"); - // fold("function f(){if(x){if(y)foo();else bar()}}", "function f(){x&&(y?foo():bar())}"); - // fold("function f(){if(x){if(y)foo()}else bar()}", "function f(){x?y&&foo():bar()}"); - // fold( - // "function f(){if(x){if(y)foo();else bar()}else{baz()}}", - // "function f(){x?y?foo():bar():baz()}", - // ); + fold("function f(){if(x){if(y)foo();else bar()}}", "function f(){x&&(y?foo():bar())}"); + fold("function f(){if(x){if(y)foo()}else bar()}", "function f(){x?y&&foo():bar()}"); + fold( + "function f(){if(x){if(y)foo();else bar()}else{baz()}}", + "function f(){x?y?foo():bar():baz()}", + ); // fold("if(e1){while(e2){if(e3){foo()}}}else{bar()}", "if(e1)while(e2)e3&&foo();else bar()"); @@ -371,11 +392,10 @@ mod test { } #[test] - #[ignore] fn test_not_cond() { fold("function f(){if(!x)foo()}", "function f(){x||foo()}"); fold("function f(){if(!x)b=1}", "function f(){x||(b=1)}"); - fold("if(!x)z=1;else if(y)z=2", "if(x){y&&(z=2);}else{z=1;}"); + fold("if(!x)z=1;else if(y)z=2", "x ? y&&(z=2) : z=1;"); fold("if(x)y&&(z=2);else z=1;", "x ? y&&(z=2) : z=1"); fold("function f(){if(!(x=1))a.b=1}", "function f(){(x=1)||(a.b=1)}"); } diff --git a/tasks/minsize/minsize.snap b/tasks/minsize/minsize.snap index b900c987f321cd..eeb06bacc03332 100644 --- a/tasks/minsize/minsize.snap +++ b/tasks/minsize/minsize.snap @@ -1,26 +1,26 @@ Original | Minified | esbuild | Gzip | esbuild -72.14 kB | 24.08 kB | 23.70 kB | 8.68 kB | 8.54 kB | react.development.js +72.14 kB | 24.00 kB | 23.70 kB | 8.67 kB | 8.54 kB | react.development.js -173.90 kB | 61.56 kB | 59.82 kB | 19.66 kB | 19.33 kB | moment.js +173.90 kB | 61.17 kB | 59.82 kB | 19.63 kB | 19.33 kB | moment.js -287.63 kB | 92.41 kB | 90.07 kB | 32.50 kB | 31.95 kB | jquery.js +287.63 kB | 91.79 kB | 90.07 kB | 32.41 kB | 31.95 kB | jquery.js -342.15 kB | 121.37 kB | 118.14 kB | 44.90 kB | 44.37 kB | vue.js +342.15 kB | 120.32 kB | 118.14 kB | 44.78 kB | 44.37 kB | vue.js -544.10 kB | 73.41 kB | 72.48 kB | 26.20 kB | 26.20 kB | lodash.js +544.10 kB | 73.04 kB | 72.48 kB | 26.17 kB | 26.20 kB | lodash.js -555.77 kB | 276.33 kB | 270.13 kB | 91.52 kB | 90.80 kB | d3.js +555.77 kB | 275.31 kB | 270.13 kB | 91.37 kB | 90.80 kB | d3.js -1.01 MB | 466.82 kB | 458.89 kB | 127.24 kB | 126.71 kB | bundle.min.js +1.01 MB | 465.00 kB | 458.89 kB | 127.13 kB | 126.71 kB | bundle.min.js -1.25 MB | 662.29 kB | 646.76 kB | 164.78 kB | 163.73 kB | three.js +1.25 MB | 660.57 kB | 646.76 kB | 164.64 kB | 163.73 kB | three.js -2.14 MB | 741.26 kB | 724.14 kB | 181.86 kB | 181.07 kB | victory.js +2.14 MB | 740.02 kB | 724.14 kB | 181.65 kB | 181.07 kB | victory.js -3.20 MB | 1.02 MB | 1.01 MB | 333.53 kB | 331.56 kB | echarts.js +3.20 MB | 1.02 MB | 1.01 MB | 333.18 kB | 331.56 kB | echarts.js -6.69 MB | 2.39 MB | 2.31 MB | 497.30 kB | 488.28 kB | antd.js +6.69 MB | 2.39 MB | 2.31 MB | 496.85 kB | 488.28 kB | antd.js -10.95 MB | 3.55 MB | 3.49 MB | 914.18 kB | 915.50 kB | typescript.js +10.95 MB | 3.55 MB | 3.49 MB | 913.86 kB | 915.50 kB | typescript.js