Skip to content

Commit

Permalink
Add auto-fix for no-unreadable-array-destructuring (#1010)
Browse files Browse the repository at this point in the history
  • Loading branch information
fisker committed Jan 14, 2021
1 parent 4c92d4c commit 27bc3c3
Show file tree
Hide file tree
Showing 6 changed files with 247 additions and 26 deletions.
22 changes: 22 additions & 0 deletions docs/rules/no-unreadable-array-destructuring.md
Expand Up @@ -2,13 +2,23 @@

Destructuring is very useful, but it can also make some code harder to read. This rule prevents ignoring consecutive values when destructuring from an array.

This rule is partly fixable.

## Fail

```js
const [,, foo] = parts;
```

```js
const [,,, foo] = parts;
```

```js
const [,,,, foo] = parts;
```

```js
const [,,...rest] = parts;
```

Expand All @@ -17,11 +27,23 @@ const [,,...rest] = parts;

```js
const [, foo] = parts;
```

```js
const [foo] = parts;
```

```js
const foo = parts[3];
```

```js
const [,...rest] = parts;
```

```js
const foo = parts.slice(3);
```

## Note

Expand Down
2 changes: 1 addition & 1 deletion readme.md
Expand Up @@ -138,7 +138,7 @@ Configure it in `package.json`.
- [no-null](docs/rules/no-null.md) - Disallow the use of the `null` literal.
- [no-object-as-default-parameter](docs/rules/no-object-as-default-parameter.md) - Disallow the use of objects as default parameters.
- [no-process-exit](docs/rules/no-process-exit.md) - Disallow `process.exit()`.
- [no-unreadable-array-destructuring](docs/rules/no-unreadable-array-destructuring.md) - Disallow unreadable array destructuring.
- [no-unreadable-array-destructuring](docs/rules/no-unreadable-array-destructuring.md) - Disallow unreadable array destructuring. *(partly fixable)*
- [no-unsafe-regex](docs/rules/no-unsafe-regex.md) - Disallow unsafe regular expressions.
- [no-unused-properties](docs/rules/no-unused-properties.md) - Disallow unused object properties.
- [no-useless-undefined](docs/rules/no-useless-undefined.md) - Disallow useless `undefined`. *(fixable)*
Expand Down
56 changes: 49 additions & 7 deletions rules/no-unreadable-array-destructuring.js
@@ -1,5 +1,7 @@
'use strict';
const {isParenthesized} = require('eslint-utils');
const getDocumentationUrl = require('./utils/get-documentation-url');
const shouldAddParenthesesToMemberExpressionObject = require('./utils/should-add-parentheses-to-member-expression-object');

const MESSAGE_ID = 'no-unreadable-array-destructuring';
const messages = {
Expand All @@ -10,14 +12,53 @@ const isCommaFollowedWithComma = (element, index, array) =>
element === null && array[index + 1] === null;

const create = context => {
const sourceCode = context.getSourceCode();

return {
'ArrayPattern[elements.length>=3]': node => {
if (node.elements.some((element, index, array) => isCommaFollowedWithComma(element, index, array))) {
context.report({
node,
messageId: MESSAGE_ID
});
'ArrayPattern[elements.length>=3]'(node) {
const {elements, parent} = node;

if (!elements.some((element, index, elements) => isCommaFollowedWithComma(element, index, elements))) {
return;
}

const problem = {
node,
messageId: MESSAGE_ID
};

const nonNullElements = elements.filter(node => node !== null);
if (
parent.type === 'VariableDeclarator' &&
parent.id === node &&
nonNullElements.length === 1
) {
const [element] = nonNullElements;

if (element.type !== 'AssignmentPattern') {
problem.fix = function * (fixer) {
const index = elements.indexOf(element);
const isSlice = element.type === 'RestElement';
const variable = isSlice ? element.argument : element;

yield fixer.replaceText(node, sourceCode.getText(variable));

const code = isSlice ? `.slice(${index})` : `[${index}]`;
const array = parent.init;
if (
!isParenthesized(array, sourceCode) &&
shouldAddParenthesesToMemberExpressionObject(array, sourceCode)
) {
yield fixer.insertTextBefore(array, '(');
yield fixer.insertTextAfter(parent, `)${code}`);
} else {
yield fixer.insertTextAfter(parent, code);
}
};
}
}

context.report(problem);
}
};
};
Expand All @@ -29,6 +70,7 @@ module.exports = {
docs: {
url: getDocumentationUrl(__filename)
},
messages
messages,
fixable: 'code'
}
};
46 changes: 28 additions & 18 deletions test/no-unreadable-array-destructuring.js
@@ -1,6 +1,6 @@
import {test} from './utils/test.js';

test({
test.visualize({
valid: [
'const [, foo] = parts;',
'const [foo] = parts;',
Expand All @@ -22,21 +22,31 @@ test({
// This is stupid, but valid code
'const [,,] = parts;'
],
invalid: []
invalid: [
'const [,, foo] = parts;',
'const [foo,,, bar] = parts;',
'const [foo,,,] = parts;',
'const [foo, bar,, baz ,,, qux] = parts;',
'[,, foo] = bar;',
'({parts: [,, foo]} = bar);',
'function foo([,, bar]) {}',
'function foo([bar,,, baz]) {}',
'function foo([bar,,,]) {}',
'function foo([bar, baz,, qux ,,, quux]) {}',
'const [,,...rest] = parts;',
// This is stupid, but valid code
'const [,,,] = parts;',
// Should add parentheses to array
'const [,,...rest] = new Array;',
'const [,,...rest] = (0, foo);',
'let [,,thirdElement] = new Array;',
'var [,,thirdElement] = (((0, foo)));',
// Variable is not `Identifier`
'let [,,[,,thirdElementInThirdElement]] = foo',
'let [,,{propertyOfThirdElement}] = foo',
// Multiple declarations
'let [,,thirdElement] = foo, anotherVariable = bar;',
// Default value
'let [,,thirdElement = {}] = foo;'
]
});

test.visualize([
'const [,, foo] = parts;',
'const [foo,,, bar] = parts;',
'const [foo,,,] = parts;',
'const [foo, bar,, baz ,,, qux] = parts;',
'[,, foo] = bar;',
'({parts: [,, foo]} = bar);',
'function foo([,, bar]) {}',
'function foo([bar,,, baz]) {}',
'function foo([bar,,,]) {}',
'function foo([bar, baz,, qux ,,, quux]) {}',
'const [,,...rest] = parts;',
// This is stupid, but valid code
'const [,,,] = parts;'
]);
147 changes: 147 additions & 0 deletions test/snapshots/no-unreadable-array-destructuring.js.md
Expand Up @@ -7,6 +7,12 @@ Generated by [AVA](https://avajs.dev).
## Invalid #1
1 | const [,, foo] = parts;

> Output
`␊
1 | const foo = parts[2];␊
`

> Error 1/1
`␊
Expand All @@ -27,6 +33,12 @@ Generated by [AVA](https://avajs.dev).
## Invalid #3
1 | const [foo,,,] = parts;

> Output
`␊
1 | const foo = parts[0];␊
`

> Error 1/1
`␊
Expand Down Expand Up @@ -107,6 +119,12 @@ Generated by [AVA](https://avajs.dev).
## Invalid #11
1 | const [,,...rest] = parts;

> Output
`␊
1 | const rest = parts.slice(2);␊
`

> Error 1/1
`␊
Expand All @@ -123,3 +141,132 @@ Generated by [AVA](https://avajs.dev).
> 1 | const [,,,] = parts;␊
| ^^^^^ Array destructuring may not contain consecutive ignored values.␊
`

## Invalid #13
1 | const [,,...rest] = new Array;

> Output
`␊
1 | const rest = (new Array).slice(2);␊
`

> Error 1/1
`␊
> 1 | const [,,...rest] = new Array;␊
| ^^^^^^^^^^^ Array destructuring may not contain consecutive ignored values.␊
`

## Invalid #14
1 | const [,,...rest] = (0, foo);

> Output
`␊
1 | const rest = (0, foo).slice(2);␊
`

> Error 1/1
`␊
> 1 | const [,,...rest] = (0, foo);␊
| ^^^^^^^^^^^ Array destructuring may not contain consecutive ignored values.␊
`

## Invalid #15
1 | let [,,thirdElement] = new Array;

> Output
`␊
1 | let thirdElement = (new Array)[2];␊
`

> Error 1/1
`␊
> 1 | let [,,thirdElement] = new Array;␊
| ^^^^^^^^^^^^^^^^ Array destructuring may not contain consecutive ignored values.␊
`

## Invalid #16
1 | var [,,thirdElement] = (((0, foo)));

> Output
`␊
1 | var thirdElement = (((0, foo)))[2];␊
`

> Error 1/1
`␊
> 1 | var [,,thirdElement] = (((0, foo)));␊
| ^^^^^^^^^^^^^^^^ Array destructuring may not contain consecutive ignored values.␊
`

## Invalid #17
1 | let [,,[,,thirdElementInThirdElement]] = foo

> Output
`␊
1 | let thirdElementInThirdElement = foo[2][2]␊
`

> Error 1/2
`␊
> 1 | let [,,[,,thirdElementInThirdElement]] = foo␊
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Array destructuring may not contain consecutive ignored values.␊
`

> Error 2/2
`␊
> 1 | let [,,[,,thirdElementInThirdElement]] = foo␊
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Array destructuring may not contain consecutive ignored values.␊
`

## Invalid #18
1 | let [,,{propertyOfThirdElement}] = foo

> Output
`␊
1 | let {propertyOfThirdElement} = foo[2]␊
`

> Error 1/1
`␊
> 1 | let [,,{propertyOfThirdElement}] = foo␊
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Array destructuring may not contain consecutive ignored values.␊
`

## Invalid #19
1 | let [,,thirdElement] = foo, anotherVariable = bar;

> Output
`␊
1 | let thirdElement = foo[2], anotherVariable = bar;␊
`

> Error 1/1
`␊
> 1 | let [,,thirdElement] = foo, anotherVariable = bar;␊
| ^^^^^^^^^^^^^^^^ Array destructuring may not contain consecutive ignored values.␊
`

## Invalid #20
1 | let [,,thirdElement = {}] = foo;

> Error 1/1
`␊
> 1 | let [,,thirdElement = {}] = foo;␊
| ^^^^^^^^^^^^^^^^^^^^^ Array destructuring may not contain consecutive ignored values.␊
`
Binary file modified test/snapshots/no-unreadable-array-destructuring.js.snap
Binary file not shown.

0 comments on commit 27bc3c3

Please sign in to comment.