'use strict' ; import * as path from 'path' ; import * as crypto from 'crypto' ; import * as ts from 'typescript' ; import * as Is from './common/is' ; export namespace Node { export type Declaration = ts . ModuleDeclaration | ts . ClassDeclaration | ts . InterfaceDeclaration | ts . TypeParameterDeclaration | ts . FunctionDeclaration | ts . MethodDeclaration | ts . MethodSignature | ts . ParameterDeclaration | ts . PropertyDeclaration | ts . PropertySignature ; export function isNamedDeclaration ( node ) { let candidate = node as ts . NamedDeclaration ; return candidate !== undefined && candidate . name !== undefined ; } export function isNode ( value ) { let candidate = value ; return candidate !== undefined && Is . number ( candidate . flags ) && Is . number ( candidate . kind ) ; } } export function getDefaultCompilerOptions ( configFileName ? ) { const options = configFileName && path . basename ( configFileName ) === 'jsconfig.json' ? { allowJs : true , maxNodeModuleJsDepth : 2 , allowSyntheticDefaultImports : true , skipLibCheck : true , noEmit : true } : { } ; return options ; } const isWindows = process . platform === 'win32' ; export function normalizePath ( value ) { if ( isWindows ) { value = value . replace ( /\\/g , '/' ) ; if ( /^[a-z]:/ . test ( value ) ) { value = value . charAt ( 0 ) . toUpperCase ( ) + value . substring ( 1 ) ; } } const result = path . posix . normalize ( value ) ; return result . length > 0 && result . charAt ( result . length - 1 ) === '/' ? result . substr ( 0 , result . length - 1 ) : result ; } export function makeAbsolute ( p , root ? ) { if ( path . isAbsolute ( p ) ) { return normalizePath ( p ) ; } if ( root === undefined ) { return normalizePath ( path . join ( process . cwd ( ) , p ) ) ; } else { return normalizePath ( path . join ( root , p ) ) ; } } export function toOutLocation ( path , rootDir , outDir ) { if ( ! path . startsWith ( rootDir ) ) { return path ; } return `${ outDir }${ path . substr ( rootDir . length ) }` ; } export function computeMonikerPath ( from , to ) { let result = path . posix . relative ( from , to ) ; if ( result . endsWith ( '.d.ts' ) ) { return result . substring ( 0 , result . length - 5 ) ; } else if ( result . endsWith ( '.ts' ) || result . endsWith ( '.js' ) ) { return result . substring ( 0 , result . length - 3 ) ; } else { return result ; } } export function createMonikerIdentifier ( path , symbol ) ; export function createMonikerIdentifier ( path , symbol ) ; export function createMonikerIdentifier ( path , symbol ) { if ( path === undefined || path . length === 0 ) { if ( symbol === undefined || symbol . length === 0 ) { throw new Error ( `Either path or symbol must be provided.` ) ; } return `:${ symbol }` ; } if ( symbol === undefined || symbol . length === 0 ) { return `${ path . replace ( /\:/g , '::' ) }:` ; } return `${ path . replace ( /\:/g , '::' ) }:${ symbol }` ; } export function makeRelative ( from , to ) { return path . posix . relative ( from , to ) ; } function isString ( text ) { return typeof text === 'string' ; } export function flattenDiagnosticMessageText ( diag , newLine , indent = 0 ) { if ( isString ( diag ) ) { return diag ; } else if ( diag === undefined ) { return '' ; } let result = '' ; if ( indent ) { result += newLine ; for ( let i = 0 ; i < indent ; i ++ ) { result += '  ' ; } } result += diag . messageText ; indent ++ ; if ( diag . next ) { for ( const kid of diag . next ) { result += flattenDiagnosticMessageText ( kid , newLine , indent ) ; } } return result ; } interface InternalSymbol extends ts . Symbol { parent ? : ts . Symbol ; containingType ? : ts . UnionOrIntersectionType ; __symbol__data__key__ : string | undefined ; } interface InternalSymbolLinks { type ? : ts . Type ; } export namespace Symbol { const Unknown = 'unknown' ; const Undefined = 'undefined' ; const None = 'none' ; export function createKey ( typeChecker , symbol ) { let result = ( symbol as InternalSymbol ) . __symbol__data__key__ ; if ( result !== undefined ) { return result ; } let declarations = symbol . getDeclarations ( ) ; if ( declarations === undefined ) { if ( typeChecker . isUnknownSymbol ( symbol ) ) { return Unknown ; } else if ( typeChecker . isUndefinedSymbol ( symbol ) ) { return Undefined ; } else { return None ; } } let fragments = [ ] ; for ( let declaration of declarations ) { fragments . push ( { f : declaration . getSourceFile ( ) . fileName , s : declaration . getStart ( ) , e : declaration . getEnd ( ) , k : declaration . kind } ) ; } if ( fragments . length > 1 ) { fragments . sort ( ( a , b ) => { let result = a . f < b . f ? - 1 : ( a . f > b . f ? 1 : 0 ) ; if ( result !== 0 ) { return result ; } result = a . s - b . s ; if ( result !== 0 ) { return result ; } result = a . e - b . e ; if ( result !== 0 ) { return result ; } return a . k - b . k ; } ) ; } const hash = crypto . createHash ( 'md5' ) ; if ( ( symbol . flags & ts . SymbolFlags . Transient ) !== 0 ) { hash . update ( JSON . stringify ( { trans : true } , undefined , 0 ) ) ; } hash . update ( JSON . stringify ( fragments , undefined , 0 ) ) ; result = hash . digest ( 'base64' ) ; ( symbol as InternalSymbol ) . __symbol__data__key__ = result ; return result ; } export function getParent ( symbol ) { return ( symbol as InternalSymbol ) . parent ; } export function getTypeFromSymbolLink ( symbol ) { if ( ( symbol . flags & ts . SymbolFlags . Transient ) !== 0 ) { return ( symbol as InternalSymbolLinks ) . type ; } return undefined ; } export function is ( value ) { const sc = value as ts . Symbol ; const tc = value as ts . Type ; return typeof tc . getSymbol !== 'function' && typeof tc . isUnion !== 'function' && typeof sc . getDeclarations === 'function' && typeof sc . getDocumentationComment === 'function' ; } } export namespace Type { export function hasCallSignature ( type ) { const signatures = type . getCallSignatures ( ) ; return signatures . length > 0 ; } export function hasConstructSignatures ( type ) { const signatures = type . getConstructSignatures ( ) ; return signatures . length > 0 ; } export function isObjectType ( type ) { return ( type . flags & ts . TypeFlags . Object ) !== 0 ; } export function isTypeReference ( type ) { return ( type . objectFlags & ts . ObjectFlags . Reference ) !== 0 ; } export function isConditionalType ( type ) { return ( type . flags & ts . TypeFlags . Conditional ) !== 0 ; } export function isAnonymous ( type ) { return ( type . objectFlags & ts . ObjectFlags . Anonymous ) !== 0 ; } export function isClassOrInterface ( type ) { const symbol = type . getSymbol ( ) ; if ( symbol !== undefined ) { return ( symbol . flags & ( ts . SymbolFlags . Interface | ts . SymbolFlags . Class ) ) !== 0 ; } return type . isClassOrInterface ( ) ; } export function isClass ( type ) { const symbol = type . getSymbol ( ) ; if ( symbol !== undefined ) { return ( symbol . flags & ts . SymbolFlags . Class ) !== 0 ; } return type . isClass ( ) ; } export function isInterface ( type ) { const symbol = type . getSymbol ( ) ; if ( symbol !== undefined ) { return ( symbol . flags & ts . SymbolFlags . Interface ) !== 0 ; } return type . isClassOrInterface ( ) && ! type . isClass ( ) ; } export function isTypeParameter ( type ) { return ( type . flags & ts . TypeFlags . TypeParameter ) !== 0 ; } } interface InternalNode extends ts . Node { symbol ? : ts . Symbol ; } interface InternalJSDocContainer extends ts . JSDocContainer { jsDoc ? : ts . JSDoc [ ] ; } export namespace Node { export function getSymbol ( node ) { return ( node as InternalNode ) . symbol ; } export function getJsDoc ( node ) { return ( node as InternalJSDocContainer ) . jsDoc ; } } interface InternalSourceFile extends ts . SourceFile { resolvedModules ? : ts . Map < ts . ResolvedModuleFull | undefined > ; } export namespace SourceFile { export function getResolvedModules ( sourceFile ) { return ( sourceFile as InternalSourceFile ) . resolvedModules ; } } export interface DefinitionInfo { file : string ; kind : ts . SyntaxKind ; start : number ; end : number ; } export namespace DefinitionInfo { export function equals ( a , sourceFile , node ) { return a . file === sourceFile . fileName && a . kind === node . kind && a . start === node . getStart ( ) && a . end === node . getEnd ( ) ; } } export function createDefinitionInfo ( sourceFile , node ) { return { file : sourceFile . fileName , kind : node . kind , start : node . getStart ( ) , end : node . getEnd ( ) } ; } export function isComposite ( typeChecker , symbol ) { const containingType = ( symbol as InternalSymbol ) . containingType ; if ( containingType !== undefined && containingType . isUnionOrIntersection ( ) ) { return true ; } const type = typeChecker . getDeclaredTypeOfSymbol ( symbol ) ; if ( type . isUnionOrIntersection ( ) ) { return true ; } return false ; } export function getCompositeLeafSymbols ( typeChecker , symbol ) { function _getCompositeLeafSymbols ( result , processed , typeChecker , symbol ) { const symbolKey = Symbol . createKey ( typeChecker , symbol ) ; if ( processed . has ( symbolKey ) ) { return ; } processed . add ( symbolKey ) ; const containingType = ( symbol as InternalSymbol ) . containingType ; if ( containingType !== undefined ) { for ( let typeElem of containingType . types ) { const symbolElem = typeElem . getProperty ( symbol . getName ( ) ) ; if ( symbolElem !== undefined ) { _getCompositeLeafSymbols ( result , processed , typeChecker , symbolElem ) ; } } } else { const type = typeChecker . getDeclaredTypeOfSymbol ( symbol ) ; if ( type . isUnionOrIntersection ( ) ) { for ( let typeElem of type . types ) { const symbolElem = typeElem . symbol ; if ( symbolElem !== undefined ) { _getCompositeLeafSymbols ( result , processed , typeChecker , symbolElem ) ; } } } else { result . set ( symbolKey , symbol ) ; } } } const result = new Map ( ) ; _getCompositeLeafSymbols ( result , new Set ( ) , typeChecker , symbol ) ; if ( result . size === 0 ) { return undefined ; } return Array . from ( result . values ( ) ) ; } export function getUniqueSourceFiles ( declarations ) { let result = new Set ( ) ; if ( declarations === undefined || declarations . length === 0 ) { return result ; } for ( let declaration of declarations ) { result . add ( declaration . getSourceFile ( ) ) ; } return result ; } interface InternalProgram extends ts . Program { getCommonSourceDirectory ( ) : string ; sourceFileToPackageName : ts . ESMap < string , string > ; getResolvedModuleWithFailedLookupLocationsFromCache ( moduleName , containingFile ) : ts . ResolvedModuleWithFailedLookupLocations | undefined ; } export namespace Program { export function getCommonSourceDirectory ( program ) { const internal = program as InternalProgram ; if ( typeof internal . getCommonSourceDirectory !== 'function' ) { throw new Error ( `Program is missing getCommonSourceDirectory` ) ; } return internal . getCommonSourceDirectory ( ) ; } export function sourceFileToPackageName ( program , sourceFile ) { const internal = program as InternalProgram ; if ( internal . sourceFileToPackageName === undefined ) { throw new Error ( `Program is missing sourceFileToPackageName` ) ; } return internal . sourceFileToPackageName . get ( sourceFile . fileName . toLowerCase ( ) ) ; } export function getResolvedModule ( program , sourceFile ) { const internal = program as InternalProgram ; if ( internal . sourceFileToPackageName === undefined ) { throw new Error ( `Program is missing sourceFileToPackageName` ) ; } const moduleName = internal . sourceFileToPackageName . get ( sourceFile . fileName . toLowerCase ( ) ) ; if ( moduleName === undefined ) { return undefined ; } if ( typeof internal . getResolvedModuleWithFailedLookupLocationsFromCache !== 'function' ) { throw new Error ( `Program is missing getResolvedModuleWithFailedLookupLocationsFromCache` ) ; } const value = internal . getResolvedModuleWithFailedLookupLocationsFromCache ( moduleName , sourceFile . fileName ) ; return value && value . resolvedModule ; } } interface InternalCompilerOptions extends ts . CompilerOptions { configFilePath ? : string ; } export namespace CompileOptions { export function getConfigFilePath ( options ) { if ( options . project ) { const projectPath = path . resolve ( options . project ) ; if ( ts . sys . directoryExists ( projectPath ) ) { return normalizePath ( path . join ( projectPath , 'tsconfig.json' ) ) ; } else { return normalizePath ( projectPath ) ; } } const result = ( options as InternalCompilerOptions ) . configFilePath ; return result && makeAbsolute ( result ) ; } } interface InternalLanguageServiceHost extends ts . LanguageServiceHost { useSourceOfProjectReferenceRedirect ? ( ) : boolean ; } export namespace LanguageServiceHost { export function useSourceOfProjectReferenceRedirect ( host , value ) { ( host as InternalLanguageServiceHost ) . useSourceOfProjectReferenceRedirect = value ; } }