1- /*eslint no-use-before-define: 0*/
21import type { NodePath } from '@babel/traverse' ;
32import { getDocblock } from '../utils/docblock.js' ;
43import getMembers from './getMembers.js' ;
@@ -8,13 +7,8 @@ import printValue from './printValue.js';
87import resolveToValue from './resolveToValue.js' ;
98import resolveObjectKeysToArray from './resolveObjectKeysToArray.js' ;
109import 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' ;
1812
1913function getEnumValuesFromArrayExpression (
2014 path : NodePath < ArrayExpression > ,
@@ -51,11 +45,15 @@ function getEnumValuesFromArrayExpression(
5145 return values ;
5246}
5347
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 ) ;
5753
58- if ( ! value . isArrayExpression ( ) ) {
54+ if ( value . isArrayExpression ( ) ) {
55+ type . value = getEnumValuesFromArrayExpression ( value ) ;
56+ } else {
5957 const objectValues =
6058 resolveObjectKeysToArray ( value ) || resolveObjectValuesToArray ( value ) ;
6159
@@ -69,20 +67,16 @@ function getPropTypeOneOf(argumentPath: NodePath): PropTypeDescriptor {
6967 type . computed = true ;
7068 type . value = printValue ( argumentPath ) ;
7169 }
72- } else {
73- type . value = getEnumValuesFromArrayExpression ( value ) ;
7470 }
7571
7672 return type ;
7773}
7874
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 ( ) ) {
8680 type . value = argumentPath . get ( 'elements' ) . map ( ( elementPath ) => {
8781 if ( ! elementPath . hasNode ( ) ) return ;
8882 const descriptor : PropTypeDescriptor = getPropType ( elementPath ) ;
@@ -101,9 +95,7 @@ function getPropTypeOneOfType(argumentPath: NodePath): PropTypeDescriptor {
10195 return type ;
10296}
10397
104- function getPropTypeArrayOf ( argumentPath : NodePath ) {
105- const type : PropTypeDescriptor = { name : 'arrayOf' } ;
106-
98+ function getPropTypeArrayOf ( type : PropTypeDescriptor , argumentPath : NodePath ) {
10799 const docs = getDocblock ( argumentPath ) ;
108100
109101 if ( docs ) {
@@ -113,19 +105,14 @@ function getPropTypeArrayOf(argumentPath: NodePath) {
113105 const subType = getPropType ( argumentPath ) ;
114106
115107 // @ts -ignore
116- if ( subType . name === 'unknown' ) {
117- type . value = printValue ( argumentPath ) ;
118- type . computed = true ;
119- } else {
108+ if ( subType . name !== 'unknown' ) {
120109 type . value = subType ;
121110 }
122111
123112 return type ;
124113}
125114
126- function getPropTypeObjectOf ( argumentPath : NodePath ) {
127- const type : PropTypeDescriptor = { name : 'objectOf' } ;
128-
115+ function getPropTypeObjectOf ( type : PropTypeDescriptor , argumentPath : NodePath ) {
129116 const docs = getDocblock ( argumentPath ) ;
130117
131118 if ( docs ) {
@@ -135,63 +122,87 @@ function getPropTypeObjectOf(argumentPath: NodePath) {
135122 const subType = getPropType ( argumentPath ) ;
136123
137124 // @ts -ignore
138- if ( subType . name === 'unknown' ) {
139- type . value = printValue ( argumentPath ) ;
140- type . computed = true ;
141- } else {
125+ if ( subType . name !== 'unknown' ) {
142126 type . value = subType ;
143127 }
144128
145129 return type ;
146130}
147131
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+
148155/**
149156 * Handles shape and exact prop types
150157 */
151- function getPropTypeShapish ( name : 'exact' | 'shape' , argumentPath : NodePath ) {
152- const type : PropTypeDescriptor = { name } ;
153-
158+ function getPropTypeShapish ( type : PropTypeDescriptor , argumentPath : NodePath ) {
154159 if ( ! argumentPath . isObjectExpression ( ) ) {
155160 argumentPath = resolveToValue ( argumentPath ) ;
156161 }
157162
158163 if ( argumentPath . isObjectExpression ( ) ) {
159- const value = { } ;
164+ let value : Record < string , PropTypeDescriptor > | string = { } ;
160165
161166 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 ) ;
166171
167- const propertyName = getPropertyName ( propertyPath ) ;
172+ if ( ! propertyName ) return ;
168173
169- if ( ! propertyName ) return ;
174+ const valuePath = propertyPath . get ( 'value' ) ;
175+ const argument = getFirstArgument ( valuePath ) ;
170176
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 ) ;
172181
173- const descriptor : PropDescriptor | PropTypeDescriptor =
174- getPropType ( valuePath ) ;
175- const docs = getDocblock ( propertyPath ) ;
182+ return ;
183+ }
176184
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 ;
179193 }
180- descriptor . required = isRequiredPropType ( valuePath ) ;
181- value [ propertyName ] = descriptor ;
182194 } ) ;
183- type . value = value ;
184- }
185195
186- if ( ! type . value ) {
187- type . value = printValue ( argumentPath ) ;
188- type . computed = true ;
196+ type . value = value ;
189197 }
190198
191199 return type ;
192200}
193201
194- function getPropTypeInstanceOf ( argumentPath : NodePath ) : PropTypeDescriptor {
202+ function getPropTypeInstanceOf (
203+ _type : PropTypeDescriptor ,
204+ argumentPath : NodePath ,
205+ ) : PropTypeDescriptor {
195206 return {
196207 name : 'instanceOf' ,
197208 value : printValue ( argumentPath ) ,
@@ -218,16 +229,47 @@ function isSimplePropType(
218229 return simplePropTypes . includes ( name as ( typeof simplePropTypes ) [ number ] ) ;
219230}
220231
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 ) ] ,
229251] ) ;
230252
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+
231273/**
232274 * Tries to identify the prop type by inspecting the path for known
233275 * 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 {
256298 }
257299 const propTypeHandler = propTypes . get ( name ) ;
258300
259- if ( propTypeHandler && member . argumentPaths . length ) {
301+ if ( propTypeHandler ) {
260302 descriptor = propTypeHandler ( member . argumentPaths [ 0 ] ) ;
261303
262304 return true ;
0 commit comments