9
9
10
10
'use strict' ;
11
11
12
+ var Components = require ( '../util/Components' ) ;
13
+
12
14
// Descend through all wrapping TypeCastExpressions and return the expression
13
15
// that was cast.
14
16
function uncast ( node ) {
@@ -31,19 +33,27 @@ function getName(node) {
31
33
return null ;
32
34
}
33
35
34
- function isMethodDefinitionWithName ( node , name , isStatic ) {
35
- isStatic = isStatic || false ;
36
- return (
37
- node . type === 'MethodDefinition' &&
38
- node . static === isStatic &&
39
- getName ( node . key ) === name
40
- ) ;
41
- }
42
-
43
36
function isThisExpression ( node ) {
44
37
return uncast ( node ) . type === 'ThisExpression' ;
45
38
}
46
39
40
+ function getInitialClassInfo ( ) {
41
+ return {
42
+ // Set of nodes where state fields were defined.
43
+ stateFields : [ ] ,
44
+
45
+ // Set of names of state fields that we've seen used.
46
+ usedStateFields : [ ] ,
47
+
48
+ // Names of local variables that may be pointing to this.state. To
49
+ // track this properly, we would need to keep track of all locals,
50
+ // shadowing, assignments, etc. To keep things simple, we only
51
+ // maintain one set of aliases per method and accept that it will
52
+ // produce some false negatives.
53
+ aliases : null
54
+ } ;
55
+ }
56
+
47
57
module . exports = {
48
58
meta : {
49
59
docs : {
@@ -54,7 +64,7 @@ module.exports = {
54
64
schema : [ ]
55
65
} ,
56
66
57
- create : function ( context ) {
67
+ create : Components . detect ( function ( context , components , utils ) {
58
68
// Non-null when we are inside a React component ClassDeclaration and we have
59
69
// not yet encountered any use of this.state which we have chosen not to
60
70
// analyze. If we encounter any such usage (like this.state being spread as
@@ -142,42 +152,45 @@ module.exports = {
142
152
}
143
153
}
144
154
155
+ function reportUnusedFields ( ) {
156
+ // Report all unused state fields.
157
+ classInfo . stateFields . forEach ( function ( node ) {
158
+ var name = getName ( node . key ) ;
159
+ if ( classInfo . usedStateFields . indexOf ( name ) < 0 ) {
160
+ context . report ( node , 'Unused state field: \'' + name + '\'' ) ;
161
+ }
162
+ } ) ;
163
+ }
164
+
145
165
return {
146
166
ClassDeclaration : function ( node ) {
147
- // Simple heuristic for determining whether we're in a React component.
148
- var isReactComponent = node . body . body . some ( function ( child ) {
149
- return isMethodDefinitionWithName ( child , 'render' ) ;
150
- } ) ;
151
-
152
- if ( isReactComponent ) {
153
- classInfo = {
154
- // Set of nodes where state fields were defined.
155
- stateFields : [ ] ,
156
-
157
- // Set of names of state fields that we've seen used.
158
- usedStateFields : [ ] ,
159
-
160
- // Names of local variables that may be pointing to this.state. To
161
- // track this properly, we would need to keep track of all locals,
162
- // shadowing, assignments, etc. To keep things simple, we only
163
- // maintain one set of aliases per method and accept that it will
164
- // produce some false negatives.
165
- aliases : null
166
- } ;
167
+ if ( utils . isES6Component ( node ) ) {
168
+ classInfo = getInitialClassInfo ( ) ;
169
+ }
170
+ } ,
171
+
172
+ ObjectExpression : function ( node ) {
173
+ if ( utils . isES5Component ( node ) ) {
174
+ classInfo = getInitialClassInfo ( ) ;
175
+ }
176
+ } ,
177
+
178
+ 'ObjectExpression:exit' : function ( node ) {
179
+ if ( ! classInfo ) {
180
+ return ;
181
+ }
182
+
183
+ if ( utils . isES5Component ( node ) ) {
184
+ reportUnusedFields ( ) ;
185
+ classInfo = null ;
167
186
}
168
187
} ,
169
188
170
189
'ClassDeclaration:exit' : function ( ) {
171
190
if ( ! classInfo ) {
172
191
return ;
173
192
}
174
- // Report all unused state fields.
175
- classInfo . stateFields . forEach ( function ( node ) {
176
- var name = getName ( node . key ) ;
177
- if ( classInfo . usedStateFields . indexOf ( name ) < 0 ) {
178
- context . report ( node , 'Unused state field: \'' + name + '\'' ) ;
179
- }
180
- } ) ;
193
+ reportUnusedFields ( ) ;
181
194
classInfo = null ;
182
195
} ,
183
196
@@ -230,6 +243,32 @@ module.exports = {
230
243
classInfo . aliases = null ;
231
244
} ,
232
245
246
+ FunctionExpression : function ( node ) {
247
+ if ( ! classInfo ) {
248
+ return ;
249
+ }
250
+
251
+ var parent = node . parent ;
252
+ if ( ! utils . isES5Component ( parent . parent ) ) {
253
+ return ;
254
+ }
255
+
256
+ if ( parent . key . name === 'getInitialState' ) {
257
+ var body = node . body . body ;
258
+ var lastBodyNode = body [ body . length - 1 ] ;
259
+
260
+ if (
261
+ lastBodyNode . type === 'ReturnStatement' &&
262
+ lastBodyNode . argument . type === 'ObjectExpression'
263
+ ) {
264
+ addStateFields ( lastBodyNode . argument ) ;
265
+ }
266
+ } else {
267
+ // Create a new set for this.state aliases local to this method.
268
+ classInfo . aliases = [ ] ;
269
+ }
270
+ } ,
271
+
233
272
AssignmentExpression : function ( node ) {
234
273
if ( ! classInfo ) {
235
274
return ;
@@ -295,5 +334,5 @@ module.exports = {
295
334
}
296
335
}
297
336
} ;
298
- }
337
+ } )
299
338
} ;
0 commit comments