You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.js 5.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. "use strict";
  2. // code based upon https://github.com/microsoft/TypeScript-wiki/blob/master/Using-the-Compiler-API.md
  3. // and modified to output class and member declarations for use by a diff tool
  4. const ts = require( 'typescript' );
  5. const glob = require( 'glob' );
  6. const fs = require( 'fs' );
  7. const path = require( 'path' );
  8. const args = process.argv.slice( 2 );
  9. const dirname = args[ 0 ];
  10. const outputFile = args.length > 1 ? args[ 1 ] : undefined;
  11. const normalisedPathName = path.normalize( path.resolve( dirname ) ).replace( /\\/g, "/" ) + '/';
  12. const normaliseFilename = ( filename ) => {
  13. return filename.replace( new RegExp( normalisedPathName ), '' );
  14. };
  15. glob( normalisedPathName + "**/*.d.ts", null, ( error, files ) => {
  16. const program = ts.createProgram( files, { maxNodeModuleJsDepth: 10 } );
  17. const checker = program.getTypeChecker();
  18. let output = [];
  19. for ( const sourceFile of program.getSourceFiles() ) {
  20. const serializeSymbol = ( symbol ) => {
  21. if ( symbol.getName() === "default" ) {
  22. return {
  23. name: checker.typeToString( checker.getTypeOfSymbolAtLocation( symbol, symbol.valueDeclaration ) ).substring( 7 )
  24. };
  25. } else {
  26. return {
  27. name: symbol.getName()
  28. };
  29. }
  30. };
  31. const serializeModifierFlags = ( symbol ) => {
  32. const flags = ts.getCombinedModifierFlags( symbol );
  33. if ( flags === ts.ModifierFlags.None ) return "";
  34. if ( flags === ts.ModifierFlags.Export ) return "";
  35. //Export = 1,
  36. //Ambient = 2,
  37. //Public = 4,
  38. //Private = 8,
  39. //Protected = 16,
  40. //Static = 32,
  41. //Readonly = 64,
  42. //Abstract = 128,
  43. //Async = 256,
  44. //Default = 512,
  45. //Const = 2048,
  46. //HasComputedJSDocModifiers = 4096,
  47. //Deprecated = 8192,
  48. //HasComputedFlags = 536870912,
  49. //AccessibilityModifier = 28,
  50. //ParameterPropertyModifier = 92,
  51. //NonPublicAccessibilityModifier = 24,
  52. //TypeScriptModifier = 2270,
  53. //ExportDefault = 513,
  54. //All = 11263
  55. return `[${ flags }]`;
  56. };
  57. const normaliseImportedType = ( type ) => {
  58. if ( type.indexOf( 'typeof import(' ) >= 0 ) {
  59. type = normaliseFilename( type ).replace( /\"/g, "'" );
  60. }
  61. return type;
  62. };
  63. const serializeMember = ( symbol ) => `${ serializeModifierFlags( symbol ) }${ symbol.getName() }: ${ normaliseImportedType( checker.typeToString( checker.getTypeOfSymbolAtLocation( symbol, symbol.valueDeclaration ) ) ) }`;
  64. const serializeSignature = ( signature ) => signature.parameters.map( symbol => serializeMember( symbol ) ).join( ", " );
  65. const serializeClass = ( symbol ) => {
  66. let details = serializeSymbol( symbol );
  67. // Get the construct signatures
  68. let constructorType = checker.getTypeOfSymbolAtLocation( symbol, symbol.valueDeclaration );
  69. details.constructors = constructorType
  70. .getConstructSignatures()
  71. .map( serializeSignature );
  72. const memberCount = symbol.members.size;
  73. if ( memberCount > 0 ) {
  74. details.members = [];
  75. for ( const kp of symbol.members ) {
  76. const memberKey = kp[ 0 ];
  77. if ( memberKey.startsWith( '_' ) ) continue; // ignore private members
  78. const memberValue = kp[ 1 ];
  79. details.members.push( serializeMember( memberValue ) );
  80. }
  81. details.members.sort();
  82. }
  83. return details;
  84. };
  85. const isNodeExported = ( node ) => ( ts.getCombinedModifierFlags( node ) & ts.ModifierFlags.Export ) !== 0 || ( !!node.parent && node.parent.kind === ts.SyntaxKind.SourceFile );
  86. const visit = ( node ) => {
  87. // Only consider exported nodes
  88. if ( !isNodeExported( node ) ) {
  89. return;
  90. }
  91. if ( ts.isClassDeclaration( node ) && node.name ) {
  92. // This is a top level class, get its symbol
  93. let symbol = checker.getSymbolAtLocation( node.name );
  94. if ( symbol ) {
  95. const definition = serializeClass( symbol );
  96. output.push( { fileName: normaliseFilename( sourceFile.fileName ), ...definition } );
  97. }
  98. } else if ( ts.isModuleDeclaration( node ) ) {
  99. // This is a namespace, visit its children
  100. ts.forEachChild( node, visit );
  101. }
  102. };
  103. ts.forEachChild( sourceFile, visit );
  104. }
  105. output = output.filter( m => m.fileName.indexOf( "node_modules/" ) < 0 );
  106. output.sort( ( a, b ) => {
  107. if ( a.name < b.name ) return -1;
  108. if ( a.name > b.name ) return 1;
  109. return 0;
  110. } );
  111. if ( outputFile ) {
  112. fs.writeFileSync( outputFile, JSON.stringify( output, null, 2 ) );
  113. } else {
  114. console.log( JSON.stringify( output, null, 2 ) );
  115. }
  116. } );