import { Ast } from "../Ast/Ast" ; import { Logger } from "../Reporting/Logger" ; import { Utils } from "../Utils/Utilities" ; import { TsCore } from "../Utils/TsCore" ; import * as ts from "typescript" ; import * as fs from "fs" ; import * as path from "path" ; export class DependencyBuilder { private host : ts . CompilerHost ; private program : ts . Program ; private options : ts . CompilerOptions ; private moduleImportsByName : ts . MapLike < ts . Symbol [ ] > = { } ; constructor ( host , program ) { this . host = host ; this . program = program ; this . options = this . program . getCompilerOptions ( ) ; } public getSourceFileDependencies ( sourceFile ) { var self = this ; var dependencies = { } ; var importWalked = { } ; function walkModuleImports ( importNodes ) { importNodes . forEach ( importNode => { let importSymbol = self . getSymbolFromNode ( importNode ) ; let importSourceFile = self . getSourceFileFromSymbol ( importSymbol ) ; let canonicalFileName = self . host . getCanonicalFileName ( importSourceFile . fileName ) ; if ( ! Utils . hasProperty ( importWalked , canonicalFileName ) ) { importWalked [ canonicalFileName ] = true ; if ( ! importSourceFile . isDeclarationFile ) { Logger . info ( "Walking Import module: " , canonicalFileName ) ; walkModuleImports ( self . getImportsOfModule ( importSourceFile ) ) ; } } if ( ! Utils . hasProperty ( dependencies , canonicalFileName ) ) { Logger . info ( "Getting and adding imports of module file: " , canonicalFileName ) ; dependencies [ canonicalFileName ] = self . getImportsOfModule ( importSourceFile ) ; } } ) ; } var sourceFileImports = self . getImportsOfModule ( sourceFile ) ; walkModuleImports ( sourceFileImports ) ; let canonicalSourceFileName = self . host . getCanonicalFileName ( sourceFile . fileName ) ; if ( ! Utils . hasProperty ( dependencies , canonicalSourceFileName ) ) { Logger . info ( "Adding top level import dependencies for file: " , canonicalSourceFileName ) ; dependencies [ canonicalSourceFileName ] = sourceFileImports ; } return dependencies ; } public getImportsOfModule ( file ) { if ( ! Ast . isSourceCodeFile ( file ) ) { return [ ] ; } var importNodes = [ ] ; var self = this ; function getImports ( searchNode ) { ts . forEachChild ( searchNode , node => { if ( node . kind === ts . SyntaxKind . ImportDeclaration || node . kind === ts . SyntaxKind . ImportEqualsDeclaration || node . kind === ts . SyntaxKind . ExportDeclaration ) { let moduleNameExpr = TsCore . getExternalModuleName ( node ) ; if ( moduleNameExpr && moduleNameExpr . kind === ts . SyntaxKind . StringLiteral ) { let moduleSymbol = self . program . getTypeChecker ( ) . getSymbolAtLocation ( moduleNameExpr ) ; if ( moduleSymbol ) { Logger . info ( "Adding import node: " , moduleSymbol . name ) ; importNodes . push ( node ) ; } else { Logger . warn ( "Module symbol not found" ) ; } } } else if ( node . kind === ts . SyntaxKind . ModuleDeclaration ) { const moduleDeclaration = < ts . ModuleDeclaration > node ; if ( ( moduleDeclaration . name . kind === ts . SyntaxKind . StringLiteral ) && ( Ast . getSyntacticModifierFlagsNoCache ( moduleDeclaration ) & ts . ModifierFlags . Ambient || file . isDeclarationFile ) ) { Logger . info ( "Scanning for dependencies within ambient module declaration: " , moduleDeclaration . name . text ) ; getImports ( moduleDeclaration . body ) ; } } } ) ; } ; Logger . info ( "Getting imports for source file: " , file . fileName ) ; getImports ( file ) ; return importNodes ; } private isExternalModuleImportEqualsDeclaration ( node ) { return node . kind === ts . SyntaxKind . ImportEqualsDeclaration && ( < ts . ImportEqualsDeclaration > node ) . moduleReference . kind === ts . SyntaxKind . ExternalModuleReference ; } private getExternalModuleImportEqualsDeclarationExpression ( node ) { return ( < ts . ExternalModuleReference > ( < ts . ImportEqualsDeclaration > node ) . moduleReference ) . expression ; } private getSymbolFromNode ( node ) { let moduleNameExpr = TsCore . getExternalModuleName ( node ) ; if ( moduleNameExpr && moduleNameExpr . kind === ts . SyntaxKind . StringLiteral ) { return this . program . getTypeChecker ( ) . getSymbolAtLocation ( moduleNameExpr ) ; } return undefined ; } private getSourceFileFromSymbol ( importSymbol ) { let declaration = importSymbol . getDeclarations ( ) [ 0 ] ; return declaration . getSourceFile ( ) ; } }