1 | 'use strict';
|
2 | const getDocumentationUrl = require('./utils/get-documentation-url');
|
3 | const isMethodNamed = require('./utils/is-method-named');
|
4 | const isLiteralValue = require('./utils/is-literal-value');
|
5 |
|
6 | const message = 'Use `.includes()`, rather than `.indexOf()`, when checking for existence.';
|
7 |
|
8 | const ignoredVariables = new Set(['_', 'lodash', 'underscore']);
|
9 | const isIgnoredTarget = node => node.type === 'Identifier' && ignoredVariables.has(node.name);
|
10 | const isNegativeOne = node => node.type === 'UnaryExpression' && node.operator === '-' && node.argument && node.argument.type === 'Literal' && node.argument.value === 1;
|
11 | const isLiteralZero = node => isLiteralValue(node, 0);
|
12 | const isNegativeResult = node => ['===', '==', '<'].includes(node.operator);
|
13 |
|
14 | const report = (context, node, target, argumentsNodes) => {
|
15 | const sourceCode = context.getSourceCode();
|
16 | const memberExpressionNode = target.parent;
|
17 | const dotToken = sourceCode.getTokenBefore(memberExpressionNode.property);
|
18 | const targetSource = sourceCode.getText().slice(memberExpressionNode.range[0], dotToken.range[0]);
|
19 |
|
20 |
|
21 | if (isLiteralZero(argumentsNodes[1])) {
|
22 | argumentsNodes = argumentsNodes.slice(0, 1);
|
23 | }
|
24 |
|
25 | const argumentsSource = argumentsNodes.map(argument => sourceCode.getText(argument));
|
26 |
|
27 | context.report({
|
28 | node,
|
29 | message,
|
30 | fix: fixer => {
|
31 | const replacement = `${isNegativeResult(node) ? '!' : ''}${targetSource}.includes(${argumentsSource.join(', ')})`;
|
32 | return fixer.replaceText(node, replacement);
|
33 | }
|
34 | });
|
35 | };
|
36 |
|
37 | const create = context => ({
|
38 | BinaryExpression: node => {
|
39 | const {left, right} = node;
|
40 |
|
41 | if (!isMethodNamed(left, 'indexOf')) {
|
42 | return;
|
43 | }
|
44 |
|
45 | const target = left.callee.object;
|
46 |
|
47 | if (isIgnoredTarget(target)) {
|
48 | return;
|
49 | }
|
50 |
|
51 | const {arguments: argumentsNodes} = left;
|
52 |
|
53 |
|
54 | if (argumentsNodes.length > 2) {
|
55 | return;
|
56 | }
|
57 |
|
58 | if (
|
59 | (['!==', '!=', '>', '===', '=='].includes(node.operator) && isNegativeOne(right)) ||
|
60 | (['>=', '<'].includes(node.operator) && isLiteralZero(right))
|
61 | ) {
|
62 | report(
|
63 | context,
|
64 | node,
|
65 | target,
|
66 | argumentsNodes
|
67 | );
|
68 | }
|
69 | }
|
70 | });
|
71 |
|
72 | module.exports = {
|
73 | create,
|
74 | meta: {
|
75 | type: 'suggestion',
|
76 | docs: {
|
77 | url: getDocumentationUrl(__filename)
|
78 | },
|
79 | fixable: 'code'
|
80 | }
|
81 | };
|