@@ -3,7 +3,7 @@ import * as path from "path"
33import  {  emptyDir ,  readdir ,  readFile ,  readJson ,  writeFile  }  from  "fs-extra-p" 
44import  {  JsDocRenderer  }  from  "./JsDocRenderer" 
55import  {  checkErrors ,  processTree  }  from  "./util" 
6- import  {  Class ,  Member ,  MethodDescriptor ,  Property ,  SourceFileDescriptor ,  SourceFileModuleInfo ,  Variable  }  from  "./psi" 
6+ import  {  Class ,  Descriptor ,   Member ,  MethodDescriptor ,  Property ,  SourceFileDescriptor ,  SourceFileModuleInfo ,   Type ,  Variable  }  from  "./psi" 
77import  BluebirdPromise  from  "bluebird-lst" 
88
99export  interface  TsToJsdocOptions  { 
@@ -44,7 +44,7 @@ export async function generateAndWrite(basePath: string, config: ts.ParsedComman
4444 continue 
4545 } 
4646
47-  moveMember ( psi . functions ,  mainPsi . functions ,  name )  ||  moveMember ( psi . variables ,  mainPsi . variables ,  name ) 
47+  moveMember ( psi . functions ,  mainPsi . functions ,  name )  ||  moveMember ( psi . members ,  mainPsi . members ,  name ) 
4848 } 
4949 } 
5050
@@ -53,12 +53,6 @@ export async function generateAndWrite(basePath: string, config: ts.ParsedComman
5353 const  existingClassExampleDirs  =  exampleDir  ==  null  ? null  : new  Set ( ( await  readdir ( exampleDir ) ) . filter ( it  =>  it [ 0 ]  !=  "."  &&  ! it . includes ( "." ) ) ) 
5454
5555 for  ( const  [ moduleId ,  psi ]  of  moduleNameToResult . entries ( ) )  { 
56-  let  result  =  "" 
57-  const  externalToModuleName  =  new  Map < string ,  string > ( ) 
58-  for  ( const  d  of  copyAndSort ( psi . variables ) )  { 
59-  result  +=  generator . renderer . renderVariable ( d ) 
60-  } 
61-  
6256 const  modulePathMapper : ModulePathMapper  =  oldPath  =>  { 
6357 if  ( ! oldPath . startsWith ( "module:" ) )  { 
6458 return  oldPath 
@@ -81,6 +75,17 @@ export async function generateAndWrite(basePath: string, config: ts.ParsedComman
8175 return  oldPath 
8276 } 
8377
78+  let  result  =  "" 
79+  const  externalToModuleName  =  new  Map < string ,  string > ( ) 
80+  for  ( const  d  of  copyAndSort ( psi . members ) )  { 
81+  if  ( ( < any > d ) . kind  ==  null )  { 
82+  result  +=  generator . renderer . renderVariable ( < Variable > d ,  modulePathMapper ) 
83+  } 
84+  else  { 
85+  result  +=  generator . renderer . renderMember ( < Descriptor > d ) 
86+  } 
87+  } 
88+  
8489 for  ( const  d  of  copyAndSort ( psi . classes ) )  { 
8590 let  examples : Array < Example >  =  [ ] 
8691 if  ( existingClassExampleDirs  !=  null  &&  existingClassExampleDirs . has ( d . name ) )  { 
@@ -215,7 +220,7 @@ export class JsDocGenerator {
215220
216221 const  classes : Array < Class >  =  [ ] 
217222 const  functions : Array < MethodDescriptor >  =  [ ] 
218-  const  variables : Array < Variable >  =  [ ] 
223+  const  members : Array < Variable   |   Descriptor >  =  [ ] 
219224
220225 processTree ( sourceFile ,  ( node )  =>  { 
221226 if  ( node . kind  ===  ts . SyntaxKind . InterfaceDeclaration  ||  node . kind  ===  ts . SyntaxKind . ClassDeclaration )  { 
@@ -240,20 +245,26 @@ export class JsDocGenerator {
240245 else  if  ( node . kind  ===  ts . SyntaxKind . VariableStatement )  { 
241246 const  descriptor  =  this . describeVariable ( < ts . VariableStatement > node ) 
242247 if  ( descriptor  !=  null )  { 
243-  variables . push ( descriptor ) 
248+  members . push ( descriptor ) 
249+  } 
250+  } 
251+  else  if  ( node . kind  ===  ts . SyntaxKind . EnumDeclaration )  { 
252+  const  descriptor  =  this . describeEnum ( < ts . EnumDeclaration > node ) 
253+  if  ( descriptor  !=  null )  { 
254+  members . push ( descriptor ) 
244255 } 
245256 } 
246257 return  true 
247258 } ) 
248259
249260 const  existingPsi  =  this . moduleNameToResult . get ( moduleId . id ) 
250261 if  ( existingPsi  ==  null )  { 
251-  this . moduleNameToResult . set ( moduleId . id ,  { classes,  functions,  variables } ) 
262+  this . moduleNameToResult . set ( moduleId . id ,  { classes,  functions,  members } ) 
252263 } 
253264 else  { 
254265 existingPsi . classes . push ( ...classes ) 
255266 existingPsi . functions . push ( ...functions ) 
256-  existingPsi . variables . push ( ...variables ) 
267+  existingPsi . members . push ( ...members ) 
257268 } 
258269 } 
259270
@@ -292,7 +303,7 @@ export class JsDocGenerator {
292303 this . mainMappings . set ( this . sourceFileToModuleId ( sourceFile ) . id ,  names ) 
293304 } 
294305
295-  getTypeNamePathByNode ( node : ts . Node ) : Array < string >  |  null  { 
306+  getTypeNamePathByNode ( node : ts . Node ) : Array < string   |   Type >  |  null  { 
296307 if  ( node . kind  ===  ts . SyntaxKind . UnionType )  { 
297308 return  this . typesToList ( ( < ts . UnionType > ( < any > node ) ) . types ,  node ) 
298309 } 
@@ -318,33 +329,46 @@ export class JsDocGenerator {
318329 const  text  =  ( < ts . LiteralLikeNode > ( < any > ( < ts . LiteralTypeNode > node ) . literal ) ) . text 
319330 return  [ `"${ text }  ] 
320331 } 
332+  else  if  ( node . kind  ===  ts . SyntaxKind . TypeLiteral )  { 
333+  // todo 
334+  return  [ 'Object.<string, any>' ] 
335+  } 
321336
322337 const  type  =  this . program . getTypeChecker ( ) . getTypeAtLocation ( node ) 
323-  if  ( type  ==  null )  { 
324-  return  null 
338+  return  type  ==  null  ? null  : this . getTypeNames ( type ,  node ) 
339+  } 
340+ 
341+  private  typesToList ( types : Array < ts . Type > ,  node : ts . Node )  { 
342+  const  typeNames : Array < string  |  Type >  =  [ ] 
343+  for  ( const  type  of  types )  { 
344+  const  name  =  ( < any > type ) . kind  ==  null  ? [ this . getTypeNamePath ( < any > type ) ]  : this . getTypeNamePathByNode ( < any > type ) 
345+  if  ( name  ==  null )  { 
346+  throw  new  Error ( "cannot get name for "  +  node . getText ( node . getSourceFile ( ) ) ) 
347+  } 
348+  typeNames . push ( ...name ) 
325349 } 
350+  return  typeNames 
351+  } 
326352
353+  getTypeNames ( type : ts . Type ,  node : ts . Node ) : Array < string  |  Type >  |  null  { 
327354 if  ( type . flags  &  ts . TypeFlags . UnionOrIntersection  &&  ! ( type . flags  &  ts . TypeFlags . Enum ) )  { 
328355 return  this . typesToList ( ( < ts . UnionOrIntersectionType > type ) . types ,  node ) 
329356 } 
330-   
357+ 
331358 let  result  =  this . getTypeNamePath ( type ) 
332359 if  ( result  ==  null )  { 
333360 throw  new  Error ( "Cannot infer getTypeNamePath" ) 
334361 } 
335-  return  [ result ] 
336-  } 
337362
338-  private  typesToList ( types : Array < ts . Type > ,  node : ts . Node )  { 
339-  const  typeNames : Array < string >  =  [ ] 
340-  for  ( const  type  of  types )  { 
341-  const  name  =  ( < any > type ) . kind  ==  null  ? [ this . getTypeNamePath ( < any > type ) ]  : this . getTypeNamePathByNode ( < any > type ) 
342-  if  ( name  ==  null )  { 
343-  throw  new  Error ( "cannot get name for "  +  node . getText ( node . getSourceFile ( ) ) ) 
363+  const  typeArguments  =  ( < ts . TypeReference > type ) . typeArguments 
364+  if  ( typeArguments  !=  null )  { 
365+  const  subTypes  =  [ ] 
366+  for  ( const  type  of  typeArguments )  { 
367+  subTypes . push ( ...this . getTypeNames ( type ,  node ) ) 
344368 } 
345-  typeNames . push ( ... name ) 
369+  return   [ { name :  result ,   subTypes :  subTypes } ] 
346370 } 
347-  return  typeNames 
371+  return  [ result ] 
348372 } 
349373
350374 getTypeNamePath ( type : ts . Type ) : string  |  null  { 
@@ -407,6 +431,47 @@ export class JsDocGenerator {
407431 console . warn ( `Cannot find parent for ${ symbol }  ) 
408432 return  null 
409433 } 
434+  
435+  private  describeEnum ( node : ts . EnumDeclaration ) : Descriptor  { 
436+  const  flags  =  ts . getCombinedModifierFlags ( node ) 
437+  if  ( ! ( flags  &  ts . ModifierFlags . Export ) )  { 
438+  return  null 
439+  } 
440+  
441+  const  type  =  { 
442+  names : [ "number" ] 
443+  } 
444+  
445+  const  name  =  ( < ts . Identifier > node . name ) . text 
446+  const  moduleId  =  this . computeTypePath ( ) 
447+  const  id  =  `${ moduleId } ${ name }  
448+  
449+  const  properties : Array < Descriptor >  =  [ ] 
450+  for  ( const  member  of  node . members )  { 
451+  const  name  =  ( < ts . Identifier > member . name ) . text 
452+  properties . push ( { 
453+  name : name , 
454+  kind : "member" , 
455+  scope : "static" , 
456+  memberof : id , 
457+  type : type , 
458+  } ) 
459+  } 
460+ 
461+  // we don't set readonly because it is clear that enum is not mutable 
462+  // e.g. jsdoc2md wil add useless "Read only: true" 
463+  return  { 
464+  node : node , 
465+  id : id , 
466+  name : name , 
467+  longname : id , 
468+  kind : "enum" , 
469+  scope : "static" , 
470+  memberof : moduleId , 
471+  type : type , 
472+  properties : properties , 
473+  } 
474+  } 
410475
411476 private  describeVariable ( node : ts . VariableStatement ) : Variable  { 
412477 const  flags  =  ts . getCombinedModifierFlags ( node ) 
@@ -456,7 +521,7 @@ export class JsDocGenerator {
456521 const  className  =  ( < ts . Identifier > nodeDeclaration . name ) . text 
457522
458523 const  clazz  =  < ts . ClassDeclaration > node 
459-  let  parents : Array < string >  =  [ ] 
524+  let  parents : Array < string   |   Type >  =  [ ] 
460525 if  ( clazz . heritageClauses  !=  null )  { 
461526 for  ( const  heritageClause  of  clazz . heritageClauses )  { 
462527 if  ( heritageClause . types  !=  null )  { 
0 commit comments