diff --git a/crates/oxc_minifier/src/peephole/minimize_conditions.rs b/crates/oxc_minifier/src/peephole/minimize_conditions.rs index 8d2a2dbabec2f..d921a86605a77 100644 --- a/crates/oxc_minifier/src/peephole/minimize_conditions.rs +++ b/crates/oxc_minifier/src/peephole/minimize_conditions.rs @@ -1,5 +1,8 @@ use oxc_ast::ast::*; -use oxc_ecmascript::{ToInt32, constant_evaluation::DetermineValueType}; +use oxc_ecmascript::{ + ToInt32, + constant_evaluation::{ConstantEvaluation, ConstantValue, DetermineValueType}, +}; use oxc_span::GetSpan; use oxc_syntax::es_target::ESTarget; @@ -22,7 +25,12 @@ impl<'a> PeepholeOptimizations { let mut local_change = false; if let Some(folded_expr) = match expr { Expression::UnaryExpression(e) => self.try_minimize_not(e, ctx), - Expression::BinaryExpression(e) => Self::try_minimize_binary(e, ctx), + Expression::BinaryExpression(e) => { + if Self::try_compress_is_loose_boolean(e, ctx) { + local_change = true; + } + Self::try_minimize_binary(e, ctx) + } Expression::LogicalExpression(e) => self.minimize_logical_expression(e, ctx), Expression::ConditionalExpression(logical_expr) => { if self.try_fold_expr_in_boolean_context(&mut logical_expr.test, ctx) { @@ -154,6 +162,39 @@ impl<'a> PeepholeOptimizations { } } + /// Compress `foo == true` into `foo == 1`. + /// + /// - `foo == true` => `foo == 1` + /// - `foo != false` => `foo != 0` + /// + /// In `IsLooselyEqual`, `true` and `false` are converted to `1` and `0` first. + /// + fn try_compress_is_loose_boolean(e: &mut BinaryExpression<'a>, ctx: Ctx<'a, '_>) -> bool { + if !matches!(e.operator, BinaryOperator::Equality | BinaryOperator::Inequality) { + return false; + } + + if let Some(ConstantValue::Boolean(left_bool)) = e.left.evaluate_value(&ctx) { + e.left = ctx.ast.expression_numeric_literal( + e.left.span(), + if left_bool { 1.0 } else { 0.0 }, + None, + NumberBase::Decimal, + ); + return true; + } + if let Some(ConstantValue::Boolean(right_bool)) = e.right.evaluate_value(&ctx) { + e.right = ctx.ast.expression_numeric_literal( + e.right.span(), + if right_bool { 1.0 } else { 0.0 }, + None, + NumberBase::Decimal, + ); + return true; + } + false + } + /// Returns the identifier or the assignment target's identifier of the given expression. pub fn extract_id_or_assign_to_id<'b>( expr: &'b Expression<'a>, @@ -1438,4 +1479,16 @@ mod test { run(code, None) ); } + + #[test] + fn test_compress_is_loose_boolean() { + test("v = x == true", "v = x == 1"); + test("v = x != true", "v = x != 1"); + test("v = x == false", "v = x == 0"); + test("v = x != false", "v = x != 0"); + test("v = x == !0", "v = x == 1"); + test("v = x != !0", "v = x != 1"); + test("v = x == !1", "v = x == 0"); + test("v = x != !1", "v = x != 0"); + } } diff --git a/crates/oxc_minifier/src/peephole/substitute_alternate_syntax.rs b/crates/oxc_minifier/src/peephole/substitute_alternate_syntax.rs index 70a9694518966..e8853432edd54 100644 --- a/crates/oxc_minifier/src/peephole/substitute_alternate_syntax.rs +++ b/crates/oxc_minifier/src/peephole/substitute_alternate_syntax.rs @@ -1324,9 +1324,9 @@ mod test { #[test] fn test_fold_true_false_comparison() { - test("v = x == true", "v = x == !0"); - test("v = x == false", "v = x == !1"); - test("v = x != true", "v = x != !0"); + test("v = x == true", "v = x == 1"); + test("v = x == false", "v = x == 0"); + test("v = x != true", "v = x != 1"); test("v = x < true", "v = x < !0"); test("v = x <= true", "v = x <= !0"); test("v = x > true", "v = x > !0"); diff --git a/tasks/minsize/minsize.snap b/tasks/minsize/minsize.snap index 1b8426a857cd6..7b3c45dd2b828 100644 --- a/tasks/minsize/minsize.snap +++ b/tasks/minsize/minsize.snap @@ -13,7 +13,7 @@ Original | minified | minified | gzip | gzip | Fixture 555.77 kB | 270.84 kB | 270.13 kB | 88.26 kB | 90.80 kB | d3.js -1.01 MB | 440.17 kB | 458.89 kB | 122.38 kB | 126.71 kB | bundle.min.js +1.01 MB | 440.17 kB | 458.89 kB | 122.37 kB | 126.71 kB | bundle.min.js 1.25 MB | 647.00 kB | 646.76 kB | 160.28 kB | 163.73 kB | three.js