@@ -43,68 +43,108 @@ export default function extractDocumentation(
4343 }
4444
4545 const exportSymbols = checker . getExportsOfModule ( moduleSymbol ) ;
46- const definitions : Array < TestUtilsDoc > = [ ] ;
46+ const definitions = new Map < string , TestUtilsDoc > ( ) ;
4747
4848 for ( const symbol of exportSymbols ) {
4949 const className = symbol . getName ( ) ;
5050 if ( extraExports . includes ( className ) ) {
5151 continue ;
5252 }
5353 const classType = checker . getDeclaredTypeOfSymbol ( symbol ) ;
54- if ( ! classType . isClass ( ) ) {
55- throw new Error ( `Exported symbol is not a class, got ${ checker . symbolToString ( symbol ) } ` ) ;
56- }
54+ documentClass ( definitions , symbol , classType , checker ) ;
55+ }
5756
58- const classDefinition : TestUtilsDoc = { name : className , methods : [ ] } ;
59- for ( const property of classType . getProperties ( ) ) {
60- const declaration = property . valueDeclaration ;
61- if ( ! declaration ) {
62- throw new Error ( `Unexpected member on ${ className } – ${ property . getName ( ) } ` ) ;
63- }
64- const modifiers = ( ts . canHaveModifiers ( declaration ) && ts . getModifiers ( declaration ) ) || [ ] ;
65- if (
66- modifiers . find (
67- modifier => modifier . kind & ts . SyntaxKind . ProtectedKeyword || modifier . kind & ts . SyntaxKind . PrivateKeyword
68- )
69- ) {
70- continue ;
71- }
72- const type = checker . getTypeAtLocation ( declaration ) ;
73- // report each function signature as a separate method
74- for ( const signature of type . getCallSignatures ( ) ) {
75- const returnType = signature . getReturnType ( ) ;
76- // non-nullable type of `void` is `never`
77- const realReturnType = returnType . flags & ts . TypeFlags . Void ? returnType : returnType . getNonNullableType ( ) ;
78- const { typeName, typeParameters } = extractTypeArguments ( realReturnType , checker ) ;
57+ return Array . from ( definitions . values ( ) ) ;
58+ }
7959
80- classDefinition . methods . push ( {
81- name : property . getName ( ) ,
82- description : getDescription ( property . getDocumentationComment ( checker ) , declaration ) . text ,
83- returnType : {
84- name : typeName ,
85- isNullable : isNullable ( returnType ) ,
86- typeArguments : typeParameters ?. map ( typeArgument => ( {
87- name : stringifyType ( typeArgument , checker ) ,
88- } ) ) ,
89- } ,
90- parameters : signature . parameters . map ( parameter => {
91- const paramType = checker . getTypeAtLocation ( extractDeclaration ( parameter ) ) ;
92- return {
93- name : parameter . name ,
94- typeName : stringifyType ( paramType , checker ) ,
95- description : getDescription ( parameter . getDocumentationComment ( checker ) , declaration ) . text ,
96- flags : { isOptional : isOptional ( paramType ) } ,
97- defaultValue : getDefaultValue ( extractDeclaration ( parameter ) ) ,
98- } ;
99- } ) ,
100- inheritedFrom : getInheritedFrom ( declaration , className ) ,
101- } ) ;
60+ function documentClass (
61+ definitions : Map < string , TestUtilsDoc > ,
62+ symbol : ts . Symbol ,
63+ classType : ts . Type ,
64+ checker : ts . TypeChecker
65+ ) {
66+ if ( ! classType . isClass ( ) ) {
67+ throw new Error ( `Exported symbol is not a class, got ${ checker . symbolToString ( symbol ) } ` ) ;
68+ }
69+ const className = symbol . getName ( ) ;
70+ const definition : TestUtilsDoc = { name : className , methods : [ ] } ;
71+ definitions . set ( className , definition ) ;
72+
73+ for ( const property of classType . getProperties ( ) ) {
74+ const declaration = property . valueDeclaration ;
75+ if ( ! declaration ) {
76+ throw new Error ( `Unexpected member on ${ className } – ${ property . getName ( ) } ` ) ;
77+ }
78+ const modifiers = ( ts . canHaveModifiers ( declaration ) && ts . getModifiers ( declaration ) ) || [ ] ;
79+ if (
80+ modifiers . find (
81+ modifier => modifier . kind & ts . SyntaxKind . ProtectedKeyword || modifier . kind & ts . SyntaxKind . PrivateKeyword
82+ )
83+ ) {
84+ continue ;
85+ }
86+ const type = checker . getTypeAtLocation ( declaration ) ;
87+ // report each function signature as a separate method
88+ for ( const signature of type . getCallSignatures ( ) ) {
89+ const maybeReturnType = signature . getReturnType ( ) ;
90+ // non-nullable type of `void` is `never`
91+ const returnType =
92+ maybeReturnType . flags & ts . TypeFlags . Void ? maybeReturnType : maybeReturnType . getNonNullableType ( ) ;
93+ const dependency = findDependencyType ( returnType , checker ) ;
94+ if ( dependency && ! definitions . has ( dependency . symbol . getName ( ) ) ) {
95+ documentClass ( definitions , dependency . symbol , dependency . type , checker ) ;
10296 }
97+
98+ const { typeName, typeParameters } = extractTypeArguments ( returnType , checker ) ;
99+
100+ definition . methods . push ( {
101+ name : property . getName ( ) ,
102+ description : getDescription ( property . getDocumentationComment ( checker ) , declaration ) . text ,
103+ returnType : {
104+ name : typeName ,
105+ isNullable : isNullable ( maybeReturnType ) ,
106+ typeArguments : typeParameters ?. map ( typeArgument => ( {
107+ name : stringifyType ( typeArgument , checker ) ,
108+ } ) ) ,
109+ } ,
110+ parameters : signature . parameters . map ( parameter => {
111+ const paramType = checker . getTypeAtLocation ( extractDeclaration ( parameter ) ) ;
112+ return {
113+ name : parameter . name ,
114+ typeName : stringifyType ( paramType , checker ) ,
115+ description : getDescription ( parameter . getDocumentationComment ( checker ) , declaration ) . text ,
116+ flags : { isOptional : isOptional ( paramType ) } ,
117+ defaultValue : getDefaultValue ( extractDeclaration ( parameter ) ) ,
118+ } ;
119+ } ) ,
120+ inheritedFrom : getInheritedFrom ( declaration , className ) ,
121+ } ) ;
103122 }
104- classDefinition . methods . sort ( ( a , b ) => a . name . localeCompare ( b . name ) ) ;
123+ }
124+ definition . methods . sort ( ( a , b ) => a . name . localeCompare ( b . name ) ) ;
125+ }
126+
127+ function findDependencyType ( type : ts . Type , checker : ts . TypeChecker ) : { type : ts . Type ; symbol : ts . Symbol } | undefined {
128+ const symbol = type . getSymbol ( ) ;
129+ if ( ! symbol ) {
130+ return ;
131+ }
105132
106- definitions . push ( classDefinition ) ;
133+ const typeName = symbol . getName ( ) ;
134+ if ( typeName === 'Array' ) {
135+ const itemType = checker . getTypeArguments ( type as ts . TypeReference ) [ 0 ] ;
136+ return findDependencyType ( itemType , checker ) ;
137+ }
138+ if (
139+ ! typeName . endsWith ( 'Wrapper' ) ||
140+ [ 'ElementWrapper' , 'ComponentWrapper' ] . includes ( typeName ) ||
141+ ! type . isClassOrInterface ( )
142+ ) {
143+ return ;
107144 }
108145
109- return definitions ;
146+ return {
147+ type,
148+ symbol,
149+ } ;
110150}
0 commit comments