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

"array-element-newline" must handle ignored elements when destructuring arrays #12773

Closed
justincorrigible opened this issue Jan 10, 2020 · 12 comments
Labels
accepted There is consensus among the team that this change meets the criteria for inclusion archived due to age This issue has been archived; please open a new issue for any further discussion auto closed The bot closed this issue bug ESLint is working incorrectly rule Relates to ESLint's core rules

Comments

@justincorrigible
Copy link

Tell us about your environment

  • ESLint Version: 6.8.0
  • Node Version: 13.6.0
  • NPM Version: 6.13.4
  • Parser @typescript-eslint

Please show your full configuration:

Configuration ```js module.exports = { env: { browser: true, node: true, es6: true, }, extends: [ 'airbnb', 'plugin:react/recommended', 'plugin:import/typescript', ], globals: { PropTypes: false, React: false, uiVersion: false, }, parser: '@typescript-eslint/parser', parserOptions: { ecmaFeatures: { jsx: true, }, ecmaVersion: 2018, sourceType: 'module', }, plugins: [ '@typescript-eslint', 'react', 'react-hooks', 'sort-destructure-keys', ], rules: { 'array-bracket-newline': ['error', { 'multiline': true, 'minItems': 3, }], 'array-element-newline': ['warn', { 'multiline': true, 'minItems': 3, }], 'arrow-body-style': 'off', 'arrow-parens': 'off', 'camelcase': ['warn', { allow: ["^UNSAFE_", "doc_count", "^active_"], }], 'comma-dangle': ['warn', 'always-multiline'], 'consistent-return': ['warn', { treatUndefinedAsUnspecified: true }], 'func-names': ['warn', 'as-needed'], 'function-paren-newline': ['warn', 'consistent'], 'implicit-arrow-linebreak': 'off', 'indent': ['warn', 4, { ArrayExpression: 'first', CallExpression: { arguments: 'first' }, flatTernaryExpressions: true, FunctionDeclaration: { parameters: 'first' }, FunctionExpression: { parameters: 'first' }, ignoreComments: true, ignoredNodes: [ 'ConditionalExpression', 'JSXAttribute', 'JSXClosingElement', 'JSXElement', 'JSXElement > *', 'JSXEmptyExpression', 'JSXExpressionContainer', 'JSXIdentifier', 'JSXMemberExpression', 'JSXNamespacedName', 'JSXOpeningElement', 'JSXSpreadAttribute', 'JSXSpreadChild', 'JSXText', 'TemplateLiteral > *', ], ImportDeclaration: 'first', MemberExpression: 1, ObjectExpression: 'first', SwitchCase: 1, VariableDeclarator: 'first', }], 'multiline-ternary': ['warn', 'always-multiline'], 'no-console': ['warn', { allow: [ 'info', 'warn', 'error', ] }], 'no-debugger': 'warn', 'no-fallthrough': ['warn', { commentPattern: 'break[\\s\\w]*omitted', }], 'no-nested-ternary': 'off', 'no-unneeded-ternary': 'warn', 'no-unused-expressions': ['warn', { allowShortCircuit: true, allowTaggedTemplates: true, allowTernary: true, }], 'no-var': 'error', // Must use const or let. 'object-property-newline': ['warn', { // allowAllPropertiesOnSameLine: false, }], 'object-curly-newline': 'warn', // ['warn', { // ObjectExpression: { // 'multiline': true, // 'minProperties': 2, // }, // ObjectPattern: { // 'multiline': true, // 'minProperties': 2, // }, // ImportDeclaration: { // 'multiline': true, // 'minProperties': 2, // }, // ExportDeclaration: { // 'multiline': true, // 'minProperties': 2, // }, // }], 'operator-linebreak': ['warn', 'after', { overrides: { '?': 'before', ':': 'before', } }], 'padded-blocks': 'error', 'semi': ['warn', 'always'], 'sort-keys': ['warn', 'asc', { caseSensitive: false, natural: true, }], 'quotes': ['warn', 'single'], 'import/extensions': 'off', 'react/jsx-closing-bracket-location': ['warn', 'props-aligned'], 'react/jsx-filename-extension': ['warn', { extensions: [ '.js', '.jsx', '.tsx', ], }], 'react/jsx-first-prop-new-line': ['warn', 'multiline'], 'react/jsx-fragments': ['error', 'element'], 'react/jsx-indent': ['warn', 2, { checkAttributes: true, indentLogicalExpressions: true, }], 'react/jsx-indent-props': ['warn', 2], 'react/jsx-max-props-per-line': ['warn', { maximum: 1, when: 'multiline', }], 'react/jsx-one-expression-per-line': ['warn', { allow: 'single-child', }], 'react/jsx-sort-default-props': 'error', 'react/jsx-sort-props': ['warn', { ignoreCase: true, }], 'react/jsx-tag-spacing': ['warn', { closingSlash: 'never', beforeSelfClosing: 'always', afterOpening: 'never', beforeClosing: 'never', }], 'react/jsx-wrap-multilines': ['error', { declaration: 'parens-new-line', assignment: 'parens-new-line', return: 'parens-new-line', arrow: 'parens-new-line', condition: 'parens-new-line', logical: 'parens-new-line', prop: 'parens-new-line', }], 'react/no-did-mount-set-state': 'warn', 'react/no-did-update-set-state': 'warn', 'react/no-direct-mutation-state': 'warn', 'react/no-multi-comp': 'warn', 'react/no-unknown-property': 'warn', 'react/sort-comp': 'warn', 'react/sort-prop-types': 'error', 'react/prop-types': 'off', 'sort-destructure-keys/sort-destructure-keys': ['warn', { caseSensitive: false, }], '@typescript-eslint/explicit-function-return-type': 'off', '@typescript-eslint/explicit-member-accessibility': 'off', }, settings: { 'import/resolver': { node: { paths: ['./src/packages'] }, }, react: { version: 'detect', }, }, } ```

What did you do? Please include the actual source code causing the issue, as well as the command that you used to run ESLint.

As continuation of the fix for #12756, I expect each element (ignored or not) in a new line. However, while working on that other issue, I've realised different users may prefer having consecutive ignored elements (i.e. consecutive commas) in the same line.
Examples of different cases:

const [
    ,, // consecutive commas. valid?
    configPath = 'config.json',
    authFilePath = 'freshbooks.auth',
] = process.argv;
 
// vs

const [
    ,
    , // enforce separate lines?
    configPath = 'config.json',
    authFilePath = 'freshbooks.auth',
] = process.argv;

My only right now opinion is we should not allow consecutive commas after a valid (as of before this "fix") line.

Example:

const [
    configPath = 'config.json', // good old comma
    , // this comma is now valid as an ignored element.
    authFilePath = 'freshbooks.auth',,, // these two extra commas should be in the next line,
    // either together or lines of their own.
] = process.argv;

This issue is meant to open a community discussion on what the handling for successive commas should be; whether it should be a config in this rule or not.

Afterwards, I'll try to implement the agreed changes, unless someone else wants to take a stab at it.

@justincorrigible justincorrigible added bug ESLint is working incorrectly triage An ESLint team member will look at this issue soon labels Jan 10, 2020
@kaicataldo
Copy link
Member

This looks like two separate issues to me. The first is a bug fix:

I think the expected behavior would be for the rule to treat each comma as if it were an element in the array:

/*eslint array-element-newline: ["error", "always"]*/

// Correct
const [
    configPath = 'config.json',
    ,
    authFilePath = 'freshbooks.auth',
    ,
    ,
] = process.argv;
/*eslint array-element-newline: ["error", "never"]*/

// Correct
const [
    configPath = 'config.json',  authFilePath = 'freshbooks.auth', , ,
] = process.argv;

This is not the current behavior, and I would advocate for fixing it in a semver-minor bug fix PR. It doesn't look like null destructured array elements are in tests either, so it doesn't seem like it has been considered up to this point.

Secondly, I think it makes sense to update the rule with an option that allows for groups of commas to be either ignored or treated as a single entity.

@kaicataldo kaicataldo added accepted There is consensus among the team that this change meets the criteria for inclusion rule Relates to ESLint's core rules and removed triage An ESLint team member will look at this issue soon labels Jan 10, 2020
@justincorrigible
Copy link
Author

justincorrigible commented Jan 10, 2020

This looks like two separate issues to me.

tl;dr: Agreed. That's what I've meant.

Fair, I've simply thought since I was going to tackle the bug, might well add the extra config if others agreed with that option being desirable. (I don't really care, but some may.)
Figured some debate on whether multiple commas could be allowed or not, and wanted to catch the consideration in the issue's description, just to ease later documentation on the discussion.

If the topic grows and there's interest in debating it, I'll branch it out in a new ticket. Else, I can just open a separate PR for the option, and tag both to this issue. Let's see how it goes.

I would advocate for fixing it in a semver-minor bug fix PR

Not quite sure what you mean by this, and yes, I've noticed there are no tests for destructuring assignments neither here, nor at "comma-style". We can proceed as you see fit, but it is my first couple attempted contributions at ESLint, so thanks for bearing with me.

@kaicataldo
Copy link
Member

We appreciate your help!

I would advocate for fixing it in a semver-minor bug fix PR

What I meant by this is that, while this will result in more errors being reported, that I think we should treat it as a bug fix and therefore releasable in a minor release (indicating non-breaking changes). You can read more about our semantic versioning policy here (and happy to explain if anything is unclear). That being said, we've begun work on our next major release and are currently able to make breaking changes.

Let's see what others on the team have to say!

@mdjermanovic
Copy link
Member

I think the expected behavior would be for the rule to treat each comma as if it were an element in the array:

/*eslint array-element-newline: ["error", "always"]*/

// Correct
const [
    configPath = 'config.json',
    ,
    authFilePath = 'freshbooks.auth',
    ,
    ,
] = process.argv;

What would be the correct formatting if we also have comma-style with the option first. Maybe the following:

/*eslint array-element-newline: ["error", "always"]*/
/*eslint comma-style: ["error", "first", { 
    "exceptions": { "ArrayPattern": false }
}]*/

// Correct ?
const [
    configPath = 'config.json'
    ,
    ,authFilePath = 'freshbooks.auth'
    ,
    ,
    ,
] = process.argv;

I'm not sure if this is the expected code, especially about those 3 commas at the end, but perhaps we should analyze could there be any conflicts and how will the fixers for these two rules together transform the code into the desired state (this, or some other), e.g., which one will put the linebreak between 'config.json' and the comma after.

Currently, comma-style with first fixes the quoted code to:

/*eslint array-element-newline: ["error", "always"]*/
/*eslint comma-style: ["error", "first", { 
    "exceptions": { "ArrayPattern": false }
}]*/

const [
    configPath = 'config.json',    
    ,authFilePath = 'freshbooks.auth',
    ,    
,] = process.argv;

The new array-element-newline behavior would probably want to put a linebreak before authFilePath:

/*eslint array-element-newline: ["error", "always"]*/
/*eslint comma-style: ["error", "first", { 
    "exceptions": { "ArrayPattern": false }
}]*/

const [
    configPath = 'config.json',    
    ,
    authFilePath = 'freshbooks.auth',
    ,    
,] = process.argv;

And there's a conflict, comma-style wants authFilePath back to the comma. If PR #12772 allows that lone comma, there will be no conflicts and this will be the final state. But, this code looks more like the Comma Last style.

@justincorrigible
Copy link
Author

justincorrigible commented Jan 21, 2020

I'm not sure if this is the expected code, especially about those 3 commas at the end

Haven't been able to sit down and test some options out (slammed at my 9-5 for the moment), but it just hit me: Those three commas are redundant... the point of ignored elements is that you care about another one after. Those dangling 3 from the example are being ignored for no reason, and I think they should be flagged by "comma-style".
Else, anyone could add N purposeless dangling commas after (which migh also in some future conflict with some weird ,,, they may implement for some reason).
Will make another issue for that specific change, as it doesn't pertain the current scope.

In regards to @mdjermanovic, I agree with all of his response.
#12772 will ideally resolve the conflict, and those examples would indeed be the expected response, IMO.

@mdjermanovic
Copy link
Member

Those dangling 3 from the example are being ignored for no reason, and I think they should be flagged by "comma-style".

I agree they are most likely a mistake (though, those 2 empty elements at the end of the pattern are still invoking next() on the iterator and thus may be used for some side effects), but that shouldn't be the responsibility of either of these two rules.

comma-style and array-element-newline should flag just missing/extra spaces and linebreaks, their responsibility isn't to control the content of arrays/patterns.

@justincorrigible
Copy link
Author

Agreed, yet wouldn't "comma-style" normally flag a dangling comma, if that option is disabled?
This somehow seems would basically be the same case. 3 dangling commas.
(Confessedly, my argument may be a slight stretch; just making sure we consider all angles.)

@mdjermanovic
Copy link
Member

Agreed, yet wouldn't "comma-style" normally flag a dangling comma, if that option is disabled?

Yes, it could have been an option in the comma-style rule.

Dangling commas are required/disallowed by the comma-dangle rule, but that's the same type of ESLint core rules: "layout" rules.

It was a design decision to separate these preferences into two rules, though a single rule to enforce both things would also make sense.

This somehow seems would basically be the same case. 3 dangling commas.

It's different because 1 dangling comma has no semantics. Code has the same behavior with or without that comma. One dangling comma or not is just a "layout" preference.

On the other hand, multiple dangling commas in array expressions and array patterns have a meaning. They represent elisions, which do change the behavior of the code: array expression will create an array with more elements, array pattern will pull more elements from the source.

It's probably best practice to avoid this feature, but this isn't something that "layout" rules such as comma-style and comma-dangle should disallow.

@justincorrigible
Copy link
Author

justincorrigible commented Jan 21, 2020

Indeed. I agree wholeheartedly. Just had to play devil’s advocate to document the decisions. Will work on this once done with #12772

@mysticatea
Copy link
Member

mysticatea commented Jan 22, 2020 via email

@eslint-deprecated eslint-deprecated bot added the auto closed The bot closed this issue label Apr 11, 2020
@eslint-deprecated
Copy link

Unfortunately, it looks like there wasn't enough interest from the team
or community to implement this change. While we wish we'd be able to
accommodate everyone's requests, we do need to prioritize. We've found
that accepted issues failing to be implemented after 90 days tend to
never be implemented, and as such, we close those issues.
This doesn't mean the idea isn't interesting or useful, just that it's
not something the team can commit to.

Thanks for contributing to ESLint and we appreciate your understanding.

@cafesanu
Copy link

Since the issue was auto closed, does this mean this won't be added?

@eslint-deprecated eslint-deprecated bot locked and limited conversation to collaborators Oct 9, 2020
@eslint-deprecated eslint-deprecated bot added the archived due to age This issue has been archived; please open a new issue for any further discussion label Oct 9, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
accepted There is consensus among the team that this change meets the criteria for inclusion archived due to age This issue has been archived; please open a new issue for any further discussion auto closed The bot closed this issue bug ESLint is working incorrectly rule Relates to ESLint's core rules
Projects
None yet
Development

No branches or pull requests

5 participants