@@ -92,6 +92,7 @@ module.exports = (file, api, options) => {
92
92
! filterDefaultPropsField ( prop ) &&
93
93
! filterGetInitialStateField ( prop ) &&
94
94
! isFunctionExpression ( prop ) &&
95
+ ! isPrimExpression ( prop ) &&
95
96
MIXIN_KEY != prop . key . name
96
97
)
97
98
) ) ;
@@ -152,6 +153,16 @@ module.exports = (file, api, options) => {
152
153
node . value . type === 'FunctionExpression'
153
154
) ;
154
155
156
+ const isPrimExpression = node => (
157
+ node . key &&
158
+ node . key . type === 'Identifier' &&
159
+ node . value && (
160
+ node . value . type === 'Literal' || ( // TODO this might change in babylon v6
161
+ node . value . type === 'Identifier' &&
162
+ node . value . name === 'undefined'
163
+ ) )
164
+ ) ;
165
+
155
166
// Collects `childContextTypes`, `contextTypes`, `displayName`, and `propTypes`;
156
167
// simplifies `getDefaultProps` or converts it to an IIFE;
157
168
// and collects everything else in the `statics` property object.
@@ -172,25 +183,20 @@ module.exports = (file, api, options) => {
172
183
return result ;
173
184
} ;
174
185
175
- const collectFunctions = specPath => specPath . properties
186
+ const collectProperties = specPath => specPath . properties
176
187
. filter ( prop =>
177
188
! ( filterDefaultPropsField ( prop ) || filterGetInitialStateField ( prop ) )
178
189
)
179
- . filter ( isFunctionExpression ) ;
190
+ . filter ( prop => isFunctionExpression ( prop ) || isPrimExpression ( prop ) ) ;
180
191
181
192
const findRequirePathAndBinding = ( moduleName ) => {
182
193
let result = null ;
183
194
184
195
const requireStatement = root . find ( j . VariableDeclarator , {
196
+ id : { type : 'Identifier' } ,
185
197
init : {
186
- type : 'CallExpression' ,
187
- callee : {
188
- type : 'Identifier' ,
189
- name : 'require' ,
190
- } ,
191
- arguments : [ {
192
- value : moduleName ,
193
- } ] ,
198
+ callee : { name : 'require' } ,
199
+ arguments : [ { value : moduleName } ] ,
194
200
} ,
195
201
} ) ;
196
202
@@ -344,13 +350,21 @@ module.exports = (file, api, options) => {
344
350
false
345
351
) , fn ) ;
346
352
347
- const createArrowPropertyFromMethod = method =>
353
+ const createArrowProperty = prop =>
354
+ withComments ( j . classProperty (
355
+ j . identifier ( prop . key . name ) ,
356
+ createArrowFunctionExpression ( prop . value ) ,
357
+ null ,
358
+ false
359
+ ) , prop ) ;
360
+
361
+ const createClassProperty = prop =>
348
362
withComments ( j . classProperty (
349
- j . identifier ( method . key . name ) ,
350
- createArrowFunctionExpression ( method . value ) ,
363
+ j . identifier ( prop . key . name ) ,
364
+ prop . value ,
351
365
null ,
352
366
false
353
- ) , method ) ;
367
+ ) , prop ) ;
354
368
355
369
// if there's no `getInitialState` or the `getInitialState` function is simple
356
370
// (i.e., it does not reference `this.props`) then we don't need a constructor.
@@ -368,7 +382,7 @@ module.exports = (file, api, options) => {
368
382
baseClassName ,
369
383
staticProperties ,
370
384
getInitialState ,
371
- methods ,
385
+ rawProperties ,
372
386
comments
373
387
) => {
374
388
let maybeConstructor = [ ] ;
@@ -382,11 +396,15 @@ module.exports = (file, api, options) => {
382
396
maybeConstructor = createConstructor ( getInitialState ) ;
383
397
}
384
398
385
- const arrowBindFunctionsAndMethods = methods . map ( method =>
386
- AUTOBIND_IGNORE_KEYS [ method . key . name ] ?
387
- method :
388
- createArrowPropertyFromMethod ( method )
389
- ) ;
399
+ const propertiesAndMethods = rawProperties . map ( prop => {
400
+ if ( isPrimExpression ( prop ) ) {
401
+ return createClassProperty ( prop ) ;
402
+ } else if ( AUTOBIND_IGNORE_KEYS [ prop . key . name ] ) {
403
+ return createMethodDefinition ( prop ) ;
404
+ }
405
+
406
+ return createArrowProperty ( prop ) ;
407
+ } ) ;
390
408
391
409
return withComments ( j . classDeclaration (
392
410
name ? j . identifier ( name ) : null ,
@@ -395,7 +413,7 @@ module.exports = (file, api, options) => {
395
413
staticProperties ,
396
414
maybeConstructor ,
397
415
initialStateProperty ,
398
- arrowBindFunctionsAndMethods
416
+ propertiesAndMethods
399
417
)
400
418
) ,
401
419
j . memberExpression (
@@ -449,11 +467,30 @@ module.exports = (file, api, options) => {
449
467
return null ;
450
468
} ;
451
469
470
+ const findUnusedVariables = ( path , varName ) => j ( path )
471
+ . closestScope ( )
472
+ . find ( j . Identifier , { name : varName } )
473
+ // Ignore require vars
474
+ . filter ( identifierPath => identifierPath . value !== path . value . id )
475
+ // Ignore import bindings
476
+ . filter ( identifierPath => ! (
477
+ path . value . type === 'ImportDeclaration' &&
478
+ path . value . specifiers . some ( specifier => specifier . local === identifierPath . value )
479
+ ) )
480
+ // Ignore properties in MemberExpressions
481
+ . filter ( identifierPath => {
482
+ const parent = identifierPath . parent . value ;
483
+ return ! (
484
+ j . MemberExpression . check ( parent ) &&
485
+ parent . property === identifierPath . value
486
+ ) ;
487
+ } ) ;
488
+
452
489
const updateToClass = ( classPath , type ) => {
453
490
const specPath = ReactUtils . getReactCreateClassSpec ( classPath ) ;
454
491
const name = ReactUtils . getComponentName ( classPath ) ;
455
492
const statics = collectStatics ( specPath ) ;
456
- const functions = collectFunctions ( specPath ) ;
493
+ const properties = collectProperties ( specPath ) ;
457
494
const comments = getComments ( classPath ) ;
458
495
459
496
const getInitialState = findGetInitialState ( specPath ) ;
@@ -478,7 +515,7 @@ module.exports = (file, api, options) => {
478
515
baseClassName ,
479
516
staticProperties ,
480
517
getInitialState ,
481
- functions . map ( createMethodDefinition ) ,
518
+ properties ,
482
519
comments
483
520
)
484
521
) ;
@@ -496,7 +533,8 @@ module.exports = (file, api, options) => {
496
533
if ( ! ReactUtils . hasMixins ( classPath ) ) {
497
534
return true ;
498
535
} else if ( pureRenderMixinPathAndBinding ) {
499
- if ( areMixinsConvertible ( [ pureRenderMixinPathAndBinding . binding ] , classPath ) ) {
536
+ const { binding} = pureRenderMixinPathAndBinding ;
537
+ if ( areMixinsConvertible ( [ binding ] , classPath ) ) {
500
538
return true ;
501
539
}
502
540
}
@@ -526,8 +564,10 @@ module.exports = (file, api, options) => {
526
564
if ( didTransform ) {
527
565
// prune removed requires
528
566
if ( pureRenderMixinPathAndBinding ) {
529
- // FIXME check the scope before removing stuff
530
- j ( pureRenderMixinPathAndBinding . path ) . remove ( ) ;
567
+ const { binding, path} = pureRenderMixinPathAndBinding ;
568
+ if ( findUnusedVariables ( path , binding ) . size ( ) === 0 ) {
569
+ j ( path ) . remove ( ) ;
570
+ }
531
571
}
532
572
533
573
return root . toSource ( printOptions ) ;
0 commit comments