6
6
* @returns {boolean } `true` if the node is a normal function expression
7
7
*/
8
8
function isNormalFunctionExpression ( node ) {
9
- return ( node . type === 'FunctionExpression' || node . type === 'ArrowFunctionExpression' ) && ! node . generator && ! node . async ;
9
+ const functionTypes = [
10
+ 'FunctionExpression' ,
11
+ 'ArrowFunctionExpression' ,
12
+ 'FunctionDeclaration' ,
13
+ ] ;
14
+ return functionTypes . includes ( node . type ) && ! node . generator && ! node . async ;
15
+ }
16
+
17
+ /**
18
+ * Determines whether a node is a reference to function expression.
19
+ * @param {ASTNode } node The node in question
20
+ * @param {ScopeManager } scopeManager The scope manager to use for resolving references
21
+ * @returns {boolean } `true` if the node is a reference to a function expression
22
+ */
23
+ function isNormalFunctionExpressionReference ( node , scopeManager ) {
24
+ if ( ! node || ! scopeManager ) {
25
+ return false ;
26
+ }
27
+
28
+ if ( node . type !== 'Identifier' ) {
29
+ return false ;
30
+ }
31
+
32
+ const scope = scopeManager . acquire ( node ) || scopeManager . globalScope ;
33
+ if ( ! scope ) {
34
+ return false ;
35
+ }
36
+
37
+ const references = scope . references ;
38
+ const createReference = references . find ( reference => {
39
+ return reference . identifier === node ;
40
+ } ) ;
41
+
42
+ if ( ! createReference || ! createReference . resolved ) {
43
+ return false ;
44
+ }
45
+
46
+ const definitions = createReference . resolved . defs ;
47
+ if ( ! definitions || ! definitions . length ) {
48
+ return false ;
49
+ }
50
+
51
+ // Assumes it is immediately initialized to a function
52
+ let definitionNode = definitions [ 0 ] . node ;
53
+
54
+ // If we find something like `const create = () => {}` then send the
55
+ // righthand side into the type check.
56
+ if ( definitionNode . type === 'VariableDeclarator' ) {
57
+ definitionNode = definitionNode . init ;
58
+ }
59
+
60
+ return isNormalFunctionExpression ( definitionNode ) ;
10
61
}
11
62
12
63
/**
@@ -33,7 +84,7 @@ module.exports = {
33
84
is an object, and `false` if module.exports is just the `create` function. If no valid ESLint rule info can be extracted
34
85
from the file, the return value will be `null`.
35
86
*/
36
- getRuleInfo ( ast ) {
87
+ getRuleInfo ( ast , scopeManager ) {
37
88
const INTERESTING_KEYS = new Set ( [ 'create' , 'meta' ] ) ;
38
89
let exportsVarOverridden = false ;
39
90
let exportsIsFunction = false ;
@@ -90,9 +141,19 @@ module.exports = {
90
141
return currentExports ;
91
142
} , { } ) ;
92
143
93
- return Object . prototype . hasOwnProperty . call ( exportNodes , 'create' ) && isNormalFunctionExpression ( exportNodes . create )
94
- ? Object . assign ( { isNewStyle : ! exportsIsFunction , meta : null } , exportNodes )
95
- : null ;
144
+ const createExists = Object . prototype . hasOwnProperty . call ( exportNodes , 'create' ) ;
145
+ if ( ! createExists ) {
146
+ return null ;
147
+ }
148
+
149
+ const createIsFunction = isNormalFunctionExpression ( exportNodes . create ) ;
150
+ const createIsFunctionReference = isNormalFunctionExpressionReference ( exportNodes . create , scopeManager ) ;
151
+
152
+ if ( ! createIsFunction && ! createIsFunctionReference ) {
153
+ return null ;
154
+ }
155
+
156
+ return Object . assign ( { isNewStyle : ! exportsIsFunction , meta : null } , exportNodes ) ;
96
157
} ,
97
158
98
159
/**
0 commit comments