@@ -93,7 +93,7 @@ module.exports = (file, api, options) => {
93
93
! filterDefaultPropsField ( prop ) &&
94
94
! filterGetInitialStateField ( prop ) &&
95
95
! isFunctionExpression ( prop ) &&
96
- ! isPrimExpression ( prop ) &&
96
+ ! isPrimProperty ( prop ) &&
97
97
MIXIN_KEY != prop . key . name
98
98
)
99
99
) ) ;
@@ -154,16 +154,19 @@ module.exports = (file, api, options) => {
154
154
node . value . type === 'FunctionExpression'
155
155
) ;
156
156
157
- const isPrimExpression = node => (
158
- node . key &&
159
- node . key . type === 'Identifier' &&
160
- node . value && (
161
- node . value . type === 'Literal' || ( // TODO this might change in babylon v6
162
- node . value . type === 'Identifier' &&
163
- node . value . name === 'undefined'
164
- ) )
157
+ const isPrimProperty = prop => (
158
+ prop . key &&
159
+ prop . key . type === 'Identifier' &&
160
+ prop . value &&
161
+ isPrimExpression ( prop . value )
165
162
) ;
166
163
164
+ const isPrimExpression = node => (
165
+ node . type === 'Literal' || ( // TODO this might change in babylon v6
166
+ node . type === 'Identifier' &&
167
+ node . name === 'undefined'
168
+ ) ) ;
169
+
167
170
// Collects `childContextTypes`, `contextTypes`, `displayName`, and `propTypes`;
168
171
// simplifies `getDefaultProps` or converts it to an IIFE;
169
172
// and collects everything else in the `statics` property object.
@@ -188,7 +191,7 @@ module.exports = (file, api, options) => {
188
191
. filter ( prop =>
189
192
! ( filterDefaultPropsField ( prop ) || filterGetInitialStateField ( prop ) )
190
193
)
191
- . filter ( prop => isFunctionExpression ( prop ) || isPrimExpression ( prop ) ) ;
194
+ . filter ( prop => isFunctionExpression ( prop ) || isPrimProperty ( prop ) ) ;
192
195
193
196
const findRequirePathAndBinding = ( moduleName ) => {
194
197
let result = null ;
@@ -211,7 +214,7 @@ module.exports = (file, api, options) => {
211
214
importStatement . forEach ( path => {
212
215
result = {
213
216
path,
214
- binding : path . value . specifiers [ 0 ] . local . name ,
217
+ binding : path . value . specifiers [ 0 ] . id . name ,
215
218
} ;
216
219
} ) ;
217
220
} else if ( requireStatement . size ( ) ) {
@@ -391,6 +394,169 @@ module.exports = (file, api, options) => {
391
394
false
392
395
) , prop ) ;
393
396
397
+ // ---------------------------------------------------------------------------
398
+ // Flow!
399
+
400
+ const flowAnyType = j . anyTypeAnnotation ( ) ;
401
+
402
+ const literalToFlowType = node => {
403
+ switch ( typeof node . value ) {
404
+ case 'string' :
405
+ return j . stringLiteralTypeAnnotation ( node . value , node . raw ) ;
406
+ case 'number' :
407
+ return j . numberLiteralTypeAnnotation ( node . value , node . raw ) ;
408
+ case 'boolean' :
409
+ return j . booleanLiteralTypeAnnotation ( node . value , node . raw ) ;
410
+ case 'object' :
411
+ return j . nullLiteralTypeAnnotation ( ) ;
412
+ default :
413
+ return flowAnyType ; // meh
414
+ }
415
+ } ;
416
+
417
+ const propTypeToFlowMapping = {
418
+ // prim types
419
+ any : flowAnyType ,
420
+ array : j . genericTypeAnnotation (
421
+ j . identifier ( 'Array' ) ,
422
+ j . typeParameterInstantiation ( [ flowAnyType ] )
423
+ ) ,
424
+ bool : j . booleanTypeAnnotation ( ) ,
425
+ element : flowAnyType ,
426
+ func : j . genericTypeAnnotation (
427
+ j . identifier ( 'Function' ) ,
428
+ null
429
+ ) ,
430
+ node : flowAnyType ,
431
+ number : j . numberTypeAnnotation ( ) ,
432
+ object : j . genericTypeAnnotation (
433
+ j . identifier ( 'Object' ) ,
434
+ null
435
+ ) ,
436
+ string : j . stringTypeAnnotation ( ) ,
437
+
438
+ // type classes
439
+ arrayOf : ( type ) => j . genericTypeAnnotation (
440
+ j . identifier ( 'Array' ) ,
441
+ j . typeParameterInstantiation ( [ type ] )
442
+ ) ,
443
+ instanceOf : ( type ) => j . genericTypeAnnotation (
444
+ type ,
445
+ null
446
+ ) ,
447
+ objectOf : ( type ) => j . objectTypeAnnotation (
448
+ [ ] ,
449
+ [ j . objectTypeIndexer ( j . identifier ( 'key' ) , j . stringTypeAnnotation ( ) , type ) ] ,
450
+ [ ]
451
+ ) ,
452
+ oneOf : ( typeList ) => j . unionTypeAnnotation ( typeList ) ,
453
+ oneOfType : ( typeList ) => j . unionTypeAnnotation ( typeList ) ,
454
+ } ;
455
+
456
+ const propTypeToFlowAnnotation = val => {
457
+ let cursor = val ;
458
+ let isOptional = true ;
459
+ let typeResult = flowAnyType ;
460
+
461
+ if (
462
+ cursor . type === 'MemberExpression' &&
463
+ cursor . property . type === 'Identifier' &&
464
+ cursor . property . name === 'isRequired'
465
+ ) {
466
+ isOptional = false ;
467
+ cursor = cursor . object ;
468
+ }
469
+
470
+ if (
471
+ cursor . type === 'CallExpression'
472
+ ) {
473
+ switch ( cursor . callee . property . name ) {
474
+ case 'arrayOf' : {
475
+ const arg = cursor . arguments [ 0 ] ;
476
+ const constructor = propTypeToFlowMapping [ 'arrayOf' ] ;
477
+ typeResult = constructor (
478
+ propTypeToFlowAnnotation ( arg ) [ 0 ]
479
+ ) ;
480
+ break ;
481
+ }
482
+ case 'instanceOf' : {
483
+ const arg = cursor . arguments [ 0 ] ;
484
+ if ( arg . type !== 'Identifier' ) {
485
+ typeResult = flowAnyType ;
486
+ break ;
487
+ }
488
+
489
+ const constructor = propTypeToFlowMapping [ 'instanceOf' ] ;
490
+ typeResult = constructor ( arg ) ;
491
+ break ;
492
+ }
493
+ case 'objectOf' : {
494
+ const arg = cursor . arguments [ 0 ] ;
495
+ const constructor = propTypeToFlowMapping [ 'objectOf' ] ;
496
+ typeResult = constructor (
497
+ propTypeToFlowAnnotation ( arg ) [ 0 ]
498
+ ) ;
499
+ break ;
500
+ }
501
+ case 'oneOf' : {
502
+ const argList = cursor . arguments [ 0 ] . elements ;
503
+ if ( ! argList . every ( node => node . type === 'Literal' ) ) {
504
+ typeResult = flowAnyType ;
505
+ break ;
506
+ }
507
+ const constructor = propTypeToFlowMapping [ 'oneOf' ] ;
508
+ typeResult = constructor (
509
+ argList . map ( literalToFlowType )
510
+ ) ;
511
+ break ;
512
+ }
513
+ case 'oneOfType' : {
514
+ const argList = cursor . arguments [ 0 ] . elements ;
515
+ const constructor = propTypeToFlowMapping [ 'oneOfType' ] ;
516
+ typeResult = constructor (
517
+ argList . map ( arg => propTypeToFlowAnnotation ( arg ) [ 0 ] )
518
+ ) ;
519
+ break ;
520
+ }
521
+ case 'shape' : { // TODO
522
+
523
+ }
524
+ }
525
+ } else if (
526
+ cursor . type === 'MemberExpression' &&
527
+ cursor . property . type === 'Identifier'
528
+ ) {
529
+ typeResult = propTypeToFlowMapping [ cursor . property . name ] || flowAnyType ;
530
+ }
531
+
532
+ return [ typeResult , isOptional ] ;
533
+ } ;
534
+
535
+ const createFlowAnnotationsFromPropTypesProperties = ( prop ) => {
536
+ const typeProperty = [ ] ;
537
+
538
+ if ( ! prop ) {
539
+ return typeProperty ;
540
+ }
541
+
542
+ prop . value . properties . forEach ( typeProp => {
543
+ const name = typeProp . key . name ;
544
+ const [ valueType , isOptional ] = propTypeToFlowAnnotation ( typeProp . value ) ;
545
+ typeProperty . push ( j . objectTypeProperty (
546
+ j . identifier ( name ) ,
547
+ valueType ,
548
+ isOptional
549
+ ) ) ;
550
+ } ) ;
551
+
552
+ return j . classProperty (
553
+ j . identifier ( 'props' ) ,
554
+ null ,
555
+ j . typeAnnotation ( j . objectTypeAnnotation ( typeProperty ) ) ,
556
+ false
557
+ ) ;
558
+ } ;
559
+
394
560
// if there's no `getInitialState` or the `getInitialState` function is simple
395
561
// (i.e., it's just a return statement) then we don't need a constructor.
396
562
// we can simply lift `state = {...}` as a property initializer.
@@ -428,7 +594,7 @@ module.exports = (file, api, options) => {
428
594
}
429
595
430
596
const propertiesAndMethods = rawProperties . map ( prop => {
431
- if ( isPrimExpression ( prop ) ) {
597
+ if ( isPrimProperty ( prop ) ) {
432
598
return createClassProperty ( prop ) ;
433
599
} else if ( AUTOBIND_IGNORE_KEYS [ prop . key . name ] ) {
434
600
return createMethodDefinition ( prop ) ;
@@ -437,10 +603,17 @@ module.exports = (file, api, options) => {
437
603
return createArrowProperty ( prop ) ;
438
604
} ) ;
439
605
606
+ const flowPropsAnnotation = options [ 'flow' ] ?
607
+ createFlowAnnotationsFromPropTypesProperties (
608
+ staticProperties . find ( ( path ) => path . key . name === 'propTypes' )
609
+ ) :
610
+ [ ] ;
611
+
440
612
return withComments ( j . classDeclaration (
441
613
name ? j . identifier ( name ) : null ,
442
614
j . classBody (
443
615
[ ] . concat (
616
+ flowPropsAnnotation ,
444
617
staticProperties ,
445
618
maybeConstructor ,
446
619
initialStateProperty ,
@@ -516,7 +689,7 @@ module.exports = (file, api, options) => {
516
689
// Ignore import bindings
517
690
. filter ( identifierPath => ! (
518
691
path . value . type === 'ImportDeclaration' &&
519
- path . value . specifiers . some ( specifier => specifier . local === identifierPath . value )
692
+ path . value . specifiers . some ( specifier => specifier . id === identifierPath . value )
520
693
) )
521
694
// Ignore properties in MemberExpressions
522
695
. filter ( identifierPath => {
@@ -618,3 +791,5 @@ module.exports = (file, api, options) => {
618
791
619
792
return null ;
620
793
} ;
794
+
795
+ module . exports . parser = 'flow' ;
0 commit comments