diff --git a/crates/oxc_minifier/src/compressor/mod.rs b/crates/oxc_minifier/src/compressor/mod.rs index d6fc4331b161e..5284299b9f386 100644 --- a/crates/oxc_minifier/src/compressor/mod.rs +++ b/crates/oxc_minifier/src/compressor/mod.rs @@ -203,20 +203,63 @@ impl<'a> Compressor<'a> { /// Transforms `typeof foo == "undefined"` into `foo === void 0` /// Enabled by `compress.typeofs` fn compress_typeof_undefined(&self, expr: &mut BinaryExpression<'a>) { - if expr.operator.is_equality() && self.options.typeofs { - if let Expression::UnaryExpression(unary_expr) = &expr.left { - if unary_expr.operator == UnaryOperator::Typeof { - if let Expression::Identifier(ident) = &unary_expr.argument { - if expr.right.is_specific_string_literal("undefined") { - let left = self.ast.identifier_reference_expression((*ident).clone()); - let right = self.ast.void_0(); - let operator = BinaryOperator::StrictEquality; - *expr = BinaryExpression { span: SPAN, left, operator, right }; + if !self.options.typeofs { + return; + } + match expr.operator { + BinaryOperator::Equality | BinaryOperator::StrictEquality => { + let pair = self.commutative_pair( + (&expr.left, &expr.right), + |a| { + if a.is_specific_string_literal("undefined") { + return Some(()); } - } + None + }, + |b| { + if let Expression::UnaryExpression(op) = b { + if op.operator == UnaryOperator::Typeof { + if let Expression::Identifier(id) = &op.argument { + return Some((*id).clone()); + } + } + } + None + }, + ); + if let Some((_void_exp, id_ref)) = pair { + let span = expr.span; + let left = self.ast.void_0(); + let operator = BinaryOperator::StrictEquality; + let right = self.ast.identifier_reference_expression(id_ref); + let cmp = BinaryExpression { span, left, operator, right }; + *expr = cmp; } } + _ => {} + }; + } + + fn commutative_pair( + &self, + pair: (&A, &A), + check_a: F, + check_b: G, + ) -> Option<(RetF, RetG)> + where + F: Fn(&A) -> Option, + G: Fn(&A) -> Option, + { + if let Some(a) = check_a(pair.0) { + if let Some(b) = check_b(pair.1) { + return Some((a, b)); + } + } else if let Some(a) = check_a(pair.1) { + if let Some(b) = check_b(pair.0) { + return Some((a, b)); + } } + None } /// Removes redundant argument of `ReturnStatement` diff --git a/crates/oxc_minifier/tests/closure/fold_constants.rs b/crates/oxc_minifier/tests/closure/fold_constants.rs index dd5666e33293f..e24de555fd2ec 100644 --- a/crates/oxc_minifier/tests/closure/fold_constants.rs +++ b/crates/oxc_minifier/tests/closure/fold_constants.rs @@ -286,10 +286,10 @@ fn test_string_string_comparison() { test("typeof function() {} < typeof function() {}", "!1;"); test("'a' == 'a'", "!0;"); test("'b' != 'a'", "!0;"); - test_same("'undefined'==typeof a;"); + // test_same("'undefined'==typeof a;"); // compresses to void 0 === a test_same("typeof a!='number';"); - test_same("'undefined'==typeof a;"); - test_same("'undefined'==typeof a;"); + // test_same("'undefined'==typeof a;"); // compresses to void 0 === a + // test_same("'undefined'==typeof a;"); // compresses to void 0 === a test("typeof a == typeof a", "!0;"); test("'a' === 'a'", "!0;"); test("'b' !== 'a'", "!0;"); diff --git a/crates/oxc_minifier/tests/oxc/folding.rs b/crates/oxc_minifier/tests/oxc/folding.rs index 8cb2131b320a8..e85f771f1a05f 100644 --- a/crates/oxc_minifier/tests/oxc/folding.rs +++ b/crates/oxc_minifier/tests/oxc/folding.rs @@ -8,6 +8,12 @@ fn addition_folding() { test("x+''", "x+'';"); } +#[test] +fn typeof_folding() { + test("typeof x === 'undefined'", "void 0===x;"); + test("'undefined' === typeof x", "void 0===x;"); +} + #[test] fn addition_folding_snapshots() { test_snapshot(