diff --git a/crates/oxc_ast/src/ast/js.rs b/crates/oxc_ast/src/ast/js.rs index 8386c7f5b7c4e..48a1330bef39e 100644 --- a/crates/oxc_ast/src/ast/js.rs +++ b/crates/oxc_ast/src/ast/js.rs @@ -894,6 +894,8 @@ pub struct AssignmentTargetPropertyProperty<'a> { pub span: Span, pub name: PropertyKey<'a>, pub binding: AssignmentTargetMaybeDefault<'a>, + /// Property was declared with a computed key + pub computed: bool, } /// `a++, b++` in `let a = 1, b = 2; let result = (a++, b++);` diff --git a/crates/oxc_ast/src/generated/assert_layouts.rs b/crates/oxc_ast/src/generated/assert_layouts.rs index d57fa98e7f9e8..8e5bf9b0fa026 100644 --- a/crates/oxc_ast/src/generated/assert_layouts.rs +++ b/crates/oxc_ast/src/generated/assert_layouts.rs @@ -299,11 +299,12 @@ const _: () = { assert!(offset_of!(AssignmentTargetPropertyIdentifier, binding) == 8usize); assert!(offset_of!(AssignmentTargetPropertyIdentifier, init) == 40usize); - assert!(size_of::() == 40usize); + assert!(size_of::() == 48usize); assert!(align_of::() == 8usize); assert!(offset_of!(AssignmentTargetPropertyProperty, span) == 0usize); assert!(offset_of!(AssignmentTargetPropertyProperty, name) == 8usize); assert!(offset_of!(AssignmentTargetPropertyProperty, binding) == 24usize); + assert!(offset_of!(AssignmentTargetPropertyProperty, computed) == 40usize); assert!(size_of::() == 40usize); assert!(align_of::() == 8usize); @@ -1861,11 +1862,12 @@ const _: () = { assert!(offset_of!(AssignmentTargetPropertyIdentifier, binding) == 8usize); assert!(offset_of!(AssignmentTargetPropertyIdentifier, init) == 28usize); - assert!(size_of::() == 24usize); + assert!(size_of::() == 28usize); assert!(align_of::() == 4usize); assert!(offset_of!(AssignmentTargetPropertyProperty, span) == 0usize); assert!(offset_of!(AssignmentTargetPropertyProperty, name) == 8usize); assert!(offset_of!(AssignmentTargetPropertyProperty, binding) == 16usize); + assert!(offset_of!(AssignmentTargetPropertyProperty, computed) == 24usize); assert!(size_of::() == 24usize); assert!(align_of::() == 4usize); diff --git a/crates/oxc_ast/src/generated/ast_builder.rs b/crates/oxc_ast/src/generated/ast_builder.rs index 9737be84a8868..5f16d9ccfb02b 100644 --- a/crates/oxc_ast/src/generated/ast_builder.rs +++ b/crates/oxc_ast/src/generated/ast_builder.rs @@ -2871,15 +2871,17 @@ impl<'a> AstBuilder<'a> { /// - span: The [`Span`] covering this node /// - name /// - binding + /// - computed: Property was declared with a computed key #[inline] pub fn assignment_target_property_assignment_target_property_property( self, span: Span, name: PropertyKey<'a>, binding: AssignmentTargetMaybeDefault<'a>, + computed: bool, ) -> AssignmentTargetProperty<'a> { AssignmentTargetProperty::AssignmentTargetPropertyProperty( - self.alloc(self.assignment_target_property_property(span, name, binding)), + self.alloc(self.assignment_target_property_property(span, name, binding, computed)), ) } @@ -2927,14 +2929,16 @@ impl<'a> AstBuilder<'a> { /// - span: The [`Span`] covering this node /// - name /// - binding + /// - computed: Property was declared with a computed key #[inline] pub fn assignment_target_property_property( self, span: Span, name: PropertyKey<'a>, binding: AssignmentTargetMaybeDefault<'a>, + computed: bool, ) -> AssignmentTargetPropertyProperty<'a> { - AssignmentTargetPropertyProperty { span, name, binding } + AssignmentTargetPropertyProperty { span, name, binding, computed } } /// Build an [`AssignmentTargetPropertyProperty`], and store it in the memory arena. @@ -2945,14 +2949,19 @@ impl<'a> AstBuilder<'a> { /// - span: The [`Span`] covering this node /// - name /// - binding + /// - computed: Property was declared with a computed key #[inline] pub fn alloc_assignment_target_property_property( self, span: Span, name: PropertyKey<'a>, binding: AssignmentTargetMaybeDefault<'a>, + computed: bool, ) -> Box<'a, AssignmentTargetPropertyProperty<'a>> { - Box::new_in(self.assignment_target_property_property(span, name, binding), self.allocator) + Box::new_in( + self.assignment_target_property_property(span, name, binding, computed), + self.allocator, + ) } /// Build a [`SequenceExpression`]. diff --git a/crates/oxc_ast/src/generated/derive_clone_in.rs b/crates/oxc_ast/src/generated/derive_clone_in.rs index 0396f4f69d158..1803eba602303 100644 --- a/crates/oxc_ast/src/generated/derive_clone_in.rs +++ b/crates/oxc_ast/src/generated/derive_clone_in.rs @@ -1161,6 +1161,7 @@ impl<'new_alloc> CloneIn<'new_alloc> for AssignmentTargetPropertyProperty<'_> { span: CloneIn::clone_in(&self.span, allocator), name: CloneIn::clone_in(&self.name, allocator), binding: CloneIn::clone_in(&self.binding, allocator), + computed: CloneIn::clone_in(&self.computed, allocator), } } } diff --git a/crates/oxc_ast/src/generated/derive_content_eq.rs b/crates/oxc_ast/src/generated/derive_content_eq.rs index 89dddee2ff2ac..6f0bbbb01ea1b 100644 --- a/crates/oxc_ast/src/generated/derive_content_eq.rs +++ b/crates/oxc_ast/src/generated/derive_content_eq.rs @@ -1277,6 +1277,7 @@ impl ContentEq for AssignmentTargetPropertyProperty<'_> { fn content_eq(&self, other: &Self) -> bool { ContentEq::content_eq(&self.name, &other.name) && ContentEq::content_eq(&self.binding, &other.binding) + && ContentEq::content_eq(&self.computed, &other.computed) } } diff --git a/crates/oxc_ast/src/generated/derive_content_hash.rs b/crates/oxc_ast/src/generated/derive_content_hash.rs index 53c742f2a0309..ad73aef7e58c4 100644 --- a/crates/oxc_ast/src/generated/derive_content_hash.rs +++ b/crates/oxc_ast/src/generated/derive_content_hash.rs @@ -612,6 +612,7 @@ impl ContentHash for AssignmentTargetPropertyProperty<'_> { fn content_hash(&self, state: &mut H) { ContentHash::content_hash(&self.name, state); ContentHash::content_hash(&self.binding, state); + ContentHash::content_hash(&self.computed, state); } } diff --git a/crates/oxc_ast/src/generated/derive_estree.rs b/crates/oxc_ast/src/generated/derive_estree.rs index 3b8d4c6193869..461ee9f5204da 100644 --- a/crates/oxc_ast/src/generated/derive_estree.rs +++ b/crates/oxc_ast/src/generated/derive_estree.rs @@ -791,6 +791,7 @@ impl Serialize for AssignmentTargetPropertyProperty<'_> { self.span.serialize(serde::__private::ser::FlatMapSerializer(&mut map))?; map.serialize_entry("name", &self.name)?; map.serialize_entry("binding", &self.binding)?; + map.serialize_entry("computed", &self.computed)?; map.end() } } diff --git a/crates/oxc_codegen/src/gen.rs b/crates/oxc_codegen/src/gen.rs index 6d0cd25e50d98..13f6f7d8e5e34 100644 --- a/crates/oxc_codegen/src/gen.rs +++ b/crates/oxc_codegen/src/gen.rs @@ -2112,9 +2112,13 @@ impl Gen for AssignmentTargetPropertyProperty<'_> { ident.print(p, ctx); } key @ match_expression!(PropertyKey) => { - p.print_ascii_byte(b'['); + if self.computed { + p.print_ascii_byte(b'['); + } key.to_expression().print_expr(p, Precedence::Comma, Context::empty()); - p.print_ascii_byte(b']'); + if self.computed { + p.print_ascii_byte(b']'); + } } } p.print_colon(); diff --git a/crates/oxc_codegen/tests/integration/unit.rs b/crates/oxc_codegen/tests/integration/unit.rs index 65962f8930d0d..d5f5d9f933def 100644 --- a/crates/oxc_codegen/tests/integration/unit.rs +++ b/crates/oxc_codegen/tests/integration/unit.rs @@ -155,6 +155,11 @@ fn assignment() { test_minify("a %= async () => {}", "a%=async()=>{};"); test_minify("a -= (1, 2)", "a-=(1,2);"); test_minify("({ x: x = flag1 = true } = {})", "({x=flag1=true}={});"); + + test_minify("({ 0: x } = foo);", "({0:x}=foo);"); + test_minify("({ [0]: x } = foo);", "({[0]:x}=foo);"); + test_minify("({ a: x } = foo);", "({a:x}=foo);"); + test_minify("({ [a.b]: x } = foo);", "({[a.b]:x}=foo);"); } #[test] diff --git a/crates/oxc_parser/src/js/grammar.rs b/crates/oxc_parser/src/js/grammar.rs index 668e61c3fa042..d954e478ea615 100644 --- a/crates/oxc_parser/src/js/grammar.rs +++ b/crates/oxc_parser/src/js/grammar.rs @@ -169,6 +169,7 @@ impl<'a> CoverGrammar<'a, ObjectProperty<'a>> for AssignmentTargetProperty<'a> { property.span, property.key, binding, + property.computed, )) } } diff --git a/crates/oxc_traverse/src/generated/ancestor.rs b/crates/oxc_traverse/src/generated/ancestor.rs index aedb763a6b038..46a49d12c0e4b 100644 --- a/crates/oxc_traverse/src/generated/ancestor.rs +++ b/crates/oxc_traverse/src/generated/ancestor.rs @@ -4455,6 +4455,8 @@ pub(crate) const OFFSET_ASSIGNMENT_TARGET_PROPERTY_PROPERTY_NAME: usize = offset_of!(AssignmentTargetPropertyProperty, name); pub(crate) const OFFSET_ASSIGNMENT_TARGET_PROPERTY_PROPERTY_BINDING: usize = offset_of!(AssignmentTargetPropertyProperty, binding); +pub(crate) const OFFSET_ASSIGNMENT_TARGET_PROPERTY_PROPERTY_COMPUTED: usize = + offset_of!(AssignmentTargetPropertyProperty, computed); #[repr(transparent)] #[derive(Clone, Copy, Debug)] @@ -4479,6 +4481,14 @@ impl<'a, 't> AssignmentTargetPropertyPropertyWithoutName<'a, 't> { as *const AssignmentTargetMaybeDefault<'a>) } } + + #[inline] + pub fn computed(self) -> &'t bool { + unsafe { + &*((self.0 as *const u8).add(OFFSET_ASSIGNMENT_TARGET_PROPERTY_PROPERTY_COMPUTED) + as *const bool) + } + } } impl<'a, 't> GetAddress for AssignmentTargetPropertyPropertyWithoutName<'a, 't> { @@ -4511,6 +4521,14 @@ impl<'a, 't> AssignmentTargetPropertyPropertyWithoutBinding<'a, 't> { as *const PropertyKey<'a>) } } + + #[inline] + pub fn computed(self) -> &'t bool { + unsafe { + &*((self.0 as *const u8).add(OFFSET_ASSIGNMENT_TARGET_PROPERTY_PROPERTY_COMPUTED) + as *const bool) + } + } } impl<'a, 't> GetAddress for AssignmentTargetPropertyPropertyWithoutBinding<'a, 't> { diff --git a/npm/oxc-types/types.d.ts b/npm/oxc-types/types.d.ts index 0c36bac2f45b2..bf5b006945c00 100644 --- a/npm/oxc-types/types.d.ts +++ b/npm/oxc-types/types.d.ts @@ -431,6 +431,7 @@ export interface AssignmentTargetPropertyProperty extends Span { type: 'AssignmentTargetPropertyProperty'; name: PropertyKey; binding: AssignmentTargetMaybeDefault; + computed: boolean; } export interface SequenceExpression extends Span {