Skip to content

Commit

Permalink
prefer-array-some: Check cases comparing .find() with undefined (
Browse files Browse the repository at this point in the history
…#1422)

Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
  • Loading branch information
fisker and sindresorhus committed Jul 14, 2021
1 parent d0ed9c0 commit 3dc2f77
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 2 deletions.
10 changes: 10 additions & 0 deletions docs/rules/prefer-array-some.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ We only check `.filter().length > 0` and `.filter().length !== 0`. These two non

- Using [`Array#find()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find) to ensure at least one element in the array passes a given check.

- Comparing the result of [`Array#find()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find) with `undefined`.

This rule is fixable for `.filter(…).length` check and has a suggestion for `.find(…)`.

## Fail
Expand All @@ -34,6 +36,14 @@ if (array.find(element => isUnicorn(element))) {
const foo = array.find(element => isUnicorn(element)) ? bar : baz;
```

```js
const hasUnicorn = array.find(element => isUnicorn(element) !== undefined;
```
```js
const hasUnicorn = array.find(element => isUnicorn(element) != null;
```
```vue
<template>
<div v-if="array.find(element => isUnicorn(element))">Vue</div>
Expand Down
46 changes: 44 additions & 2 deletions rules/prefer-array-some.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const {methodCallSelector, matches, memberExpressionSelector} = require('./selec
const {checkVueTemplate} = require('./utils/rule.js');
const {isBooleanNode} = require('./utils/boolean.js');
const {getParenthesizedRange} = require('./utils/parentheses.js');
const isLiteralValue = require('./utils/is-literal-value.js');
const {removeMemberExpressionProperty} = require('./fix/index.js');

const ERROR_ID_ARRAY_SOME = 'some';
Expand All @@ -20,6 +21,31 @@ const arrayFindCallSelector = methodCallSelector({
max: 2,
});

const isCheckingUndefined = node =>
node.parent.type === 'BinaryExpression' &&
// Not checking yoda expression `null != foo.find()` and `undefined !== foo.find()
node.parent.left === node &&
(
(
(
node.parent.operator === '!=' ||
node.parent.operator === '==' ||
node.parent.operator === '===' ||
node.parent.operator === '!=='
) &&
node.parent.right.type === 'Identifier' &&
node.parent.right.name === 'undefined'
) ||
(
(
node.parent.operator === '!=' ||
node.parent.operator === '=='
) &&
// eslint-disable-next-line unicorn/no-null
isLiteralValue(node.parent.right, null)
)
);

const arrayFilterCallSelector = [
'BinaryExpression',
'[right.type="Literal"]',
Expand All @@ -35,7 +61,8 @@ const arrayFilterCallSelector = [
const create = context => {
return {
[arrayFindCallSelector](findCall) {
if (!isBooleanNode(findCall)) {
const isCompare = isCheckingUndefined(findCall);
if (!isCompare && !isBooleanNode(findCall)) {
return;
}

Expand All @@ -46,7 +73,22 @@ const create = context => {
suggest: [
{
messageId: SUGGESTION_ID_ARRAY_SOME,
fix: fixer => fixer.replaceText(findProperty, 'some'),
* fix(fixer) {
yield fixer.replaceText(findProperty, 'some');

if (!isCompare) {
return;
}

const parenthesizedRange = getParenthesizedRange(findCall, context.getSourceCode());
yield fixer.replaceTextRange([parenthesizedRange[1], findCall.parent.range[1]], '');

if (findCall.parent.operator === '!=' || findCall.parent.operator === '!==') {
return;
}

yield fixer.insertTextBeforeRange(parenthesizedRange, '!');
},
},
],
};
Expand Down
23 changes: 23 additions & 0 deletions test/prefer-array-some.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -206,3 +206,26 @@ test.vue({
},
],
});

// Compare with `undefined`
test.snapshot({
valid: [
'foo.find(fn) == 0',
'foo.find(fn) != ""',
'foo.find(fn) === null',
'foo.find(fn) !== "null"',
'foo.find(fn) >= undefined',
'foo.find(fn) instanceof undefined',
// We are not checking this right now
'typeof foo.find(fn) === "undefined"',
],
invalid: [
'foo.find(fn) == null',
'foo.find(fn) == undefined',
'foo.find(fn) === undefined',
'foo.find(fn) != null',
'foo.find(fn) != undefined',
'foo.find(fn) !== undefined',
'a = (( ((foo.find(fn))) == ((null)) )) ? "no" : "yes";',
],
});
98 changes: 98 additions & 0 deletions test/snapshots/prefer-array-some.mjs.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,3 +153,101 @@ Generated by [AVA](https://avajs.dev).
14 | ))␊
15 | );␊
`

## Invalid #1
1 | foo.find(fn) == null

> Error 1/1
`␊
> 1 | foo.find(fn) == null␊
| ^^^^ Prefer \`.some(…)\` over \`.find(…)\`.␊
--------------------------------------------------------------------------------␊
Suggestion 1/1: Replace \`.find(…)\` with \`.some(…)\`.␊
1 | !foo.some(fn)␊
`

## Invalid #2
1 | foo.find(fn) == undefined

> Error 1/1
`␊
> 1 | foo.find(fn) == undefined␊
| ^^^^ Prefer \`.some(…)\` over \`.find(…)\`.␊
--------------------------------------------------------------------------------␊
Suggestion 1/1: Replace \`.find(…)\` with \`.some(…)\`.␊
1 | !foo.some(fn)␊
`

## Invalid #3
1 | foo.find(fn) === undefined

> Error 1/1
`␊
> 1 | foo.find(fn) === undefined␊
| ^^^^ Prefer \`.some(…)\` over \`.find(…)\`.␊
--------------------------------------------------------------------------------␊
Suggestion 1/1: Replace \`.find(…)\` with \`.some(…)\`.␊
1 | !foo.some(fn)␊
`

## Invalid #4
1 | foo.find(fn) != null

> Error 1/1
`␊
> 1 | foo.find(fn) != null␊
| ^^^^ Prefer \`.some(…)\` over \`.find(…)\`.␊
--------------------------------------------------------------------------------␊
Suggestion 1/1: Replace \`.find(…)\` with \`.some(…)\`.␊
1 | foo.some(fn)␊
`

## Invalid #5
1 | foo.find(fn) != undefined

> Error 1/1
`␊
> 1 | foo.find(fn) != undefined␊
| ^^^^ Prefer \`.some(…)\` over \`.find(…)\`.␊
--------------------------------------------------------------------------------␊
Suggestion 1/1: Replace \`.find(…)\` with \`.some(…)\`.␊
1 | foo.some(fn)␊
`

## Invalid #6
1 | foo.find(fn) !== undefined

> Error 1/1
`␊
> 1 | foo.find(fn) !== undefined␊
| ^^^^ Prefer \`.some(…)\` over \`.find(…)\`.␊
--------------------------------------------------------------------------------␊
Suggestion 1/1: Replace \`.find(…)\` with \`.some(…)\`.␊
1 | foo.some(fn)␊
`

## Invalid #7
1 | a = (( ((foo.find(fn))) == ((null)) )) ? "no" : "yes";

> Error 1/1
`␊
> 1 | a = (( ((foo.find(fn))) == ((null)) )) ? "no" : "yes";␊
| ^^^^ Prefer \`.some(…)\` over \`.find(…)\`.␊
--------------------------------------------------------------------------------␊
Suggestion 1/1: Replace \`.find(…)\` with \`.some(…)\`.␊
1 | a = (( !((foo.some(fn))) )) ? "no" : "yes";␊
`
Binary file modified test/snapshots/prefer-array-some.mjs.snap
Binary file not shown.

0 comments on commit 3dc2f77

Please sign in to comment.