From 2331ea85d919e33ee028cd132a41e961d389fd65 Mon Sep 17 00:00:00 2001 From: Boshen <1430279+Boshen@users.noreply.github.com> Date: Wed, 25 Dec 2024 14:52:58 +0000 Subject: [PATCH] feat(minifier): `typeof foo === 'number'` => `typeof foo == 'number'` (#8112) --- .../peephole_substitute_alternate_syntax.rs | 37 +++++++++++++++++++ crates/oxc_syntax/src/operator.rs | 5 +++ tasks/minsize/minsize.snap | 22 +++++------ 3 files changed, 53 insertions(+), 11 deletions(-) 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 75f2166eb2106..692b2b06517ba 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 @@ -86,6 +86,7 @@ impl<'a> Traverse<'a> for PeepholeSubstituteAlternateSyntax { self.try_compress_arrow_expression(arrow_expr, ctx); } Expression::ChainExpression(e) => self.try_compress_chain_call_expression(e, ctx), + Expression::BinaryExpression(e) => self.try_compress_type_of_equal_string(e, ctx), _ => {} } @@ -622,6 +623,30 @@ impl<'a, 'b> PeepholeSubstituteAlternateSyntax { } } + /// `typeof foo === 'number'` -> `typeof foo == 'number'` + fn try_compress_type_of_equal_string( + &mut self, + e: &mut BinaryExpression<'a>, + _ctx: Ctx<'a, 'b>, + ) { + let op = match e.operator { + BinaryOperator::StrictEquality => BinaryOperator::Equality, + BinaryOperator::StrictInequality => BinaryOperator::Inequality, + _ => return, + }; + if Self::commutative_pair( + (&e.left, &e.right), + |a| a.is_string_literal().then_some(()), + |b| matches!(b, Expression::UnaryExpression(e) if e.operator.is_typeof()).then_some(()), + ) + .is_none() + { + return; + } + e.operator = op; + self.changed = true; + } + fn try_compress_chain_call_expression( &mut self, chain_expr: &mut ChainExpression<'a>, @@ -1100,4 +1125,16 @@ mod test { test("foo !== 1 || foo !== void 0 && foo !== null", "foo !== 1 || foo != null"); test_same("foo !== void 0 && bar !== null"); } + + #[test] + fn test_try_compress_type_of_equal_string() { + test("typeof foo === 'number'", "typeof foo == 'number'"); + test("'number' === typeof foo", "'number' == typeof foo"); + test("typeof foo === `number`", "typeof foo == 'number'"); + test("`number` === typeof foo", "'number' == typeof foo"); + test("typeof foo !== 'number'", "typeof foo != 'number'"); + test("'number' !== typeof foo", "'number' != typeof foo"); + test("typeof foo !== `number`", "typeof foo != 'number'"); + test("`number` !== typeof foo", "'number' != typeof foo"); + } } diff --git a/crates/oxc_syntax/src/operator.rs b/crates/oxc_syntax/src/operator.rs index 6c1c40cb64337..76ac35d9e8bfc 100644 --- a/crates/oxc_syntax/src/operator.rs +++ b/crates/oxc_syntax/src/operator.rs @@ -488,6 +488,11 @@ impl UnaryOperator { self == Self::BitwiseNot } + /// Returns `true` if this is the [`void`](UnaryOperator::Typeof) operator. + pub fn is_typeof(self) -> bool { + self == Self::Typeof + } + /// Returns `true` if this is the [`void`](UnaryOperator::Void) operator. pub fn is_void(self) -> bool { self == Self::Void diff --git a/tasks/minsize/minsize.snap b/tasks/minsize/minsize.snap index bc9fede0fae17..517880f8272fb 100644 --- a/tasks/minsize/minsize.snap +++ b/tasks/minsize/minsize.snap @@ -1,27 +1,27 @@ | Oxc | ESBuild | Oxc | ESBuild | Original | minified | minified | gzip | gzip | Fixture ------------------------------------------------------------------------------------- -72.14 kB | 24.00 kB | 23.70 kB | 8.58 kB | 8.54 kB | react.development.js +72.14 kB | 23.96 kB | 23.70 kB | 8.58 kB | 8.54 kB | react.development.js -173.90 kB | 61.55 kB | 59.82 kB | 19.54 kB | 19.33 kB | moment.js +173.90 kB | 61.52 kB | 59.82 kB | 19.54 kB | 19.33 kB | moment.js -287.63 kB | 92.59 kB | 90.07 kB | 32.28 kB | 31.95 kB | jquery.js +287.63 kB | 92.51 kB | 90.07 kB | 32.29 kB | 31.95 kB | jquery.js -342.15 kB | 121.55 kB | 118.14 kB | 44.64 kB | 44.37 kB | vue.js +342.15 kB | 121.48 kB | 118.14 kB | 44.65 kB | 44.37 kB | vue.js 544.10 kB | 73.32 kB | 72.48 kB | 26.13 kB | 26.20 kB | lodash.js -555.77 kB | 276.01 kB | 270.13 kB | 91.13 kB | 90.80 kB | d3.js +555.77 kB | 275.83 kB | 270.13 kB | 91.13 kB | 90.80 kB | d3.js -1.01 MB | 466.62 kB | 458.89 kB | 126.69 kB | 126.71 kB | bundle.min.js +1.01 MB | 466.57 kB | 458.89 kB | 126.69 kB | 126.71 kB | bundle.min.js -1.25 MB | 661.47 kB | 646.76 kB | 163.94 kB | 163.73 kB | three.js +1.25 MB | 661.44 kB | 646.76 kB | 163.95 kB | 163.73 kB | three.js -2.14 MB | 740.43 kB | 724.14 kB | 181.34 kB | 181.07 kB | victory.js +2.14 MB | 740.15 kB | 724.14 kB | 181.34 kB | 181.07 kB | victory.js -3.20 MB | 1.02 MB | 1.01 MB | 332.00 kB | 331.56 kB | echarts.js +3.20 MB | 1.02 MB | 1.01 MB | 332.02 kB | 331.56 kB | echarts.js -6.69 MB | 2.39 MB | 2.31 MB | 495.62 kB | 488.28 kB | antd.js +6.69 MB | 2.39 MB | 2.31 MB | 495.64 kB | 488.28 kB | antd.js -10.95 MB | 3.54 MB | 3.49 MB | 909.73 kB | 915.50 kB | typescript.js +10.95 MB | 3.54 MB | 3.49 MB | 909.75 kB | 915.50 kB | typescript.js