import * as ts from 'typescript' ; import * as fs from 'fs' ; import { TestModuleTemplateWriter } from './test-module-writer' ; export interface GeneratedSourceParserConfig { files ? : string [ ] ; testSourceFiles ? : ts . SourceFile [ ] ; } export interface GeneratedModuleDocs { moduleRefName ? : string ; fileName ? : string ; includeTestForComponent ? : string ; } export class GeneratedSourceParser { private program : ts . Program ; private checker : ts . TypeChecker ; constructor ( private config , tsOptions ) { let files = config . files ; this . program = ts . createProgram ( [ ... files ] , tsOptions , this . getCompilerHost ( ) ) ; this . checker = this . program . getTypeChecker ( ) ; } private getCompilerHost ( ) { let testSourceFiles = this . config . testSourceFiles ; let compilerHost = ts . createCompilerHost ( { target : ts . ScriptTarget . ES5 , module : ts . ModuleKind . CommonJS } ) ; compilerHost . getSourceFile = ( fileName , languageVersion , onError ? ) : ts . SourceFile => { if ( fileName . indexOf ( TestModuleTemplateWriter . outputFilename ) > - 1 ) { let sourceFile = testSourceFiles . filter ( ( file ) => { return file . fileName === fileName ; } ) . pop ( ) ; return sourceFile ; } return ts . createSourceFile ( fileName , fs . readFileSync ( fileName , 'UTF-8' ) , ts . ScriptTarget . ES5 ) ; } ; return compilerHost ; } getGeneratedDocumentation ( ) { const moduleDocs = this . getModuleDocs ( this . config . files ) ; return moduleDocs ; } private getModuleDocs ( moduleFiles ) { let moduleDocs = [ ] ; for ( let currentFile of moduleFiles ) { let sourceFileAsText = this . program . getSourceFile ( currentFile ) . getFullText ( ) ; let moduleDoc = this . getSourceFileData ( this . program . getSourceFile ( currentFile ) ) ; moduleDoc . includeTestForComponent = this . getIncludedTestForComponent ( sourceFileAsText ) ; moduleDoc . fileName = ( this . program . getSourceFile ( currentFile ) as ts . FileReference ) . fileName ; if ( moduleDoc . includeTestForComponent ) { moduleDocs . push ( moduleDoc ) ; } } return moduleDocs ; } private getIncludedTestForComponent ( sourceFileAsText ) { let match = sourceFileAsText . replace ( /[\n\r\s]+/gi , '' ) . match ( /\/\*\*::ui-jar_source_module::([a-zA-Z0-9_-\s]+)\*\// ) ; return match && match . length > 0 ? match [ 1 ] : null ; } private getSourceFileData ( node ) { let details = { } ; const traverseChild = ( childNode ) => { if ( childNode . kind === ts . SyntaxKind . ClassDeclaration ) { const traverseDecorator = ( decoratorChildNode ) => { if ( decoratorChildNode . kind === ts . SyntaxKind . Identifier && decoratorChildNode . getText ( ) === 'NgModule' ) { details . moduleRefName = ( childNode as ts . ClassDeclaration ) . name . text ; } ts . forEachChild ( decoratorChildNode , traverseDecorator ) ; } ; const currentNode = ( childNode as ts . ClassDeclaration ) ; if ( currentNode . decorators ) { currentNode . decorators . forEach ( ( decorator ) => { traverseDecorator ( decorator ) ; } ) ; } } ts . forEachChild ( childNode , traverseChild ) ; } ; traverseChild ( node ) ; return details ; } }