Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement objectWrap config option #16163

Merged
merged 9 commits into from
Jan 15, 2025

Conversation

pauldraper
Copy link
Contributor

@pauldraper pauldraper commented Mar 25, 2024

Description

Implement multilineObject config option to control how multi-line JavaScript objects are formatted.

Choices

"preserve"

This is the default. Objects are kept on multiple lines if there is a newline between the opening brace and the first property key. (I.e. the existing behavior of Prettier.)

"collapse"

Objects ignore whitespace-only formatting from the source, and are collapsed to a single line when possible.

Examples

Input
const obj1 = {
  name1: "value1",
  name2: "value2",
};

const obj2 = {
  name1: "value1", name2: "value2",
};

const obj3 = {
  name1: "value1",
  name2: "value2", };

const obj4 = { name1: "value1",
  name2: "value2",
};

const obj5 = { name1: "value1", name2: "value2", };
"preserve"
const obj1 = {
  name1: "value1",
  name2: "value2",
};

const obj2 = {
  name1: "value1",
  name2: "value2",
};

const obj3 = {
  name1: "value1",
  name2: "value2",
};

const obj4 = { name1: "value1", name2: "value2" };

const obj5 = { name1: "value1", name2: "value2" };
"collapse"
const obj1 = { name1: "value1", name2: "value2" };

const obj2 = { name1: "value1", name2: "value2" };

const obj3 = { name1: "value1", name2: "value2" };

const obj4 = { name1: "value1", name2: "value2" };

const obj5 = { name1: "value1", name2: "value2" };

Motivation

By far the biggest reason for adopting Prettier is to stop all the ongoing debates over styles.

https://prettier.io/docs/en/why-prettier

John submits a PR:

const buttonStyles = { background: "#45da09", borderRadius: "4px" };

Jessica submits a PR:

const buttonStyles = {
  background: "#45da09",
  borderRadius: "4px"
};

Prettier fails to prevent these two contributors from quibbling over the style. The project must publish a style guide to resolve the syntax debate of where and when to use multi-line objects. Moreover, to follow that guide for the single-line object cases, reviewers must now measure characters to see if the object can fit on a single-line without violating line length rules. (Which by itself is a complex task.)

This debate can be settled by using --multiline-object=collapse.

The pain of the above situation has been felt by many, many users who expect Prettier to solve this stylistic variation, and is a perennial source of consternation marked by complaints of "instability" and "inconsistency":

However, the original justifications for the semi-manual formatting still apply. Even though there are many who prefer to Prettier to be opinionated (including myself), it is a non-starter to impose whitespace agnostic object formatting on all Prettier users.

Thus, this change is the only path forward.

Aren't we not supposed to add options?

Normally yes, but this option fundamentally differs from the usual proposals.

1. This option makes Prettier more opinionated, not less.

Normally, options are eschewed because they contradict Prettier's purpose to be an opinionated formatter.

But today, the single-line vs multi-line is configured not project-by-project, not even file-by-file, but object-literal-by-object-literal. That's literally the most flexibility/least opinionated possible. This change allows users to reign in that flexibility.

Unlike other proposals for adding options, this does not change the variety of styles that Prettier outputs. It simply changes how those styles are configured (project-wide vs line-by-line).

2. The current behavior is "not a feature"

The semi-manual formatting for object literals is in fact a workaround, not a feature.

https://prettier.io/docs/en/rationale.html#%EF%B8%8F-a-note-on-formatting-reversibility

This option allows users to opt out of this non-feature workaround.

3. Non-reversibility is significantly undesirable

What does reversible mean? Once an object literal becomes multiline, Prettier won’t collapse it back. If in Prettier-formatted code, we add a property to an object literal, run Prettier, then change our mind, remove the added property, and then run Prettier again, we might end up with a formatting not identical to the initial one. This useless change might even get included in a commit, which is exactly the kind of situation Prettier was created to prevent.

https://prettier.io/docs/en/rationale.html#%EF%B8%8F-a-note-on-formatting-reversibility

This option allows users to make Prettier a reversible formatter.

4. Prettier maintainers have invited this change

#2068 (comment)

Checklist

  • I’ve added tests to confirm my change works.
  • (If changing the API or CLI) I’ve documented the changes I’ve made (in the docs/ directory).
  • (If the change is user-facing) I’ve added my changes to changelog_unreleased/*/XXXX.md file following changelog_unreleased/TEMPLATE.md.
  • I’ve read the contributing guidelines.

Try the playground for this PR

Copy link

@jaydenseric jaydenseric left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about an option that's the opposite of the "collapse" option? This way objects could be forced to always be expanded on multiple lines. That approach would minimise Git diffs when someone adds a new property to an object, otherwise prettier might suddenly convert a previously single line object to multiple lines if it can't fit within the max line length anymore.

I'm not pushing for more configurability; just talking out loud to see what the best approach is. I would be happy with whatever the community lands on as the default.

@pauldraper
Copy link
Contributor Author

pauldraper commented Mar 25, 2024

@jaydenseric that hypothetical option would be "expand". It would not be without precedence (singleAttributePerLine, introduced version 2.6.0).

However, I do not believe this to be viable for short objects. Moreover, it makes more sense for object members to be consistent with array, function call, argument members. Which are placed on multiple lines only as necessary.

@pauldraper
Copy link
Contributor Author

Can I get some eyes on this?

@fisker @sosukesuzuki @alexander-akait

@fisker
Copy link
Member

fisker commented Apr 4, 2024

Personally, I like --multiline-object=collapse. 👍 from me.

@pauldraper pauldraper force-pushed the pauldraper/multiline-object branch from 7c3754c to debff30 Compare April 6, 2024 08:32
@sosukesuzuki
Copy link
Member

I too am 👍 on this proposal. Ideally I don't want to add the option, but it seems like a good compromise. I will review it properly later.

Copy link
Member

@sosukesuzuki sosukesuzuki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation looks good. However, additional tests are needed. This change also affects the type Foo = { field1: number }; syntax in TypeScript and Flow, so please add tests for it.
Also, look for existing test cases that may be relevant and enable the { multilineObjects: "collapse" } option. And please fix CI fail.

changelog_unreleased/javascript/16163.md Outdated Show resolved Hide resolved
changelog_unreleased/javascript/16163.md Outdated Show resolved Hide resolved
@fisker
Copy link
Member

fisker commented May 10, 2024

@pauldraper #16163 (review)

@pauldraper
Copy link
Contributor Author

pauldraper commented Dec 27, 2024

  • Fixed CI check
  • Added TypeScript and Flow tests

Copy link

pkg-pr-new bot commented Dec 27, 2024

Open in Stackblitz

npm i https://pkg.pr.new/prettier@16163

commit: ced64c2

@pauldraper pauldraper force-pushed the pauldraper/multiline-object branch 2 times, most recently from 0287e17 to dda1608 Compare December 27, 2024 07:05
@pauldraper
Copy link
Contributor Author

@sosukesuzuki

docs/options.md Outdated Show resolved Hide resolved
@sosukesuzuki sosukesuzuki requested a review from fisker January 3, 2025 16:04
@sosukesuzuki sosukesuzuki added this to the 3.5 milestone Jan 3, 2025
@pauldraper pauldraper force-pushed the pauldraper/multiline-object branch 3 times, most recently from 2487cc6 to 7a6a1b5 Compare January 4, 2025 20:03
@pauldraper pauldraper force-pushed the pauldraper/multiline-object branch from 7a6a1b5 to 8bcb6d3 Compare January 4, 2025 20:04
Copy link
Member

@sosukesuzuki sosukesuzuki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm beginning to think that multilineObject is not the best name. It sounds like it takes a boolean value, which isn't what we want to convey. Instead, it would be better to use a name that suggests "determining how objects wrap across multiple lines." For example, how about objectWrapping? It’s reminiscent of CSS’s text-wrapping, so JavaScript developers likely won’t find it off-putting.

@sosukesuzuki sosukesuzuki changed the title Implement multilineObject config option Implement objectWrapping config option Jan 12, 2025
@sosukesuzuki
Copy link
Member

@pauldraper @fisker I've updated the option name from multilineObject to objectWrapping. What do you think?

@pauldraper
Copy link
Contributor Author

pauldraper commented Jan 13, 2025

@pauldraper @fisker I've updated the option name from multilineObject to objectWrapping. What do you think?

I like it.

Could be objectWrap, for similarity to proseWrap. (Though the option values/semantics are different.)

@sosukesuzuki
Copy link
Member

objectWrap sounds good to me, I'll update it

@sosukesuzuki sosukesuzuki changed the title Implement objectWrapping config option Implement objectWrap config option Jan 15, 2025
@sosukesuzuki sosukesuzuki merged commit bef1d61 into prettier:main Jan 15, 2025
31 checks passed
@pauldraper
Copy link
Contributor Author

🎉

Thanks @sosukesuzuki !

@pauldraper pauldraper deleted the pauldraper/multiline-object branch January 16, 2025 15:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants