@@ -10,7 +10,7 @@ import ts from 'typescript';
1010
1111import { MetadataReader } from '../../metadata' ;
1212
13- import { DocEntry } from './entities' ;
13+ import { ClassEntry , DocEntry , EntryType , FunctionEntry , MemberEntry , MemberTags , MemberType , MethodEntry , ParameterEntry , PropertyEntry } from './entities' ;
1414
1515
1616/**
@@ -24,25 +24,151 @@ export class DocsExtractor {
2424 * Gets the set of all documentable entries from a source file.
2525 * @param sourceFile The file from which to extract documentable entries.
2626 */
27- extract ( sourceFile : ts . SourceFile ) : DocEntry [ ] {
28- let entries : DocEntry [ ] = [ ] ;
27+ extractAll ( sourceFile : ts . SourceFile ) : DocEntry [ ] {
28+ const entries : DocEntry [ ] = [ ] ;
2929
3030 for ( const statement of sourceFile . statements ) {
3131 // TODO(jelbourn): get all of rest of the docs
32+ // TODO(jelbourn): ignore un-exported nodes
3233 if ( ts . isClassDeclaration ( statement ) ) {
3334 // Assume that anonymous classes should not be part of public documentation.
3435 if ( ! statement . name ) continue ;
3536
36- entries = entries . concat ( this . extractClassDocs ( statement ) ) ;
37+ entries . push ( this . extractClass ( statement ) ) ;
3738 }
3839 }
3940
4041 return entries ;
4142 }
4243
4344 /** Extract docs info specific to classes. */
44- private extractClassDocs ( statement : ts . ClassDeclaration ) : DocEntry {
45+ private extractClass ( classDeclaration : ts . ClassDeclaration ) : ClassEntry {
4546 // TODO(jelbourn): get all of the rest of the docs
46- return { name : statement . name ! . text } ;
47+ return {
48+ name : classDeclaration . name ! . text ,
49+ entryType : EntryType . undecorated_class ,
50+ members : this . extractAllClassMembers ( classDeclaration ) ,
51+ } ;
52+ }
53+
54+ /** Extracts doc info for a class's members. */
55+ private extractAllClassMembers ( classDeclaration : ts . ClassDeclaration ) : MemberEntry [ ] {
56+ const members : MemberEntry [ ] = [ ] ;
57+
58+ for ( const member of classDeclaration . members ) {
59+ if ( this . isMemberExcluded ( member ) ) continue ;
60+
61+ const memberEntry = this . extractClassMember ( member ) ;
62+ if ( memberEntry ) {
63+ members . push ( memberEntry ) ;
64+ }
65+ }
66+
67+ return members ;
68+ }
69+
70+ /** Extract docs for a class's members (methods and properties). */
71+ private extractClassMember ( memberDeclaration : ts . ClassElement ) : MemberEntry | undefined {
72+ if ( ts . isMethodDeclaration ( memberDeclaration ) ) {
73+ return this . extractMethod ( memberDeclaration ) ;
74+ } else if ( ts . isPropertyDeclaration ( memberDeclaration ) ) {
75+ return this . extractClassProperty ( memberDeclaration ) ;
76+ }
77+
78+ // We only expect methods and properties. If we encounter something else,
79+ // return undefined and let the rest of the program filter it out.
80+ return undefined ;
81+ }
82+
83+ /** Extracts docs for a class method. */
84+ private extractMethod ( methodDeclaration : ts . MethodDeclaration ) : MethodEntry {
85+ return {
86+ ...this . extractFunction ( methodDeclaration ) ,
87+ memberType : MemberType . method ,
88+ memberTags : this . getMemberTags ( methodDeclaration ) ,
89+ } ;
90+ }
91+
92+ /** Extracts docs for a function, including class method declarations. */
93+ private extractFunction ( fn : ts . FunctionDeclaration | ts . MethodDeclaration ) : FunctionEntry {
94+ return {
95+ params : this . extractAllParams ( fn . parameters ) ,
96+ // We know that the function has a name here because we would have skipped it
97+ // already before getting to this point if it was anonymous.
98+ name : fn . name ! . getText ( ) ,
99+ returnType : 'TODO' ,
100+ entryType : EntryType . function ,
101+ } ;
102+ }
103+
104+ /** Extracts doc info for a collection of function parameters. */
105+ private extractAllParams ( params : ts . NodeArray < ts . ParameterDeclaration > ) : ParameterEntry [ ] {
106+ // TODO: handle var args
107+ return params . map ( param => ( {
108+ name : param . name . getText ( ) ,
109+ description : 'TODO' ,
110+ type : 'TODO' ,
111+ isOptional : ! ! ( param . questionToken || param . initializer ) ,
112+ } ) ) ;
113+ }
114+
115+ /** Extracts doc info for a property declaration. */
116+ private extractClassProperty ( propertyDeclaration : ts . PropertyDeclaration ) : PropertyEntry {
117+ return {
118+ name : propertyDeclaration . name . getText ( ) ,
119+ getType : 'TODO' ,
120+ setType : 'TODO' ,
121+ memberType : MemberType . property ,
122+ memberTags : this . getMemberTags ( propertyDeclaration ) ,
123+ } ;
124+ }
125+
126+ /** Gets the tags for a member (protected, readonly, static, etc.) */
127+ private getMemberTags ( member : ts . MethodDeclaration | ts . PropertyDeclaration ) : MemberTags [ ] {
128+ const tags : MemberTags [ ] = [ ] ;
129+ for ( const mod of member . modifiers ?? [ ] ) {
130+ const tag = this . getTagForMemberModifier ( mod ) ;
131+ if ( tag ) tags . push ( tag ) ;
132+ }
133+
134+ if ( member . questionToken ) {
135+ tags . push ( MemberTags . optional ) ;
136+ }
137+
138+ // TODO: mark inputs and outputs
139+
140+ return tags ;
141+ }
142+
143+ /** Gets the doc tag corresponding to a class member modifier (readonly, protected, etc.). */
144+ private getTagForMemberModifier ( mod : ts . ModifierLike ) : MemberTags | undefined {
145+ switch ( mod . kind ) {
146+ case ts . SyntaxKind . StaticKeyword :
147+ return MemberTags . static ;
148+ case ts . SyntaxKind . ReadonlyKeyword :
149+ return MemberTags . readonly ;
150+ case ts . SyntaxKind . ProtectedKeyword :
151+ return MemberTags . protected ;
152+ default :
153+ return undefined ;
154+ }
155+ }
156+
157+ /**
158+ * Gets whether a given class member should be excluded from public API docs.
159+ * This is the case if:
160+ * - The member does not have a name
161+ * - The member is neither a method nor property
162+ * - The member is private
163+ */
164+ private isMemberExcluded ( member : ts . ClassElement ) : boolean {
165+ return ! member . name || ! this . isMethodOrProperty ( member ) ||
166+ ! ! member . modifiers ?. some ( mod => mod . kind === ts . SyntaxKind . PrivateKeyword ) ;
167+ }
168+
169+ /** Gets whether a class member is either a member or a property. */
170+ private isMethodOrProperty ( member : ts . ClassElement ) : member is ts . MethodDeclaration
171+ | ts . PropertyDeclaration {
172+ return ts . isMethodDeclaration ( member ) || ts . isPropertyDeclaration ( member ) ;
47173 }
48174}
0 commit comments