3
3
* @license https://opensource.org/licenses/MIT
4
4
*/
5
5
6
+ /* eslint-disable prefer-destructuring */
7
+
6
8
import { declare } from '@babel/helper-plugin-utils' ;
7
9
import { addDefault , addNamed } from '@babel/helper-module-imports' ;
8
10
import syntaxTypeScript from '@babel/plugin-syntax-typescript' ;
9
11
import { types as t } from '@babel/core' ;
12
+ import { TSTypeParameterInstantiation } from '@babel/types' ;
10
13
import addToClass from './addToClass' ;
11
14
import addToFunctionOrVar from './addToFunctionOrVar' ;
12
15
import extractTypeProperties from './extractTypeProperties' ;
13
16
// import { loadProgram } from './typeChecker';
14
17
import upsertImport from './upsertImport' ;
15
- import { Path , PluginOptions , ConvertState } from './types' ;
18
+ import { Path , PluginOptions , ConvertState , PropTypeDeclaration } from './types' ;
16
19
17
20
const BABEL_VERSION = 7 ;
18
21
const MAX_DEPTH = 3 ;
@@ -27,7 +30,7 @@ function isComponentName(name: string) {
27
30
return ! ! name . match ( / ^ [ A - Z ] / u) ;
28
31
}
29
32
30
- function isPropsParam ( param : t . Node ) {
33
+ function isPropsParam ( param : t . Node ) : param is t . Identifier | t . ObjectPattern {
31
34
return (
32
35
// (props: Props)
33
36
( t . isIdentifier ( param ) && ! ! param . typeAnnotation ) ||
@@ -36,6 +39,10 @@ function isPropsParam(param: t.Node) {
36
39
) ;
37
40
}
38
41
42
+ function isPropsType ( param : t . Node ) : param is PropTypeDeclaration {
43
+ return t . isTSTypeReference ( param ) || t . isTSIntersectionType ( param ) || t . isTSUnionType ( param ) ;
44
+ }
45
+
39
46
export default declare ( ( api : any , options : PluginOptions , root : string ) => {
40
47
api . assertVersion ( BABEL_VERSION ) ;
41
48
@@ -116,7 +123,9 @@ export default declare((api: any, options: PluginOptions, root: string) => {
116
123
}
117
124
118
125
if ( node . source . value === 'airbnb-prop-types' ) {
119
- const response = upsertImport ( node , { checkForNamed : 'forbidExtraProps' } ) ;
126
+ const response = upsertImport ( node , {
127
+ checkForNamed : 'forbidExtraProps' ,
128
+ } ) ;
120
129
121
130
state . airbnbPropTypes . hasImport = true ;
122
131
state . airbnbPropTypes . namedImports = response . namedImports ;
@@ -164,8 +173,6 @@ export default declare((api: any, options: PluginOptions, root: string) => {
164
173
165
174
programPath . traverse ( {
166
175
// airbnbPropTypes.componentWithName()
167
- // React.forwardRef()
168
- // React.memo()
169
176
CallExpression ( path : Path < t . CallExpression > ) {
170
177
const { node } = path ;
171
178
const { namedImports } = state . airbnbPropTypes ;
@@ -177,28 +184,6 @@ export default declare((api: any, options: PluginOptions, root: string) => {
177
184
) {
178
185
state . airbnbPropTypes . count += 1 ;
179
186
}
180
-
181
- // INCOMPLETE
182
- if (
183
- t . isMemberExpression ( node . callee ) &&
184
- t . isIdentifier ( node . callee . object ) &&
185
- t . isIdentifier ( node . callee . property ) &&
186
- node . callee . object . name === state . reactImportedName &&
187
- ( node . callee . property . name === 'forwardRef' || node . callee . property . name === 'memo' )
188
- ) {
189
- if (
190
- t . isVariableDeclarator ( path . parent ) &&
191
- t . isVariableDeclaration ( path . parentPath . parent )
192
- ) {
193
- transformers . push ( ( ) =>
194
- addToFunctionOrVar (
195
- path . parentPath . parentPath as any ,
196
- ( ( path . parent as t . VariableDeclarator ) . id as t . Identifier ) . name ,
197
- state ,
198
- ) ,
199
- ) ;
200
- }
201
- }
202
187
} ,
203
188
204
189
// `class Foo extends React.Component<Props> {}`
@@ -232,13 +217,22 @@ export default declare((api: any, options: PluginOptions, root: string) => {
232
217
// `function Foo(props: Props) {}`
233
218
FunctionDeclaration ( path : Path < t . FunctionDeclaration > ) {
234
219
const { node } = path ;
235
- const valid =
220
+
221
+ if (
236
222
! ! state . reactImportedName &&
237
223
isComponentName ( node . id . name ) &&
238
- isPropsParam ( node . params [ 0 ] ) ;
239
-
240
- if ( valid ) {
241
- transformers . push ( ( ) => addToFunctionOrVar ( path , node . id . name , state ) ) ;
224
+ isPropsParam ( node . params [ 0 ] ) &&
225
+ t . isTSTypeAnnotation ( node . params [ 0 ] . typeAnnotation ) &&
226
+ isPropsType ( node . params [ 0 ] . typeAnnotation . typeAnnotation )
227
+ ) {
228
+ transformers . push ( ( ) =>
229
+ addToFunctionOrVar (
230
+ path ,
231
+ node . id . name ,
232
+ ( node . params [ 0 ] as any ) . typeAnnotation . typeAnnotation ,
233
+ state ,
234
+ ) ,
235
+ ) ;
242
236
}
243
237
} ,
244
238
@@ -253,7 +247,11 @@ export default declare((api: any, options: PluginOptions, root: string) => {
253
247
254
248
// PropTypes.*
255
249
MemberExpression ( { node } : Path < t . MemberExpression > ) {
256
- if ( t . isIdentifier ( node . object , { name : state . propTypes . defaultImport } ) ) {
250
+ if (
251
+ t . isIdentifier ( node . object , {
252
+ name : state . propTypes . defaultImport ,
253
+ } )
254
+ ) {
257
255
state . propTypes . count += 1 ;
258
256
}
259
257
} ,
@@ -291,6 +289,8 @@ export default declare((api: any, options: PluginOptions, root: string) => {
291
289
292
290
// `const Foo = (props: Props) => {};`
293
291
// `const Foo: React.FC<Props> = () => {};`
292
+ // `const Ref = React.forwardRef<Element, Props>();`
293
+ // `const Memo = React.memo<Props>();`
294
294
VariableDeclaration ( path : Path < t . VariableDeclaration > ) {
295
295
const { node } = path ;
296
296
@@ -300,39 +300,102 @@ export default declare((api: any, options: PluginOptions, root: string) => {
300
300
301
301
const decl = node . declarations [ 0 ] ;
302
302
const id = decl . id as t . Identifier ;
303
- let valid = false ;
303
+ let props : PropTypeDeclaration | null = null ;
304
304
305
305
// const Foo: React.FC<Props> = () => {};
306
306
if ( id . typeAnnotation && id . typeAnnotation . typeAnnotation ) {
307
307
const type = id . typeAnnotation . typeAnnotation ;
308
308
309
- // prettier-ignore
310
- valid = t . isTSTypeReference ( type ) &&
309
+ if (
310
+ t . isTSTypeReference ( type ) &&
311
311
! ! type . typeParameters &&
312
- type . typeParameters . params . length > 0 && (
312
+ type . typeParameters . params . length > 0 &&
313
+ isPropsType ( type . typeParameters . params [ 0 ] ) &&
313
314
// React.FC, React.FunctionComponent
314
- (
315
- t . isTSQualifiedName ( type . typeName ) &&
316
- t . isIdentifier ( type . typeName . left , { name : state . reactImportedName } ) &&
317
- REACT_FC_NAMES . some ( name => t . isIdentifier ( ( type . typeName as any ) . right , { name } ) )
318
- ) ||
319
- // FC, FunctionComponent
320
- (
321
- ! ! state . reactImportedName &&
322
- REACT_FC_NAMES . some ( name => t . isIdentifier ( type . typeName , { name } ) )
323
- )
324
- ) ;
315
+ ( ( t . isTSQualifiedName ( type . typeName ) &&
316
+ t . isIdentifier ( type . typeName . left , {
317
+ name : state . reactImportedName ,
318
+ } ) &&
319
+ REACT_FC_NAMES . some ( name =>
320
+ t . isIdentifier ( ( type . typeName as any ) . right , { name } ) ,
321
+ ) ) ||
322
+ // FC, FunctionComponent
323
+ ( ! ! state . reactImportedName &&
324
+ REACT_FC_NAMES . some ( name => t . isIdentifier ( type . typeName , { name } ) ) ) )
325
+ ) {
326
+ props = type . typeParameters . params [ 0 ] ;
327
+ }
325
328
326
329
// const Foo = (props: Props) => {};
327
330
} else if ( t . isArrowFunctionExpression ( decl . init ) ) {
328
- valid =
331
+ if (
329
332
! ! state . reactImportedName &&
330
333
isComponentName ( id . name ) &&
331
- isPropsParam ( decl . init . params [ 0 ] ) ;
334
+ isPropsParam ( decl . init . params [ 0 ] ) &&
335
+ t . isTSTypeAnnotation ( decl . init . params [ 0 ] . typeAnnotation ) &&
336
+ isPropsType ( decl . init . params [ 0 ] . typeAnnotation . typeAnnotation )
337
+ ) {
338
+ props = decl . init . params [ 0 ] . typeAnnotation . typeAnnotation ;
339
+ }
340
+
341
+ // const Ref = React.forwardRef();
342
+ // const Memo = React.memo<Props>();
343
+ } else if ( t . isCallExpression ( decl . init ) ) {
344
+ const { init } = decl ;
345
+ const typeParameters = ( init as any ) . typeParameters as TSTypeParameterInstantiation ;
346
+
347
+ if (
348
+ t . isMemberExpression ( init . callee ) &&
349
+ t . isIdentifier ( init . callee . object ) &&
350
+ t . isIdentifier ( init . callee . property ) &&
351
+ init . callee . object . name === state . reactImportedName
352
+ ) {
353
+ if ( init . callee . property . name === 'forwardRef' ) {
354
+ // const Ref = React.forwardRef<Element, Props>();
355
+ if (
356
+ ! ! typeParameters &&
357
+ t . isTSTypeParameterInstantiation ( typeParameters ) &&
358
+ typeParameters . params . length > 1 &&
359
+ isPropsType ( typeParameters . params [ 1 ] )
360
+ ) {
361
+ props = typeParameters . params [ 1 ] ;
362
+
363
+ // const Ref = React.forwardRef((props: Props) => {});
364
+ } else if (
365
+ t . isArrowFunctionExpression ( init . arguments [ 0 ] ) &&
366
+ init . arguments [ 0 ] . params . length > 0 &&
367
+ isPropsParam ( init . arguments [ 0 ] . params [ 0 ] ) &&
368
+ t . isTSTypeAnnotation ( init . arguments [ 0 ] . params [ 0 ] . typeAnnotation ) &&
369
+ isPropsType ( init . arguments [ 0 ] . params [ 0 ] . typeAnnotation . typeAnnotation )
370
+ ) {
371
+ props = init . arguments [ 0 ] . params [ 0 ] . typeAnnotation . typeAnnotation ;
372
+ }
373
+ } else if ( init . callee . property . name === 'memo' ) {
374
+ // const Ref = React.memo<Props>();
375
+ if (
376
+ ! ! typeParameters &&
377
+ t . isTSTypeParameterInstantiation ( typeParameters ) &&
378
+ typeParameters . params . length > 0 &&
379
+ isPropsType ( typeParameters . params [ 0 ] )
380
+ ) {
381
+ props = typeParameters . params [ 0 ] ;
382
+
383
+ // const Ref = React.memo((props: Props) => {});
384
+ } else if (
385
+ t . isArrowFunctionExpression ( init . arguments [ 0 ] ) &&
386
+ init . arguments [ 0 ] . params . length > 0 &&
387
+ isPropsParam ( init . arguments [ 0 ] . params [ 0 ] ) &&
388
+ t . isTSTypeAnnotation ( init . arguments [ 0 ] . params [ 0 ] . typeAnnotation ) &&
389
+ isPropsType ( init . arguments [ 0 ] . params [ 0 ] . typeAnnotation . typeAnnotation )
390
+ ) {
391
+ props = init . arguments [ 0 ] . params [ 0 ] . typeAnnotation . typeAnnotation ;
392
+ }
393
+ }
394
+ }
332
395
}
333
396
334
- if ( valid ) {
335
- transformers . push ( ( ) => addToFunctionOrVar ( path , id . name , state ) ) ;
397
+ if ( props ) {
398
+ transformers . push ( ( ) => addToFunctionOrVar ( path , id . name , props ! , state ) ) ;
336
399
}
337
400
} ,
338
401
} ) ;
0 commit comments