eslint-plugin-unicorn 53.0.0 β 54.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +10 -7
- package/package.json +5 -5
- package/readme.md +3 -2
- package/rules/ast/is-reference-identifier.js +7 -0
- package/rules/fix/switch-call-expression-to-new-expression.js +2 -0
- package/rules/no-negation-in-equality-check.js +104 -0
- package/rules/prefer-array-find.js +3 -3
- package/rules/prefer-array-flat.js +0 -4
- package/rules/prefer-array-some.js +64 -7
- package/rules/prefer-includes.js +7 -4
- package/rules/prefer-modern-math-apis.js +0 -1
- package/rules/prefer-string-raw.js +1 -0
- package/rules/prefer-structured-clone.js +0 -1
package/index.js
CHANGED
|
@@ -47,9 +47,12 @@ const allRules = Object.fromEntries(
|
|
|
47
47
|
]),
|
|
48
48
|
);
|
|
49
49
|
|
|
50
|
-
const createConfig = (rules,
|
|
51
|
-
...(
|
|
52
|
-
|
|
50
|
+
const createConfig = (rules, flatConfigName = false) => ({
|
|
51
|
+
...(
|
|
52
|
+
flatConfigName
|
|
53
|
+
? {...flatConfigBase, name: flatConfigName, plugins: {unicorn}}
|
|
54
|
+
: {...legacyConfigBase, plugins: ['unicorn']}
|
|
55
|
+
),
|
|
53
56
|
rules: {...externalRules, ...rules},
|
|
54
57
|
});
|
|
55
58
|
|
|
@@ -65,10 +68,10 @@ const unicorn = {
|
|
|
65
68
|
};
|
|
66
69
|
|
|
67
70
|
const configs = {
|
|
68
|
-
recommended: createConfig(recommendedRules
|
|
69
|
-
all: createConfig(allRules
|
|
70
|
-
'flat/recommended': createConfig(recommendedRules),
|
|
71
|
-
'flat/all': createConfig(allRules),
|
|
71
|
+
recommended: createConfig(recommendedRules),
|
|
72
|
+
all: createConfig(allRules),
|
|
73
|
+
'flat/recommended': createConfig(recommendedRules, 'unicorn/flat/recommended'),
|
|
74
|
+
'flat/all': createConfig(allRules, 'unicorn/flat/all'),
|
|
72
75
|
};
|
|
73
76
|
|
|
74
77
|
module.exports = {...unicorn, configs};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-unicorn",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "54.0.0",
|
|
4
4
|
"description": "More than 100 powerful ESLint rules",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "sindresorhus/eslint-plugin-unicorn",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"lint:markdown": "markdownlint \"**/*.md\"",
|
|
30
30
|
"lint:package-json": "npmPkgJsonLint .",
|
|
31
31
|
"run-rules-on-codebase": "node ./test/run-rules-on-codebase/lint.mjs",
|
|
32
|
-
"smoke": "eslint-remote-tester --config ./test/smoke/eslint-remote-tester.config.
|
|
32
|
+
"smoke": "eslint-remote-tester --config ./test/smoke/eslint-remote-tester.config.mjs",
|
|
33
33
|
"test": "npm-run-all --continue-on-error lint test:*",
|
|
34
34
|
"test:js": "c8 ava"
|
|
35
35
|
},
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"@babel/core": "^7.24.5",
|
|
72
72
|
"@babel/eslint-parser": "^7.24.5",
|
|
73
73
|
"@lubien/fixture-beta-package": "^1.0.0-beta.1",
|
|
74
|
-
"@typescript-eslint/parser": "^
|
|
74
|
+
"@typescript-eslint/parser": "^8.0.0-alpha.12",
|
|
75
75
|
"ava": "^6.1.3",
|
|
76
76
|
"c8": "^9.1.0",
|
|
77
77
|
"chalk": "^5.3.0",
|
|
@@ -81,8 +81,8 @@
|
|
|
81
81
|
"eslint-doc-generator": "1.7.0",
|
|
82
82
|
"eslint-plugin-eslint-plugin": "^6.1.0",
|
|
83
83
|
"eslint-plugin-internal-rules": "file:./scripts/internal-rules/",
|
|
84
|
-
"eslint-remote-tester": "^
|
|
85
|
-
"eslint-remote-tester-repositories": "^
|
|
84
|
+
"eslint-remote-tester": "^4.0.0",
|
|
85
|
+
"eslint-remote-tester-repositories": "^2.0.0",
|
|
86
86
|
"espree": "^10.0.1",
|
|
87
87
|
"execa": "^8.0.1",
|
|
88
88
|
"listr": "^0.14.3",
|
package/readme.md
CHANGED
|
@@ -145,6 +145,7 @@ If you don't use the preset, ensure you use the same `env` and `parserOptions` c
|
|
|
145
145
|
| [no-lonely-if](docs/rules/no-lonely-if.md) | Disallow `if` statements as the only statement in `if` blocks without `else`. | β
| π§ | |
|
|
146
146
|
| [no-magic-array-flat-depth](docs/rules/no-magic-array-flat-depth.md) | Disallow a magic number as the `depth` argument in `Array#flat(β¦).` | β
| | |
|
|
147
147
|
| [no-negated-condition](docs/rules/no-negated-condition.md) | Disallow negated conditions. | β
| π§ | |
|
|
148
|
+
| [no-negation-in-equality-check](docs/rules/no-negation-in-equality-check.md) | Disallow negated expression in equality check. | β
| | π‘ |
|
|
148
149
|
| [no-nested-ternary](docs/rules/no-nested-ternary.md) | Disallow nested ternary expressions. | β
| π§ | |
|
|
149
150
|
| [no-new-array](docs/rules/no-new-array.md) | Disallow `new Array()`. | β
| π§ | π‘ |
|
|
150
151
|
| [no-new-buffer](docs/rules/no-new-buffer.md) | Enforce the use of `Buffer.from()` and `Buffer.alloc()` instead of the deprecated `new Buffer()`. | β
| π§ | π‘ |
|
|
@@ -175,7 +176,7 @@ If you don't use the preset, ensure you use the same `env` and `parserOptions` c
|
|
|
175
176
|
| [prefer-array-flat](docs/rules/prefer-array-flat.md) | Prefer `Array#flat()` over legacy techniques to flatten arrays. | β
| π§ | |
|
|
176
177
|
| [prefer-array-flat-map](docs/rules/prefer-array-flat-map.md) | Prefer `.flatMap(β¦)` over `.map(β¦).flat()`. | β
| π§ | |
|
|
177
178
|
| [prefer-array-index-of](docs/rules/prefer-array-index-of.md) | Prefer `Array#{indexOf,lastIndexOf}()` over `Array#{findIndex,findLastIndex}()` when looking for the index of an item. | β
| π§ | π‘ |
|
|
178
|
-
| [prefer-array-some](docs/rules/prefer-array-some.md) | Prefer `.some(β¦)` over `.filter(β¦).length` check and `.{find,findLast}(β¦)`.
|
|
179
|
+
| [prefer-array-some](docs/rules/prefer-array-some.md) | Prefer `.some(β¦)` over `.filter(β¦).length` check and `.{find,findLast,findIndex,findLastIndex}(β¦)`. | β
| π§ | π‘ |
|
|
179
180
|
| [prefer-at](docs/rules/prefer-at.md) | Prefer `.at()` method for index access and `String#charAt()`. | β
| π§ | π‘ |
|
|
180
181
|
| [prefer-blob-reading-methods](docs/rules/prefer-blob-reading-methods.md) | Prefer `Blob#arrayBuffer()` over `FileReader#readAsArrayBuffer(β¦)` and `Blob#text()` over `FileReader#readAsText(β¦)`. | β
| | |
|
|
181
182
|
| [prefer-code-point](docs/rules/prefer-code-point.md) | Prefer `String#codePointAt(β¦)` over `String#charCodeAt(β¦)` and `String.fromCodePoint(β¦)` over `String.fromCharCode(β¦)`. | β
| | π‘ |
|
|
@@ -187,7 +188,7 @@ If you don't use the preset, ensure you use the same `env` and `parserOptions` c
|
|
|
187
188
|
| [prefer-dom-node-text-content](docs/rules/prefer-dom-node-text-content.md) | Prefer `.textContent` over `.innerText`. | β
| | π‘ |
|
|
188
189
|
| [prefer-event-target](docs/rules/prefer-event-target.md) | Prefer `EventTarget` over `EventEmitter`. | β
| | |
|
|
189
190
|
| [prefer-export-from](docs/rules/prefer-export-from.md) | Prefer `exportβ¦from` when re-exporting. | β
| π§ | π‘ |
|
|
190
|
-
| [prefer-includes](docs/rules/prefer-includes.md) | Prefer `.includes()` over `.indexOf()
|
|
191
|
+
| [prefer-includes](docs/rules/prefer-includes.md) | Prefer `.includes()` over `.indexOf()`, `.lastIndexOf()`, and `Array#some()` when checking for existence or non-existence. | β
| π§ | π‘ |
|
|
191
192
|
| [prefer-json-parse-buffer](docs/rules/prefer-json-parse-buffer.md) | Prefer reading a JSON file as a buffer. | | π§ | |
|
|
192
193
|
| [prefer-keyboard-event-key](docs/rules/prefer-keyboard-event-key.md) | Prefer `KeyboardEvent#key` over `KeyboardEvent#keyCode`. | β
| π§ | |
|
|
193
194
|
| [prefer-logical-operator-over-ternary](docs/rules/prefer-logical-operator-over-ternary.md) | Prefer using a logical operator over a ternary. | β
| | π‘ |
|
|
@@ -120,11 +120,18 @@ function isNotReference(node) {
|
|
|
120
120
|
return parent.parameters.includes(node);
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
// `@typescript-eslint/parse` v7
|
|
123
124
|
// `type Foo = { [Identifier in keyof string]: number; };`
|
|
124
125
|
case 'TSTypeParameter': {
|
|
125
126
|
return parent.name === node;
|
|
126
127
|
}
|
|
127
128
|
|
|
129
|
+
// `@typescript-eslint/parse` v8
|
|
130
|
+
// `type Foo = { [Identifier in keyof string]: number; };`
|
|
131
|
+
case 'TSMappedType': {
|
|
132
|
+
return parent.key === node;
|
|
133
|
+
}
|
|
134
|
+
|
|
128
135
|
// `type Identifier = Foo`
|
|
129
136
|
case 'TSTypeAliasDeclaration': {
|
|
130
137
|
return parent.id === node;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
const {isParenthesized} = require('../utils/parentheses.js');
|
|
3
3
|
const shouldAddParenthesesToNewExpressionCallee = require('../utils/should-add-parentheses-to-new-expression-callee.js');
|
|
4
|
+
const fixSpaceAroundKeyword = require('./fix-space-around-keywords.js');
|
|
4
5
|
|
|
5
6
|
function * switchCallExpressionToNewExpression(node, sourceCode, fixer) {
|
|
7
|
+
yield * fixSpaceAroundKeyword(fixer, node, sourceCode);
|
|
6
8
|
yield fixer.insertTextBefore(node, 'new ');
|
|
7
9
|
|
|
8
10
|
const {callee} = node;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const {
|
|
3
|
+
fixSpaceAroundKeyword,
|
|
4
|
+
addParenthesizesToReturnOrThrowExpression,
|
|
5
|
+
} = require('./fix/index.js');
|
|
6
|
+
const {
|
|
7
|
+
needsSemicolon,
|
|
8
|
+
isParenthesized,
|
|
9
|
+
isOnSameLine,
|
|
10
|
+
} = require('./utils/index.js');
|
|
11
|
+
|
|
12
|
+
const MESSAGE_ID_ERROR = 'no-negation-in-equality-check/error';
|
|
13
|
+
const MESSAGE_ID_SUGGESTION = 'no-negation-in-equality-check/suggestion';
|
|
14
|
+
const messages = {
|
|
15
|
+
[MESSAGE_ID_ERROR]: 'Negated expression in not allowed in equality check.',
|
|
16
|
+
[MESSAGE_ID_SUGGESTION]: 'Switch to \'{{operator}}\' check.',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const EQUALITY_OPERATORS = new Set([
|
|
20
|
+
'===',
|
|
21
|
+
'!==',
|
|
22
|
+
'==',
|
|
23
|
+
'!=',
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
const isEqualityCheck = node => node.type === 'BinaryExpression' && EQUALITY_OPERATORS.has(node.operator);
|
|
27
|
+
const isNegatedExpression = node => node.type === 'UnaryExpression' && node.prefix && node.operator === '!';
|
|
28
|
+
|
|
29
|
+
/** @param {import('eslint').Rule.RuleContext} context */
|
|
30
|
+
const create = context => ({
|
|
31
|
+
BinaryExpression(binaryExpression) {
|
|
32
|
+
const {operator, left} = binaryExpression;
|
|
33
|
+
|
|
34
|
+
if (
|
|
35
|
+
!isEqualityCheck(binaryExpression)
|
|
36
|
+
|| !isNegatedExpression(left)
|
|
37
|
+
) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const {sourceCode} = context;
|
|
42
|
+
const bangToken = sourceCode.getFirstToken(left);
|
|
43
|
+
const negatedOperator = `${operator.startsWith('!') ? '=' : '!'}${operator.slice(1)}`;
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
node: bangToken,
|
|
47
|
+
messageId: MESSAGE_ID_ERROR,
|
|
48
|
+
/** @param {import('eslint').Rule.RuleFixer} fixer */
|
|
49
|
+
suggest: [
|
|
50
|
+
{
|
|
51
|
+
messageId: MESSAGE_ID_SUGGESTION,
|
|
52
|
+
data: {
|
|
53
|
+
operator: negatedOperator,
|
|
54
|
+
},
|
|
55
|
+
/** @param {import('eslint').Rule.RuleFixer} fixer */
|
|
56
|
+
* fix(fixer) {
|
|
57
|
+
yield * fixSpaceAroundKeyword(fixer, binaryExpression, sourceCode);
|
|
58
|
+
|
|
59
|
+
const tokenAfterBang = sourceCode.getTokenAfter(bangToken);
|
|
60
|
+
|
|
61
|
+
const {parent} = binaryExpression;
|
|
62
|
+
if (
|
|
63
|
+
(parent.type === 'ReturnStatement' || parent.type === 'ThrowStatement')
|
|
64
|
+
&& !isParenthesized(binaryExpression, sourceCode)
|
|
65
|
+
) {
|
|
66
|
+
const returnToken = sourceCode.getFirstToken(parent);
|
|
67
|
+
if (!isOnSameLine(returnToken, tokenAfterBang)) {
|
|
68
|
+
yield * addParenthesizesToReturnOrThrowExpression(fixer, parent, sourceCode);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
yield fixer.remove(bangToken);
|
|
73
|
+
|
|
74
|
+
const previousToken = sourceCode.getTokenBefore(bangToken);
|
|
75
|
+
if (needsSemicolon(previousToken, sourceCode, tokenAfterBang.value)) {
|
|
76
|
+
yield fixer.insertTextAfter(bangToken, ';');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const operatorToken = sourceCode.getTokenAfter(
|
|
80
|
+
left,
|
|
81
|
+
token => token.type === 'Punctuator' && token.value === operator,
|
|
82
|
+
);
|
|
83
|
+
yield fixer.replaceText(operatorToken, negatedOperator);
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
};
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
/** @type {import('eslint').Rule.RuleModule} */
|
|
92
|
+
module.exports = {
|
|
93
|
+
create,
|
|
94
|
+
meta: {
|
|
95
|
+
type: 'problem',
|
|
96
|
+
docs: {
|
|
97
|
+
description: 'Disallow negated expression in equality check.',
|
|
98
|
+
recommended: true,
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
hasSuggestions: true,
|
|
102
|
+
messages,
|
|
103
|
+
},
|
|
104
|
+
};
|
|
@@ -180,7 +180,7 @@ const create = context => {
|
|
|
180
180
|
const {
|
|
181
181
|
checkFromLast,
|
|
182
182
|
} = {
|
|
183
|
-
checkFromLast:
|
|
183
|
+
checkFromLast: true,
|
|
184
184
|
...context.options[0],
|
|
185
185
|
};
|
|
186
186
|
|
|
@@ -428,8 +428,8 @@ const schema = [
|
|
|
428
428
|
properties: {
|
|
429
429
|
checkFromLast: {
|
|
430
430
|
type: 'boolean',
|
|
431
|
-
// TODO:
|
|
432
|
-
default:
|
|
431
|
+
// TODO: Remove the option at some point.
|
|
432
|
+
default: true,
|
|
433
433
|
},
|
|
434
434
|
},
|
|
435
435
|
},
|
|
@@ -46,7 +46,6 @@ const arrayFlatMap = {
|
|
|
46
46
|
},
|
|
47
47
|
getArrayNode: node => node.callee.object,
|
|
48
48
|
description: 'Array#flatMap()',
|
|
49
|
-
recommended: true,
|
|
50
49
|
};
|
|
51
50
|
|
|
52
51
|
// `array.reduce((a, b) => a.concat(b), [])`
|
|
@@ -100,7 +99,6 @@ const arrayReduce = {
|
|
|
100
99
|
},
|
|
101
100
|
getArrayNode: node => node.callee.object,
|
|
102
101
|
description: 'Array#reduce()',
|
|
103
|
-
recommended: true,
|
|
104
102
|
};
|
|
105
103
|
|
|
106
104
|
// `[].concat(maybeArray)`
|
|
@@ -121,7 +119,6 @@ const emptyArrayConcat = {
|
|
|
121
119
|
return argumentNode.type === 'SpreadElement' ? argumentNode.argument : argumentNode;
|
|
122
120
|
},
|
|
123
121
|
description: '[].concat()',
|
|
124
|
-
recommended: true,
|
|
125
122
|
shouldSwitchToArray: node => node.arguments[0].type !== 'SpreadElement',
|
|
126
123
|
};
|
|
127
124
|
|
|
@@ -157,7 +154,6 @@ const arrayPrototypeConcat = {
|
|
|
157
154
|
return argumentNode.type === 'SpreadElement' ? argumentNode.argument : argumentNode;
|
|
158
155
|
},
|
|
159
156
|
description: 'Array.prototype.concat()',
|
|
160
|
-
recommended: true,
|
|
161
157
|
shouldSwitchToArray: node => node.arguments[1].type !== 'SpreadElement' && node.callee.property.name === 'call',
|
|
162
158
|
};
|
|
163
159
|
|
|
@@ -40,10 +40,14 @@ const isCheckingUndefined = node =>
|
|
|
40
40
|
&& isLiteral(node.parent.right, null)
|
|
41
41
|
)
|
|
42
42
|
);
|
|
43
|
+
const isNegativeOne = node => node.type === 'UnaryExpression' && node.operator === '-' && node.argument && node.argument.type === 'Literal' && node.argument.value === 1;
|
|
44
|
+
const isLiteralZero = node => isLiteral(node, 0);
|
|
43
45
|
|
|
44
46
|
/** @param {import('eslint').Rule.RuleContext} context */
|
|
45
|
-
const create = context =>
|
|
46
|
-
|
|
47
|
+
const create = context => {
|
|
48
|
+
// `.find(β¦)`
|
|
49
|
+
// `.findLast(β¦)`
|
|
50
|
+
context.on('CallExpression', callExpression => {
|
|
47
51
|
if (!isMethodCall(callExpression, {
|
|
48
52
|
methods: ['find', 'findLast'],
|
|
49
53
|
minimumArguments: 1,
|
|
@@ -86,8 +90,61 @@ const create = context => ({
|
|
|
86
90
|
},
|
|
87
91
|
],
|
|
88
92
|
};
|
|
89
|
-
}
|
|
90
|
-
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// These operators also used in `prefer-includes`, try to reuse the code in future
|
|
96
|
+
// `.{findIndex,findLastIndex}(β¦) !== -1`
|
|
97
|
+
// `.{findIndex,findLastIndex}(β¦) != -1`
|
|
98
|
+
// `.{findIndex,findLastIndex}(β¦) > -1`
|
|
99
|
+
// `.{findIndex,findLastIndex}(β¦) === -1`
|
|
100
|
+
// `.{findIndex,findLastIndex}(β¦) == -1`
|
|
101
|
+
// `.{findIndex,findLastIndex}(β¦) >= 0`
|
|
102
|
+
// `.{findIndex,findLastIndex}(β¦) < 0`
|
|
103
|
+
context.on('BinaryExpression', binaryExpression => {
|
|
104
|
+
const {left, right, operator} = binaryExpression;
|
|
105
|
+
|
|
106
|
+
if (!(
|
|
107
|
+
isMethodCall(left, {
|
|
108
|
+
methods: ['findIndex', 'findLastIndex'],
|
|
109
|
+
argumentsLength: 1,
|
|
110
|
+
optionalCall: false,
|
|
111
|
+
optionalMember: false,
|
|
112
|
+
})
|
|
113
|
+
&& (
|
|
114
|
+
(['!==', '!=', '>', '===', '=='].includes(operator) && isNegativeOne(right))
|
|
115
|
+
|| (['>=', '<'].includes(operator) && isLiteralZero(right))
|
|
116
|
+
)
|
|
117
|
+
)) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const methodNode = left.callee.property;
|
|
122
|
+
return {
|
|
123
|
+
node: methodNode,
|
|
124
|
+
messageId: ERROR_ID_ARRAY_SOME,
|
|
125
|
+
data: {method: methodNode.name},
|
|
126
|
+
* fix(fixer) {
|
|
127
|
+
if (['===', '==', '<'].includes(operator)) {
|
|
128
|
+
yield fixer.insertTextBefore(binaryExpression, '!');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
yield fixer.replaceText(methodNode, 'some');
|
|
132
|
+
|
|
133
|
+
const operatorToken = context.sourceCode.getTokenAfter(
|
|
134
|
+
left,
|
|
135
|
+
token => token.type === 'Punctuator' && token.value === operator,
|
|
136
|
+
);
|
|
137
|
+
const [start] = operatorToken.range;
|
|
138
|
+
const [, end] = binaryExpression.range;
|
|
139
|
+
|
|
140
|
+
yield fixer.removeRange([start, end]);
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// `.filter(β¦).length > 0`
|
|
146
|
+
// `.filter(β¦).length !== 0`
|
|
147
|
+
context.on('BinaryExpression', binaryExpression => {
|
|
91
148
|
if (!(
|
|
92
149
|
// We assume the user already follows `unicorn/explicit-length-check`. These are allowed in that rule.
|
|
93
150
|
(binaryExpression.operator === '>' || binaryExpression.operator === '!==')
|
|
@@ -139,8 +196,8 @@ const create = context => ({
|
|
|
139
196
|
// The `BinaryExpression` always ends with a number or `)`, no need check for ASI
|
|
140
197
|
},
|
|
141
198
|
};
|
|
142
|
-
}
|
|
143
|
-
}
|
|
199
|
+
});
|
|
200
|
+
};
|
|
144
201
|
|
|
145
202
|
/** @type {import('eslint').Rule.RuleModule} */
|
|
146
203
|
module.exports = {
|
|
@@ -148,7 +205,7 @@ module.exports = {
|
|
|
148
205
|
meta: {
|
|
149
206
|
type: 'suggestion',
|
|
150
207
|
docs: {
|
|
151
|
-
description: 'Prefer `.some(β¦)` over `.filter(β¦).length` check and `.{find,findLast}(β¦)`.',
|
|
208
|
+
description: 'Prefer `.some(β¦)` over `.filter(β¦).length` check and `.{find,findLast,findIndex,findLastIndex}(β¦)`.',
|
|
152
209
|
recommended: true,
|
|
153
210
|
},
|
|
154
211
|
fixable: 'code',
|
package/rules/prefer-includes.js
CHANGED
|
@@ -5,9 +5,9 @@ const {isLiteral} = require('./ast/index.js');
|
|
|
5
5
|
|
|
6
6
|
const MESSAGE_ID = 'prefer-includes';
|
|
7
7
|
const messages = {
|
|
8
|
-
[MESSAGE_ID]: 'Use `.includes()`, rather than `.
|
|
8
|
+
[MESSAGE_ID]: 'Use `.includes()`, rather than `.{{method}}()`, when checking for existence.',
|
|
9
9
|
};
|
|
10
|
-
// Ignore {_,lodash,underscore}.indexOf
|
|
10
|
+
// Ignore `{_,lodash,underscore}.{indexOf,lastIndexOf}`
|
|
11
11
|
const ignoredVariables = new Set(['_', 'lodash', 'underscore']);
|
|
12
12
|
const isIgnoredTarget = node => node.type === 'Identifier' && ignoredVariables.has(node.name);
|
|
13
13
|
const isNegativeOne = node => node.type === 'UnaryExpression' && node.operator === '-' && node.argument && node.argument.type === 'Literal' && node.argument.value === 1;
|
|
@@ -30,6 +30,9 @@ const getProblem = (context, node, target, argumentsNodes) => {
|
|
|
30
30
|
return {
|
|
31
31
|
node: memberExpressionNode.property,
|
|
32
32
|
messageId: MESSAGE_ID,
|
|
33
|
+
data: {
|
|
34
|
+
method: node.left.callee.property.name,
|
|
35
|
+
},
|
|
33
36
|
fix(fixer) {
|
|
34
37
|
const replacement = `${isNegativeResult(node) ? '!' : ''}${targetSource}.includes(${argumentsSource.join(', ')})`;
|
|
35
38
|
return fixer.replaceText(node, replacement);
|
|
@@ -49,7 +52,7 @@ const create = context => {
|
|
|
49
52
|
context.on('BinaryExpression', node => {
|
|
50
53
|
const {left, right, operator} = node;
|
|
51
54
|
|
|
52
|
-
if (!isMethodNamed(left, 'indexOf')) {
|
|
55
|
+
if (!isMethodNamed(left, 'indexOf') && !isMethodNamed(left, 'lastIndexOf')) {
|
|
53
56
|
return;
|
|
54
57
|
}
|
|
55
58
|
|
|
@@ -86,7 +89,7 @@ module.exports = {
|
|
|
86
89
|
meta: {
|
|
87
90
|
type: 'suggestion',
|
|
88
91
|
docs: {
|
|
89
|
-
description: 'Prefer `.includes()` over `.indexOf()
|
|
92
|
+
description: 'Prefer `.includes()` over `.indexOf()`, `.lastIndexOf()`, and `Array#some()` when checking for existence or non-existence.',
|
|
90
93
|
recommended: true,
|
|
91
94
|
},
|
|
92
95
|
fixable: 'code',
|
|
@@ -47,6 +47,7 @@ const create = context => {
|
|
|
47
47
|
)
|
|
48
48
|
|| (node.parent.type === 'Property' && !node.parent.computed && node.parent.key === node)
|
|
49
49
|
|| (node.parent.type === 'JSXAttribute' && node.parent.value === node)
|
|
50
|
+
|| (node.parent.type === 'TSEnumMember' && (node.parent.initializer === node || node.parent.id === node))
|
|
50
51
|
) {
|
|
51
52
|
return;
|
|
52
53
|
}
|