From 8b42e3648e40fe9616545d5677ac66d9ed450d39 Mon Sep 17 00:00:00 2001 From: Boshen Date: Tue, 7 Jan 2025 17:25:43 +0800 Subject: [PATCH] feat(minifier): minimize `x["0"]` -> x[0] --- .../convert_to_dotted_properties.rs | 51 ++++++++++++++++--- .../peephole_substitute_alternate_syntax.rs | 18 +++---- crates/oxc_minifier/src/node_util/mod.rs | 29 +++++++++++ 3 files changed, 80 insertions(+), 18 deletions(-) diff --git a/crates/oxc_minifier/src/ast_passes/convert_to_dotted_properties.rs b/crates/oxc_minifier/src/ast_passes/convert_to_dotted_properties.rs index 2068c7a148334d..91ab7a70a885a7 100644 --- a/crates/oxc_minifier/src/ast_passes/convert_to_dotted_properties.rs +++ b/crates/oxc_minifier/src/ast_passes/convert_to_dotted_properties.rs @@ -46,15 +46,26 @@ impl<'a> ConvertToDottedProperties { ) { if let MemberExpression::ComputedMemberExpression(e) = expr { let Expression::StringLiteral(s) = &e.expression else { return }; - if !is_identifier_name(&s.value) { + if is_identifier_name(&s.value) { + let property = ctx.ast.identifier_name(s.span, s.value.clone()); + let object = ctx.ast.move_expression(&mut e.object); + *expr = MemberExpression::StaticMemberExpression( + ctx.ast.alloc_static_member_expression(e.span, object, property, e.optional), + ); + self.changed = true; return; } - let property = ctx.ast.identifier_name(s.span, s.value.clone()); - let object = ctx.ast.move_expression(&mut e.object); - *expr = MemberExpression::StaticMemberExpression( - ctx.ast.alloc_static_member_expression(e.span, object, property, e.optional), - ); - self.changed = true; + let v = s.value.as_str(); + if !e.optional { + if let Some(n) = Ctx::string_to_equivalent_number_value(v) { + e.expression = ctx.ast.expression_numeric_literal( + s.span, + n as f64, + None, + NumberBase::Decimal, + ); + } + } } } } @@ -110,7 +121,6 @@ mod test { test_same("a[';']"); test_same("a[':']"); test_same("a['.']"); - test_same("a['0']"); test_same("a['p ']"); test_same("a['p' + '']"); test_same("a[p]"); @@ -282,4 +292,29 @@ mod test { ", ); } + + #[test] + fn test_index() { + test("x['y']", "x.y;"); + test_same("x['y z']"); + test("x?.['y']", "x?.y;"); + test_same("x?.['y z']"); + test("x?.['y']()", "x?.y();"); + test_same("x?.['y z']()"); + test_same("x['y' + 'z']"); + test_same("x?.['y' + 'z']"); + test("x['0']", "x[0];"); + test("x['123']", "x[123];"); + test("x['-123']", "x[-123];"); + test_same("x['-0']"); + test_same("x['+0']"); + test_same("x['01']"); + test_same("x['-01']"); + test_same("x['0x1']"); + test_same("x['-0x1']"); + test("x['2147483647']", "x[2147483647]"); + test_same("x['2147483648']"); + test_same("x['-2147483648']"); + test_same("x['-2147483649']"); + } } 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 2fcd1449a7871b..0750d17c70315e 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 @@ -843,16 +843,14 @@ impl<'a, 'b> PeepholeSubstituteAlternateSyntax { *key = PropertyKey::StaticIdentifier( ctx.ast.alloc_identifier_name(s.span, s.value.clone()), ); - } else if (!s.value.starts_with('0') && !s.value.starts_with('+')) || s.value.len() <= 1 { - if let Ok(value) = s.value.parse::() { - self.changed = true; - *key = PropertyKey::NumericLiteral(ctx.ast.alloc_numeric_literal( - s.span, - value as f64, - None, - NumberBase::Decimal, - )); - } + } else if let Some(value) = Ctx::string_to_equivalent_number_value(s.value.as_str()) { + self.changed = true; + *key = PropertyKey::NumericLiteral(ctx.ast.alloc_numeric_literal( + s.span, + value as f64, + None, + NumberBase::Decimal, + )); } } diff --git a/crates/oxc_minifier/src/node_util/mod.rs b/crates/oxc_minifier/src/node_util/mod.rs index d1fea52b3ff517..038ca070048980 100644 --- a/crates/oxc_minifier/src/node_util/mod.rs +++ b/crates/oxc_minifier/src/node_util/mod.rs @@ -82,4 +82,33 @@ impl<'a> Ctx<'a, '_> { } false } + + pub fn string_to_equivalent_number_value(s: &str) -> Option { + if s.is_empty() { + return None; + } + let mut is_negative = false; + let mut int_value = 0i32; + let mut start = 0; + let bytes = s.as_bytes(); + if bytes[0] == b'-' && s.len() > 1 { + is_negative = true; + start += 1; + } + if bytes[start] == b'0' && s.len() > 1 { + return None; + } + for b in &bytes[start..] { + if b.is_ascii_digit() { + int_value = + int_value.checked_mul(10).and_then(|v| v.checked_add((b & 15) as i32))?; + } else { + return None; + } + } + if is_negative { + int_value = -int_value; + } + Some(int_value as f64) + } }