namespace ts . server { export interface InstallPackageOptionsWithProject extends InstallPackageOptions { projectName : string ; projectRootPath : Path ; } export interface ITypingsInstaller { isKnownTypesPackageName ( name ) : boolean ; installPackage ( options ) : Promise < ApplyCodeActionCommandResult > ; enqueueInstallTypingsRequest ( p , typeAcquisition , unresolvedImports ) : void ; attach ( projectService ) : void ; onProjectClosed ( p ) : void ; readonly globalTypingsCacheLocation : string | undefined ; } export const nullTypingsInstaller = { isKnownTypesPackageName : returnFalse , installPackage : notImplemented , enqueueInstallTypingsRequest : noop , attach : noop , onProjectClosed : noop , globalTypingsCacheLocation : undefined ! } ; interface TypingsCacheEntry { readonly typeAcquisition : TypeAcquisition ; readonly compilerOptions : CompilerOptions ; readonly typings : SortedReadonlyArray < string > ; readonly unresolvedImports : SortedReadonlyArray < string > | undefined ; poisoned : boolean ; } function setIsEqualTo ( arr1 , arr2 ) { if ( arr1 === arr2 ) { return true ; } if ( ( arr1 || emptyArray ) . length === 0 && ( arr2 || emptyArray ) . length === 0 ) { return true ; } const set = new Map < string , boolean > ( ) ; let unique = 0 ; for ( const v of arr1 ! ) { if ( set . get ( v ) !== true ) { set . set ( v , true ) ; unique ++ ; } } for ( const v of arr2 ! ) { const isSet = set . get ( v ) ; if ( isSet === undefined ) { return false ; } if ( isSet === true ) { set . set ( v , false ) ; unique -- ; } } return unique === 0 ; } function typeAcquisitionChanged ( opt1 , opt2 ) { return opt1 . enable !== opt2 . enable || ! setIsEqualTo ( opt1 . include , opt2 . include ) || ! setIsEqualTo ( opt1 . exclude , opt2 . exclude ) ; } function compilerOptionsChanged ( opt1 , opt2 ) { return getAllowJSCompilerOption ( opt1 ) !== getAllowJSCompilerOption ( opt2 ) ; } function unresolvedImportsChanged ( imports1 , imports2 ) { if ( imports1 === imports2 ) { return false ; } return ! arrayIsEqualTo ( imports1 , imports2 ) ; } export class TypingsCache { private readonly perProjectCache = new Map < string , TypingsCacheEntry > ( ) ; constructor ( private readonly installer ) { } isKnownTypesPackageName ( name ) { return this . installer . isKnownTypesPackageName ( name ) ; } installPackage ( options ) { return this . installer . installPackage ( options ) ; } enqueueInstallTypingsForProject ( project , unresolvedImports , forceRefresh ) { const typeAcquisition = project . getTypeAcquisition ( ) ; if ( ! typeAcquisition || ! typeAcquisition . enable ) { return ; } const entry = this . perProjectCache . get ( project . getProjectName ( ) ) ; if ( forceRefresh || ! entry || typeAcquisitionChanged ( typeAcquisition , entry . typeAcquisition ) || compilerOptionsChanged ( project . getCompilationSettings ( ) , entry . compilerOptions ) || unresolvedImportsChanged ( unresolvedImports , entry . unresolvedImports ) ) { this . perProjectCache . set ( project . getProjectName ( ) , { compilerOptions : project . getCompilationSettings ( ) , typeAcquisition , typings : entry ? entry . typings : emptyArray , unresolvedImports , poisoned : true } ) ; this . installer . enqueueInstallTypingsRequest ( project , typeAcquisition , unresolvedImports ) ; } } updateTypingsForProject ( projectName , compilerOptions , typeAcquisition , unresolvedImports , newTypings ) { const typings = sort ( newTypings ) ; this . perProjectCache . set ( projectName , { compilerOptions , typeAcquisition , typings , unresolvedImports , poisoned : false } ) ; return ! typeAcquisition || ! typeAcquisition . enable ? emptyArray : typings ; } onProjectClosed ( project ) { this . perProjectCache . delete ( project . getProjectName ( ) ) ; this . installer . onProjectClosed ( project ) ; } } }