fix(formatter): format assignment target property as assignment-like (#27289)

- Break after `:` for own-line comment before a destructuring property value
- Keep `a: // c` line comment on the operator line in assignment patterns
This commit is contained in:
leaysgur committed 2026-10-02 14:25:34 +00:00
1 parent 6c08f2aaf1
commit 7f65b757df
5 files changed
+164 -12

No files matched your search

+1 -2
View File
@@ -438,8 +438,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, AssignmentTargetPropertyIdentifier<'a>>
impl<'a> FormatWrite<'a> for AstNode<'a, AssignmentTargetPropertyProperty<'a>> {
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
format_property_key(self.name(), self.computed(), f);
write!(f, [":", space(), self.binding()]);
AssignmentLike::AssignmentTargetPropertyProperty(self).fmt(f);
}
}
@@ -30,6 +30,7 @@ pub enum AssignmentLike<'a, 'b> {
AssignmentExpression(&'b AstNode<'a, AssignmentExpression<'a>>),
ObjectProperty(&'b AstNode<'a, ObjectProperty<'a>>),
BindingProperty(&'b AstNode<'a, BindingProperty<'a>>),
AssignmentTargetPropertyProperty(&'b AstNode<'a, AssignmentTargetPropertyProperty<'a>>),
PropertyDefinition(&'b AstNode<'a, PropertyDefinition<'a>>),
AccessorProperty(&'b AstNode<'a, AccessorProperty<'a>>),
TSTypeAliasDeclaration(&'b AstNode<'a, TSTypeAliasDeclaration<'a>>),
@@ -232,6 +233,19 @@ impl<'a> AssignmentLike<'a, '_> {
width < text_width_for_break
}
}
AssignmentLike::AssignmentTargetPropertyProperty(property) => {
let text_width_for_break =
(f.options().indent_width.value() + MIN_OVERLAP_FOR_BREAK) as usize;
if property.computed {
write!(f, ["[", property.name(), "]"]);
f.source_text().span_width(property.name.span()) + 2 < text_width_for_break
} else {
let width = write_member_name(property.name(), f);
width < text_width_for_break
}
}
AssignmentLike::PropertyDefinition(property) => {
write!(f, [property.decorators()]);
@@ -331,6 +345,9 @@ impl<'a> AssignmentLike<'a, '_> {
write!(f, [":"]);
}
}
Self::AssignmentTargetPropertyProperty(_) => {
write!(f, [":"]);
}
Self::PropertyDefinition(property_class_member) => {
debug_assert!(property_class_member.value().is_some());
write!(f, [space(), "="]);
@@ -361,6 +378,9 @@ impl<'a> AssignmentLike<'a, '_> {
Self::BindingProperty(property) => {
write!(f, property.value());
}
Self::AssignmentTargetPropertyProperty(property) => {
write!(f, property.binding());
}
Self::PropertyDefinition(property) => {
write!(f, [with_assignment_layout(property.value().unwrap(), Some(layout))]);
}
@@ -481,6 +501,7 @@ impl<'a> AssignmentLike<'a, '_> {
AssignmentLike::AssignmentExpression(assignment) => assignment.right.span(),
AssignmentLike::ObjectProperty(property) => property.value.span(),
AssignmentLike::BindingProperty(property) => property.value.span(),
AssignmentLike::AssignmentTargetPropertyProperty(property) => property.binding.span(),
AssignmentLike::PropertyDefinition(property) => property.value.as_ref()?.span(),
AssignmentLike::AccessorProperty(property) => property.value.as_ref()?.span(),
AssignmentLike::TSTypeAliasDeclaration(decl) => decl.type_annotation.span(),
@@ -497,7 +518,9 @@ impl<'a> AssignmentLike<'a, '_> {
property_class_member.value()
}
AssignmentLike::AccessorProperty(property) => property.value(),
AssignmentLike::BindingProperty(_) | AssignmentLike::TSTypeAliasDeclaration(_) => None,
AssignmentLike::BindingProperty(_)
| AssignmentLike::AssignmentTargetPropertyProperty(_)
| AssignmentLike::TSTypeAliasDeclaration(_) => None,
}
}
@@ -510,7 +533,9 @@ impl<'a> AssignmentLike<'a, '_> {
return None;
}
let operator = match self {
Self::ObjectProperty(_) | Self::BindingProperty(_) => b':',
Self::ObjectProperty(_)
| Self::BindingProperty(_)
| Self::AssignmentTargetPropertyProperty(_) => b':',
_ => b'=',
};
Some(comments.position_after_character(self.left_end(), operator))
@@ -538,6 +563,7 @@ impl<'a> AssignmentLike<'a, '_> {
Self::AssignmentExpression(assignment) => assignment.left.span().end,
Self::ObjectProperty(property) => property.key.span().end,
Self::BindingProperty(property) => property.key.span().end,
Self::AssignmentTargetPropertyProperty(property) => property.name.span().end,
Self::PropertyDefinition(property) => property
.type_annotation
.as_ref()
@@ -554,7 +580,9 @@ impl<'a> AssignmentLike<'a, '_> {
/// when a [variable declarator](VariableDeclarator) doesn't have initializer.
fn has_only_left_hand_side(&self) -> bool {
match self {
Self::AssignmentExpression(_) | Self::TSTypeAliasDeclaration(_) => false,
Self::AssignmentExpression(_)
| Self::AssignmentTargetPropertyProperty(_)
| Self::TSTypeAliasDeclaration(_) => false,
Self::VariableDeclarator(declarator) => declarator.init.is_none(),
Self::PropertyDefinition(property) => property.value().is_none(),
Self::AccessorProperty(property) => property.value().is_none(),
@@ -709,7 +737,8 @@ impl<'a> AssignmentLike<'a, '_> {
}
}
} else {
false
// A pattern on the right side: only comments on their own line force the break
self.right_start().is_some_and(|start| comments.has_leading_own_line_comment(start))
}
}
@@ -763,6 +792,7 @@ impl<'a> AssignmentLike<'a, '_> {
}
AssignmentLike::ObjectProperty(_)
| AssignmentLike::BindingProperty(_)
| AssignmentLike::AssignmentTargetPropertyProperty(_)
| AssignmentLike::PropertyDefinition(_)
| AssignmentLike::AccessorProperty(_)
| AssignmentLike::TSTypeAliasDeclaration(_) => false,
@@ -0,0 +1,22 @@
let { [
y6
/* after */
]: y6 } = {};
let { a:
/* c */
b } = {};
let { a:
// c
b } = {};
let { a: /* c */ b } = {};
({ a:
/* c */
b } = {});
({ [
y6
/* after */
]: y6 } = {});
({ a:
// c
b } = {});
({ a: /* c */ b } = {});
@@ -0,0 +1,101 @@
---
source: crates/oxc_formatter/tests/fixtures/mod.rs
---
==================== Input ====================
let { [
y6
/* after */
]: y6 } = {};
let { a:
/* c */
b } = {};
let { a:
// c
b } = {};
let { a: /* c */ b } = {};
({ a:
/* c */
b } = {});
({ [
y6
/* after */
]: y6 } = {});
({ a:
// c
b } = {});
({ a: /* c */ b } = {});
==================== Output ====================
------------------
{ printWidth: 80 }
------------------
let {
[y6]:
/* after */
y6,
} = {};
let {
a:
/* c */
b,
} = {};
let {
a:
// c
b,
} = {};
let { a: /* c */ b } = {};
({
a:
/* c */
b,
} = {});
({
[y6]:
/* after */
y6,
} = {});
({
a:
// c
b,
} = {});
({ a: /* c */ b } = {});
-------------------
{ printWidth: 100 }
-------------------
let {
[y6]:
/* after */
y6,
} = {};
let {
a:
/* c */
b,
} = {};
let {
a:
// c
b,
} = {};
let { a: /* c */ b } = {};
({
a:
/* c */
b,
} = {});
({
[y6]:
/* after */
y6,
} = {});
({
a:
// c
b,
} = {});
({ a: /* c */ b } = {});
===================== End =====================
@@ -1,8 +1,8 @@
checker.ts:
file size: 2922154 # 2.92 MB
sys allocs: 19386
sys reallocs: 1169
sys deallocs: 19385
sys allocs: 19387
sys reallocs: 1170
sys deallocs: 19386
sys alloc bytes: 6767781 # 6.77 MB
sys peak growth: 2973674 # 2.97 MB
arena allocs: 1060573
@@ -33,9 +33,9 @@ RadixUIAdoptionSection.jsx:
pdf.mjs:
file size: 567296 # 567.30 kB
sys allocs: 9778
sys reallocs: 1327
sys deallocs: 9777
sys allocs: 9782
sys reallocs: 1328
sys deallocs: 9781
sys alloc bytes: 3141552 # 3.14 MB
sys peak growth: 1156432 # 1.16 MB
arena allocs: 430193