@@ -12,14 +12,88 @@ const report = require('../util/report');
12
12
const variableUtil = require ( '../util/variable' ) ;
13
13
14
14
const messages = {
15
- returnsUndefined : "Don't return undefined from react components" ,
15
+ returnsUndefined : "Don't return ` undefined` from react components" ,
16
16
} ;
17
17
18
+ function getReturnValue ( context , returnNode ) {
19
+ const variables = variableUtil . variablesInScope ( context ) ;
20
+ const returnIdentifierName = returnNode && returnNode . name ;
21
+ const returnIdentifierVar = variableUtil . getVariable (
22
+ variables ,
23
+ returnIdentifierName
24
+ ) ;
25
+
26
+ if ( ! returnNode ) return undefined ;
27
+
28
+ if (
29
+ returnIdentifierVar
30
+ && returnIdentifierVar . defs
31
+ && returnIdentifierVar . defs [ 0 ]
32
+ ) {
33
+ const value = returnIdentifierVar . defs [ 0 ] . node . init ;
34
+ if (
35
+ returnIdentifierVar . defs [ 0 ] . node
36
+ && returnIdentifierVar . defs [ 0 ] . node . type === 'VariableDeclarator'
37
+ && value === null
38
+ ) {
39
+ return undefined ;
40
+ }
41
+ return value ;
42
+ }
43
+
44
+ switch ( returnNode . type ) {
45
+ case 'LogicalExpression' : {
46
+ return getReturnValue ( context , returnNode . right ) ;
47
+ }
48
+ case 'ConditionalExpression' : {
49
+ const possibleReturnValue = [
50
+ getReturnValue ( context , returnNode . consequent ) ,
51
+ getReturnValue ( context , returnNode . alternate ) ,
52
+ ] ;
53
+ const returnsUndefined = possibleReturnValue . some ( ( val ) => typeof val === 'undefined' ) ;
54
+ if ( returnsUndefined ) return ;
55
+ return possibleReturnValue ;
56
+ }
57
+ case 'CallExpression' : {
58
+ if ( returnNode . callee . type === 'MemberExpression' ) {
59
+ const calleeObjName = returnNode . callee . object . name ;
60
+ const calleePropertyName = returnNode . callee . property . name ;
61
+ const calleeObjNode = variables . find ( ( item ) => item && item . name === calleeObjName ) ;
62
+ const isCalleeObjArray = calleeObjNode . defs [ 0 ] . node . init . type === 'ArrayExpression' ;
63
+ const isMapCall = isCalleeObjArray && calleePropertyName === 'map' ;
64
+ if ( isMapCall ) {
65
+ const mapCallback = returnNode . arguments [ 0 ] ;
66
+ const mapReturnStatement = mapCallback . body . type === 'BlockStatement'
67
+ && astUtil . findReturnStatement ( returnNode . arguments [ 0 ] ) ;
68
+ const mapReturnNode = ( mapReturnStatement && mapReturnStatement . argument ) || mapCallback . body ;
69
+ // console.log('DEBUG', mapReturnNode);
70
+ return getReturnValue ( context , mapReturnNode ) ;
71
+ }
72
+ }
73
+ const calleeName = returnNode . callee . name ;
74
+ const calleeNode = variables . find ( ( item ) => item && item . name === calleeName ) ;
75
+ const calleeDefinitionNode = calleeNode && calleeNode . defs && calleeNode . defs [ 0 ] && calleeNode . defs [ 0 ] . node ;
76
+ const calleeReturnStatement = astUtil . findReturnStatement ( calleeDefinitionNode ) ;
77
+ const calleeReturnNode = ( calleeReturnStatement && calleeReturnStatement . argument )
78
+ || ( calleeDefinitionNode . init && calleeDefinitionNode . init . body ) ;
79
+ return getReturnValue ( context , calleeReturnNode ) ;
80
+ }
81
+ case 'ArrayExpression' : {
82
+ return returnNode . elements ;
83
+ }
84
+ case 'JSXElement' : {
85
+ return returnNode ;
86
+ }
87
+ default :
88
+ return returnNode . value ;
89
+ }
90
+ }
91
+
18
92
/** @type {import('eslint').Rule.RuleModule } */
19
93
module . exports = {
20
94
meta : {
21
95
docs : {
22
- description : 'Disallow returning undefined from react components' ,
96
+ description : 'Disallow returning ` undefined` from react components' ,
23
97
category : 'Best Practices' ,
24
98
recommended : false ,
25
99
url : docsUrl ( 'no-render-return-undefined' ) ,
@@ -29,89 +103,18 @@ module.exports = {
29
103
} ,
30
104
31
105
create ( context ) {
32
- function getReturnValue ( returnNode ) {
33
- const variables = variableUtil . variablesInScope ( context ) ;
34
- const returnIdentifierName = returnNode && returnNode . name ;
35
- const returnIdentifierVar = variableUtil . getVariable (
36
- variables ,
37
- returnIdentifierName
38
- ) ;
39
-
40
- if ( ! returnNode ) return undefined ;
41
-
42
- if (
43
- returnIdentifierVar
44
- && returnIdentifierVar . defs
45
- && returnIdentifierVar . defs [ 0 ]
46
- ) {
47
- const value = returnIdentifierVar . defs [ 0 ] . node . init ;
48
- if (
49
- returnIdentifierVar . defs [ 0 ] . node
50
- && returnIdentifierVar . defs [ 0 ] . node . type === 'VariableDeclarator'
51
- && value === null
52
- ) {
53
- return undefined ;
54
- }
55
- return value ;
56
- }
57
-
58
- switch ( returnNode . type ) {
59
- case 'LogicalExpression' : {
60
- return getReturnValue ( returnNode . right ) ;
61
- }
62
- case 'ConditionalExpression' : {
63
- const possibleReturnValue = [ getReturnValue ( returnNode . consequent ) , getReturnValue ( returnNode . alternate ) ] ;
64
- const returnsUndefined = possibleReturnValue . some ( ( val ) => val === undefined ) ;
65
- if ( returnsUndefined ) return undefined ;
66
- return possibleReturnValue ;
67
- }
68
- case 'CallExpression' : {
69
- if ( returnNode . callee . type === 'MemberExpression' ) {
70
- const calleeObjName = returnNode . callee . object . name ;
71
- const calleePropertyName = returnNode . callee . property . name ;
72
- const calleeObjNode = variables . find ( ( item ) => item && item . name === calleeObjName ) ;
73
- const isCalleeObjArray = calleeObjNode . defs [ 0 ] . node . init . type === 'ArrayExpression' ;
74
- const isMapCall = isCalleeObjArray && calleePropertyName === 'map' ;
75
- if ( isMapCall ) {
76
- const mapCallback = returnNode . arguments [ 0 ] ;
77
- const mapReturnStatement = mapCallback . body . type === 'BlockStatement'
78
- && astUtil . findReturnStatement ( returnNode . arguments [ 0 ] ) ;
79
- const mapReturnNode = ( mapReturnStatement && mapReturnStatement . argument ) || mapCallback . body ;
80
- // console.log('DEBUG', mapReturnNode);
81
- return getReturnValue ( mapReturnNode ) ;
82
- }
83
- }
84
- const calleeName = returnNode . callee . name ;
85
- const calleeNode = variables . find ( ( item ) => item && item . name === calleeName ) ;
86
- const calleeDefinitionNode = calleeNode && calleeNode . defs && calleeNode . defs [ 0 ] && calleeNode . defs [ 0 ] . node ;
87
- const calleeReturnStatement = astUtil . findReturnStatement ( calleeDefinitionNode ) ;
88
- const calleeReturnNode = ( calleeReturnStatement && calleeReturnStatement . argument )
89
- || ( calleeDefinitionNode . init && calleeDefinitionNode . init . body ) ;
90
- return getReturnValue ( calleeReturnNode ) ;
91
- }
92
- case 'ArrayExpression' : {
93
- return returnNode . elements ;
94
- }
95
- case 'JSXElement' : {
96
- return returnNode ;
97
- }
98
- default :
99
- return returnNode . value ;
100
- }
101
- }
102
-
103
106
const isReturningUndefined = ( returnStatement ) => {
104
107
const returnNode = returnStatement && returnStatement . argument ;
105
108
const returnIdentifierName = returnNode && returnNode . name ;
106
109
107
- const returnIdentifierValue = getReturnValue ( returnNode ) ;
110
+ const returnIdentifierValue = getReturnValue ( context , returnNode ) ;
108
111
109
112
const returnsArrayHavingUndefined = Array . isArray ( returnIdentifierValue )
110
113
&& returnIdentifierValue . some ( ( el ) => el && el . type === 'Identifier' && el . name === 'undefined' ) ;
111
114
112
115
return ! returnStatement
113
116
|| returnIdentifierName === 'undefined'
114
- || returnIdentifierValue === undefined
117
+ || typeof returnIdentifierValue === ' undefined'
115
118
|| ( returnIdentifierValue && returnIdentifierValue . name === 'undefined' )
116
119
|| returnsArrayHavingUndefined ;
117
120
} ;
0 commit comments