|
| 1 | +import ts from "typescript"; |
| 2 | + |
| 3 | +let checker; |
| 4 | + |
| 5 | +function getParents( node /* ts.ClassDeclaration*/ ) |
| 6 | +{ |
| 7 | + if( !node.heritageClauses ) |
| 8 | + return []; |
| 9 | + let ret = []; |
| 10 | + node.heritageClauses |
| 11 | + .filter( clause => clause.token == ts.SyntaxKind.ExtendsKeyword |
| 12 | + || clause.token == ts.SyntaxKind.ImplementsKeyword ) |
| 13 | + .map( clause => |
| 14 | + clause.types.map( t => |
| 15 | + { let symbol = checker.getSymbolAtLocation( t.expression ); |
| 16 | + symbol && ret.push( checker.getFullyQualifiedName( symbol ) ); |
| 17 | + })); |
| 18 | + return ret; |
| 19 | +} |
| 20 | + |
| 21 | +const mapInterfaceName2deps = { |
| 22 | + // HTMLElement: // given for sample |
| 23 | + // { |
| 24 | + // extends: [ 'Element', 'DocumentAndElementEventHandlers', 'ElementCSSInlineStyle', 'ElementContentEditable', 'GlobalEventHandlers', 'HTMLOrSVGElement' ], |
| 25 | + // inheritedBy: [ 'HTMLEmbedElement' ] |
| 26 | + // } |
| 27 | +}; |
| 28 | + |
| 29 | +const assureMemberAsMap = ( o, field ) => o[field] || ( o[field] = { extends: {}, inheritedBy: {} } ); |
| 30 | + |
| 31 | +/** |
| 32 | + * Prints out particular nodes from a source file |
| 33 | + * |
| 34 | + * @param file a path to a file |
| 35 | + */ |
| 36 | +function extract( file ) |
| 37 | +{ |
| 38 | + // Create a Program to represent the project, then pull out the |
| 39 | + // source file to parse its AST. |
| 40 | + let program = ts.createProgram( [ file ], { allowJs: true } ); |
| 41 | + checker = program.getTypeChecker(); |
| 42 | + |
| 43 | + const sourceFile = program.getSourceFile( file ); |
| 44 | + // To print the AST, we'll use TypeScript's printer |
| 45 | + const printer = ts.createPrinter( { newLine: ts.NewLineKind.LineFeed } ); |
| 46 | + // To give constructive error messages, keep track of found and un-found identifiers |
| 47 | + const foundNodes = []; |
| 48 | + // Loop through the root AST nodes of the file |
| 49 | + // @ts-ignore |
| 50 | + ts.forEachChild( sourceFile, node => |
| 51 | + { |
| 52 | + let name = ""; |
| 53 | + // This is an incomplete set of AST nodes which could have a top level identifier |
| 54 | + // it's left to you to expand this list, which you can do by using |
| 55 | + // https://ts-ast-viewer.com/ to see the AST of a file then use the same patterns |
| 56 | + // as below |
| 57 | + if( ts.isFunctionDeclaration( node ) ) |
| 58 | + { |
| 59 | + // @ts-ignore |
| 60 | + name = node.name.text; |
| 61 | + // Hide the method body when printing |
| 62 | + // @ts-ignore |
| 63 | + node.body = undefined; |
| 64 | + }else if( ts.isVariableStatement( node ) ) |
| 65 | + { |
| 66 | + name = node.declarationList.declarations[ 0 ].name.getText( sourceFile ); |
| 67 | + }else if( ts.isInterfaceDeclaration( node ) ) |
| 68 | + { |
| 69 | + name = node.name.text; |
| 70 | + foundNodes.push( [ name, node ] ); |
| 71 | + let deps = assureMemberAsMap( mapInterfaceName2deps, name ); |
| 72 | + const classes = getParents( node ); |
| 73 | + classes.map( c => |
| 74 | + { |
| 75 | + deps.extends[ c ] = 1; |
| 76 | + assureMemberAsMap( mapInterfaceName2deps, c ).inheritedBy[name]=1; |
| 77 | + } ); |
| 78 | + } |
| 79 | + } ); |
| 80 | +} |
| 81 | + |
| 82 | +// Run the extract function with the script's arguments |
| 83 | +// extract(process.argv[2], process.argv.slice(3)); |
| 84 | +extract( "node_modules/typescript/lib/lib.dom.d.ts" ); |
| 85 | +const interfaces = Object.keys(mapInterfaceName2deps); |
| 86 | +console.log( mapInterfaceName2deps ); |
0 commit comments