module ts { var checker = null ; var program = null ; var errors = null ; var classNameToFileMap = { } ; var classNames = { } ; var fileToClassNameMap = { } ; var classNameToBaseClassMap = { } ; var constructorToClassMap = { } ; var functionCallToClassMap = { } ; var functionId = 0 ; var staticToClassNameMap = { } ; var fileNodesList = [ ] ; var fileNameToNodeMap = { } ; var orderedFileList = [ ] ; function addClassToFileMap ( name , file ) { var map = classNameToFileMap [ name ] ; if ( ! map || file == map ) { classNameToFileMap [ name ] = file ; } else { if ( typeof ( map ) == 'string' ) { map = { map : true } ; } map [ file ] = true ; classNameToFileMap [ name ] = map ; } } export class SortHelper { static getOrderedFiles ( ) { return orderedFileList ; } static getErrors ( ) { return errors ; } static getClassNameAndProps ( ) { var names = { } ; var files = program . getSourceFiles ( ) . concat ( ) ; files . forEach ( f => { if ( f . filename . indexOf ( '.d.ts' ) > 0 ) return ; var symbols = checker . getSymbolsInScope ( f , SymbolFlags . Module | SymbolFlags . PropertyOrAccessor | SymbolFlags . Method | SymbolFlags . Class | SymbolFlags . Export ) ; symbols . forEach ( s => { var name = s . name ; names [ name ] = true ; } ) ; } ) ; return names ; } classNameToFileMap = classNameToFileMap ; public orderFiles ( chk , prog ) { errors = [ ] ; classNameToFileMap = { } ; fileToClassNameMap = { } ; classNameToBaseClassMap = { } ; constructorToClassMap = { } ; functionCallToClassMap = { } ; functionId = 0 ; staticToClassNameMap = { } ; fileNodesList = [ ] ; fileNameToNodeMap = { } ; var files = prog . getSourceFiles ( ) . concat ( ) ; var libdts = files . shift ( ) ; orderedFileList = files . map ( f => f . filename ) ; checker = chk ; program = prog ; forEach ( files , file => this . symbolTabelToFileMap ( file , file . locals ) ) ; this . sortFiles ( ) ; var sources = prog . getSourceFiles ( ) ; orderedFileList . forEach ( f => { for ( var i = 0 ; i < sources . length ; i ++ ) { var s = sources [ i ] ; if ( s . filename == f ) { sources . splice ( i , 1 ) ; sources . push ( s ) ; break ; } } } ) ; } private symbolToFileMap ( file , symbol ) { var classtype = < InterfaceType > checker . getDeclaredTypeOfSymbol ( symbol . exportSymbol || symbol ) ; if ( symbol . flags & ( SymbolFlags . Module ) ) return ; var isInterface = symbol . flags & SymbolFlags . Interface ; if ( classtype ) { var fullName = getFullyQualifiedName ( symbol ) ; addClassToFileMap ( fullName , file . filename ) ; var classes = fileToClassNameMap [ file . filename ] ; if ( ! classes ) { classes = [ ] ; fileToClassNameMap [ file . filename ] = classes ; } if ( classes . indexOf ( fullName ) < 0 ) classes . push ( fullName ) ; if ( isInterface ) return ; if ( classtype . baseTypes && classtype . baseTypes . length ) { forEach ( classtype . baseTypes , ( t ) => this . classNameToBaseClass ( fullName , t ) ) ; } this . constructorToClassName ( file , < ClassDeclaration > symbol . valueDeclaration ) ; } } private symbolTabelToFileMap ( file , symbolTable ) { ts . forEachValue ( symbolTable , symbol => { symbol = symbol . exportSymbol || symbol ; if ( symbol . name == "prototype" ) return ; this . symbolToFileMap ( file , symbol ) ; this . staticMemberToClassName ( file , symbol ) ; if ( symbol . valueDeclaration && symbol . valueDeclaration . locals ) { this . symbolTabelToFileMap ( file , symbol . valueDeclaration . locals ) ; } if ( symbol . exports ) { this . symbolTabelToFileMap ( file , symbol . exports ) ; } } ) ; if ( file . filename . indexOf ( ".d.ts" ) > 0 ) return ; var self = this ; findFunctionCall ( file ) ; function findFunctionCall ( pnode ) { forEachChild ( pnode , node => { if ( node . kind == SyntaxKind . ClassDeclaration || node . kind == SyntaxKind . InterfaceDeclaration || node . kind == SyntaxKind . FunctionDeclaration ) return ; if ( node . kind == SyntaxKind . PropertyAccessExpression || node . kind == SyntaxKind . CallExpression || node . kind == SyntaxKind . NewExpression || node . kind == SyntaxKind . Identifier ) { var name = "callExpression" + ++ functionId ; self . findUsedClasses ( node , name , functionCallToClassMap ) ; } else findFunctionCall ( node ) ; } ) ; } } private constructorToClassName ( file , classNode ) { if ( ! classNode || ! classNode . symbol ) return ; var className = checker . getFullyQualifiedName ( classNode . symbol ) ; var nodesToCheck = [ ] ; forEachChild ( classNode , node => { if ( node . kind == SyntaxKind . Constructor ) { nodesToCheck . push ( ( < ConstructorDeclaration > node ) . body ) ; } if ( node . kind == SyntaxKind . Property && ( < PropertyDeclaration > node ) . initializer ) { nodesToCheck . push ( ( < PropertyDeclaration > node ) . initializer ) ; } } ) ; if ( ! nodesToCheck . length ) { return ; } nodesToCheck . forEach ( node => this . findUsedClasses ( node , className , constructorToClassMap ) ) ; } private findUsedClasses = ( node , name , collection ) => { if ( ! node ) return ; var nodeToGet = null ; switch ( node . kind ) { case SyntaxKind . PropertyAccessExpression : case SyntaxKind . Identifier : nodeToGet = node ; break ; case SyntaxKind . CallExpression : case SyntaxKind . NewExpression : nodeToGet = ( < CallExpression > node ) . expression ; break ; default : nodeToGet = null ; } if ( nodeToGet ) { var definedSymbol = checker . egretGetResolveSymbol ( < Identifier > nodeToGet ) ; if ( definedSymbol && ! ( definedSymbol . exportSymbol && ( definedSymbol . exportSymbol . flags & SymbolFlags . Module ) ) && ( definedSymbol . flags & SymbolFlags . Value || definedSymbol . flags & SymbolFlags . Export ) ) { var targetName = checker . getFullyQualifiedName ( definedSymbol ) ; if ( targetName == "unknown" ) return ; var classes = collection [ name ] || [ ] ; classes . push ( targetName ) ; collection [ name ] = classes ; if ( ! ( targetName in classNameToFileMap ) && definedSymbol . declarations && definedSymbol . declarations . length ) { var declareNode = definedSymbol . declarations [ 0 ] ; var source = < SourceFile > getAncestor ( declareNode , SyntaxKind . SourceFile ) ; addClassToFileMap ( targetName , source . filename ) ; } if ( ! ( name in classNameToFileMap ) ) { var source = < SourceFile > getAncestor ( nodeToGet , SyntaxKind . SourceFile ) ; addClassToFileMap ( name , source . filename ) ; } } } else { forEachChild ( node , n => this . findUsedClasses ( n , name , collection ) ) ; } } ; private staticMemberToClassName ( file , symbol ) { symbol = symbol . exportSymbol || symbol ; var fullName = getFullyQualifiedName ( symbol ) ; var declarExp = < PropertyDeclaration > symbol . valueDeclaration ; if ( declarExp && declarExp . initializer ) { var staticMemberType = checker . checkAndMarkExpression ( declarExp . initializer ) ; if ( staticMemberType . symbol == undefined || ( staticMemberType . symbol . flags & SymbolFlags . Interface ) ) return ; var initializerClass = checker . getFullyQualifiedName ( staticMemberType . symbol ) ; staticToClassNameMap [ fullName ] = initializerClass ; } } private addStaticDepend ( staticMemberName , className ) { } private classNameToBaseClass ( className , baseType ) { var fullName = checker . getFullyQualifiedName ( baseType . symbol ) ; var bases = classNameToBaseClassMap [ className ] || [ ] ; if ( bases . indexOf ( fullName ) < 0 ) bases . push ( fullName ) ; classNameToBaseClassMap [ className ] = bases ; } private getFileNode ( file ) { if ( file in fileNameToNodeMap ) return fileNameToNodeMap [ file ] ; var typeNode = new FileNode ( ) ; if ( typeof ( file ) != 'string' ) { console . log ( file ) ; } typeNode . name = file ; typeNode . subs = [ ] ; typeNode . depends = [ ] ; typeNode . supers = [ ] ; typeNode . subdepends = [ ] ; fileNodesList . push ( typeNode ) ; fileNameToNodeMap [ file ] = typeNode ; return typeNode ; } private sortFiles ( ) { forEachKey ( classNameToFileMap , className => { var file = classNameToFileMap [ className ] ; if ( typeof ( file ) != 'string' ) return ; var fileName = < string > file ; var fileNode = this . getFileNode ( fileName ) ; var supers = classNameToBaseClassMap [ className ] ; if ( supers ) { supers . forEach ( superClass => { var dependFile = classNameToFileMap [ superClass ] ; if ( dependFile && dependFile [ 'map' ] || dependFile == fileName || dependFile [ fileName ] ) return ; var dependNode = this . getFileNode ( < string > dependFile ) ; dependNode . addSub ( fileNode ) ; fileNode . addSuper ( dependNode ) ; } ) ; } var staticDepends = staticToClassNameMap [ className ] ; if ( staticDepends ) { var constructorDepends = constructorToClassMap [ staticDepends ] || [ ] ; constructorDepends . push ( staticDepends ) ; constructorDepends . forEach ( depend => { var dependFile = classNameToFileMap [ depend ] ; if ( dependFile && dependFile [ 'map' ] == undefined && dependFile != file && typeof ( dependFile ) == 'string' ) { var dependFileNode = this . getFileNode ( < string > dependFile ) ; fileNode . addDepends ( dependFileNode ) ; dependFileNode . addSubDepends ( fileNode ) ; } } ) ; } var functionName = className ; var functionCallDepens = functionCallToClassMap [ functionName ] ; if ( functionCallDepens ) { functionCallDepens . forEach ( depend => { var dependFile = classNameToFileMap [ depend ] ; if ( dependFile && dependFile [ 'map' ] || dependFile == file || typeof ( dependFile ) != 'string' ) return ; var dependNode = this . getFileNode ( < string > dependFile ) ; dependNode . addSub ( fileNode ) ; fileNode . addSuper ( dependNode ) ; } ) ; } } ) ; var singleTypes = [ ] , bottomTypes = [ ] , topTypes = [ ] , otherTypes = [ ] ; fileNodesList . forEach ( t => { if ( ( t . subs . length || t . supers . length || t . depends . length || t . subdepends . length ) == 0 ) singleTypes . push ( t ) ; else if ( t . subs . length == 0 && t . subdepends . length == 0 ) bottomTypes . push ( t ) ; else if ( t . supers . length == 0 && t . depends . length == 0 ) topTypes . push ( t ) ; else otherTypes . push ( t ) ; } ) ; if ( hasRefCircle ( ) ) { } else { topTypes . forEach ( t => t . setOrder ( 0 ) ) ; bottomTypes . forEach ( t => t . setOrder ( t . order ) ) ; fileNodesList . sort ( ( a , b ) => compareFileNode ( a , b ) ) ; } orderedFileList = [ ] ; for ( var i = 0 , length = fileNodesList . length , registerClassAdded = false ; i < length ; i ++ ) { var name = fileNodesList [ i ] . name ; if ( registerClassAdded == false && name . indexOf ( 'registerClass' ) > 0 ) { orderedFileList . unshift ( name ) ; registerClassAdded = true ; } else orderedFileList . push ( name ) ; } for ( var i = 0 , length = orderedFileList . length ; i < length ; i ++ ) { var name = orderedFileList [ i ] ; if ( name . indexOf ( 'Defines.debug.ts' ) > 0 ) { orderedFileList . splice ( i , 1 ) ; orderedFileList . unshift ( name ) ; } } } } var fileToFileMap = { } ; function hasRefCircle ( ) { return fileNodesList . some ( f => f . checkCircle ( ) == false ) ; } export class UsedFileResolver { static mapFile ( source , used ) { var depends = fileToFileMap [ source ] ; if ( ! depends ) { depends = { } ; fileToFileMap [ source ] = depends ; } depends [ used ] = true ; } } function compareFileNode ( a , b ) { if ( a . order != b . order ) return a . order - b . order ; if ( a . subs . length == 0 && a . subdepends . length == 0 ) return - 1 ; if ( b . subs . length == 0 && b . subdepends . length == 0 ) return 1 ; return 0 ; } /**
    * no export properties may have same name, add id after the name
    */ function getFullyQualifiedName ( symbol ) { var name = null ; if ( symbol . exportSymbol ) { name = checker . getFullyQualifiedName ( symbol . exportSymbol ) ; classNames [ name ] = Object . keys ( classNames ) . length ; } else { name = checker . getFullyQualifiedName ( symbol ) ; if ( ! ( symbol . flags & ( SymbolFlags . Class | SymbolFlags . Interface | SymbolFlags . Module ) ) ) { name += symbol . id ; } else { classNames [ name ] = Object . keys ( classNames ) . length ; } } return name ; } class FileNode { name : string ; supers : FileNode [ ] ; subs : FileNode [ ] ; depends : FileNode [ ] ; subdepends : FileNode [ ] ; file : string ; addSuper ( node ) { if ( this . depends . indexOf ( node ) >= 0 ) return ; this . depends . push ( node ) ; } addDepends ( node ) { if ( this . depends . indexOf ( node ) >= 0 ) return ; this . depends . push ( node ) ; } checkCircle ( ) { var self = this ; var path = [ ] ; function getSupers ( node ) { path . push ( node . name ) ; var supers = node . depends . concat ( ) ; for ( var i = supers . length - 1 ; i >= 0 ; i -- ) { var sup = supers [ i ] ; if ( sup == self ) { path . push ( sup . name ) ; errors . push ( { code : 10018 , category : DiagnosticCategory . Error , file : null , messageText : "Found circular dependency:" + path . join ( "=>" ) , start : 0 , length : 0 , isEarly : false } ) ; return false ; } var ok = getSupers ( sup ) ; return ok ; } } return getSupers ( this ) ; } addSub ( node ) { if ( this . subdepends . indexOf ( node ) >= 0 ) return ; this . subdepends . push ( node ) ; } addSubDepends ( node ) { if ( this . subdepends . indexOf ( node ) >= 0 ) return ; this . subdepends . push ( node ) ; } _order : number = 0 ; setOrder ( value , nest = 0 ) { this . depends . forEach ( s => { if ( s . order > value ) value = s . order + 1 ; } ) ; var offset = value - this . _order ; this . _order = value ; this . subdepends . forEach ( s => { if ( s . _order <= this . _order ) s . setOrder ( this . _order + 1 , nest + 1 ) ; s . setOrder ( s . _order + offset , nest + 1 ) ; } ) ; } get order ( ) : number { return this . _order ; ; } } }