|
1 | 1 | 'use strict'; |
2 | | -const {hasSideEffect, isParenthesized, findVariable} = require('eslint-utils'); |
3 | 2 | const getDocumentationUrl = require('./utils/get-documentation-url'); |
4 | | -const methodSelector = require('./utils/method-selector'); |
5 | | -const isFunctionSelfUsedInside = require('./utils/is-function-self-used-inside'); |
| 3 | +const simpleArraySearchRule = require('./shared/simple-array-search-rule'); |
6 | 4 |
|
7 | | -const MESSAGE_ID_FIND_INDEX = 'findIndex'; |
8 | | -const MESSAGE_ID_REPLACE = 'replaceFindIndex'; |
9 | | -const messages = { |
10 | | - [MESSAGE_ID_FIND_INDEX]: 'Use `.indexOf()` instead of `.findIndex()` when looking for the index of an item.', |
11 | | - [MESSAGE_ID_REPLACE]: 'Replace `.findIndex()` with `.indexOf()`.' |
12 | | -}; |
13 | | - |
14 | | -const getBinaryExpressionSelector = path => [ |
15 | | - `[${path}.type="BinaryExpression"]`, |
16 | | - `[${path}.operator="==="]`, |
17 | | - `:matches([${path}.left.type="Identifier"], [${path}.right.type="Identifier"])` |
18 | | -].join(''); |
19 | | -const getFunctionSelector = path => [ |
20 | | - `[${path}.generator=false]`, |
21 | | - `[${path}.async=false]`, |
22 | | - `[${path}.params.length=1]`, |
23 | | - `[${path}.params.0.type="Identifier"]` |
24 | | -].join(''); |
25 | | -const selector = [ |
26 | | - methodSelector({ |
27 | | - name: 'findIndex', |
28 | | - length: 1 |
29 | | - }), |
30 | | - `:matches(${ |
31 | | - [ |
32 | | - // Matches `foo.findIndex(bar => bar === baz)` |
33 | | - [ |
34 | | - '[arguments.0.type="ArrowFunctionExpression"]', |
35 | | - getFunctionSelector('arguments.0'), |
36 | | - getBinaryExpressionSelector('arguments.0.body') |
37 | | - ].join(''), |
38 | | - // Matches `foo.findIndex(bar => {return bar === baz})` |
39 | | - // Matches `foo.findIndex(function (bar) {return bar === baz})` |
40 | | - [ |
41 | | - ':matches([arguments.0.type="ArrowFunctionExpression"], [arguments.0.type="FunctionExpression"])', |
42 | | - getFunctionSelector('arguments.0'), |
43 | | - '[arguments.0.body.type="BlockStatement"]', |
44 | | - '[arguments.0.body.body.length=1]', |
45 | | - '[arguments.0.body.body.0.type="ReturnStatement"]', |
46 | | - getBinaryExpressionSelector('arguments.0.body.body.0.argument') |
47 | | - ].join('') |
48 | | - ].join(', ') |
49 | | - })` |
50 | | -].join(''); |
51 | | - |
52 | | -const isIdentifierNamed = ({type, name}, expectName) => type === 'Identifier' && name === expectName; |
53 | | - |
54 | | -const create = context => { |
55 | | - const sourceCode = context.getSourceCode(); |
56 | | - const {scopeManager} = sourceCode; |
57 | | - |
58 | | - return { |
59 | | - [selector](node) { |
60 | | - const [callback] = node.arguments; |
61 | | - const binaryExpression = callback.body.type === 'BinaryExpression' ? |
62 | | - callback.body : |
63 | | - callback.body.body[0].argument; |
64 | | - const [parameter] = callback.params; |
65 | | - const {left, right} = binaryExpression; |
66 | | - const {name} = parameter; |
67 | | - |
68 | | - let searchValueNode; |
69 | | - let parameterInBinaryExpression; |
70 | | - if (isIdentifierNamed(left, name)) { |
71 | | - searchValueNode = right; |
72 | | - parameterInBinaryExpression = left; |
73 | | - } else if (isIdentifierNamed(right, name)) { |
74 | | - searchValueNode = left; |
75 | | - parameterInBinaryExpression = right; |
76 | | - } else { |
77 | | - return; |
78 | | - } |
| 5 | +const {messages, createListeners} = simpleArraySearchRule({ |
| 6 | + method: 'findIndex', |
| 7 | + replacement: 'indexOf' |
| 8 | +}); |
79 | 9 |
|
80 | | - const callbackScope = scopeManager.acquire(callback); |
81 | | - if ( |
82 | | - // `parameter` is used somewhere else |
83 | | - findVariable(callbackScope, parameter).references.some(({identifier}) => identifier !== parameterInBinaryExpression) || |
84 | | - isFunctionSelfUsedInside(callback, callbackScope) |
85 | | - ) { |
86 | | - return; |
87 | | - } |
88 | | - |
89 | | - const method = node.callee.property; |
90 | | - const problem = { |
91 | | - node: method, |
92 | | - messageId: MESSAGE_ID_FIND_INDEX, |
93 | | - suggest: [] |
94 | | - }; |
95 | | - |
96 | | - const fix = function * (fixer) { |
97 | | - let text = sourceCode.getText(searchValueNode); |
98 | | - if (isParenthesized(searchValueNode, sourceCode) && !isParenthesized(callback, sourceCode)) { |
99 | | - text = `(${text})`; |
100 | | - } |
101 | | - |
102 | | - yield fixer.replaceText(method, 'indexOf'); |
103 | | - yield fixer.replaceText(callback, text); |
104 | | - }; |
105 | | - |
106 | | - if (hasSideEffect(searchValueNode, sourceCode)) { |
107 | | - problem.suggest.push({messageId: MESSAGE_ID_REPLACE, fix}); |
108 | | - } else { |
109 | | - problem.fix = fix; |
110 | | - } |
111 | | - |
112 | | - context.report(problem); |
113 | | - } |
114 | | - }; |
115 | | -}; |
| 10 | +const create = context => createListeners(context); |
116 | 11 |
|
117 | 12 | module.exports = { |
118 | 13 | create, |
|
0 commit comments