prettier: Improve the heuristic for breaking nested object destructuring

Prettier 1.16.4 Playground link

Input:

const { size: { width, height } } = useSomething()
return <el onEvent={({ size: { width, height } }) => null} />

Output:

const {
  size: { width, height },
} = useSomething()
return <el onEvent={({ size: { width, height } }) => null} />

Expected behavior:

It should let the input stand (meaning, not break up), the format appears to be correct to me. It also doesn’t break up the jsx event.

About this issue

  • Original URL
  • State: open
  • Created 5 years ago
  • Reactions: 17
  • Comments: 24 (8 by maintainers)

Most upvoted comments

Also, to try to contribute my two cents to a solution:

I think Prettier’s heuristic should break nested object destructuring onto multiple lines only for sibling objects.

In other words, all of these examples could easily stay on one line:

const { match: { params: { id } } } = useMatch()
const { size: { width, height } } = useMeasure()
const { viewport: { aspect } } = useViewport()

While this one could still break, due to sibling objects size and viewport:

const {
  size: { width, height },
  viewport: { vWidth, vHeight },
} = useThree();

IMO this would be a happy medium that fulfills the original objective of #4267 without just exploding everything Prettier sees. It’s most consistent with how I would format my code for readability.

@lydell could this be reconsidered? With react hooks we’re running into this constantly now and it’s normal that you have multiple of these in row. Previously this perhaps wasn’t used that often but that’s shifting as hooks become the defacto standard for component notation (react, preact, vue-next, etc).

How you write it:

const { match: { params: { id } } } = useMatch()
const { size: { width, height } } = useMeasure()
const { viewport: { aspect } } = useViewport()

prettier:

const { 
    match: { 
        params: { id }
    } 
} = useMatch()
const { 
    size: { width, height } 
} = useMeasure()
const { 
    viewport: { aspect } 
} = useViewport()

this is hideous, distracting, and puzzling. I can’t stop staring at it and wondering “why, why do that? is this a failed attempt at ascii art?” It looks like the front of a super soaker.

 const {
    result: [address],
  } = lookup;

is this supposed to suggest something to the reader? should i have an easier time understanding the syntax because the statement spans multiple lines?

it bothers me so much that i stopped working on my project to research this issue and write this comment. i really like Prettier. Please make this optional.

Hi! Thanks for the issue. Prettier breaks object destructuring with nested object destructuring onto multiple lines even if it all fits on one line on purpose – see #4267.

Let’s hear from what chatgpt says😂🤣:

" If you would like to format ES6 destructuring assignments into a single line, you can use the inline-destructuring option in prettier. Here is an example .prettierrc configuration that should produce the desired output

{
  "singleQuote": true,
  "trailingComma": "es5",
  "printWidth": 120,
  "overrides": [
    {
      "files": "*.js",
      "options": {
        "semi": false,
        "singleQuote": true,
        "printWidth": 120,
        "trailingComma": "es5",
        "jsxSingleQuote": true,
        "inline-destructuring": true
      }
    },
    {
      "files": "*.json",
      "options": {
        "printWidth": 200
      }
    }
  ],
  "semi": false,
  "bracketSpacing": false,
  "jsxBracketSameLine": true,
  "arrowParens": "avoid",
  "tabWidth": 2,
  "useTabs": false,
  "endOfLine": "auto"
}

After adding this configuration to your .prettierrc file, please run prettier again and let me know if it produces the desired output.

"

If the pairs in the OP can be left unchanged that would be a relief already : )

What i don’t understand is, all of these behave fine in inline functions,

callback(({ size: { width, height }, viewport: { vWidth, vHeight } }) => null)

Do you have a different set of heuristics for these?

@cesasol If Prettier is introducing syntax errors in your code, please open a new issue.

@drcmda We won’t revert, but we are open to suggestions on how to improve the heuristics we added

I think we could keep examples like the OP as-is, but this one should break IMO:

const { size: { width, height }, viewport: { vWidth, vHeight } } = useThree()

I really want this to be optional, this is infuriating.

For example, prettier is turning things like:

const validSignedChallenge: SignedChallenge[] = [
  {
    address: 'a very long string',
    type: 'persona',
    challenge: 'a very long string',
    proof: {
      publicKey: 'a very long string',
      signature: 'a very long string',
      curve: 'curve25519',
    },
  },
  {
    address: 'a very long string',
    type: 'account',
    challenge: 'a very long string',
    proof: {
      publicKey: 'a very long string',
      signature: 'a very long string',
      curve: 'curve25519',
    },
  },
]

into:

const validSignedChallenge: SignedChallenge[] = [
  {
    address: 'a very long string',
    type: 'persona',
    challenge:
      'a very long string',
    proof: {
      publicKey:
        'a very long string',
      signature:
        'a very long string',
      curve: 'curve25519',
    },
  },
  {
    address:
      'a very long string',
    type: 'account',
    challenge:
      'a very long string',
    proof: {
      publicKey:
        'a very long string',
      signature:
        'a very long string',
      curve: 'curve25519',
    },
  },
]

I never, EVER want prettier to do this. I can handle my own lengthiness, and this is an unavoidable situation (without extracting the strings into separate variables, which would be worse, since this is only for a test expectation). I use the line wrap feature of vscode when I want the lines to wrap, which is more readable than this crap.

I echo the previous statements:

“This project’s name and purpose is “prettier”. More and more I’m finding it loves to turn 1-liners that were already “pretty” into 3-5 lines of decidedly un-pretty, jagged, hard-to-visually-scan code.”

"is this supposed to suggest something to the reader? should i have an easier time understanding the syntax because the statement spans multiple lines?

it bothers me so much that i stopped working on my project to research this issue and write this comment. i really like Prettier. Please make this optional."

I think it’s not so much “errors in the code” as it is this project’s name and purpose is “prettier”. More and more I’m finding it loves to turn 1-liners that were WELL under 80 chars and already “pretty” (a hook following a very standard, if new, pattern in React) into 3-5 lines of decidedly un-pretty, jagged, hard-to-visually-scan code.

This is a personal opinion of course but it feels like Prettier is almost at war with some approaches in the JS community. I thought the original purpose, the driving mission, was code readability. Nearly all patterns of importing modules and doing “setup” work now use destructuring, sometimes several levels worth. But these steps are not real “code”… They’re just setup. I now have code files where Prettier has reformatted the import and hook-setup blocks to be longer than the component code itself!

My opinion, such as it is, is that the CODE should be the #1 priority, and when you start to get away from that you lose sight of the real value such a tool provides the developer. Doubling or tripling the length of a file containing a button component just to make import and hook lines horizontally shorter does not achieve this - it does the opposite.

@drcmda Actually the heuristics don’t apply to the top ObjectPattern if it’s a function argument.

It’s this piece of code: https://github.com/prettier/prettier/blob/8160d84c75f84dfc3ae3a45fc1f5d1889219f395/src/language-js/printer-estree.js#L1301-L1312

One option:

const { params: { id } } = useMatch().match
const { width, height } = useMeasure().size
const { aspect } = useViewport().viewport