1- /*eslint no-use-before-define: 0*/
21import type { NodePath } from '@babel/traverse' ;
32import { getDocblock } from '../utils/docblock.js' ;
43import getMembers from './getMembers.js' ;
@@ -51,11 +50,15 @@ function getEnumValuesFromArrayExpression(
5150 return values ;
5251}
5352
54- function getPropTypeOneOf ( argumentPath : NodePath ) : PropTypeDescriptor {
55- const type : PropTypeDescriptor = { name : 'enum' } ;
56- const value : NodePath = resolveToValue ( argumentPath ) ;
53+ function getPropTypeOneOf (
54+ type : PropTypeDescriptor ,
55+ argumentPath : NodePath ,
56+ ) : PropTypeDescriptor {
57+ const value = resolveToValue ( argumentPath ) ;
5758
58- if ( ! value . isArrayExpression ( ) ) {
59+ if ( value . isArrayExpression ( ) ) {
60+ type . value = getEnumValuesFromArrayExpression ( value ) ;
61+ } else {
5962 const objectValues =
6063 resolveObjectKeysToArray ( value ) || resolveObjectValuesToArray ( value ) ;
6164
@@ -69,20 +72,16 @@ function getPropTypeOneOf(argumentPath: NodePath): PropTypeDescriptor {
6972 type . computed = true ;
7073 type . value = printValue ( argumentPath ) ;
7174 }
72- } else {
73- type . value = getEnumValuesFromArrayExpression ( value ) ;
7475 }
7576
7677 return type ;
7778}
7879
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 {
80+ function getPropTypeOneOfType (
81+ type : PropTypeDescriptor ,
82+ argumentPath : NodePath ,
83+ ) : PropTypeDescriptor {
84+ if ( argumentPath . isArrayExpression ( ) ) {
8685 type . value = argumentPath . get ( 'elements' ) . map ( ( elementPath ) => {
8786 if ( ! elementPath . hasNode ( ) ) return ;
8887 const descriptor : PropTypeDescriptor = getPropType ( elementPath ) ;
@@ -101,9 +100,7 @@ function getPropTypeOneOfType(argumentPath: NodePath): PropTypeDescriptor {
101100 return type ;
102101}
103102
104- function getPropTypeArrayOf ( argumentPath : NodePath ) {
105- const type : PropTypeDescriptor = { name : 'arrayOf' } ;
106-
103+ function getPropTypeArrayOf ( type : PropTypeDescriptor , argumentPath : NodePath ) {
107104 const docs = getDocblock ( argumentPath ) ;
108105
109106 if ( docs ) {
@@ -113,19 +110,14 @@ function getPropTypeArrayOf(argumentPath: NodePath) {
113110 const subType = getPropType ( argumentPath ) ;
114111
115112 // @ts -ignore
116- if ( subType . name === 'unknown' ) {
117- type . value = printValue ( argumentPath ) ;
118- type . computed = true ;
119- } else {
113+ if ( subType . name !== 'unknown' ) {
120114 type . value = subType ;
121115 }
122116
123117 return type ;
124118}
125119
126- function getPropTypeObjectOf ( argumentPath : NodePath ) {
127- const type : PropTypeDescriptor = { name : 'objectOf' } ;
128-
120+ function getPropTypeObjectOf ( type : PropTypeDescriptor , argumentPath : NodePath ) {
129121 const docs = getDocblock ( argumentPath ) ;
130122
131123 if ( docs ) {
@@ -135,63 +127,87 @@ function getPropTypeObjectOf(argumentPath: NodePath) {
135127 const subType = getPropType ( argumentPath ) ;
136128
137129 // @ts -ignore
138- if ( subType . name === 'unknown' ) {
139- type . value = printValue ( argumentPath ) ;
140- type . computed = true ;
141- } else {
130+ if ( subType . name !== 'unknown' ) {
142131 type . value = subType ;
143132 }
144133
145134 return type ;
146135}
147136
137+ function getFirstArgument ( path : NodePath ) : NodePath | undefined {
138+ let argument : NodePath | undefined ;
139+
140+ if ( path . isCallExpression ( ) ) {
141+ argument = path . get ( 'arguments' ) [ 0 ] ;
142+ } else {
143+ const members = getMembers ( path , true ) ;
144+
145+ if ( members [ 0 ] && members [ 0 ] . argumentPaths [ 0 ] ) {
146+ argument = members [ 0 ] . argumentPaths [ 0 ] ;
147+ }
148+ }
149+
150+ return argument ;
151+ }
152+
153+ function isCyclicReference (
154+ argument : NodePath ,
155+ argumentPath : NodePath ,
156+ ) : boolean {
157+ return Boolean ( argument && resolveToValue ( argument ) === argumentPath ) ;
158+ }
159+
148160/**
149161 * Handles shape and exact prop types
150162 */
151- function getPropTypeShapish ( name : 'exact' | 'shape' , argumentPath : NodePath ) {
152- const type : PropTypeDescriptor = { name } ;
153-
163+ function getPropTypeShapish ( type : PropTypeDescriptor , argumentPath : NodePath ) {
154164 if ( ! argumentPath . isObjectExpression ( ) ) {
155165 argumentPath = resolveToValue ( argumentPath ) ;
156166 }
157167
158168 if ( argumentPath . isObjectExpression ( ) ) {
159- const value = { } ;
169+ let value : Record < string , PropTypeDescriptor > | string = { } ;
160170
161171 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- }
172+ // We only handle ObjectProperty as there is nothing to handle for
173+ // SpreadElements and ObjectMethods
174+ if ( propertyPath . isObjectProperty ( ) ) {
175+ const propertyName = getPropertyName ( propertyPath ) ;
166176
167- const propertyName = getPropertyName ( propertyPath ) ;
177+ if ( ! propertyName ) return ;
168178
169- if ( ! propertyName ) return ;
179+ const valuePath = propertyPath . get ( 'value' ) ;
180+ const argument = getFirstArgument ( valuePath ) ;
170181
171- const valuePath = ( propertyPath as NodePath < ObjectProperty > ) . get ( 'value' ) ;
182+ // This indicates we have a cyclic reference in the shape
183+ // In this case we simply print the argument to shape and bail
184+ if ( argument && isCyclicReference ( argument , argumentPath ) ) {
185+ value = printValue ( argument ) ;
172186
173- const descriptor : PropDescriptor | PropTypeDescriptor =
174- getPropType ( valuePath ) ;
175- const docs = getDocblock ( propertyPath ) ;
187+ return ;
188+ }
176189
177- if ( docs ) {
178- descriptor . description = docs ;
190+ const descriptor = getPropType ( valuePath ) ;
191+ const docs = getDocblock ( propertyPath ) ;
192+
193+ if ( docs ) {
194+ descriptor . description = docs ;
195+ }
196+ descriptor . required = isRequiredPropType ( valuePath ) ;
197+ value [ propertyName ] = descriptor ;
179198 }
180- descriptor . required = isRequiredPropType ( valuePath ) ;
181- value [ propertyName ] = descriptor ;
182199 } ) ;
183- type . value = value ;
184- }
185200
186- if ( ! type . value ) {
187- type . value = printValue ( argumentPath ) ;
188- type . computed = true ;
201+ type . value = value ;
189202 }
190203
191204 return type ;
192205}
193206
194- function getPropTypeInstanceOf ( argumentPath : NodePath ) : PropTypeDescriptor {
207+ function getPropTypeInstanceOf (
208+ _type : PropTypeDescriptor ,
209+ argumentPath : NodePath ,
210+ ) : PropTypeDescriptor {
195211 return {
196212 name : 'instanceOf' ,
197213 value : printValue ( argumentPath ) ,
@@ -218,16 +234,47 @@ function isSimplePropType(
218234 return simplePropTypes . includes ( name as ( typeof simplePropTypes ) [ number ] ) ;
219235}
220236
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' ) ] ,
237+ type PropTypeHandler = (
238+ type : PropTypeDescriptor ,
239+ argumentPath : NodePath ,
240+ ) => PropTypeDescriptor ;
241+
242+ const propTypes = new Map <
243+ string ,
244+ ( argumentPath : NodePath | undefined ) => PropTypeDescriptor
245+ > ( [
246+ [ 'oneOf' , callPropTypeHandler . bind ( null , 'enum' , getPropTypeOneOf ) ] ,
247+ [ 'oneOfType' , callPropTypeHandler . bind ( null , 'union' , getPropTypeOneOfType ) ] ,
248+ [
249+ 'instanceOf' ,
250+ callPropTypeHandler . bind ( null , 'instanceOf' , getPropTypeInstanceOf ) ,
251+ ] ,
252+ [ 'arrayOf' , callPropTypeHandler . bind ( null , 'arrayOf' , getPropTypeArrayOf ) ] ,
253+ [ 'objectOf' , callPropTypeHandler . bind ( null , 'objectOf' , getPropTypeObjectOf ) ] ,
254+ [ 'shape' , callPropTypeHandler . bind ( null , 'shape' , getPropTypeShapish ) ] ,
255+ [ 'exact' , callPropTypeHandler . bind ( null , 'exact' , getPropTypeShapish ) ] ,
229256] ) ;
230257
258+ function callPropTypeHandler (
259+ name : PropTypeDescriptor [ 'name' ] ,
260+ handler : PropTypeHandler ,
261+ argumentPath : NodePath | undefined ,
262+ ) {
263+ let type : PropTypeDescriptor = { name } ;
264+
265+ if ( argumentPath ) {
266+ type = handler ( type , argumentPath ) ;
267+ }
268+
269+ if ( ! type . value ) {
270+ // If there is no argument then leave the value an empty string
271+ type . value = argumentPath ? printValue ( argumentPath ) : '' ;
272+ type . computed = true ;
273+ }
274+
275+ return type ;
276+ }
277+
231278/**
232279 * Tries to identify the prop type by inspecting the path for known
233280 * prop type names. This method doesn't check whether the found type is actually
@@ -256,7 +303,7 @@ export default function getPropType(path: NodePath): PropTypeDescriptor {
256303 }
257304 const propTypeHandler = propTypes . get ( name ) ;
258305
259- if ( propTypeHandler && member . argumentPaths . length ) {
306+ if ( propTypeHandler ) {
260307 descriptor = propTypeHandler ( member . argumentPaths [ 0 ] ) ;
261308
262309 return true ;
0 commit comments