1
- /*eslint no-use-before-define: 0*/
2
1
import type { NodePath } from '@babel/traverse' ;
3
2
import { getDocblock } from '../utils/docblock.js' ;
4
3
import getMembers from './getMembers.js' ;
@@ -8,13 +7,8 @@ import printValue from './printValue.js';
8
7
import resolveToValue from './resolveToValue.js' ;
9
8
import resolveObjectKeysToArray from './resolveObjectKeysToArray.js' ;
10
9
import resolveObjectValuesToArray from './resolveObjectValuesToArray.js' ;
11
- import type { PropTypeDescriptor , PropDescriptor } from '../Documentation.js' ;
12
- import type {
13
- ArrayExpression ,
14
- Expression ,
15
- ObjectProperty ,
16
- SpreadElement ,
17
- } from '@babel/types' ;
10
+ import type { PropTypeDescriptor } from '../Documentation.js' ;
11
+ import type { ArrayExpression , Expression , SpreadElement } from '@babel/types' ;
18
12
19
13
function getEnumValuesFromArrayExpression (
20
14
path : NodePath < ArrayExpression > ,
@@ -51,11 +45,15 @@ function getEnumValuesFromArrayExpression(
51
45
return values ;
52
46
}
53
47
54
- function getPropTypeOneOf ( argumentPath : NodePath ) : PropTypeDescriptor {
55
- const type : PropTypeDescriptor = { name : 'enum' } ;
56
- const value : NodePath = resolveToValue ( argumentPath ) ;
48
+ function getPropTypeOneOf (
49
+ type : PropTypeDescriptor ,
50
+ argumentPath : NodePath ,
51
+ ) : PropTypeDescriptor {
52
+ const value = resolveToValue ( argumentPath ) ;
57
53
58
- if ( ! value . isArrayExpression ( ) ) {
54
+ if ( value . isArrayExpression ( ) ) {
55
+ type . value = getEnumValuesFromArrayExpression ( value ) ;
56
+ } else {
59
57
const objectValues =
60
58
resolveObjectKeysToArray ( value ) || resolveObjectValuesToArray ( value ) ;
61
59
@@ -69,20 +67,16 @@ function getPropTypeOneOf(argumentPath: NodePath): PropTypeDescriptor {
69
67
type . computed = true ;
70
68
type . value = printValue ( argumentPath ) ;
71
69
}
72
- } else {
73
- type . value = getEnumValuesFromArrayExpression ( value ) ;
74
70
}
75
71
76
72
return type ;
77
73
}
78
74
79
- function getPropTypeOneOfType ( argumentPath : NodePath ) : PropTypeDescriptor {
80
- const type : PropTypeDescriptor = { name : 'union' } ;
81
-
82
- if ( ! argumentPath . isArrayExpression ( ) ) {
83
- type . computed = true ;
84
- type . value = printValue ( argumentPath ) ;
85
- } else {
75
+ function getPropTypeOneOfType (
76
+ type : PropTypeDescriptor ,
77
+ argumentPath : NodePath ,
78
+ ) : PropTypeDescriptor {
79
+ if ( argumentPath . isArrayExpression ( ) ) {
86
80
type . value = argumentPath . get ( 'elements' ) . map ( ( elementPath ) => {
87
81
if ( ! elementPath . hasNode ( ) ) return ;
88
82
const descriptor : PropTypeDescriptor = getPropType ( elementPath ) ;
@@ -101,9 +95,7 @@ function getPropTypeOneOfType(argumentPath: NodePath): PropTypeDescriptor {
101
95
return type ;
102
96
}
103
97
104
- function getPropTypeArrayOf ( argumentPath : NodePath ) {
105
- const type : PropTypeDescriptor = { name : 'arrayOf' } ;
106
-
98
+ function getPropTypeArrayOf ( type : PropTypeDescriptor , argumentPath : NodePath ) {
107
99
const docs = getDocblock ( argumentPath ) ;
108
100
109
101
if ( docs ) {
@@ -113,19 +105,14 @@ function getPropTypeArrayOf(argumentPath: NodePath) {
113
105
const subType = getPropType ( argumentPath ) ;
114
106
115
107
// @ts -ignore
116
- if ( subType . name === 'unknown' ) {
117
- type . value = printValue ( argumentPath ) ;
118
- type . computed = true ;
119
- } else {
108
+ if ( subType . name !== 'unknown' ) {
120
109
type . value = subType ;
121
110
}
122
111
123
112
return type ;
124
113
}
125
114
126
- function getPropTypeObjectOf ( argumentPath : NodePath ) {
127
- const type : PropTypeDescriptor = { name : 'objectOf' } ;
128
-
115
+ function getPropTypeObjectOf ( type : PropTypeDescriptor , argumentPath : NodePath ) {
129
116
const docs = getDocblock ( argumentPath ) ;
130
117
131
118
if ( docs ) {
@@ -135,63 +122,87 @@ function getPropTypeObjectOf(argumentPath: NodePath) {
135
122
const subType = getPropType ( argumentPath ) ;
136
123
137
124
// @ts -ignore
138
- if ( subType . name === 'unknown' ) {
139
- type . value = printValue ( argumentPath ) ;
140
- type . computed = true ;
141
- } else {
125
+ if ( subType . name !== 'unknown' ) {
142
126
type . value = subType ;
143
127
}
144
128
145
129
return type ;
146
130
}
147
131
132
+ function getFirstArgument ( path : NodePath ) : NodePath | undefined {
133
+ let argument : NodePath | undefined ;
134
+
135
+ if ( path . isCallExpression ( ) ) {
136
+ argument = path . get ( 'arguments' ) [ 0 ] ;
137
+ } else {
138
+ const members = getMembers ( path , true ) ;
139
+
140
+ if ( members [ 0 ] && members [ 0 ] . argumentPaths [ 0 ] ) {
141
+ argument = members [ 0 ] . argumentPaths [ 0 ] ;
142
+ }
143
+ }
144
+
145
+ return argument ;
146
+ }
147
+
148
+ function isCyclicReference (
149
+ argument : NodePath ,
150
+ argumentPath : NodePath ,
151
+ ) : boolean {
152
+ return Boolean ( argument && resolveToValue ( argument ) === argumentPath ) ;
153
+ }
154
+
148
155
/**
149
156
* Handles shape and exact prop types
150
157
*/
151
- function getPropTypeShapish ( name : 'exact' | 'shape' , argumentPath : NodePath ) {
152
- const type : PropTypeDescriptor = { name } ;
153
-
158
+ function getPropTypeShapish ( type : PropTypeDescriptor , argumentPath : NodePath ) {
154
159
if ( ! argumentPath . isObjectExpression ( ) ) {
155
160
argumentPath = resolveToValue ( argumentPath ) ;
156
161
}
157
162
158
163
if ( argumentPath . isObjectExpression ( ) ) {
159
- const value = { } ;
164
+ let value : Record < string , PropTypeDescriptor > | string = { } ;
160
165
161
166
argumentPath . get ( 'properties' ) . forEach ( ( propertyPath ) => {
162
- if ( propertyPath . isSpreadElement ( ) || propertyPath . isObjectMethod ( ) ) {
163
- // It is impossible to resolve a name for a spread element
164
- return ;
165
- }
167
+ // We only handle ObjectProperty as there is nothing to handle for
168
+ // SpreadElements and ObjectMethods
169
+ if ( propertyPath . isObjectProperty ( ) ) {
170
+ const propertyName = getPropertyName ( propertyPath ) ;
166
171
167
- const propertyName = getPropertyName ( propertyPath ) ;
172
+ if ( ! propertyName ) return ;
168
173
169
- if ( ! propertyName ) return ;
174
+ const valuePath = propertyPath . get ( 'value' ) ;
175
+ const argument = getFirstArgument ( valuePath ) ;
170
176
171
- const valuePath = ( propertyPath as NodePath < ObjectProperty > ) . get ( 'value' ) ;
177
+ // This indicates we have a cyclic reference in the shape
178
+ // In this case we simply print the argument to shape and bail
179
+ if ( argument && isCyclicReference ( argument , argumentPath ) ) {
180
+ value = printValue ( argument ) ;
172
181
173
- const descriptor : PropDescriptor | PropTypeDescriptor =
174
- getPropType ( valuePath ) ;
175
- const docs = getDocblock ( propertyPath ) ;
182
+ return ;
183
+ }
176
184
177
- if ( docs ) {
178
- descriptor . description = docs ;
185
+ const descriptor = getPropType ( valuePath ) ;
186
+ const docs = getDocblock ( propertyPath ) ;
187
+
188
+ if ( docs ) {
189
+ descriptor . description = docs ;
190
+ }
191
+ descriptor . required = isRequiredPropType ( valuePath ) ;
192
+ value [ propertyName ] = descriptor ;
179
193
}
180
- descriptor . required = isRequiredPropType ( valuePath ) ;
181
- value [ propertyName ] = descriptor ;
182
194
} ) ;
183
- type . value = value ;
184
- }
185
195
186
- if ( ! type . value ) {
187
- type . value = printValue ( argumentPath ) ;
188
- type . computed = true ;
196
+ type . value = value ;
189
197
}
190
198
191
199
return type ;
192
200
}
193
201
194
- function getPropTypeInstanceOf ( argumentPath : NodePath ) : PropTypeDescriptor {
202
+ function getPropTypeInstanceOf (
203
+ _type : PropTypeDescriptor ,
204
+ argumentPath : NodePath ,
205
+ ) : PropTypeDescriptor {
195
206
return {
196
207
name : 'instanceOf' ,
197
208
value : printValue ( argumentPath ) ,
@@ -218,16 +229,47 @@ function isSimplePropType(
218
229
return simplePropTypes . includes ( name as ( typeof simplePropTypes ) [ number ] ) ;
219
230
}
220
231
221
- const propTypes = new Map < string , ( path : NodePath ) => PropTypeDescriptor > ( [
222
- [ 'oneOf' , getPropTypeOneOf ] ,
223
- [ 'oneOfType' , getPropTypeOneOfType ] ,
224
- [ 'instanceOf' , getPropTypeInstanceOf ] ,
225
- [ 'arrayOf' , getPropTypeArrayOf ] ,
226
- [ 'objectOf' , getPropTypeObjectOf ] ,
227
- [ 'shape' , getPropTypeShapish . bind ( null , 'shape' ) ] ,
228
- [ 'exact' , getPropTypeShapish . bind ( null , 'exact' ) ] ,
232
+ type PropTypeHandler = (
233
+ type : PropTypeDescriptor ,
234
+ argumentPath : NodePath ,
235
+ ) => PropTypeDescriptor ;
236
+
237
+ const propTypes = new Map <
238
+ string ,
239
+ ( argumentPath : NodePath | undefined ) => PropTypeDescriptor
240
+ > ( [
241
+ [ 'oneOf' , callPropTypeHandler . bind ( null , 'enum' , getPropTypeOneOf ) ] ,
242
+ [ 'oneOfType' , callPropTypeHandler . bind ( null , 'union' , getPropTypeOneOfType ) ] ,
243
+ [
244
+ 'instanceOf' ,
245
+ callPropTypeHandler . bind ( null , 'instanceOf' , getPropTypeInstanceOf ) ,
246
+ ] ,
247
+ [ 'arrayOf' , callPropTypeHandler . bind ( null , 'arrayOf' , getPropTypeArrayOf ) ] ,
248
+ [ 'objectOf' , callPropTypeHandler . bind ( null , 'objectOf' , getPropTypeObjectOf ) ] ,
249
+ [ 'shape' , callPropTypeHandler . bind ( null , 'shape' , getPropTypeShapish ) ] ,
250
+ [ 'exact' , callPropTypeHandler . bind ( null , 'exact' , getPropTypeShapish ) ] ,
229
251
] ) ;
230
252
253
+ function callPropTypeHandler (
254
+ name : PropTypeDescriptor [ 'name' ] ,
255
+ handler : PropTypeHandler ,
256
+ argumentPath : NodePath | undefined ,
257
+ ) {
258
+ let type : PropTypeDescriptor = { name } ;
259
+
260
+ if ( argumentPath ) {
261
+ type = handler ( type , argumentPath ) ;
262
+ }
263
+
264
+ if ( ! type . value ) {
265
+ // If there is no argument then leave the value an empty string
266
+ type . value = argumentPath ? printValue ( argumentPath ) : '' ;
267
+ type . computed = true ;
268
+ }
269
+
270
+ return type ;
271
+ }
272
+
231
273
/**
232
274
* Tries to identify the prop type by inspecting the path for known
233
275
* prop type names. This method doesn't check whether the found type is actually
@@ -256,7 +298,7 @@ export default function getPropType(path: NodePath): PropTypeDescriptor {
256
298
}
257
299
const propTypeHandler = propTypes . get ( name ) ;
258
300
259
- if ( propTypeHandler && member . argumentPaths . length ) {
301
+ if ( propTypeHandler ) {
260
302
descriptor = propTypeHandler ( member . argumentPaths [ 0 ] ) ;
261
303
262
304
return true ;
0 commit comments