1
1
'use strict' ;
2
2
3
3
const { hasSideEffect, isParenthesized, findVariable} = require ( '@eslint-community/eslint-utils' ) ;
4
- const { matches, methodCallSelector} = require ( '../selectors/index.js' ) ;
5
- const isFunctionSelfUsedInside = require ( '../utils/is-function-self-used-inside.js' ) ;
6
-
7
- const getBinaryExpressionSelector = path => [
8
- `[${ path } .type="BinaryExpression"]` ,
9
- `[${ path } .operator="==="]` ,
10
- `:matches([${ path } .left.type="Identifier"], [${ path } .right.type="Identifier"])` ,
11
- ] . join ( '' ) ;
12
- const getFunctionSelector = path => [
13
- `[${ path } .generator!=true]` ,
14
- `[${ path } .async!=true]` ,
15
- `[${ path } .params.length=1]` ,
16
- `[${ path } .params.0.type="Identifier"]` ,
17
- ] . join ( '' ) ;
18
- const callbackFunctionSelector = path => matches ( [
4
+ const { isMethodCall} = require ( '../ast/index.js' ) ;
5
+ const { isSameIdentifier, isFunctionSelfUsedInside} = require ( '../utils/index.js' ) ;
6
+
7
+ const isSimpleCompare = ( node , compareNode ) =>
8
+ node . type === 'BinaryExpression'
9
+ && node . operator === '==='
10
+ && (
11
+ isSameIdentifier ( node . left , compareNode )
12
+ || isSameIdentifier ( node . right , compareNode )
13
+ ) ;
14
+ const isSimpleCompareCallbackFunction = node =>
19
15
// Matches `foo.findIndex(bar => bar === baz)`
20
- [
21
- `[${ path } .type="ArrowFunctionExpression"]` ,
22
- getFunctionSelector ( path ) ,
23
- getBinaryExpressionSelector ( `${ path } .body` ) ,
24
- ] . join ( '' ) ,
16
+ (
17
+ node . type === 'ArrowFunctionExpression'
18
+ && ! node . async
19
+ && node . params . length === 1
20
+ && isSimpleCompare ( node . body , node . params [ 0 ] )
21
+ )
25
22
// Matches `foo.findIndex(bar => {return bar === baz})`
26
23
// Matches `foo.findIndex(function (bar) {return bar === baz})`
27
- [
28
- `:matches([${ path } .type="ArrowFunctionExpression"], [${ path } .type="FunctionExpression"])` ,
29
- getFunctionSelector ( path ) ,
30
- `[${ path } .body.type="BlockStatement"]` ,
31
- `[${ path } .body.body.length=1]` ,
32
- `[${ path } .body.body.0.type="ReturnStatement"]` ,
33
- getBinaryExpressionSelector ( `${ path } .body.body.0.argument` ) ,
34
- ] . join ( '' ) ,
35
- ] ) ;
24
+ || (
25
+ ( node . type === 'ArrowFunctionExpression' || node . type === 'FunctionExpression' )
26
+ && ! node . async
27
+ && ! node . generator
28
+ && node . params . length === 1
29
+ && node . body . type === 'BlockStatement'
30
+ && node . body . body . length === 1
31
+ && node . body . body [ 0 ] . type === 'ReturnStatement'
32
+ && isSimpleCompare ( node . body . body [ 0 ] . argument , node . params [ 0 ] )
33
+ ) ;
36
34
const isIdentifierNamed = ( { type, name} , expectName ) => type === 'Identifier' && name === expectName ;
37
35
38
36
function simpleArraySearchRule ( { method, replacement} ) {
@@ -51,78 +49,80 @@ function simpleArraySearchRule({method, replacement}) {
51
49
[ SUGGESTION ] : `Replace \`.${ method } ()\` with \`.${ replacement } ()\`.` ,
52
50
} ;
53
51
54
- const selector = [
55
- methodCallSelector ( {
56
- method,
57
- argumentsLength : 1 ,
58
- } ) ,
59
- callbackFunctionSelector ( 'arguments.0' ) ,
60
- ] . join ( '' ) ;
61
-
62
- function createListeners ( context ) {
52
+ function listen ( context ) {
63
53
const { sourceCode} = context ;
64
54
const { scopeManager} = sourceCode ;
65
55
66
- return {
67
- [ selector ] ( node ) {
68
- const [ callback ] = node . arguments ;
69
- const binaryExpression = callback . body . type === 'BinaryExpression'
70
- ? callback . body
71
- : callback . body . body [ 0 ] . argument ;
72
- const [ parameter ] = callback . params ;
73
- const { left, right} = binaryExpression ;
74
- const { name} = parameter ;
75
-
76
- let searchValueNode ;
77
- let parameterInBinaryExpression ;
78
- if ( isIdentifierNamed ( left , name ) ) {
79
- searchValueNode = right ;
80
- parameterInBinaryExpression = left ;
81
- } else if ( isIdentifierNamed ( right , name ) ) {
82
- searchValueNode = left ;
83
- parameterInBinaryExpression = right ;
84
- } else {
85
- return ;
56
+ context . on ( 'CallExpression' , callExpression => {
57
+ if (
58
+ ! isMethodCall ( callExpression , {
59
+ method,
60
+ argumentsLength : 1 ,
61
+ optionalCall : false ,
62
+ optionalMember : false ,
63
+ } )
64
+ || ! isSimpleCompareCallbackFunction ( callExpression . arguments [ 0 ] )
65
+ ) {
66
+ return ;
67
+ }
68
+
69
+ const [ callback ] = callExpression . arguments ;
70
+ const binaryExpression = callback . body . type === 'BinaryExpression'
71
+ ? callback . body
72
+ : callback . body . body [ 0 ] . argument ;
73
+ const [ parameter ] = callback . params ;
74
+ const { left, right} = binaryExpression ;
75
+ const { name} = parameter ;
76
+
77
+ let searchValueNode ;
78
+ let parameterInBinaryExpression ;
79
+ if ( isIdentifierNamed ( left , name ) ) {
80
+ searchValueNode = right ;
81
+ parameterInBinaryExpression = left ;
82
+ } else if ( isIdentifierNamed ( right , name ) ) {
83
+ searchValueNode = left ;
84
+ parameterInBinaryExpression = right ;
85
+ } else {
86
+ return ;
87
+ }
88
+
89
+ const callbackScope = scopeManager . acquire ( callback ) ;
90
+ if (
91
+ // `parameter` is used somewhere else
92
+ findVariable ( callbackScope , parameter ) . references . some ( ( { identifier} ) => identifier !== parameterInBinaryExpression )
93
+ || isFunctionSelfUsedInside ( callback , callbackScope )
94
+ ) {
95
+ return ;
96
+ }
97
+
98
+ const methodNode = callExpression . callee . property ;
99
+ const problem = {
100
+ node : methodNode ,
101
+ messageId : ERROR ,
102
+ suggest : [ ] ,
103
+ } ;
104
+
105
+ const fix = function * ( fixer ) {
106
+ let text = sourceCode . getText ( searchValueNode ) ;
107
+ if ( isParenthesized ( searchValueNode , sourceCode ) && ! isParenthesized ( callback , sourceCode ) ) {
108
+ text = `(${ text } )` ;
86
109
}
87
110
88
- const callbackScope = scopeManager . acquire ( callback ) ;
89
- if (
90
- // `parameter` is used somewhere else
91
- findVariable ( callbackScope , parameter ) . references . some ( ( { identifier} ) => identifier !== parameterInBinaryExpression )
92
- || isFunctionSelfUsedInside ( callback , callbackScope )
93
- ) {
94
- return ;
95
- }
111
+ yield fixer . replaceText ( methodNode , replacement ) ;
112
+ yield fixer . replaceText ( callback , text ) ;
113
+ } ;
96
114
97
- const method = node . callee . property ;
98
- const problem = {
99
- node : method ,
100
- messageId : ERROR ,
101
- suggest : [ ] ,
102
- } ;
103
-
104
- const fix = function * ( fixer ) {
105
- let text = sourceCode . getText ( searchValueNode ) ;
106
- if ( isParenthesized ( searchValueNode , sourceCode ) && ! isParenthesized ( callback , sourceCode ) ) {
107
- text = `(${ text } )` ;
108
- }
109
-
110
- yield fixer . replaceText ( method , replacement ) ;
111
- yield fixer . replaceText ( callback , text ) ;
112
- } ;
113
-
114
- if ( hasSideEffect ( searchValueNode , sourceCode ) ) {
115
- problem . suggest . push ( { messageId : SUGGESTION , fix} ) ;
116
- } else {
117
- problem . fix = fix ;
118
- }
115
+ if ( hasSideEffect ( searchValueNode , sourceCode ) ) {
116
+ problem . suggest . push ( { messageId : SUGGESTION , fix} ) ;
117
+ } else {
118
+ problem . fix = fix ;
119
+ }
119
120
120
- return problem ;
121
- } ,
122
- } ;
121
+ return problem ;
122
+ } ) ;
123
123
}
124
124
125
- return { messages, createListeners } ;
125
+ return { messages, listen } ;
126
126
}
127
127
128
128
module . exports = simpleArraySearchRule ;
0 commit comments