2016-06-22 02:31:54 +02:00
namespace ts . server {
2016-06-24 23:30:45 +02:00
2016-06-22 02:31:54 +02:00
export enum ProjectKind {
Inferred ,
Configured ,
2020-06-23 01:34:27 +02:00
External ,
AutoImportProvider ,
2016-06-22 02:31:54 +02:00
}
2018-05-09 16:51:46 +02:00
/* @internal */
export type Mutable < T > = { - readonly [ K in keyof T ] : T [ K ] ; } ;
2017-05-25 22:30:27 +02:00
/* @internal */
2018-11-15 19:54:48 +01:00
export function countEachFileTypes ( infos : ScriptInfo [ ] , includeSizes = false ) : FileStats {
2018-11-16 01:00:35 +01:00
const result : Mutable < FileStats > = {
2018-11-15 19:54:48 +01:00
js : 0 , jsSize : 0 ,
jsx : 0 , jsxSize : 0 ,
ts : 0 , tsSize : 0 ,
tsx : 0 , tsxSize : 0 ,
dts : 0 , dtsSize : 0 ,
deferred : 0 , deferredSize : 0 ,
} ;
2016-10-12 04:19:40 +02:00
for ( const info of infos ) {
2018-11-15 19:54:48 +01:00
const fileSize = includeSizes ? info . getTelemetryFileSize ( ) : 0 ;
2016-10-12 04:19:40 +02:00
switch ( info . scriptKind ) {
case ScriptKind . JS :
result . js += 1 ;
2018-11-15 19:54:48 +01:00
result . jsSize ! += fileSize ;
2016-10-12 04:19:40 +02:00
break ;
case ScriptKind . JSX :
result . jsx += 1 ;
2018-11-15 19:54:48 +01:00
result . jsxSize ! += fileSize ;
2016-10-12 04:19:40 +02:00
break ;
case ScriptKind . TS :
2018-11-15 19:54:48 +01:00
if ( fileExtensionIs ( info . fileName , Extension . Dts ) ) {
result . dts += 1 ;
result . dtsSize ! += fileSize ;
}
else {
result . ts += 1 ;
result . tsSize ! += fileSize ;
}
2016-10-12 04:19:40 +02:00
break ;
case ScriptKind . TSX :
result . tsx += 1 ;
2018-11-15 19:54:48 +01:00
result . tsxSize ! += fileSize ;
2016-10-12 04:19:40 +02:00
break ;
2018-05-11 03:08:36 +02:00
case ScriptKind . Deferred :
result . deferred += 1 ;
2018-11-15 19:54:48 +01:00
result . deferredSize ! += fileSize ;
2018-05-11 03:08:36 +02:00
break ;
2016-10-12 04:19:40 +02:00
}
}
return result ;
}
function hasOneOrMoreJsAndNoTsFiles ( project : Project ) {
const counts = countEachFileTypes ( project . getScriptInfos ( ) ) ;
return counts . js > 0 && counts . ts === 0 && counts . tsx === 0 ;
2016-09-02 00:17:38 +02:00
}
2016-08-24 22:37:03 +02:00
export function allRootFilesAreJsOrDts ( project : Project ) : boolean {
2016-10-12 04:19:40 +02:00
const counts = countEachFileTypes ( project . getRootScriptInfos ( ) ) ;
return counts . ts === 0 && counts . tsx === 0 ;
2016-09-02 00:17:38 +02:00
}
export function allFilesAreJsOrDts ( project : Project ) : boolean {
2016-10-12 04:19:40 +02:00
const counts = countEachFileTypes ( project . getScriptInfos ( ) ) ;
return counts . ts === 0 && counts . tsx === 0 ;
2016-08-24 00:15:12 +02:00
}
2017-11-16 21:37:40 +01:00
/* @internal */
export function hasNoTypeScriptSource ( fileNames : string [ ] ) : boolean {
return ! fileNames . some ( fileName = > ( fileExtensionIs ( fileName , Extension . Ts ) && ! fileExtensionIs ( fileName , Extension . Dts ) ) || fileExtensionIs ( fileName , Extension . Tsx ) ) ;
}
2017-01-05 00:56:16 +01:00
/* @internal */
2016-08-26 23:37:49 +02:00
export interface ProjectFilesWithTSDiagnostics extends protocol . ProjectFiles {
2019-08-08 20:30:18 +02:00
projectErrors : readonly Diagnostic [ ] ;
2016-08-26 23:37:49 +02:00
}
2017-02-14 22:35:16 +01:00
export interface PluginCreateInfo {
project : Project ;
languageService : LanguageService ;
languageServiceHost : LanguageServiceHost ;
serverHost : ServerHost ;
2021-06-18 23:46:09 +02:00
session? : Session < unknown > ;
2017-02-14 22:35:16 +01:00
config : any ;
}
export interface PluginModule {
create ( createInfo : PluginCreateInfo ) : LanguageService ;
getExternalFiles ? ( proj : Project ) : string [ ] ;
2018-10-29 21:56:51 +01:00
onConfigurationChanged ? ( config : any ) : void ;
}
export interface PluginModuleWithName {
name : string ;
module : PluginModule ;
2017-02-14 22:35:16 +01:00
}
2017-11-03 01:16:09 +01:00
export type PluginModuleFactory = ( mod : { typescript : typeof ts } ) = > PluginModule ;
2017-02-14 22:35:16 +01:00
2017-07-06 03:03:11 +02:00
/ * *
* The project root can be script info - if root is present ,
2021-01-19 18:13:26 +01:00
* or it could be just normalized path if root wasn ' t present on the host ( only for non inferred project )
2017-07-06 03:03:11 +02:00
* /
/* @internal */
2019-12-11 03:25:10 +01:00
export interface ProjectRootFile {
fileName : NormalizedPath ;
info? : ScriptInfo ;
2017-07-06 03:03:11 +02:00
}
2019-06-25 21:15:04 +02:00
interface GeneratedFileWatcher {
generatedFilePath : Path ;
watcher : FileWatcher ;
}
2020-07-02 02:00:26 +02:00
type GeneratedFileWatcherMap = GeneratedFileWatcher | ESMap < Path , GeneratedFileWatcher > ;
2019-06-25 21:15:04 +02:00
function isGeneratedFileWatcher ( watch : GeneratedFileWatcherMap ) : watch is GeneratedFileWatcher {
return ( watch as GeneratedFileWatcher ) . generatedFilePath !== undefined ;
}
2020-03-19 22:03:21 +01:00
/*@internal*/
export interface EmitResult {
emitSkipped : boolean ;
diagnostics : readonly Diagnostic [ ] ;
}
2017-08-24 20:50:27 +02:00
export abstract class Project implements LanguageServiceHost , ModuleResolutionHost {
2016-06-28 00:29:04 +02:00
private rootFiles : ScriptInfo [ ] = [ ] ;
2020-06-26 01:03:25 +02:00
private rootFilesMap = new Map < string , ProjectRootFile > ( ) ;
2018-11-17 01:02:23 +01:00
private program : Program | undefined ;
private externalFiles : SortedReadonlyArray < string > | undefined ;
2020-07-02 02:00:26 +02:00
private missingFilesMap : ESMap < Path , FileWatcher > | undefined ;
2019-06-25 21:15:04 +02:00
private generatedFilesMap : GeneratedFileWatcherMap | undefined ;
2018-10-29 21:56:51 +01:00
private plugins : PluginModuleWithName [ ] = [ ] ;
2016-06-22 02:31:54 +02:00
2018-04-12 23:55:22 +02:00
/*@internal*/
2018-04-13 01:47:40 +02:00
/ * *
* This is map from files to unresolved imports in it
* Maop does not contain entries for files that do not have unresolved imports
* This helps in containing the set of files to invalidate
* /
2020-06-26 19:12:47 +02:00
cachedUnresolvedImportsPerFile = new Map < Path , readonly string [ ] > ( ) ;
2018-04-13 01:47:40 +02:00
2018-04-12 23:55:22 +02:00
/*@internal*/
2018-05-22 23:46:57 +02:00
lastCachedUnresolvedImportsList : SortedReadonlyArray < string > | undefined ;
2018-04-13 00:21:20 +02:00
/*@internal*/
2018-04-18 20:05:56 +02:00
private hasAddedorRemovedFiles = false ;
2021-03-26 22:47:07 +01:00
/*@internal*/
private hasAddedOrRemovedSymlinks = false ;
2016-10-26 00:24:21 +02:00
2021-01-25 19:04:39 +01:00
/*@internal*/
lastFileExceededProgramSize : string | undefined ;
2018-02-07 02:11:59 +01:00
2017-02-14 22:35:16 +01:00
// wrapper over the real language service that will suppress all semantic operations
protected languageService : LanguageService ;
2016-11-12 02:35:11 +01:00
2018-04-03 22:40:48 +02:00
public languageServiceEnabled : boolean ;
2016-11-12 02:35:11 +01:00
2017-08-24 20:50:27 +02:00
readonly trace ? : ( s : string ) = > void ;
readonly realpath ? : ( path : string ) = > string ;
2017-08-04 10:14:54 +02:00
/*@internal*/
2018-11-17 01:02:23 +01:00
hasInvalidatedResolution : HasInvalidatedResolution | undefined ;
2017-08-04 10:14:54 +02:00
2017-07-12 01:10:44 +02:00
/*@internal*/
2017-08-24 20:50:27 +02:00
resolutionCache : ResolutionCache ;
2017-02-14 22:35:16 +01:00
2017-12-07 23:04:40 +01:00
private builderState : BuilderState | undefined ;
2016-12-09 02:56:08 +01:00
/ * *
* Set of files names that were updated since the last call to getChangesSinceVersion .
* /
2020-06-26 19:12:47 +02:00
private updatedFileNames : Set < string > | undefined ;
2016-06-23 01:51:09 +02:00
/ * *
* Set of files that was returned from the last call to getChangesSinceVersion .
* /
2020-07-02 02:00:26 +02:00
private lastReportedFileNames : ESMap < string , boolean > | undefined ;
2016-06-23 01:51:09 +02:00
/ * *
* Last version that was reported .
* /
private lastReportedVersion = 0 ;
/ * *
2018-04-17 23:17:15 +02:00
* Current project ' s program version . ( incremented everytime new program is created that is not complete reuse from the old one )
2016-06-23 01:51:09 +02:00
* This property is changed in 'updateGraph' based on the set of files in program
* /
2018-04-17 23:17:15 +02:00
private projectProgramVersion = 0 ;
2016-06-23 01:51:09 +02:00
/ * *
* Current version of the project state . It is changed when :
* - new root file was added / removed
* - edit happen in some file that is currently included in the project .
* This property is different from projectStructureVersion since in most cases edits don ' t affect set of files in the project
* /
private projectStateVersion = 0 ;
2020-11-04 22:30:06 +01:00
protected projectErrors : Diagnostic [ ] | undefined ;
2018-07-25 23:01:48 +02:00
protected isInitialLoadPending : ( ) = > boolean = returnFalse ;
2018-02-12 23:55:58 +01:00
/*@internal*/
dirty = false ;
2018-04-13 01:47:40 +02:00
/*@internal*/
typingFiles : SortedReadonlyArray < string > = emptyArray ;
2016-09-21 01:52:34 +02:00
2018-07-28 00:35:13 +02:00
/*@internal*/
2020-06-26 19:12:47 +02:00
originalConfiguredProjects : Set < NormalizedPath > | undefined ;
2018-07-28 00:35:13 +02:00
2020-06-23 01:34:27 +02:00
/*@internal*/
2021-03-26 22:47:07 +01:00
private packageJsonsForAutoImport : Set < string > | undefined ;
2020-06-23 01:34:27 +02:00
2019-06-27 00:57:22 +02:00
/*@internal*/
getResolvedProjectReferenceToRedirect ( _fileName : string ) : ResolvedProjectReference | undefined {
return undefined ;
}
2021-03-26 21:23:03 +01:00
/* @internal */ useSourceOfProjectReferenceRedirect ? ( ) : boolean ;
/* @internal */ getParsedCommandLine ? ( fileName : string ) : ParsedCommandLine | undefined ;
2017-10-04 01:32:59 +02:00
private readonly cancellationToken : ThrottledCancellationToken ;
2016-10-12 04:19:40 +02:00
public isNonTsProject() {
2018-08-02 01:08:25 +02:00
updateProjectIfDirty ( this ) ;
2016-09-02 00:17:38 +02:00
return allFilesAreJsOrDts ( this ) ;
}
2016-10-12 04:19:40 +02:00
public isJsOnlyProject() {
2018-08-02 01:08:25 +02:00
updateProjectIfDirty ( this ) ;
2016-10-12 04:19:40 +02:00
return hasOneOrMoreJsAndNoTsFiles ( this ) ;
}
2019-05-25 00:04:42 +02:00
public static resolveModule ( module Name : string , initialDir : string , host : ServerHost , log : ( message : string ) = > void , logErrors ? : ( message : string ) = > void ) : { } | undefined {
2017-02-14 22:35:16 +01:00
const resolvedPath = normalizeSlashes ( host . resolvePath ( combinePaths ( initialDir , "node_modules" ) ) ) ;
log ( ` Loading ${ module Name } from ${ initialDir } (resolved to ${ resolvedPath } ) ` ) ;
2018-05-22 23:46:57 +02:00
const result = host . require ! ( resolvedPath , module Name ) ; // TODO: GH#18217
2017-02-14 22:35:16 +01:00
if ( result . error ) {
2017-08-08 16:54:08 +02:00
const err = result . error . stack || result . error . message || JSON . stringify ( result . error ) ;
2019-05-25 00:04:42 +02:00
( logErrors || log ) ( ` Failed to load module ' ${ module Name } ' from ${ resolvedPath } : ${ err } ` ) ;
2017-02-14 22:35:16 +01:00
return undefined ;
}
return result . module ;
}
2017-09-27 02:29:53 +02:00
/*@internal*/
readonly currentDirectory : string ;
2017-12-06 05:23:14 +01:00
/*@internal*/
public directoryStructureHost : DirectoryStructureHost ;
2018-02-22 20:09:23 +01:00
/*@internal*/
public readonly getCanonicalFileName : GetCanonicalFileName ;
2019-09-27 22:38:31 +02:00
/*@internal*/
2021-07-06 19:04:59 +02:00
private exportMapCache : ExportInfoMap | undefined ;
2021-03-26 22:47:07 +01:00
/*@internal*/
private changedFilesForExportMapCache : Set < Path > | undefined ;
2019-09-27 22:38:31 +02:00
/*@internal*/
2021-06-10 19:26:32 +02:00
private module SpecifierCache = createModuleSpecifierCache ( this ) ;
2019-09-27 22:38:31 +02:00
/*@internal*/
2020-07-22 22:53:30 +02:00
private symlinks : SymlinkCache | undefined ;
2020-06-23 01:34:27 +02:00
/*@internal*/
autoImportProviderHost : AutoImportProviderProject | false | undefined ;
2020-10-19 18:53:58 +02:00
/*@internal*/
protected typeAcquisition : TypeAcquisition | undefined ;
2019-09-27 22:38:31 +02:00
2017-10-03 01:14:42 +02:00
/*@internal*/
2016-06-22 02:31:54 +02:00
constructor (
2019-03-12 21:14:47 +01:00
/*@internal*/ readonly projectName : string ,
2016-06-22 02:31:54 +02:00
readonly projectKind : ProjectKind ,
readonly projectService : ProjectService ,
2017-07-13 22:08:59 +02:00
private documentRegistry : DocumentRegistry ,
2016-06-22 02:31:54 +02:00
hasExplicitListOfFiles : boolean ,
2018-02-07 02:11:59 +01:00
lastFileExceededProgramSize : string | undefined ,
2016-08-24 01:11:52 +02:00
private compilerOptions : CompilerOptions ,
2017-07-12 01:10:44 +02:00
public compileOnSaveEnabled : boolean ,
2019-12-11 22:26:44 +01:00
protected watchOptions : WatchOptions | undefined ,
2017-12-06 05:23:14 +01:00
directoryStructureHost : DirectoryStructureHost ,
2019-10-29 18:49:14 +01:00
currentDirectory : string | undefined ,
2020-03-12 21:11:11 +01:00
) {
2017-12-06 05:23:14 +01:00
this . directoryStructureHost = directoryStructureHost ;
2017-09-27 02:29:53 +02:00
this . currentDirectory = this . projectService . getNormalizedAbsolutePath ( currentDirectory || "" ) ;
2018-02-22 20:09:23 +01:00
this . getCanonicalFileName = this . projectService . toCanonicalFileName ;
2016-06-22 02:31:54 +02:00
2017-10-04 01:32:59 +02:00
this . cancellationToken = new ThrottledCancellationToken ( this . projectService . cancellationToken , this . projectService . throttleWaitMilliseconds ) ;
2016-06-22 02:31:54 +02:00
if ( ! this . compilerOptions ) {
2017-07-13 22:08:59 +02:00
this . compilerOptions = getDefaultCompilerOptions ( ) ;
2016-06-22 02:31:54 +02:00
this . compilerOptions . allowNonTsExtensions = true ;
this . compilerOptions . allowJs = true ;
}
2020-09-01 19:16:08 +02:00
else if ( hasExplicitListOfFiles || getAllowJSCompilerOption ( this . compilerOptions ) || this . projectService . hasDeferredExtension ( ) ) {
2017-02-10 02:06:47 +01:00
// If files are listed explicitly or allowJs is specified, allow all extensions
2016-06-22 02:31:54 +02:00
this . compilerOptions . allowNonTsExtensions = true ;
}
2020-08-03 03:18:26 +02:00
switch ( projectService . serverMode ) {
case LanguageServiceMode . Semantic :
this . languageServiceEnabled = true ;
break ;
2020-08-05 21:19:15 +02:00
case LanguageServiceMode . PartialSemantic :
2020-08-03 03:18:26 +02:00
this . languageServiceEnabled = true ;
2020-08-13 05:45:59 +02:00
this . compilerOptions . noResolve = true ;
2020-08-03 03:18:26 +02:00
this . compilerOptions . types = [ ] ;
break ;
2020-08-05 21:19:15 +02:00
case LanguageServiceMode . Syntactic :
2020-08-03 03:18:26 +02:00
this . languageServiceEnabled = false ;
this . compilerOptions . noResolve = true ;
this . compilerOptions . types = [ ] ;
break ;
default :
Debug . assertNever ( projectService . serverMode ) ;
2020-06-16 22:39:20 +02:00
}
2018-04-03 22:40:48 +02:00
2016-11-30 08:30:14 +01:00
this . setInternalCompilerOptionsForEmittingJsFiles ( ) ;
2017-08-24 20:50:27 +02:00
const host = this . projectService . host ;
2018-02-21 21:40:36 +01:00
if ( this . projectService . logger . loggingEnabled ( ) ) {
this . trace = s = > this . writeLog ( s ) ;
}
else if ( host . trace ) {
2018-05-22 23:46:57 +02:00
this . trace = s = > host . trace ! ( s ) ;
2017-08-24 20:50:27 +02:00
}
2020-03-12 21:11:11 +01:00
this . realpath = maybeBind ( host , host . realpath ) ;
2016-11-12 02:35:11 +01:00
2017-10-05 03:45:59 +02:00
// Use the current directory as resolution root only if the project created using current directory string
2020-07-16 23:31:24 +02:00
this . resolutionCache = createResolutionCache (
this ,
currentDirectory && this . currentDirectory ,
/*logChangesWhenResolvingModule*/ true
) ;
2020-08-03 03:18:26 +02:00
this . languageService = createLanguageService ( this , this . documentRegistry , this . projectService . serverMode ) ;
2018-02-07 02:11:59 +01:00
if ( lastFileExceededProgramSize ) {
this . disableLanguageService ( lastFileExceededProgramSize ) ;
2016-06-22 02:31:54 +02:00
}
this . markAsDirty ( ) ;
2020-06-23 01:34:27 +02:00
if ( projectKind !== ProjectKind . AutoImportProvider ) {
this . projectService . pendingEnsureProjectForOpenFiles = true ;
}
2016-06-22 02:31:54 +02:00
}
2017-10-18 00:04:09 +02:00
isKnownTypesPackageName ( name : string ) : boolean {
return this . typingsCache . isKnownTypesPackageName ( name ) ;
}
2017-10-19 01:04:42 +02:00
installPackage ( options : InstallPackageOptions ) : Promise < ApplyCodeActionCommandResult > {
2017-11-22 00:05:21 +01:00
return this . typingsCache . installPackage ( { . . . options , projectName : this.projectName , projectRootPath : this.toPath ( this . currentDirectory ) } ) ;
2017-10-18 00:04:09 +02:00
}
2018-09-18 20:47:29 +02:00
2019-10-17 22:59:09 +02:00
/*@internal*/
getGlobalTypingsCacheLocation() {
return this . getGlobalCache ( ) ;
}
2017-10-18 00:04:09 +02:00
private get typingsCache ( ) : TypingsCache {
return this . projectService . typingsCache ;
}
2019-09-27 22:38:31 +02:00
/*@internal*/
2020-07-22 22:53:30 +02:00
getSymlinkCache ( ) : SymlinkCache {
2021-06-08 19:06:55 +02:00
if ( ! this . symlinks ) {
this . symlinks = createSymlinkCache ( this . getCurrentDirectory ( ) , this . getCanonicalFileName ) ;
}
if ( this . program && ! this . symlinks . hasProcessedResolutions ( ) ) {
this . symlinks . setSymlinksFromResolutions (
this . program . getSourceFiles ( ) ,
this . program . getResolvedTypeReferenceDirectives ( ) ) ;
}
return this . symlinks ;
2019-09-27 22:38:31 +02:00
}
2017-10-16 22:06:15 +02:00
// Method of LanguageServiceHost
2017-08-24 20:50:27 +02:00
getCompilationSettings() {
return this . compilerOptions ;
}
2017-10-16 22:06:15 +02:00
// Method to support public API
2017-10-16 20:22:49 +02:00
getCompilerOptions() {
2017-10-16 22:06:15 +02:00
return this . getCompilationSettings ( ) ;
2017-10-16 20:22:49 +02:00
}
2017-08-24 20:50:27 +02:00
getNewLine() {
2017-12-05 03:17:25 +01:00
return this . projectService . host . newLine ;
2017-08-24 20:50:27 +02:00
}
getProjectVersion() {
return this . projectStateVersion . toString ( ) ;
}
2019-08-08 20:30:18 +02:00
getProjectReferences ( ) : readonly ProjectReference [ ] | undefined {
2018-09-19 01:21:05 +02:00
return undefined ;
2018-05-08 00:12:50 +02:00
}
2017-08-24 20:50:27 +02:00
getScriptFileNames() {
2017-09-26 22:34:56 +02:00
if ( ! this . rootFiles ) {
return ts . emptyArray ;
2017-08-24 20:50:27 +02:00
}
2017-09-26 22:34:56 +02:00
let result : string [ ] | undefined ;
this . rootFilesMap . forEach ( value = > {
2019-12-11 03:25:10 +01:00
if ( this . languageServiceEnabled || ( value . info && value . info . isScriptOpen ( ) ) ) {
2017-09-26 22:34:56 +02:00
// if language service is disabled - process only files that are open
2019-12-11 03:25:10 +01:00
( result || ( result = [ ] ) ) . push ( value . fileName ) ;
2017-09-26 22:34:56 +02:00
}
} ) ;
return addRange ( result , this . typingFiles ) || ts . emptyArray ;
2017-08-24 20:50:27 +02:00
}
2017-09-26 22:34:56 +02:00
private getOrCreateScriptInfoAndAttachToProject ( fileName : string ) {
2017-11-03 23:28:28 +01:00
const scriptInfo = this . projectService . getOrCreateScriptInfoNotOpenedByClient ( fileName , this . currentDirectory , this . directoryStructureHost ) ;
2017-08-24 20:50:27 +02:00
if ( scriptInfo ) {
const existingValue = this . rootFilesMap . get ( scriptInfo . path ) ;
2019-12-11 03:25:10 +01:00
if ( existingValue && existingValue . info !== scriptInfo ) {
2017-08-24 20:50:27 +02:00
// This was missing path earlier but now the file exists. Update the root
this . rootFiles . push ( scriptInfo ) ;
2019-12-11 03:25:10 +01:00
existingValue . info = scriptInfo ;
2017-08-24 20:50:27 +02:00
}
scriptInfo . attachToProject ( this ) ;
}
return scriptInfo ;
}
getScriptKind ( fileName : string ) {
2017-09-26 22:34:56 +02:00
const info = this . getOrCreateScriptInfoAndAttachToProject ( fileName ) ;
2018-05-22 23:46:57 +02:00
return ( info && info . scriptKind ) ! ; // TODO: GH#18217
2017-08-24 20:50:27 +02:00
}
getScriptVersion ( filename : string ) {
2020-02-26 01:11:21 +01:00
// Don't attach to the project if version is asked
const info = this . projectService . getOrCreateScriptInfoNotOpenedByClient ( filename , this . currentDirectory , this . directoryStructureHost ) ;
2018-05-22 23:46:57 +02:00
return ( info && info . getLatestVersion ( ) ) ! ; // TODO: GH#18217
2017-08-24 20:50:27 +02:00
}
2018-05-22 23:46:57 +02:00
getScriptSnapshot ( filename : string ) : IScriptSnapshot | undefined {
2017-09-26 22:34:56 +02:00
const scriptInfo = this . getOrCreateScriptInfoAndAttachToProject ( filename ) ;
2017-08-24 20:50:27 +02:00
if ( scriptInfo ) {
return scriptInfo . getSnapshot ( ) ;
}
}
2017-10-05 01:03:16 +02:00
getCancellationToken ( ) : HostCancellationToken {
2017-10-04 01:32:59 +02:00
return this . cancellationToken ;
2017-08-24 20:50:27 +02:00
}
getCurrentDirectory ( ) : string {
2017-10-05 03:45:59 +02:00
return this . currentDirectory ;
2017-08-24 20:50:27 +02:00
}
getDefaultLibFileName() {
2017-10-05 03:45:59 +02:00
const nodeModuleBinDir = getDirectoryPath ( normalizePath ( this . projectService . getExecutingFilePath ( ) ) ) ;
2017-08-24 20:50:27 +02:00
return combinePaths ( nodeModuleBinDir , getDefaultLibFileName ( this . compilerOptions ) ) ;
}
useCaseSensitiveFileNames() {
2017-12-06 01:34:17 +01:00
return this . projectService . host . useCaseSensitiveFileNames ;
2017-08-24 20:50:27 +02:00
}
2019-08-08 20:30:18 +02:00
readDirectory ( path : string , extensions? : readonly string [ ] , exclude? : readonly string [ ] , include? : readonly string [ ] , depth? : number ) : string [ ] {
2018-05-22 23:46:57 +02:00
return this . directoryStructureHost . readDirectory ! ( path , extensions , exclude , include , depth ) ;
2017-08-24 20:50:27 +02:00
}
readFile ( fileName : string ) : string | undefined {
2017-12-06 01:34:17 +01:00
return this . projectService . host . readFile ( fileName ) ;
2017-08-24 20:50:27 +02:00
}
2018-09-18 20:47:29 +02:00
writeFile ( fileName : string , content : string ) : void {
return this . projectService . host . writeFile ( fileName , content ) ;
}
2017-08-24 20:50:27 +02:00
fileExists ( file : string ) : boolean {
// As an optimization, don't hit the disks for files we already know don't exist
// (because we're watching for their creation).
const path = this . toPath ( file ) ;
2017-09-26 20:05:52 +02:00
return ! this . isWatchedMissingFile ( path ) && this . directoryStructureHost . fileExists ( file ) ;
2017-08-24 20:50:27 +02:00
}
2021-09-24 23:25:59 +02:00
resolveModuleNames ( module Names : string [ ] , containingFile : string , reusedNames? : string [ ] , redirectedReference? : ResolvedProjectReference , _options? : CompilerOptions , containingSourceFile? : SourceFile ) : ( ResolvedModuleFull | undefined ) [ ] {
return this . resolutionCache . resolveModuleNames ( module Names , containingFile , reusedNames , redirectedReference , containingSourceFile ) ;
2017-08-24 20:50:27 +02:00
}
2021-09-24 23:25:59 +02:00
getResolvedModuleWithFailedLookupLocationsFromCache ( module Name : string , containingFile : string , resolutionMode? : ModuleKind.CommonJS | ModuleKind . ESNext ) : ResolvedModuleWithFailedLookupLocations | undefined {
return this . resolutionCache . getResolvedModuleWithFailedLookupLocationsFromCache ( module Name , containingFile , resolutionMode ) ;
2018-05-19 01:42:42 +02:00
}
2018-10-22 21:23:04 +02:00
resolveTypeReferenceDirectives ( typeDirectiveNames : string [ ] , containingFile : string , redirectedReference? : ResolvedProjectReference ) : ( ResolvedTypeReferenceDirective | undefined ) [ ] {
2018-10-02 00:30:06 +02:00
return this . resolutionCache . resolveTypeReferenceDirectives ( typeDirectiveNames , containingFile , redirectedReference ) ;
2017-08-24 20:50:27 +02:00
}
directoryExists ( path : string ) : boolean {
2018-05-22 23:46:57 +02:00
return this . directoryStructureHost . directoryExists ! ( path ) ; // TODO: GH#18217
2017-08-24 20:50:27 +02:00
}
getDirectories ( path : string ) : string [ ] {
2018-05-22 23:46:57 +02:00
return this . directoryStructureHost . getDirectories ! ( path ) ; // TODO: GH#18217
2017-08-24 20:50:27 +02:00
}
2017-12-06 01:34:17 +01:00
/*@internal*/
getCachedDirectoryStructureHost ( ) : CachedDirectoryStructureHost {
2018-05-22 23:46:57 +02:00
return undefined ! ; // TODO: GH#18217
2017-12-06 01:34:17 +01:00
}
2017-08-24 20:50:27 +02:00
/*@internal*/
toPath ( fileName : string ) {
2017-11-03 23:28:28 +01:00
return toPath ( fileName , this . currentDirectory , this . projectService . toCanonicalFileName ) ;
2017-08-24 20:50:27 +02:00
}
/*@internal*/
2017-08-28 21:34:58 +02:00
watchDirectoryOfFailedLookupLocation ( directory : string , cb : DirectoryWatcherCallback , flags : WatchDirectoryFlags ) {
2017-12-18 22:20:42 +01:00
return this . projectService . watchFactory . watchDirectory (
2017-08-23 22:34:41 +02:00
directory ,
2017-08-24 20:50:27 +02:00
cb ,
2017-08-28 21:34:58 +02:00
flags ,
2019-12-11 22:26:44 +01:00
this . projectService . getWatchOptions ( this ) ,
2018-12-15 00:13:19 +01:00
WatchType . FailedLookupLocations ,
2017-08-23 22:34:41 +02:00
this
) ;
}
2020-06-09 21:00:37 +02:00
/*@internal*/
clearInvalidateResolutionOfFailedLookupTimer() {
return this . projectService . throttledOperations . cancel ( ` ${ this . getProjectName ( ) } FailedLookupInvalidation ` ) ;
}
/*@internal*/
scheduleInvalidateResolutionsOfFailedLookupLocations() {
this . projectService . throttledOperations . schedule ( ` ${ this . getProjectName ( ) } FailedLookupInvalidation ` , /*delay*/ 1000 , ( ) = > {
if ( this . resolutionCache . invalidateResolutionsOfFailedLookupLocations ( ) ) {
this . projectService . delayUpdateProjectGraphAndEnsureProjectStructureForOpenFiles ( this ) ;
}
} ) ;
}
/*@internal*/
invalidateResolutionsOfFailedLookupLocations() {
if ( this . clearInvalidateResolutionOfFailedLookupTimer ( ) &&
this . resolutionCache . invalidateResolutionsOfFailedLookupLocations ( ) ) {
this . markAsDirty ( ) ;
this . projectService . delayEnsureProjectForOpenFiles ( ) ;
}
}
2017-08-24 20:50:27 +02:00
/*@internal*/
onInvalidatedResolution() {
2018-02-12 23:55:58 +01:00
this . projectService . delayUpdateProjectGraphAndEnsureProjectStructureForOpenFiles ( this ) ;
2017-08-24 20:50:27 +02:00
}
2017-08-23 22:34:41 +02:00
2017-08-30 20:49:58 +02:00
/*@internal*/
watchTypeRootsDirectory ( directory : string , cb : DirectoryWatcherCallback , flags : WatchDirectoryFlags ) {
2017-12-18 22:20:42 +01:00
return this . projectService . watchFactory . watchDirectory (
2017-08-30 20:49:58 +02:00
directory ,
cb ,
flags ,
2019-12-11 22:26:44 +01:00
this . projectService . getWatchOptions ( this ) ,
2017-08-30 20:49:58 +02:00
WatchType . TypeRoots ,
this
) ;
}
2020-06-09 21:00:37 +02:00
/*@internal*/
hasChangedAutomaticTypeDirectiveNames() {
return this . resolutionCache . hasChangedAutomaticTypeDirectiveNames ( ) ;
}
2017-08-30 20:49:58 +02:00
/*@internal*/
onChangedAutomaticTypeDirectiveNames() {
2018-02-12 23:55:58 +01:00
this . projectService . delayUpdateProjectGraphAndEnsureProjectStructureForOpenFiles ( this ) ;
2017-08-30 20:49:58 +02:00
}
2017-08-24 20:50:27 +02:00
/*@internal*/
getGlobalCache() {
return this . getTypeAcquisition ( ) . enable ? this . projectService.typingsInstaller.globalTypingsCacheLocation : undefined ;
2017-08-05 11:27:27 +02:00
}
2019-06-08 01:21:39 +02:00
/*@internal*/
globalCacheResolutionModuleName = JsTyping . nonRelativeModuleNameForTypingCache ;
2019-05-31 00:05:53 +02:00
/*@internal*/
fileIsOpen ( filePath : Path ) {
return this . projectService . openFiles . has ( filePath ) ;
}
2017-08-29 02:09:07 +02:00
/*@internal*/
writeLog ( s : string ) {
this . projectService . logger . info ( s ) ;
}
2018-02-05 20:42:03 +01:00
log ( s : string ) {
this . writeLog ( s ) ;
}
error ( s : string ) {
this . projectService . logger . msg ( s , Msg . Err ) ;
}
2016-11-30 08:30:14 +01:00
private setInternalCompilerOptionsForEmittingJsFiles() {
if ( this . projectKind === ProjectKind . Inferred || this . projectKind === ProjectKind . External ) {
this . compilerOptions . noEmitForJsFiles = true ;
}
}
2017-05-24 00:38:03 +02:00
/ * *
* Get the errors that dont have any file name associated
* /
2019-08-08 20:30:18 +02:00
getGlobalProjectErrors ( ) : readonly Diagnostic [ ] {
2020-11-04 22:30:06 +01:00
return filter ( this . projectErrors , diagnostic = > ! diagnostic . file ) || emptyArray ;
2017-05-24 00:38:03 +02:00
}
2020-11-04 22:30:06 +01:00
/ * *
* Get all the project errors
* /
2019-08-08 20:30:18 +02:00
getAllProjectErrors ( ) : readonly Diagnostic [ ] {
2020-11-04 22:30:06 +01:00
return this . projectErrors || emptyArray ;
}
setProjectErrors ( projectErrors : Diagnostic [ ] | undefined ) {
this . projectErrors = projectErrors ;
2016-08-26 23:37:49 +02:00
}
2016-08-12 20:04:43 +02:00
getLanguageService ( ensureSynchronized = true ) : LanguageService {
2016-07-23 01:01:54 +02:00
if ( ensureSynchronized ) {
2018-08-02 01:08:25 +02:00
updateProjectIfDirty ( this ) ;
2016-07-23 01:01:54 +02:00
}
2016-12-13 22:21:32 +01:00
return this . languageService ;
2016-07-23 01:01:54 +02:00
}
2018-07-06 00:39:03 +02:00
/** @internal */
getSourceMapper ( ) : SourceMapper {
return this . getLanguageService ( ) . getSourceMapper ( ) ;
}
2020-02-26 01:11:21 +01:00
/** @internal */
clearSourceMapperCache() {
this . languageService . clearSourceMapperCache ( ) ;
}
2018-11-16 19:15:51 +01:00
/*@internal*/
2018-11-29 23:14:45 +01:00
getDocumentPositionMapper ( generatedFileName : string , sourceFileName? : string ) : DocumentPositionMapper | undefined {
return this . projectService . getDocumentPositionMapper ( this , generatedFileName , sourceFileName ) ;
2018-11-16 19:15:51 +01:00
}
/*@internal*/
getSourceFileLike ( fileName : string ) {
2018-11-16 20:24:01 +01:00
return this . projectService . getSourceFileLike ( fileName , this ) ;
2018-11-16 19:15:51 +01:00
}
2019-07-09 00:55:35 +02:00
/*@internal*/
shouldEmitFile ( scriptInfo : ScriptInfo | undefined ) {
return scriptInfo &&
! scriptInfo . isDynamicOrHasMixedContent ( ) &&
! this . program ! . isSourceOfProjectReferenceRedirect ( scriptInfo . path ) ;
2017-10-18 06:18:27 +02:00
}
2016-08-24 01:11:52 +02:00
getCompileOnSaveAffectedFileList ( scriptInfo : ScriptInfo ) : string [ ] {
if ( ! this . languageServiceEnabled ) {
return [ ] ;
}
2018-08-02 01:08:25 +02:00
updateProjectIfDirty ( this ) ;
2021-03-23 21:43:43 +01:00
this . builderState = BuilderState . create ( this . program ! , this . projectService . toCanonicalFileName , this . builderState , /*disableUseFileVersionAsSignature*/ true ) ;
2020-10-02 19:56:33 +02:00
return mapDefined (
BuilderState . getFilesAffectedBy (
this . builderState ,
this . program ! ,
scriptInfo . path ,
this . cancellationToken ,
maybeBind ( this . projectService . host , this . projectService . host . createHash )
) ,
sourceFile = > this . shouldEmitFile ( this . projectService . getScriptInfoForPath ( sourceFile . path ) ) ? sourceFile.fileName : undefined
) ;
2017-07-25 01:57:49 +02:00
}
/ * *
* Returns true if emit was conducted
* /
2020-03-19 22:03:21 +01:00
emitFile ( scriptInfo : ScriptInfo , writeFile : ( path : string , data : string , writeByteOrderMark? : boolean ) = > void ) : EmitResult {
2017-10-04 00:06:56 +02:00
if ( ! this . languageServiceEnabled || ! this . shouldEmitFile ( scriptInfo ) ) {
2020-03-19 22:03:21 +01:00
return { emitSkipped : true , diagnostics : emptyArray } ;
2017-10-04 00:06:56 +02:00
}
2020-03-19 22:03:21 +01:00
const { emitSkipped , diagnostics , outputFiles } = this . getLanguageService ( ) . getEmitOutput ( scriptInfo . fileName ) ;
2017-07-25 01:57:49 +02:00
if ( ! emitSkipped ) {
for ( const outputFile of outputFiles ) {
2017-10-05 03:45:59 +02:00
const outputFileAbsoluteFileName = getNormalizedAbsolutePath ( outputFile . name , this . currentDirectory ) ;
2017-07-25 01:57:49 +02:00
writeFile ( outputFileAbsoluteFileName , outputFile . text , outputFile . writeByteOrderMark ) ;
}
2020-03-20 18:16:41 +01:00
// Update the signature
if ( this . builderState && getEmitDeclarations ( this . compilerOptions ) ) {
const dtsFiles = outputFiles . filter ( f = > fileExtensionIs ( f . name , Extension . Dts ) ) ;
if ( dtsFiles . length === 1 ) {
const sourceFile = this . program ! . getSourceFile ( scriptInfo . fileName ) ! ;
2020-10-02 19:56:33 +02:00
const signature = this . projectService . host . createHash ?
this . projectService . host . createHash ( dtsFiles [ 0 ] . text ) :
generateDjb2Hash ( dtsFiles [ 0 ] . text ) ;
BuilderState . updateSignatureOfFile ( this . builderState , signature , sourceFile . resolvedPath ) ;
2020-03-20 18:16:41 +01:00
}
}
2017-07-25 01:57:49 +02:00
}
2020-03-19 22:03:21 +01:00
return { emitSkipped , diagnostics } ;
2016-08-24 01:11:52 +02:00
}
2016-06-22 02:31:54 +02:00
enableLanguageService() {
2020-08-05 21:19:15 +02:00
if ( this . languageServiceEnabled || this . projectService . serverMode === LanguageServiceMode . Syntactic ) {
2016-11-12 02:35:11 +01:00
return ;
}
2016-06-22 02:31:54 +02:00
this . languageServiceEnabled = true ;
2018-02-07 02:11:59 +01:00
this . lastFileExceededProgramSize = undefined ;
2016-11-12 02:35:11 +01:00
this . projectService . onUpdateLanguageServiceStateForProject ( this , /*languageServiceEnabled*/ true ) ;
2016-06-22 02:31:54 +02:00
}
2018-02-07 02:11:59 +01:00
disableLanguageService ( lastFileExceededProgramSize? : string ) {
2016-11-12 02:35:11 +01:00
if ( ! this . languageServiceEnabled ) {
return ;
}
2020-08-05 21:19:15 +02:00
Debug . assert ( this . projectService . serverMode !== LanguageServiceMode . Syntactic ) ;
2016-11-12 02:35:11 +01:00
this . languageService . cleanupSemanticCache ( ) ;
2016-06-22 02:31:54 +02:00
this . languageServiceEnabled = false ;
2018-02-07 02:11:59 +01:00
this . lastFileExceededProgramSize = lastFileExceededProgramSize ;
2017-12-07 23:04:40 +01:00
this . builderState = undefined ;
2020-06-23 01:34:27 +02:00
if ( this . autoImportProviderHost ) {
this . autoImportProviderHost . close ( ) ;
}
this . autoImportProviderHost = undefined ;
2017-08-30 20:49:58 +02:00
this . resolutionCache . closeTypeRootsWatch ( ) ;
2019-06-25 21:15:04 +02:00
this . clearGeneratedFileWatch ( ) ;
2016-11-12 02:35:11 +01:00
this . projectService . onUpdateLanguageServiceStateForProject ( this , /*languageServiceEnabled*/ false ) ;
2016-06-22 02:31:54 +02:00
}
2016-11-18 00:12:32 +01:00
getProjectName() {
return this . projectName ;
}
2016-06-22 02:31:54 +02:00
2020-10-29 21:30:42 +01:00
protected removeLocalTypingsFromTypeAcquisition ( newTypeAcquisition : TypeAcquisition ) : TypeAcquisition {
2017-12-06 22:17:21 +01:00
if ( ! newTypeAcquisition || ! newTypeAcquisition . include ) {
// Nothing to filter out, so just return as-is
2020-10-29 21:30:42 +01:00
return newTypeAcquisition ;
2017-12-06 22:17:21 +01:00
}
return { . . . newTypeAcquisition , include : this.removeExistingTypings ( newTypeAcquisition . include ) } ;
}
2017-04-22 00:20:55 +02:00
getExternalFiles ( ) : SortedReadonlyArray < string > {
2018-10-30 16:41:31 +01:00
return sort ( flatMap ( this . plugins , plugin = > {
2018-10-29 21:56:51 +01:00
if ( typeof plugin . module .getExternalFiles !== "function" ) return ;
2017-12-22 02:04:27 +01:00
try {
2018-10-29 21:56:51 +01:00
return plugin . module .getExternalFiles ( this ) ;
2017-12-22 02:04:27 +01:00
}
catch ( e ) {
this . projectService . logger . info ( ` A plugin threw an exception in getExternalFiles: ${ e } ` ) ;
if ( e . stack ) {
this . projectService . logger . info ( e . stack ) ;
}
}
} ) ) ;
2017-02-14 22:35:16 +01:00
}
2016-08-24 01:11:52 +02:00
getSourceFile ( path : Path ) {
if ( ! this . program ) {
return undefined ;
}
return this . program . getSourceFileByPath ( path ) ;
}
2018-06-07 23:30:19 +02:00
/* @internal */
getSourceFileOrConfigFile ( path : Path ) : SourceFile | undefined {
2018-11-17 01:02:23 +01:00
const options = this . program ! . getCompilerOptions ( ) ;
2018-06-07 23:30:19 +02:00
return path === options . configFilePath ? options.configFile : this.getSourceFile ( path ) ;
}
2016-06-22 02:31:54 +02:00
close() {
2016-06-28 00:29:04 +02:00
if ( this . program ) {
2017-10-10 05:12:53 +02:00
// if we have a program - release all files that are enlisted in program but arent root
// The releasing of the roots happens later
// The project could have pending update remaining and hence the info could be in the files but not in program graph
2016-06-28 00:29:04 +02:00
for ( const f of this . program . getSourceFiles ( ) ) {
2017-10-10 05:12:53 +02:00
this . detachScriptInfoIfNotRoot ( f . fileName ) ;
2016-06-28 00:29:04 +02:00
}
2020-10-19 21:59:59 +02:00
this . program . forEachResolvedProjectReference ( ref = >
this . detachScriptInfoFromProject ( ref . sourceFile . fileName ) ) ;
2016-06-22 02:31:54 +02:00
}
2020-06-23 01:34:27 +02:00
2017-10-10 05:12:53 +02:00
// Release external files
forEach ( this . externalFiles , externalFile = > this . detachScriptInfoIfNotRoot ( externalFile ) ) ;
// Always remove root files from the project
for ( const root of this . rootFiles ) {
root . detachFromProject ( this ) ;
2016-06-28 00:29:04 +02:00
}
2018-02-12 23:55:58 +01:00
this . projectService . pendingEnsureProjectForOpenFiles = true ;
2017-10-10 00:24:00 +02:00
2018-05-22 23:46:57 +02:00
this . rootFiles = undefined ! ;
this . rootFilesMap = undefined ! ;
2021-03-25 22:46:16 +01:00
this . externalFiles = undefined ;
this . program = undefined ;
this . builderState = undefined ;
2017-08-05 08:09:11 +02:00
this . resolutionCache . clear ( ) ;
2018-05-22 23:46:57 +02:00
this . resolutionCache = undefined ! ;
this . cachedUnresolvedImportsPerFile = undefined ! ;
2021-06-10 19:26:32 +02:00
this . module SpecifierCache = undefined ! ;
2018-05-22 23:46:57 +02:00
this . directoryStructureHost = undefined ! ;
2021-07-06 19:04:59 +02:00
this . exportMapCache = undefined ;
2020-11-04 22:30:06 +01:00
this . projectErrors = undefined ;
2016-06-28 00:29:04 +02:00
2017-06-21 01:30:33 +02:00
// Clean up file watchers waiting for missing files
2017-08-07 23:47:32 +02:00
if ( this . missingFilesMap ) {
2017-08-23 22:34:41 +02:00
clearMap ( this . missingFilesMap , closeFileWatcher ) ;
2021-03-25 22:46:16 +01:00
this . missingFilesMap = undefined ;
2017-08-07 23:47:32 +02:00
}
2019-06-25 21:15:04 +02:00
this . clearGeneratedFileWatch ( ) ;
2020-06-09 21:00:37 +02:00
this . clearInvalidateResolutionOfFailedLookupTimer ( ) ;
2020-06-23 01:34:27 +02:00
if ( this . autoImportProviderHost ) {
this . autoImportProviderHost . close ( ) ;
}
this . autoImportProviderHost = undefined ;
2017-06-21 01:30:33 +02:00
2016-06-28 00:29:04 +02:00
// signal language service to release source files acquired from document registry
2016-06-22 02:31:54 +02:00
this . languageService . dispose ( ) ;
2018-05-22 23:46:57 +02:00
this . languageService = undefined ! ;
2016-06-22 02:31:54 +02:00
}
2017-10-10 05:12:53 +02:00
private detachScriptInfoIfNotRoot ( uncheckedFilename : string ) {
2017-10-10 00:24:00 +02:00
const info = this . projectService . getScriptInfo ( uncheckedFilename ) ;
// We might not find the script info in case its not associated with the project any more
// and project graph was not updated (eg delayed update graph in case of files changed/deleted on the disk)
2017-10-10 05:12:53 +02:00
if ( info && ! this . isRoot ( info ) ) {
2017-10-10 00:24:00 +02:00
info . detachFromProject ( this ) ;
}
}
2017-08-15 23:50:52 +02:00
isClosed() {
2017-08-24 20:50:27 +02:00
return this . rootFiles === undefined ;
2016-06-22 02:31:54 +02:00
}
2016-06-27 23:25:43 +02:00
hasRoots() {
2016-06-29 02:45:29 +02:00
return this . rootFiles && this . rootFiles . length > 0 ;
2016-06-27 23:25:43 +02:00
}
2018-04-20 22:38:15 +02:00
/*@internal*/
isOrphan() {
return false ;
}
2016-06-22 02:31:54 +02:00
getRootFiles() {
2016-06-29 02:45:29 +02:00
return this . rootFiles && this . rootFiles . map ( info = > info . fileName ) ;
2016-06-22 02:31:54 +02:00
}
2017-08-18 20:55:47 +02:00
/*@internal*/
2017-07-06 03:03:11 +02:00
getRootFilesMap() {
return this . rootFilesMap ;
}
2016-07-06 00:51:39 +02:00
getRootScriptInfos() {
return this . rootFiles ;
}
2018-05-22 23:46:57 +02:00
getScriptInfos ( ) : ScriptInfo [ ] {
2016-12-13 22:21:32 +01:00
if ( ! this . languageServiceEnabled ) {
// if language service is not enabled - return just root files
return this . rootFiles ;
}
2018-11-17 01:02:23 +01:00
return map ( this . program ! . getSourceFiles ( ) , sourceFile = > {
2018-09-18 03:24:12 +02:00
const scriptInfo = this . projectService . getScriptInfoForPath ( sourceFile . resolvedPath ) ;
2018-06-01 22:12:57 +02:00
Debug . assert ( ! ! scriptInfo , "getScriptInfo" , ( ) = > ` scriptInfo for a file ' ${ sourceFile . fileName } ' Path: ' ${ sourceFile . path } ' / ' ${ sourceFile . resolvedPath } ' is missing. ` ) ;
2020-02-25 03:20:58 +01:00
return scriptInfo ;
2016-09-30 22:19:54 +02:00
} ) ;
2016-08-24 01:11:52 +02:00
}
2019-08-08 20:30:18 +02:00
getExcludedFiles ( ) : readonly NormalizedPath [ ] {
2017-07-28 01:07:50 +02:00
return emptyArray ;
}
2017-05-15 22:13:00 +02:00
getFileNames ( excludeFilesFromExternalLibraries? : boolean , excludeConfigFiles? : boolean ) {
2016-06-23 01:51:09 +02:00
if ( ! this . program ) {
return [ ] ;
}
2016-06-22 02:31:54 +02:00
if ( ! this . languageServiceEnabled ) {
// if language service is disabled assume that all files in program are root files + default library
let rootFiles = this . getRootFiles ( ) ;
if ( this . compilerOptions ) {
const defaultLibrary = getDefaultLibFilePath ( this . compilerOptions ) ;
if ( defaultLibrary ) {
( rootFiles || ( rootFiles = [ ] ) ) . push ( asNormalizedPath ( defaultLibrary ) ) ;
}
}
return rootFiles ;
}
2016-11-08 06:13:11 +01:00
const result : NormalizedPath [ ] = [ ] ;
for ( const f of this . program . getSourceFiles ( ) ) {
if ( excludeFilesFromExternalLibraries && this . program . isSourceFileFromExternalLibrary ( f ) ) {
continue ;
}
result . push ( asNormalizedPath ( f . fileName ) ) ;
}
2017-05-15 22:13:00 +02:00
if ( ! excludeConfigFiles ) {
const configFile = this . program . getCompilerOptions ( ) . configFile ;
if ( configFile ) {
result . push ( asNormalizedPath ( configFile . fileName ) ) ;
if ( configFile . extendedSourceFiles ) {
for ( const f of configFile . extendedSourceFiles ) {
result . push ( asNormalizedPath ( f ) ) ;
}
}
}
}
2016-11-08 06:13:11 +01:00
return result ;
2016-06-22 02:31:54 +02:00
}
2020-01-25 00:15:05 +01:00
/* @internal */
getFileNamesWithRedirectInfo ( includeProjectReferenceRedirectInfo : boolean ) {
2020-01-25 01:23:54 +01:00
return this . getFileNames ( ) . map ( ( fileName ) : protocol . FileWithProjectReferenceRedirectInfo = > ( {
2020-01-25 00:15:05 +01:00
fileName ,
2020-01-25 01:23:54 +01:00
isSourceOfProjectReferenceRedirect : includeProjectReferenceRedirectInfo && this . isSourceOfProjectReferenceRedirect ( fileName )
2020-01-25 00:15:05 +01:00
} ) ) ;
}
2017-05-24 00:38:03 +02:00
hasConfigFile ( configFilePath : NormalizedPath ) {
if ( this . program && this . languageServiceEnabled ) {
const configFile = this . program . getCompilerOptions ( ) . configFile ;
if ( configFile ) {
if ( configFilePath === asNormalizedPath ( configFile . fileName ) ) {
return true ;
}
if ( configFile . extendedSourceFiles ) {
for ( const f of configFile . extendedSourceFiles ) {
if ( configFilePath === asNormalizedPath ( f ) ) {
return true ;
}
}
}
}
}
return false ;
}
2016-06-22 02:31:54 +02:00
containsScriptInfo ( info : ScriptInfo ) : boolean {
2018-12-06 21:18:10 +01:00
if ( this . isRoot ( info ) ) return true ;
if ( ! this . program ) return false ;
const file = this . program . getSourceFileByPath ( info . path ) ;
return ! ! file && file . resolvedPath === info . path ;
2016-06-22 02:31:54 +02:00
}
2018-05-22 23:46:57 +02:00
containsFile ( filename : NormalizedPath , requireOpen? : boolean ) : boolean {
2018-07-27 21:21:30 +02:00
const info = this . projectService . getScriptInfoForNormalizedPath ( filename ) ;
2016-12-09 01:17:42 +01:00
if ( info && ( info . isScriptOpen ( ) || ! requireOpen ) ) {
2016-06-24 23:30:45 +02:00
return this . containsScriptInfo ( info ) ;
2016-06-22 02:31:54 +02:00
}
2018-05-22 23:46:57 +02:00
return false ;
2016-06-22 02:31:54 +02:00
}
isRoot ( info : ScriptInfo ) {
2019-12-11 03:25:10 +01:00
return this . rootFilesMap && this . rootFilesMap . get ( info . path ) ? . info === info ;
2016-06-22 02:31:54 +02:00
}
// add a root file to project
2019-12-11 03:25:10 +01:00
addRoot ( info : ScriptInfo , fileName? : NormalizedPath ) {
2017-08-07 23:47:32 +02:00
Debug . assert ( ! this . isRoot ( info ) ) ;
this . rootFiles . push ( info ) ;
2019-12-11 03:25:10 +01:00
this . rootFilesMap . set ( info . path , { fileName : fileName || info . fileName , info } ) ;
2017-08-07 23:47:32 +02:00
info . attachToProject ( this ) ;
2016-06-24 23:55:12 +02:00
2017-08-07 23:47:32 +02:00
this . markAsDirty ( ) ;
2016-06-22 02:31:54 +02:00
}
2017-08-18 20:55:47 +02:00
// add a root file that doesnt exist on host
2017-07-06 03:03:11 +02:00
addMissingFileRoot ( fileName : NormalizedPath ) {
2017-07-15 07:35:07 +02:00
const path = this . projectService . toPath ( fileName ) ;
2019-12-11 03:25:10 +01:00
this . rootFilesMap . set ( path , { fileName } ) ;
2017-07-06 03:03:11 +02:00
this . markAsDirty ( ) ;
}
2017-09-08 02:27:00 +02:00
removeFile ( info : ScriptInfo , fileExists : boolean , detachFromProject : boolean ) {
2016-12-30 02:00:04 +01:00
if ( this . isRoot ( info ) ) {
this . removeRoot ( info ) ;
}
2017-09-08 02:27:00 +02:00
if ( fileExists ) {
// If file is present, just remove the resolutions for the file
this . resolutionCache . removeResolutionsOfFile ( info . path ) ;
}
else {
this . resolutionCache . invalidateResolutionOfFile ( info . path ) ;
}
2018-04-12 23:55:22 +02:00
this . cachedUnresolvedImportsPerFile . delete ( info . path ) ;
2016-06-24 23:30:45 +02:00
2016-06-23 01:51:09 +02:00
if ( detachFromProject ) {
info . detachFromProject ( this ) ;
}
2016-06-24 23:30:45 +02:00
2016-06-22 02:31:54 +02:00
this . markAsDirty ( ) ;
}
2016-12-09 02:56:08 +01:00
registerFileUpdate ( fileName : string ) {
2020-06-26 19:12:47 +02:00
( this . updatedFileNames || ( this . updatedFileNames = new Set < string > ( ) ) ) . add ( fileName ) ;
2016-12-09 02:56:08 +01:00
}
2019-09-27 22:38:31 +02:00
/*@internal*/
markFileAsDirty ( changedFile : Path ) {
this . markAsDirty ( ) ;
2021-07-06 19:04:59 +02:00
if ( this . exportMapCache && ! this . exportMapCache . isEmpty ( ) ) {
2021-03-26 22:47:07 +01:00
( this . changedFilesForExportMapCache || = new Set ( ) ) . add ( changedFile ) ;
2019-09-27 22:38:31 +02:00
}
}
2016-06-22 02:31:54 +02:00
markAsDirty() {
2018-02-12 23:55:58 +01:00
if ( ! this . dirty ) {
this . projectStateVersion ++ ;
this . dirty = true ;
}
2016-06-22 02:31:54 +02:00
}
2020-06-23 01:34:27 +02:00
/*@internal*/
2021-03-26 22:47:07 +01:00
onAutoImportProviderSettingsChanged() {
2020-06-23 01:34:27 +02:00
if ( this . autoImportProviderHost === false ) {
this . autoImportProviderHost = undefined ;
}
2021-03-26 22:47:07 +01:00
else {
this . autoImportProviderHost ? . markAsDirty ( ) ;
}
}
/*@internal*/
onPackageJsonChange ( packageJsonPath : Path ) {
if ( this . packageJsonsForAutoImport ? . has ( packageJsonPath ) ) {
this . module SpecifierCache.clear ( ) ;
if ( this . autoImportProviderHost ) {
this . autoImportProviderHost . markAsDirty ( ) ;
}
}
2020-06-23 01:34:27 +02:00
}
2018-04-17 23:17:15 +02:00
/* @internal */
2021-03-26 22:47:07 +01:00
onFileAddedOrRemoved ( isSymlink : boolean | undefined ) {
2018-04-18 20:05:56 +02:00
this . hasAddedorRemovedFiles = true ;
2021-03-26 22:47:07 +01:00
if ( isSymlink ) {
this . hasAddedOrRemovedSymlinks = true ;
}
2018-04-17 23:17:15 +02:00
}
2021-10-30 00:47:48 +02:00
/* @internal */
onDiscoveredSymlink() {
this . hasAddedOrRemovedSymlinks = true ;
}
2016-06-30 22:23:55 +02:00
/ * *
* Updates set of files that contribute to this project
* @returns : true if set of files in the project stays the same and false - otherwise .
* /
2016-06-29 02:45:29 +02:00
updateGraph ( ) : boolean {
2019-08-01 21:46:33 +02:00
perfLogger . logStartUpdateGraph ( ) ;
2017-08-04 10:14:54 +02:00
this . resolutionCache . startRecordingFilesWithChangedResolutions ( ) ;
2016-10-26 00:24:21 +02:00
2018-04-17 23:17:15 +02:00
const hasNewProgram = this . updateGraphWorker ( ) ;
2018-04-18 20:05:56 +02:00
const hasAddedorRemovedFiles = this . hasAddedorRemovedFiles ;
this . hasAddedorRemovedFiles = false ;
2021-03-26 22:47:07 +01:00
this . hasAddedOrRemovedSymlinks = false ;
2016-10-26 00:24:21 +02:00
2019-08-08 20:30:18 +02:00
const changedFiles : readonly Path [ ] = this . resolutionCache . finishRecordingFilesWithChangedResolutions ( ) || emptyArray ;
2016-10-26 00:24:21 +02:00
for ( const file of changedFiles ) {
// delete cached information for changed files
2018-04-12 23:55:22 +02:00
this . cachedUnresolvedImportsPerFile . delete ( file ) ;
2016-10-26 00:24:21 +02:00
}
2016-12-13 22:21:32 +01:00
// update builder only if language service is enabled
// otherwise tell it to drop its internal state
2020-08-03 03:18:26 +02:00
if ( this . languageServiceEnabled && this . projectService . serverMode === LanguageServiceMode . Semantic ) {
2017-09-07 19:58:50 +02:00
// 1. no changes in structure, no changes in unresolved imports - do nothing
// 2. no changes in structure, unresolved imports were changed - collect unresolved imports for all files
// (can reuse cached imports for files that were not changed)
// 3. new files were added/removed, but compilation settings stays the same - collect unresolved imports for all new/modified files
// (can reuse cached imports for files that were not changed)
// 4. compilation settings were changed in the way that might affect module resolution - drop all caches and collect all data from the scratch
2018-04-17 23:17:15 +02:00
if ( hasNewProgram || changedFiles . length ) {
2018-11-17 01:02:23 +01:00
this . lastCachedUnresolvedImportsList = getUnresolvedImports ( this . program ! , this . cachedUnresolvedImportsPerFile ) ;
2017-09-07 19:58:50 +02:00
}
2018-04-18 20:05:56 +02:00
this . projectService . typingsCache . enqueueInstallTypingsForProject ( this , this . lastCachedUnresolvedImportsList , hasAddedorRemovedFiles ) ;
2016-12-13 22:21:32 +01:00
}
else {
2017-09-07 19:58:50 +02:00
this . lastCachedUnresolvedImportsList = undefined ;
2016-12-13 22:21:32 +01:00
}
2021-07-20 20:16:58 +02:00
const isFirstProgramLoad = this . projectProgramVersion === 0 && hasNewProgram ;
2018-04-17 23:17:15 +02:00
if ( hasNewProgram ) {
this . projectProgramVersion ++ ;
2016-08-12 20:04:43 +02:00
}
2020-06-23 01:34:27 +02:00
if ( hasAddedorRemovedFiles ) {
if ( ! this . autoImportProviderHost ) this . autoImportProviderHost = undefined ;
this . autoImportProviderHost ? . markAsDirty ( ) ;
}
2021-07-20 20:16:58 +02:00
if ( isFirstProgramLoad ) {
2020-06-23 01:34:27 +02:00
// Preload auto import provider so it's not created during completions request
this . getPackageJsonAutoImportProvider ( ) ;
}
2019-08-03 01:46:19 +02:00
perfLogger . logStopUpdateGraph ( ) ;
2018-04-17 23:17:15 +02:00
return ! hasNewProgram ;
2016-08-12 20:04:43 +02:00
}
2018-04-13 01:47:40 +02:00
/*@internal*/
updateTypingFiles ( typingFiles : SortedReadonlyArray < string > ) {
2021-01-22 20:16:07 +01:00
if ( enumerateInsertsAndDeletes < string , string > ( typingFiles , this . typingFiles , getStringComparer ( ! this . useCaseSensitiveFileNames ( ) ) ,
2018-04-19 19:59:23 +02:00
/*inserted*/ noop ,
removed = > this . detachScriptInfoFromProject ( removed )
2021-01-22 20:16:07 +01:00
) ) {
// If typing files changed, then only schedule project update
this . typingFiles = typingFiles ;
// Invalidate files with unresolved imports
this . resolutionCache . setFilesWithInvalidatedNonRelativeUnresolvedImports ( this . cachedUnresolvedImportsPerFile ) ;
this . projectService . delayUpdateProjectGraphAndEnsureProjectStructureForOpenFiles ( this ) ;
}
2018-04-13 01:47:40 +02:00
}
2018-01-11 20:52:46 +01:00
/* @internal */
2018-11-17 01:02:23 +01:00
getCurrentProgram ( ) : Program | undefined {
2018-01-11 20:52:46 +01:00
return this . program ;
}
2017-12-06 22:17:21 +01:00
protected removeExistingTypings ( include : string [ ] ) : string [ ] {
2018-03-01 23:20:18 +01:00
const existing = getAutomaticTypeDirectiveNames ( this . getCompilerOptions ( ) , this . directoryStructureHost ) ;
2017-12-06 22:17:21 +01:00
return include . filter ( i = > existing . indexOf ( i ) < 0 ) ;
}
2016-08-12 20:04:43 +02:00
private updateGraphWorker() {
2016-06-23 01:51:09 +02:00
const oldProgram = this . program ;
2017-10-10 00:24:00 +02:00
Debug . assert ( ! this . isClosed ( ) , "Called update graph worker of closed project" ) ;
2017-09-06 23:43:34 +02:00
this . writeLog ( ` Starting updateGraphWorker: Project: ${ this . getProjectName ( ) } ` ) ;
const start = timestamp ( ) ;
2017-10-09 23:25:48 +02:00
this . hasInvalidatedResolution = this . resolutionCache . createHasInvalidatedResolution ( ) ;
2017-08-29 02:09:07 +02:00
this . resolutionCache . startCachingPerDirectoryResolution ( ) ;
2021-03-25 22:46:16 +01:00
this . program = this . languageService . getProgram ( ) ; // TODO: GH#18217
2018-02-12 23:55:58 +01:00
this . dirty = false ;
2017-08-29 02:09:07 +02:00
this . resolutionCache . finishCachingPerDirectoryResolution ( ) ;
2016-06-23 01:51:09 +02:00
2018-04-03 22:40:48 +02:00
Debug . assert ( oldProgram === undefined || this . program !== undefined ) ;
2016-06-23 01:51:09 +02:00
// bump up the version if
// - oldProgram is not set - this is a first time updateGraph is called
// - newProgram is different from the old program and structure of the old program was not reused.
2021-03-25 22:46:16 +01:00
let hasNewProgram = false ;
2021-03-26 21:23:03 +01:00
if ( this . program && ( ! oldProgram || ( this . program !== oldProgram && this . program . structureIsReused !== StructureIsReused . Completely ) ) ) {
2021-03-25 22:46:16 +01:00
hasNewProgram = true ;
2016-06-24 23:30:45 +02:00
if ( oldProgram ) {
for ( const f of oldProgram . getSourceFiles ( ) ) {
2018-10-02 23:10:19 +02:00
const newFile = this . program . getSourceFileByPath ( f . resolvedPath ) ;
if ( ! newFile || ( f . resolvedPath === f . path && newFile . resolvedPath !== f . path ) ) {
// new program does not contain this file - detach it from the project
2018-10-04 20:41:53 +02:00
// - remove resolutions only if the new program doesnt contain source file by the path (not resolvedPath since path is used for resolution)
this . detachScriptInfoFromProject ( f . fileName , ! ! this . program . getSourceFileByPath ( f . path ) ) ;
2016-06-29 02:45:29 +02:00
}
2016-06-24 23:30:45 +02:00
}
2018-10-02 23:10:19 +02:00
2020-10-19 21:59:59 +02:00
oldProgram . forEachResolvedProjectReference ( resolvedProjectReference = > {
if ( ! this . program ! . getResolvedProjectReferenceByPath ( resolvedProjectReference . sourceFile . path ) ) {
2018-10-02 23:10:19 +02:00
this . detachScriptInfoFromProject ( resolvedProjectReference . sourceFile . fileName ) ;
}
} ) ;
2016-06-24 23:30:45 +02:00
}
2017-04-22 00:20:55 +02:00
2017-07-14 05:08:57 +02:00
// Update the missing file paths watcher
2017-08-13 00:31:19 +02:00
updateMissingFilePathsWatch (
this . program ,
2020-06-26 19:12:47 +02:00
this . missingFilesMap || ( this . missingFilesMap = new Map ( ) ) ,
2017-07-14 05:08:57 +02:00
// Watch the missing files
2017-08-23 22:34:41 +02:00
missingFilePath = > this . addMissingFileWatcher ( missingFilePath )
2017-07-14 05:08:57 +02:00
) ;
2017-08-29 02:09:07 +02:00
2019-06-25 21:15:04 +02:00
if ( this . generatedFilesMap ) {
2020-06-02 20:49:21 +02:00
const outPath = outFile ( this . compilerOptions ) ;
2019-06-25 21:15:04 +02:00
if ( isGeneratedFileWatcher ( this . generatedFilesMap ) ) {
// --out
if ( ! outPath || ! this . isValidGeneratedFileWatcher (
removeFileExtension ( outPath ) + Extension . Dts ,
this . generatedFilesMap ,
) ) {
this . clearGeneratedFileWatch ( ) ;
}
}
else {
// MultiFile
if ( outPath ) {
this . clearGeneratedFileWatch ( ) ;
}
else {
this . generatedFilesMap . forEach ( ( watcher , source ) = > {
2020-06-26 19:12:47 +02:00
const sourceFile = this . program ! . getSourceFileByPath ( source ) ;
2019-06-25 21:15:04 +02:00
if ( ! sourceFile ||
sourceFile . resolvedPath !== source ||
! this . isValidGeneratedFileWatcher (
getDeclarationEmitOutputFilePathWorker ( sourceFile . fileName , this . compilerOptions , this . currentDirectory , this . program ! . getCommonSourceDirectory ( ) , this . getCanonicalFileName ) ,
watcher
) ) {
closeFileWatcherOf ( watcher ) ;
2020-07-02 02:00:26 +02:00
( this . generatedFilesMap as ESMap < string , GeneratedFileWatcher > ) . delete ( source ) ;
2019-06-25 21:15:04 +02:00
}
} ) ;
}
}
}
2017-08-29 02:09:07 +02:00
// Watch the type locations that would be added to program as part of automatic type resolutions
2020-08-03 03:18:26 +02:00
if ( this . languageServiceEnabled && this . projectService . serverMode === LanguageServiceMode . Semantic ) {
2017-08-30 20:49:58 +02:00
this . resolutionCache . updateTypeRootsWatch ( ) ;
}
2017-06-21 00:54:43 +02:00
}
2021-07-06 19:04:59 +02:00
if ( this . exportMapCache && ! this . exportMapCache . isEmpty ( ) ) {
this . exportMapCache . releaseSymbols ( ) ;
2021-03-25 22:46:16 +01:00
if ( this . hasAddedorRemovedFiles || oldProgram && ! this . program ! . structureIsReused ) {
2021-03-26 22:47:07 +01:00
this . exportMapCache . clear ( ) ;
2019-09-27 22:38:31 +02:00
}
2021-03-26 22:47:07 +01:00
else if ( this . changedFilesForExportMapCache && oldProgram && this . program ) {
forEachKey ( this . changedFilesForExportMapCache , fileName = > {
const oldSourceFile = oldProgram . getSourceFileByPath ( fileName ) ;
const sourceFile = this . program ! . getSourceFileByPath ( fileName ) ;
if ( ! oldSourceFile || ! sourceFile ) {
2021-07-06 19:04:59 +02:00
this . exportMapCache ! . clear ( ) ;
2019-09-27 22:38:31 +02:00
return true ;
}
2021-07-06 19:04:59 +02:00
return this . exportMapCache ! . onFileChanged ( oldSourceFile , sourceFile , ! ! this . getTypeAcquisition ( ) . enable ) ;
2019-09-27 22:38:31 +02:00
} ) ;
}
}
2021-03-26 22:47:07 +01:00
if ( this . changedFilesForExportMapCache ) {
this . changedFilesForExportMapCache . clear ( ) ;
2019-09-27 22:38:31 +02:00
}
2021-03-26 22:47:07 +01:00
if ( this . hasAddedOrRemovedSymlinks || this . program && ! this . program . structureIsReused && this . getCompilerOptions ( ) . preserveSymlinks ) {
// With --preserveSymlinks, we may not determine that a file is a symlink, so we never set `hasAddedOrRemovedSymlinks`
2019-09-27 22:38:31 +02:00
this . symlinks = undefined ;
2021-03-26 22:47:07 +01:00
this . module SpecifierCache.clear ( ) ;
2019-09-27 22:38:31 +02:00
}
2017-04-22 00:20:55 +02:00
const oldExternalFiles = this . externalFiles || emptyArray as SortedReadonlyArray < string > ;
this . externalFiles = this . getExternalFiles ( ) ;
2018-10-30 16:41:31 +01:00
enumerateInsertsAndDeletes < string , string > ( this . externalFiles , oldExternalFiles , getStringComparer ( ! this . useCaseSensitiveFileNames ( ) ) ,
2017-04-22 00:20:55 +02:00
// Ensure a ScriptInfo is created for new external files. This is performed indirectly
2019-06-21 04:11:46 +02:00
// by the host for files in the program when the program is retrieved above but
2017-04-22 00:20:55 +02:00
// the program doesn't contain external files so this must be done explicitly.
inserted = > {
2020-12-01 01:37:40 +01:00
const scriptInfo = this . projectService . getOrCreateScriptInfoNotOpenedByClient ( inserted , this . currentDirectory , this . directoryStructureHost ) ;
scriptInfo ? . attachToProject ( this ) ;
2017-04-22 00:20:55 +02:00
} ,
2018-01-17 01:35:34 +01:00
removed = > this . detachScriptInfoFromProject ( removed )
2017-09-08 02:27:00 +02:00
) ;
2017-09-06 23:43:34 +02:00
const elapsed = timestamp ( ) - start ;
2020-06-23 01:34:27 +02:00
this . sendPerformanceEvent ( "UpdateGraph" , elapsed ) ;
2021-06-02 20:22:01 +02:00
this . writeLog ( ` Finishing updateGraphWorker: Project: ${ this . getProjectName ( ) } Version: ${ this . getProjectVersion ( ) } structureChanged: ${ hasNewProgram } ${ this . program ? ` structureIsReused:: ${ ( ts as any ) . StructureIsReused [ this . program . structureIsReused ] } ` : "" } Elapsed: ${ elapsed } ms ` ) ;
2019-08-12 22:13:28 +02:00
if ( this . hasAddedorRemovedFiles ) {
2020-03-12 00:47:11 +01:00
this . print ( /*writeProjectFileNames*/ true ) ;
2019-04-03 23:05:49 +02:00
}
2019-08-12 22:13:28 +02:00
else if ( this . program !== oldProgram ) {
2021-06-02 20:22:01 +02:00
this . writeLog ( ` Different program with same set of files ` ) ;
2019-08-12 22:13:28 +02:00
}
2018-04-17 23:17:15 +02:00
return hasNewProgram ;
2016-06-22 02:31:54 +02:00
}
2020-06-23 01:34:27 +02:00
/* @internal */
sendPerformanceEvent ( kind : PerformanceEvent [ "kind" ] , durationMs : number ) {
this . projectService . sendPerformanceEvent ( kind , durationMs ) ;
}
2018-10-02 23:10:19 +02:00
private detachScriptInfoFromProject ( uncheckedFileName : string , noRemoveResolution? : boolean ) {
2017-09-08 02:27:00 +02:00
const scriptInfoToDetach = this . projectService . getScriptInfo ( uncheckedFileName ) ;
if ( scriptInfoToDetach ) {
scriptInfoToDetach . detachFromProject ( this ) ;
2018-10-02 23:10:19 +02:00
if ( ! noRemoveResolution ) {
this . resolutionCache . removeResolutionsOfFile ( scriptInfoToDetach . path ) ;
}
2017-09-08 02:27:00 +02:00
}
}
2017-07-25 01:57:49 +02:00
private addMissingFileWatcher ( missingFilePath : Path ) {
2021-03-26 21:23:03 +01:00
if ( isConfiguredProject ( this ) ) {
// If this file is referenced config file, we are already watching it, no need to watch again
const configFileExistenceInfo = this . projectService . configFileExistenceInfoCache . get ( missingFilePath as string as NormalizedPath ) ;
if ( configFileExistenceInfo ? . config ? . projects . has ( this . canonicalConfigFilePath ) ) return noopFileWatcher ;
}
2017-12-18 22:20:42 +01:00
const fileWatcher = this . projectService . watchFactory . watchFile (
2017-08-23 22:34:41 +02:00
missingFilePath ,
2017-08-14 23:59:51 +02:00
( fileName , eventKind ) = > {
2019-12-11 03:25:10 +01:00
if ( isConfiguredProject ( this ) ) {
2017-12-06 01:34:17 +01:00
this . getCachedDirectoryStructureHost ( ) . addOrDeleteFile ( fileName , missingFilePath , eventKind ) ;
2017-08-14 23:59:51 +02:00
}
2018-11-17 01:02:23 +01:00
if ( eventKind === FileWatcherEventKind . Created && this . missingFilesMap ! . has ( missingFilePath ) ) {
this . missingFilesMap ! . delete ( missingFilePath ) ;
2017-08-23 22:34:41 +02:00
fileWatcher . close ( ) ;
2017-07-25 01:57:49 +02:00
// When a missing file is created, we should update the graph.
2018-02-12 23:55:58 +01:00
this . projectService . delayUpdateProjectGraphAndEnsureProjectStructureForOpenFiles ( this ) ;
2017-07-25 01:57:49 +02:00
}
2017-08-23 22:34:41 +02:00
} ,
2018-01-16 22:15:39 +01:00
PollingInterval . Medium ,
2019-12-11 22:26:44 +01:00
this . projectService . getWatchOptions ( this ) ,
2018-12-15 00:13:19 +01:00
WatchType . MissingFile ,
2017-08-23 22:34:41 +02:00
this
2017-07-25 01:57:49 +02:00
) ;
return fileWatcher ;
}
2017-08-24 20:50:27 +02:00
private isWatchedMissingFile ( path : Path ) {
2018-11-17 01:02:23 +01:00
return ! ! this . missingFilesMap && this . missingFilesMap . has ( path ) ;
2017-06-21 00:54:43 +02:00
}
2019-06-25 21:15:04 +02:00
/* @internal */
addGeneratedFileWatch ( generatedFile : string , sourceFile : string ) {
2020-06-02 20:49:21 +02:00
if ( outFile ( this . compilerOptions ) ) {
2019-06-25 21:15:04 +02:00
// Single watcher
if ( ! this . generatedFilesMap ) {
this . generatedFilesMap = this . createGeneratedFileWatcher ( generatedFile ) ;
}
}
else {
// Map
const path = this . toPath ( sourceFile ) ;
if ( this . generatedFilesMap ) {
if ( isGeneratedFileWatcher ( this . generatedFilesMap ) ) {
2019-06-28 19:17:58 +02:00
Debug . fail ( ` ${ this . projectName } Expected to not have --out watcher for generated file with options: ${ JSON . stringify ( this . compilerOptions ) } ` ) ;
2019-06-25 21:15:04 +02:00
return ;
}
if ( this . generatedFilesMap . has ( path ) ) return ;
}
else {
2020-06-26 01:03:25 +02:00
this . generatedFilesMap = new Map ( ) ;
2019-06-25 21:15:04 +02:00
}
this . generatedFilesMap . set ( path , this . createGeneratedFileWatcher ( generatedFile ) ) ;
}
}
private createGeneratedFileWatcher ( generatedFile : string ) : GeneratedFileWatcher {
return {
generatedFilePath : this.toPath ( generatedFile ) ,
watcher : this.projectService.watchFactory.watchFile (
generatedFile ,
2020-02-26 01:11:21 +01:00
( ) = > {
this . clearSourceMapperCache ( ) ;
this . projectService . delayUpdateProjectGraphAndEnsureProjectStructureForOpenFiles ( this ) ;
} ,
2019-06-25 21:15:04 +02:00
PollingInterval . High ,
2019-12-11 22:26:44 +01:00
this . projectService . getWatchOptions ( this ) ,
2019-06-25 21:15:04 +02:00
WatchType . MissingGeneratedFile ,
this
)
} ;
}
private isValidGeneratedFileWatcher ( generateFile : string , watcher : GeneratedFileWatcher ) {
return this . toPath ( generateFile ) === watcher . generatedFilePath ;
}
private clearGeneratedFileWatch() {
if ( this . generatedFilesMap ) {
if ( isGeneratedFileWatcher ( this . generatedFilesMap ) ) {
closeFileWatcherOf ( this . generatedFilesMap ) ;
}
else {
clearMap ( this . generatedFilesMap , closeFileWatcherOf ) ;
}
this . generatedFilesMap = undefined ;
}
}
2018-05-15 22:55:26 +02:00
getScriptInfoForNormalizedPath ( fileName : NormalizedPath ) : ScriptInfo | undefined {
2017-11-03 23:28:28 +01:00
const scriptInfo = this . projectService . getScriptInfoForPath ( this . toPath ( fileName ) ) ;
2016-08-26 23:37:49 +02:00
if ( scriptInfo && ! scriptInfo . isAttached ( this ) ) {
return Errors . ThrowProjectDoesNotContainDocument ( fileName , this ) ;
}
2016-06-22 02:31:54 +02:00
return scriptInfo ;
}
2016-06-23 01:51:09 +02:00
getScriptInfo ( uncheckedFileName : string ) {
2017-10-12 20:48:25 +02:00
return this . projectService . getScriptInfo ( uncheckedFileName ) ;
2016-06-23 01:51:09 +02:00
}
2017-09-05 23:19:40 +02:00
filesToString ( writeProjectFileNames : boolean ) {
2019-12-11 21:28:18 +01:00
if ( this . isInitialLoadPending ( ) ) return "\tFiles (0) InitialLoadPending\n" ;
if ( ! this . program ) return "\tFiles (0) NoProgram\n" ;
2017-08-31 20:35:55 +02:00
const sourceFiles = this . program . getSourceFiles ( ) ;
let strBuilder = ` \ tFiles ( ${ sourceFiles . length } ) \ n ` ;
2017-09-05 23:19:40 +02:00
if ( writeProjectFileNames ) {
for ( const file of sourceFiles ) {
strBuilder += ` \ t ${ file . fileName } \ n ` ;
}
2020-12-09 01:10:05 +01:00
strBuilder += "\n\n" ;
explainFiles ( this . program , s = > strBuilder += ` \ t ${ s } \ n ` ) ;
2016-06-22 02:31:54 +02:00
}
return strBuilder ;
}
2018-11-08 22:01:22 +01:00
/*@internal*/
2020-03-12 00:47:11 +01:00
print ( writeProjectFileNames : boolean ) {
this . writeLog ( ` Project ' ${ this . projectName } ' ( ${ ProjectKind [ this . projectKind ] } ) ` ) ;
this . writeLog ( this . filesToString ( writeProjectFileNames && this . projectService . logger . hasLevel ( LogLevel . verbose ) ) ) ;
2018-10-30 00:32:58 +01:00
this . writeLog ( "-----------------------------------------------" ) ;
2020-06-23 01:34:27 +02:00
if ( this . autoImportProviderHost ) {
this . autoImportProviderHost . print ( /*writeProjectFileNames*/ false ) ;
}
2018-10-30 00:32:58 +01:00
}
2016-06-22 02:31:54 +02:00
setCompilerOptions ( compilerOptions : CompilerOptions ) {
if ( compilerOptions ) {
compilerOptions . allowNonTsExtensions = true ;
2017-08-04 10:14:54 +02:00
const oldOptions = this . compilerOptions ;
2016-06-22 02:31:54 +02:00
this . compilerOptions = compilerOptions ;
2016-11-30 08:30:14 +01:00
this . setInternalCompilerOptionsForEmittingJsFiles ( ) ;
2017-08-04 10:14:54 +02:00
if ( changesAffectModuleResolution ( oldOptions , compilerOptions ) ) {
2018-04-13 01:47:40 +02:00
// reset cached unresolved imports if changes in compiler options affected module resolution
this . cachedUnresolvedImportsPerFile . clear ( ) ;
this . lastCachedUnresolvedImportsList = undefined ;
2017-08-04 10:14:54 +02:00
this . resolutionCache . clear ( ) ;
2021-06-10 19:26:32 +02:00
this . module SpecifierCache.clear ( ) ;
2017-08-04 10:14:54 +02:00
}
2016-06-22 02:31:54 +02:00
this . markAsDirty ( ) ;
}
}
2019-12-11 22:26:44 +01:00
/*@internal*/
setWatchOptions ( watchOptions : WatchOptions | undefined ) {
this . watchOptions = watchOptions ;
}
/*@internal*/
getWatchOptions ( ) : WatchOptions | undefined {
return this . watchOptions ;
}
2020-10-19 18:53:58 +02:00
setTypeAcquisition ( newTypeAcquisition : TypeAcquisition | undefined ) : void {
2020-10-29 21:30:42 +01:00
if ( newTypeAcquisition ) {
this . typeAcquisition = this . removeLocalTypingsFromTypeAcquisition ( newTypeAcquisition ) ;
}
2020-10-19 18:53:58 +02:00
}
getTypeAcquisition() {
return this . typeAcquisition || { } ;
}
2017-01-05 00:56:16 +01:00
/* @internal */
2020-01-23 21:32:36 +01:00
getChangesSinceVersion ( lastKnownVersion? : number , includeProjectReferenceRedirectInfo? : boolean ) : ProjectFilesWithTSDiagnostics {
2020-01-25 00:15:05 +01:00
const includeProjectReferenceRedirectInfoIfRequested =
includeProjectReferenceRedirectInfo
2020-07-02 02:00:26 +02:00
? ( files : ESMap < string , boolean > ) = > arrayFrom ( files . entries ( ) , ( [ fileName , isSourceOfProjectReferenceRedirect ] ) : protocol . FileWithProjectReferenceRedirectInfo = > ( {
2020-01-23 21:32:36 +01:00
fileName ,
2020-01-25 01:23:54 +01:00
isSourceOfProjectReferenceRedirect
2020-01-25 00:15:05 +01:00
} ) )
2020-07-02 02:00:26 +02:00
: ( files : ESMap < string , boolean > ) = > arrayFrom ( files . keys ( ) ) ;
2020-01-23 21:32:36 +01:00
2018-07-25 23:01:48 +02:00
// Update the graph only if initial configured project load is not pending
if ( ! this . isInitialLoadPending ( ) ) {
updateProjectIfDirty ( this ) ;
}
2016-07-23 01:01:54 +02:00
2018-02-06 04:25:07 +01:00
const info : protocol.ProjectVersionInfo = {
2016-06-22 02:31:54 +02:00
projectName : this.getProjectName ( ) ,
2018-04-17 23:17:15 +02:00
version : this.projectProgramVersion ,
2019-12-11 03:25:10 +01:00
isInferred : isInferredProject ( this ) ,
2017-08-24 20:50:27 +02:00
options : this.getCompilationSettings ( ) ,
2018-02-07 02:11:59 +01:00
languageServiceDisabled : ! this . languageServiceEnabled ,
lastFileExceededProgramSize : this.lastFileExceededProgramSize
2016-06-22 02:31:54 +02:00
} ;
2016-12-09 23:44:08 +01:00
const updatedFileNames = this . updatedFileNames ;
this . updatedFileNames = undefined ;
2016-06-23 01:51:09 +02:00
// check if requested version is the same that we have reported last time
2016-06-22 02:31:54 +02:00
if ( this . lastReportedFileNames && lastKnownVersion === this . lastReportedVersion ) {
2016-12-09 23:44:08 +01:00
// if current structure version is the same - return info without any changes
2018-04-17 23:17:15 +02:00
if ( this . projectProgramVersion === this . lastReportedVersion && ! updatedFileNames ) {
2017-05-24 00:38:03 +02:00
return { info , projectErrors : this.getGlobalProjectErrors ( ) } ;
2016-06-22 02:31:54 +02:00
}
2016-06-23 01:51:09 +02:00
// compute and return the difference
2016-06-22 02:31:54 +02:00
const lastReportedFileNames = this . lastReportedFileNames ;
2020-01-25 00:15:05 +01:00
const externalFiles = this . getExternalFiles ( ) . map ( ( f ) : protocol . FileWithProjectReferenceRedirectInfo = > ( {
fileName : toNormalizedPath ( f ) ,
isSourceOfProjectReferenceRedirect : false
} ) ) ;
const currentFiles = arrayToMap (
this . getFileNamesWithRedirectInfo ( ! ! includeProjectReferenceRedirectInfo ) . concat ( externalFiles ) ,
info = > info . fileName ,
info = > info . isSourceOfProjectReferenceRedirect
) ;
2020-07-02 02:00:26 +02:00
const added : ESMap < string , boolean > = new Map < string , boolean > ( ) ;
const removed : ESMap < string , boolean > = new Map < string , boolean > ( ) ;
2016-06-22 02:31:54 +02:00
2017-05-25 23:00:11 +02:00
const updated : string [ ] = updatedFileNames ? arrayFrom ( updatedFileNames . keys ( ) ) : [ ] ;
2020-01-25 00:15:05 +01:00
const updatedRedirects : protocol.FileWithProjectReferenceRedirectInfo [ ] = [ ] ;
2016-12-12 16:50:09 +01:00
2020-01-25 01:23:54 +01:00
forEachEntry ( currentFiles , ( isSourceOfProjectReferenceRedirect , fileName ) = > {
if ( ! lastReportedFileNames . has ( fileName ) ) {
added . set ( fileName , isSourceOfProjectReferenceRedirect ) ;
2020-01-25 00:15:05 +01:00
}
2020-01-25 01:23:54 +01:00
else if ( includeProjectReferenceRedirectInfo && isSourceOfProjectReferenceRedirect !== lastReportedFileNames . get ( fileName ) ) {
2020-01-25 00:15:05 +01:00
updatedRedirects . push ( {
2020-01-25 01:23:54 +01:00
fileName ,
isSourceOfProjectReferenceRedirect
2020-01-25 00:15:05 +01:00
} ) ;
2016-06-22 02:31:54 +02:00
}
2016-12-05 23:13:32 +01:00
} ) ;
2020-01-25 01:23:54 +01:00
forEachEntry ( lastReportedFileNames , ( isSourceOfProjectReferenceRedirect , fileName ) = > {
if ( ! currentFiles . has ( fileName ) ) {
removed . set ( fileName , isSourceOfProjectReferenceRedirect ) ;
2016-06-22 02:31:54 +02:00
}
2016-12-05 23:13:32 +01:00
} ) ;
2016-06-22 02:31:54 +02:00
this . lastReportedFileNames = currentFiles ;
2018-04-17 23:17:15 +02:00
this . lastReportedVersion = this . projectProgramVersion ;
2020-01-23 21:32:36 +01:00
return {
info ,
changes : {
added : includeProjectReferenceRedirectInfoIfRequested ( added ) ,
removed : includeProjectReferenceRedirectInfoIfRequested ( removed ) ,
2020-02-20 17:30:22 +01:00
updated : includeProjectReferenceRedirectInfo
2020-01-25 00:15:05 +01:00
? updated . map ( ( fileName ) : protocol . FileWithProjectReferenceRedirectInfo = > ( {
fileName ,
isSourceOfProjectReferenceRedirect : this.isSourceOfProjectReferenceRedirect ( fileName )
} ) )
: updated ,
updatedRedirects : includeProjectReferenceRedirectInfo ? updatedRedirects : undefined
2020-01-23 21:32:36 +01:00
} ,
projectErrors : this.getGlobalProjectErrors ( )
} ;
2016-06-22 02:31:54 +02:00
}
else {
// unknown version - return everything
2020-01-25 00:15:05 +01:00
const projectFileNames = this . getFileNamesWithRedirectInfo ( ! ! includeProjectReferenceRedirectInfo ) ;
const externalFiles = this . getExternalFiles ( ) . map ( ( f ) : protocol . FileWithProjectReferenceRedirectInfo = > ( {
fileName : toNormalizedPath ( f ) ,
isSourceOfProjectReferenceRedirect : false
} ) ) ;
2017-09-20 19:52:56 +02:00
const allFiles = projectFileNames . concat ( externalFiles ) ;
2020-01-25 00:15:05 +01:00
this . lastReportedFileNames = arrayToMap (
2020-01-25 01:23:54 +01:00
allFiles ,
2020-01-25 00:15:05 +01:00
info = > info . fileName ,
info = > info . isSourceOfProjectReferenceRedirect
) ;
2018-04-17 23:17:15 +02:00
this . lastReportedVersion = this . projectProgramVersion ;
2020-01-23 21:32:36 +01:00
return {
info ,
2020-01-25 00:15:05 +01:00
files : includeProjectReferenceRedirectInfo ? allFiles : allFiles.map ( f = > f . fileName ) ,
2020-01-23 21:32:36 +01:00
projectErrors : this.getGlobalProjectErrors ( )
} ;
2016-06-22 02:31:54 +02:00
}
}
2016-06-23 02:02:08 +02:00
// remove a root file from project
2016-12-30 02:00:04 +01:00
protected removeRoot ( info : ScriptInfo ) : void {
2017-05-24 17:42:02 +02:00
orderedRemoveItem ( this . rootFiles , info ) ;
2017-06-28 22:15:34 +02:00
this . rootFilesMap . delete ( info . path ) ;
2016-06-23 02:02:08 +02:00
}
2017-12-22 02:04:27 +01:00
2019-07-01 23:29:32 +02:00
/*@internal*/
isSourceOfProjectReferenceRedirect ( fileName : string ) {
return ! ! this . program && this . program . isSourceOfProjectReferenceRedirect ( fileName ) ;
}
2020-07-02 02:00:26 +02:00
protected enableGlobalPlugins ( options : CompilerOptions , pluginConfigOverrides : Map < any > | undefined ) {
2017-12-22 02:04:27 +01:00
const host = this . projectService . host ;
if ( ! host . require ) {
this . projectService . logger . info ( "Plugins were requested but not running in environment that supports 'require'. Nothing will be loaded" ) ;
return ;
}
2019-10-23 20:28:44 +02:00
// Search any globally-specified probe paths, then our peer node_modules
const searchPaths = [
. . . this . projectService . pluginProbeLocations ,
// ../../.. to walk from X/node_modules/typescript/lib/tsserver.js to X/node_modules/
combinePaths ( this . projectService . getExecutingFilePath ( ) , "../../.." ) ,
] ;
2017-12-22 02:04:27 +01:00
if ( this . projectService . globalPlugins ) {
// Enable global plugins with synthetic configuration entries
for ( const globalPluginName of this . projectService . globalPlugins ) {
// Skip empty names from odd commandline parses
if ( ! globalPluginName ) continue ;
// Skip already-locally-loaded plugins
if ( options . plugins && options . plugins . some ( p = > p . name === globalPluginName ) ) continue ;
// Provide global: true so plugins can detect why they can't find their config
this . projectService . logger . info ( ` Loading global plugin ${ globalPluginName } ` ) ;
2018-10-29 21:56:51 +01:00
this . enablePlugin ( { name : globalPluginName , global : true } as PluginImport , searchPaths , pluginConfigOverrides ) ;
2017-12-22 02:04:27 +01:00
}
}
}
2020-07-02 02:00:26 +02:00
protected enablePlugin ( pluginConfigEntry : PluginImport , searchPaths : string [ ] , pluginConfigOverrides : Map < any > | undefined ) {
2017-12-22 02:04:27 +01:00
this . projectService . logger . info ( ` Enabling plugin ${ pluginConfigEntry . name } from candidate paths: ${ searchPaths . join ( "," ) } ` ) ;
2021-02-10 00:23:29 +01:00
if ( ! pluginConfigEntry . name || parsePackageName ( pluginConfigEntry . name ) . rest ) {
this . projectService . logger . info ( ` Skipped loading plugin ${ pluginConfigEntry . name || JSON . stringify ( pluginConfigEntry ) } because only package name is allowed plugin name ` ) ;
2021-02-09 20:30:09 +01:00
return ;
}
2017-12-22 02:04:27 +01:00
2019-05-25 00:04:42 +02:00
const log = ( message : string ) = > this . projectService . logger . info ( message ) ;
let errorLogs : string [ ] | undefined ;
2021-08-16 22:53:51 +02:00
const logError = ( message : string ) = > {
( errorLogs || ( errorLogs = [ ] ) ) . push ( message ) ;
} ;
2018-05-03 00:37:22 +02:00
const resolvedModule = firstDefined ( searchPaths , searchPath = >
2021-05-18 15:20:57 +02:00
Project . resolveModule ( pluginConfigEntry . name , searchPath , this . projectService . host , log , logError ) as PluginModuleFactory | undefined ) ;
2018-05-03 00:37:22 +02:00
if ( resolvedModule ) {
2018-10-29 21:56:51 +01:00
const configurationOverride = pluginConfigOverrides && pluginConfigOverrides . get ( pluginConfigEntry . name ) ;
if ( configurationOverride ) {
// Preserve the name property since it's immutable
const pluginName = pluginConfigEntry . name ;
pluginConfigEntry = configurationOverride ;
pluginConfigEntry . name = pluginName ;
}
2018-05-03 00:37:22 +02:00
this . enableProxy ( resolvedModule , pluginConfigEntry ) ;
}
else {
2019-05-25 00:04:42 +02:00
forEach ( errorLogs , log ) ;
2018-05-03 00:37:22 +02:00
this . projectService . logger . info ( ` Couldn't find ${ pluginConfigEntry . name } ` ) ;
2017-12-22 02:04:27 +01:00
}
}
private enableProxy ( pluginModuleFactory : PluginModuleFactory , configEntry : PluginImport ) {
try {
if ( typeof pluginModuleFactory !== "function" ) {
2019-08-01 18:12:26 +02:00
this . projectService . logger . info ( ` Skipped loading plugin ${ configEntry . name } because it did not expose a proper factory function ` ) ;
2017-12-22 02:04:27 +01:00
return ;
}
const info : PluginCreateInfo = {
config : configEntry ,
project : this ,
languageService : this.languageService ,
languageServiceHost : this ,
2021-06-18 23:46:09 +02:00
serverHost : this.projectService.host ,
session : this.projectService.session
2017-12-22 02:04:27 +01:00
} ;
const pluginModule = pluginModuleFactory ( { typescript : ts } ) ;
const newLS = pluginModule . create ( info ) ;
for ( const k of Object . keys ( this . languageService ) ) {
2019-07-24 06:17:16 +02:00
// eslint-disable-next-line no-in-operator
2017-12-22 02:04:27 +01:00
if ( ! ( k in newLS ) ) {
this . projectService . logger . info ( ` Plugin activation warning: Missing proxied method ${ k } in created LS. Patching. ` ) ;
( newLS as any ) [ k ] = ( this . languageService as any ) [ k ] ;
}
}
2021-03-02 15:37:22 +01:00
this . projectService . logger . info ( ` Plugin validation succeeded ` ) ;
2017-12-22 02:04:27 +01:00
this . languageService = newLS ;
2018-10-29 21:56:51 +01:00
this . plugins . push ( { name : configEntry.name , module : pluginModule } ) ;
2017-12-22 02:04:27 +01:00
}
catch ( e ) {
this . projectService . logger . info ( ` Plugin activation failed: ${ e } ` ) ;
}
}
2018-10-29 21:56:51 +01:00
/*@internal*/
onPluginConfigurationChanged ( pluginName : string , configuration : any ) {
this . plugins . filter ( plugin = > plugin . name === pluginName ) . forEach ( plugin = > {
if ( plugin . module .onConfigurationChanged ) {
plugin . module .onConfigurationChanged ( configuration ) ;
}
} ) ;
}
/** Starts a new check for diagnostics. Call this if some file has updated that would cause diagnostics to be changed. */
refreshDiagnostics() {
this . projectService . sendProjectsUpdatedInBackgroundEvent ( ) ;
}
2019-09-27 22:38:31 +02:00
/*@internal*/
getPackageJsonsVisibleToFile ( fileName : string , rootDir? : string ) : readonly PackageJsonInfo [ ] {
2020-10-02 19:59:04 +02:00
if ( this . projectService . serverMode !== LanguageServiceMode . Semantic ) return emptyArray ;
2020-06-23 01:34:27 +02:00
return this . projectService . getPackageJsonsVisibleToFile ( fileName , rootDir ) ;
2019-09-27 22:38:31 +02:00
}
2020-11-11 20:48:32 +01:00
/*@internal*/
getNearestAncestorDirectoryWithPackageJson ( fileName : string ) : string | undefined {
return this . projectService . getNearestAncestorDirectoryWithPackageJson ( fileName ) ;
}
2019-09-27 22:38:31 +02:00
/*@internal*/
2020-06-23 01:34:27 +02:00
getPackageJsonsForAutoImport ( rootDir? : string ) : readonly PackageJsonInfo [ ] {
const packageJsons = this . getPackageJsonsVisibleToFile ( combinePaths ( this . currentDirectory , inferredTypesContainingFile ) , rootDir ) ;
2020-06-26 19:12:47 +02:00
this . packageJsonsForAutoImport = new Set ( packageJsons . map ( p = > p . fileName ) ) ;
2020-06-23 01:34:27 +02:00
return packageJsons ;
2019-09-27 22:38:31 +02:00
}
/*@internal*/
2021-07-06 19:04:59 +02:00
getCachedExportInfoMap() {
return this . exportMapCache || = createCacheableExportInfoMap ( this ) ;
}
/*@internal*/
clearCachedExportInfoMap() {
this . exportMapCache ? . clear ( ) ;
2021-03-26 22:47:07 +01:00
}
/*@internal*/
getModuleSpecifierCache() {
return this . module SpecifierCache ;
2019-09-27 22:38:31 +02:00
}
2020-06-23 01:34:27 +02:00
/*@internal*/
includePackageJsonAutoImports ( ) : PackageJsonAutoImportPreference {
2020-08-03 02:47:49 +02:00
if ( this . projectService . includePackageJsonAutoImports ( ) === PackageJsonAutoImportPreference . Off ||
2020-06-23 01:34:27 +02:00
! this . languageServiceEnabled ||
isInsideNodeModules ( this . currentDirectory ) ||
! this . isDefaultProjectForOpenFiles ( ) ) {
2020-08-03 02:47:49 +02:00
return PackageJsonAutoImportPreference . Off ;
2019-09-27 22:38:31 +02:00
}
2020-06-23 01:34:27 +02:00
return this . projectService . includePackageJsonAutoImports ( ) ;
}
2020-07-22 22:53:30 +02:00
/*@internal*/
getModuleResolutionHostForAutoImportProvider ( ) : ModuleResolutionHost {
if ( this . program ) {
return {
fileExists : this.program.fileExists ,
directoryExists : this.program.directoryExists ,
realpath : this.program.realpath || this . projectService . host . realpath ? . bind ( this . projectService . host ) ,
getCurrentDirectory : this.getCurrentDirectory.bind ( this ) ,
readFile : this.projectService.host.readFile.bind ( this . projectService . host ) ,
getDirectories : this.projectService.host.getDirectories.bind ( this . projectService . host ) ,
trace : this.projectService.host.trace?.bind ( this . projectService . host ) ,
2021-08-27 00:35:04 +02:00
useCaseSensitiveFileNames : this.program.useCaseSensitiveFileNames ( ) ,
2020-07-22 22:53:30 +02:00
} ;
}
return this . projectService . host ;
}
2020-06-23 01:34:27 +02:00
/*@internal*/
getPackageJsonAutoImportProvider ( ) : Program | undefined {
if ( this . autoImportProviderHost === false ) {
return undefined ;
}
2020-10-02 19:59:04 +02:00
if ( this . projectService . serverMode !== LanguageServiceMode . Semantic ) {
this . autoImportProviderHost = false ;
return undefined ;
}
2020-06-23 01:34:27 +02:00
if ( this . autoImportProviderHost ) {
updateProjectIfDirty ( this . autoImportProviderHost ) ;
2020-09-24 19:05:07 +02:00
if ( this . autoImportProviderHost . isEmpty ( ) ) {
2020-06-23 01:34:27 +02:00
this . autoImportProviderHost . close ( ) ;
this . autoImportProviderHost = undefined ;
return undefined ;
}
return this . autoImportProviderHost . getCurrentProgram ( ) ;
}
const dependencySelection = this . includePackageJsonAutoImports ( ) ;
if ( dependencySelection ) {
2020-10-29 17:00:50 +01:00
const start = timestamp ( ) ;
2020-07-22 22:53:30 +02:00
this . autoImportProviderHost = AutoImportProviderProject . create ( dependencySelection , this , this . getModuleResolutionHostForAutoImportProvider ( ) , this . documentRegistry ) ;
2020-06-23 01:34:27 +02:00
if ( this . autoImportProviderHost ) {
updateProjectIfDirty ( this . autoImportProviderHost ) ;
2020-10-29 17:00:50 +01:00
this . sendPerformanceEvent ( "CreatePackageJsonAutoImportProvider" , timestamp ( ) - start ) ;
2020-06-23 01:34:27 +02:00
return this . autoImportProviderHost . getCurrentProgram ( ) ;
}
}
}
/*@internal*/
private isDefaultProjectForOpenFiles ( ) : boolean {
return ! ! forEachEntry (
this . projectService . openFiles ,
( _ , fileName ) = > this . projectService . tryGetDefaultProjectForFile ( toNormalizedPath ( fileName ) ) === this ) ;
2019-09-27 22:38:31 +02:00
}
2021-06-10 19:26:32 +02:00
/*@internal*/
watchNodeModulesForPackageJsonChanges ( directoryPath : string ) {
return this . projectService . watchPackageJsonsInNodeModules ( this . toPath ( directoryPath ) , this ) ;
}
2021-06-26 00:26:53 +02:00
/*@internal*/
getIncompleteCompletionsCache() {
return this . projectService . getIncompleteCompletionsCache ( ) ;
}
2016-06-22 02:31:54 +02:00
}
2020-07-02 02:00:26 +02:00
function getUnresolvedImports ( program : Program , cachedUnresolvedImportsPerFile : ESMap < Path , readonly string [ ] > ) : SortedReadonlyArray < string > {
2018-10-30 18:15:01 +01:00
const ambientModules = program . getTypeChecker ( ) . getAmbientModules ( ) . map ( mod = > stripQuotes ( mod . getName ( ) ) ) ;
return sortAndDeduplicate ( flatMap ( program . getSourceFiles ( ) , sourceFile = >
extractUnresolvedImportsFromSourceFile ( sourceFile , ambientModules , cachedUnresolvedImportsPerFile ) ) ) ;
}
2020-07-02 02:00:26 +02:00
function extractUnresolvedImportsFromSourceFile ( file : SourceFile , ambientModules : readonly string [ ] , cachedUnresolvedImportsPerFile : ESMap < Path , readonly string [ ] > ) : readonly string [ ] {
2018-10-30 18:15:01 +01:00
return getOrUpdate ( cachedUnresolvedImportsPerFile , file . path , ( ) = > {
if ( ! file . resolvedModules ) return emptyArray ;
let unresolvedImports : string [ ] | undefined ;
file . resolvedModules . forEach ( ( resolvedModule , name ) = > {
// pick unresolved non-relative names
2018-10-31 00:39:40 +01:00
if ( ( ! resolvedModule || ! resolutionExtensionIsTSOrJson ( resolvedModule . extension ) ) &&
! isExternalModuleNameRelative ( name ) &&
! ambientModules . some ( m = > m === name ) ) {
2018-10-30 18:15:01 +01:00
unresolvedImports = append ( unresolvedImports , parsePackageName ( name ) . packageName ) ;
}
} ) ;
return unresolvedImports || emptyArray ;
} ) ;
}
2017-05-25 22:30:27 +02:00
/ * *
* If a file is opened and no tsconfig ( or jsconfig ) is found ,
* the file and its imports / references are put into an InferredProject .
* /
2016-06-23 01:51:09 +02:00
export class InferredProject extends Project {
2016-12-28 23:46:58 +01:00
private _isJsInferredProject = false ;
2016-12-29 19:26:34 +01:00
2016-12-30 02:00:04 +01:00
toggleJsInferredProject ( isJsInferredProject : boolean ) {
if ( isJsInferredProject !== this . _isJsInferredProject ) {
this . _isJsInferredProject = isJsInferredProject ;
this . setCompilerOptions ( ) ;
}
2016-12-28 23:46:58 +01:00
}
2016-12-30 02:00:04 +01:00
setCompilerOptions ( options? : CompilerOptions ) {
// Avoid manipulating the given options directly
2018-11-09 19:48:28 +01:00
if ( ! options && ! this . getCompilationSettings ( ) ) {
2016-12-28 23:46:58 +01:00
return ;
}
2018-11-09 19:48:28 +01:00
const newOptions = cloneCompilerOptions ( options || this . getCompilationSettings ( ) ) ;
2016-12-28 23:46:58 +01:00
if ( this . _isJsInferredProject && typeof newOptions . maxNodeModuleJsDepth !== "number" ) {
newOptions . maxNodeModuleJsDepth = 2 ;
}
2016-12-30 02:00:04 +01:00
else if ( ! this . _isJsInferredProject ) {
newOptions . maxNodeModuleJsDepth = undefined ;
}
2016-12-28 23:46:58 +01:00
newOptions . allowJs = true ;
super . setCompilerOptions ( newOptions ) ;
}
2017-10-11 23:21:09 +02:00
/** this is canonical project root path */
readonly projectRootPath : string | undefined ;
2018-04-20 22:38:15 +02:00
/*@internal*/
/** stored only if their is no projectRootPath and this isnt single inferred project */
readonly canonicalCurrentDirectory : string | undefined ;
2017-10-03 01:14:42 +02:00
/*@internal*/
2017-09-26 22:34:56 +02:00
constructor (
projectService : ProjectService ,
documentRegistry : DocumentRegistry ,
compilerOptions : CompilerOptions ,
2019-12-11 22:26:44 +01:00
watchOptions : WatchOptions | undefined ,
2017-10-24 23:03:58 +02:00
projectRootPath : NormalizedPath | undefined ,
2018-10-29 21:56:51 +01:00
currentDirectory : string | undefined ,
2020-10-19 18:53:58 +02:00
pluginConfigOverrides : ESMap < string , any > | undefined ,
typeAcquisition : TypeAcquisition | undefined ) {
2021-06-02 20:22:01 +02:00
super ( projectService . newInferredProjectName ( ) ,
2016-11-18 00:12:32 +01:00
ProjectKind . Inferred ,
2016-06-23 01:51:09 +02:00
projectService ,
documentRegistry ,
2018-05-22 23:46:57 +02:00
// TODO: GH#18217
/*files*/ undefined ! ,
2018-02-07 02:11:59 +01:00
/*lastFileExceededProgramSize*/ undefined ,
2016-08-24 01:11:52 +02:00
compilerOptions ,
2017-07-12 01:10:44 +02:00
/*compileOnSaveEnabled*/ false ,
2019-12-11 22:26:44 +01:00
watchOptions ,
2017-09-26 22:34:56 +02:00
projectService . host ,
2017-09-27 02:29:53 +02:00
currentDirectory ) ;
2020-10-19 18:53:58 +02:00
this . typeAcquisition = typeAcquisition ;
2017-10-11 23:21:09 +02:00
this . projectRootPath = projectRootPath && projectService . toCanonicalFileName ( projectRootPath ) ;
2018-04-20 22:38:15 +02:00
if ( ! projectRootPath && ! projectService . useSingleInferredProject ) {
this . canonicalCurrentDirectory = projectService . toCanonicalFileName ( this . currentDirectory ) ;
}
2018-10-29 21:56:51 +01:00
this . enableGlobalPlugins ( this . getCompilerOptions ( ) , pluginConfigOverrides ) ;
2016-06-23 01:51:09 +02:00
}
2016-12-30 02:00:04 +01:00
addRoot ( info : ScriptInfo ) {
2017-10-24 23:03:58 +02:00
Debug . assert ( info . isScriptOpen ( ) ) ;
2018-04-18 00:23:20 +02:00
this . projectService . startWatchingConfigFilesForInferredProjectRoot ( info ) ;
2016-12-30 02:00:04 +01:00
if ( ! this . _isJsInferredProject && info . isJavaScript ( ) ) {
this . toggleJsInferredProject ( /*isJsInferredProject*/ true ) ;
}
super . addRoot ( info ) ;
}
removeRoot ( info : ScriptInfo ) {
2017-08-23 22:34:41 +02:00
this . projectService . stopWatchingConfigFilesForInferredProjectRoot ( info ) ;
2017-07-06 03:03:11 +02:00
super . removeRoot ( info ) ;
2016-12-30 02:00:04 +01:00
if ( this . _isJsInferredProject && info . isJavaScript ( ) ) {
2017-08-21 20:39:04 +02:00
if ( every ( this . getRootScriptInfos ( ) , rootInfo = > ! rootInfo . isJavaScript ( ) ) ) {
2016-12-30 02:00:04 +01:00
this . toggleJsInferredProject ( /*isJsInferredProject*/ false ) ;
}
}
}
2018-04-20 22:38:15 +02:00
/*@internal*/
isOrphan() {
return ! this . hasRoots ( ) ;
}
2017-07-15 07:35:07 +02:00
isProjectWithSingleRoot() {
2017-08-12 05:07:47 +02:00
// - when useSingleInferredProject is not set and projectRootPath is not set,
// we can guarantee that this will be the only root
2017-07-15 07:35:07 +02:00
// - other wise it has single root if it has single root script info
2017-08-12 05:07:47 +02:00
return ( ! this . projectRootPath && ! this . projectService . useSingleInferredProject ) ||
this . getRootScriptInfos ( ) . length === 1 ;
2017-07-15 07:35:07 +02:00
}
2016-06-23 01:51:09 +02:00
close() {
2017-08-23 22:34:41 +02:00
forEach ( this . getRootScriptInfos ( ) , info = > this . projectService . stopWatchingConfigFilesForInferredProjectRoot ( info ) ) ;
2016-06-23 01:51:09 +02:00
super . close ( ) ;
}
2016-08-24 00:15:12 +02:00
2016-11-19 02:46:06 +01:00
getTypeAcquisition ( ) : TypeAcquisition {
2020-10-19 18:53:58 +02:00
return this . typeAcquisition || {
2016-11-19 02:46:06 +01:00
enable : allRootFilesAreJsOrDts ( this ) ,
2020-06-23 01:34:27 +02:00
include : ts.emptyArray ,
exclude : ts.emptyArray
2016-08-24 00:15:12 +02:00
} ;
}
2016-06-23 01:51:09 +02:00
}
2020-06-23 01:34:27 +02:00
export class AutoImportProviderProject extends Project {
2020-08-03 02:47:49 +02:00
/*@internal*/
private static readonly maxDependencies = 10 ;
2020-06-23 01:34:27 +02:00
/*@internal*/
static getRootFileNames ( dependencySelection : PackageJsonAutoImportPreference , hostProject : Project , module ResolutionHost : ModuleResolutionHost , compilerOptions : CompilerOptions ) : string [ ] {
if ( ! dependencySelection ) {
return ts . emptyArray ;
}
2021-07-20 20:16:58 +02:00
const program = hostProject . getCurrentProgram ( ) ;
if ( ! program ) {
return ts . emptyArray ;
}
2020-06-26 19:12:47 +02:00
let dependencyNames : Set < string > | undefined ;
2020-06-23 01:34:27 +02:00
let rootNames : string [ ] | undefined ;
const rootFileName = combinePaths ( hostProject . currentDirectory , inferredTypesContainingFile ) ;
const packageJsons = hostProject . getPackageJsonsForAutoImport ( combinePaths ( hostProject . currentDirectory , rootFileName ) ) ;
for ( const packageJson of packageJsons ) {
packageJson . dependencies ? . forEach ( ( _ , dependenyName ) = > addDependency ( dependenyName ) ) ;
packageJson . peerDependencies ? . forEach ( ( _ , dependencyName ) = > addDependency ( dependencyName ) ) ;
}
if ( dependencyNames ) {
2021-08-26 00:06:14 +02:00
const resolutions = mapDefined ( arrayFrom ( dependencyNames . keys ( ) ) , name = > {
const types = resolveTypeReferenceDirective (
name ,
rootFileName ,
compilerOptions ,
module ResolutionHost ) ;
if ( types . resolvedTypeReferenceDirective ) {
return types . resolvedTypeReferenceDirective ;
}
if ( compilerOptions . allowJs && compilerOptions . maxNodeModuleJsDepth ) {
return tryResolveJSModule ( name , hostProject . currentDirectory , module ResolutionHost ) ;
}
} ) ;
2020-06-23 01:34:27 +02:00
2021-05-06 23:51:30 +02:00
const symlinkCache = hostProject . getSymlinkCache ( ) ;
2020-06-23 01:34:27 +02:00
for ( const resolution of resolutions ) {
2021-08-26 00:06:14 +02:00
if ( ! resolution . resolvedFileName ) continue ;
const { resolvedFileName , originalPath } = resolution ;
2021-11-17 01:39:10 +01:00
if ( originalPath ) {
symlinkCache . setSymlinkedDirectoryFromSymlinkedFile ( originalPath , resolvedFileName ) ;
}
2021-05-06 23:51:30 +02:00
if ( ! program . getSourceFile ( resolvedFileName ) && ( ! originalPath || ! program . getSourceFile ( originalPath ) ) ) {
rootNames = append ( rootNames , resolvedFileName ) ;
2020-08-03 02:47:49 +02:00
// Avoid creating a large project that would significantly slow down time to editor interactivity
if ( dependencySelection === PackageJsonAutoImportPreference . Auto && rootNames . length > this . maxDependencies ) {
return ts . emptyArray ;
}
2020-06-23 01:34:27 +02:00
}
}
}
return rootNames || ts . emptyArray ;
function addDependency ( dependency : string ) {
if ( ! startsWith ( dependency , "@types/" ) ) {
2020-06-26 19:12:47 +02:00
( dependencyNames || ( dependencyNames = new Set ( ) ) ) . add ( dependency ) ;
2020-06-23 01:34:27 +02:00
}
}
}
2021-05-27 01:47:40 +02:00
/*@internal*/
static readonly compilerOptionsOverrides : CompilerOptions = {
diagnostics : false ,
skipLibCheck : true ,
sourceMap : false ,
types : ts.emptyArray ,
lib : ts.emptyArray ,
noLib : true ,
} ;
2020-06-23 01:34:27 +02:00
/*@internal*/
static create ( dependencySelection : PackageJsonAutoImportPreference , hostProject : Project , module ResolutionHost : ModuleResolutionHost , documentRegistry : DocumentRegistry ) : AutoImportProviderProject | undefined {
2020-08-03 02:47:49 +02:00
if ( dependencySelection === PackageJsonAutoImportPreference . Off ) {
2020-06-23 01:34:27 +02:00
return undefined ;
}
2021-05-27 01:47:40 +02:00
const compilerOptions = {
2020-06-23 01:34:27 +02:00
. . . hostProject . getCompilerOptions ( ) ,
2021-05-27 01:47:40 +02:00
. . . this . compilerOptionsOverrides ,
2020-06-23 01:34:27 +02:00
} ;
const rootNames = this . getRootFileNames ( dependencySelection , hostProject , module ResolutionHost , compilerOptions ) ;
if ( ! rootNames . length ) {
return undefined ;
}
return new AutoImportProviderProject ( hostProject , rootNames , documentRegistry , compilerOptions ) ;
}
private rootFileNames : string [ ] | undefined ;
/*@internal*/
constructor (
private hostProject : Project ,
initialRootNames : string [ ] ,
documentRegistry : DocumentRegistry ,
compilerOptions : CompilerOptions ,
) {
2021-06-02 20:22:01 +02:00
super ( hostProject . projectService . newAutoImportProviderProjectName ( ) ,
2020-06-23 01:34:27 +02:00
ProjectKind . AutoImportProvider ,
hostProject . projectService ,
documentRegistry ,
/*hasExplicitListOfFiles*/ false ,
/*lastFileExceededProgramSize*/ undefined ,
compilerOptions ,
/*compileOnSaveEnabled*/ false ,
hostProject . getWatchOptions ( ) ,
hostProject . projectService . host ,
hostProject . currentDirectory ) ;
this . rootFileNames = initialRootNames ;
2021-03-26 21:23:03 +01:00
this . useSourceOfProjectReferenceRedirect = maybeBind ( this . hostProject , this . hostProject . useSourceOfProjectReferenceRedirect ) ;
this . getParsedCommandLine = maybeBind ( this . hostProject , this . hostProject . getParsedCommandLine ) ;
2020-06-23 01:34:27 +02:00
}
2020-09-24 19:05:07 +02:00
/*@internal*/
isEmpty() {
return ! some ( this . rootFileNames ) ;
}
2020-06-23 01:34:27 +02:00
isOrphan() {
return true ;
}
updateGraph() {
let rootFileNames = this . rootFileNames ;
if ( ! rootFileNames ) {
rootFileNames = AutoImportProviderProject . getRootFileNames (
this . hostProject . includePackageJsonAutoImports ( ) ,
this . hostProject ,
2020-07-22 22:53:30 +02:00
this . hostProject . getModuleResolutionHostForAutoImportProvider ( ) ,
2020-06-23 01:34:27 +02:00
this . getCompilationSettings ( ) ) ;
}
this . projectService . setFileNamesOfAutoImportProviderProject ( this , rootFileNames ) ;
this . rootFileNames = rootFileNames ;
2021-03-26 22:47:07 +01:00
const oldProgram = this . getCurrentProgram ( ) ;
const hasSameSetOfFiles = super . updateGraph ( ) ;
if ( oldProgram && oldProgram !== this . getCurrentProgram ( ) ) {
2021-07-06 19:04:59 +02:00
this . hostProject . clearCachedExportInfoMap ( ) ;
2021-03-26 22:47:07 +01:00
}
return hasSameSetOfFiles ;
2020-06-23 01:34:27 +02:00
}
2020-11-11 20:48:32 +01:00
hasRoots() {
return ! ! this . rootFileNames ? . length ;
}
2020-06-23 01:34:27 +02:00
markAsDirty() {
this . rootFileNames = undefined ;
super . markAsDirty ( ) ;
}
getScriptFileNames() {
return this . rootFileNames || ts . emptyArray ;
}
getLanguageService ( ) : never {
throw new Error ( "AutoImportProviderProject language service should never be used. To get the program, use `project.getCurrentProgram()`." ) ;
}
2021-03-26 22:47:07 +01:00
/*@internal*/
onAutoImportProviderSettingsChanged ( ) : never {
2020-06-23 01:34:27 +02:00
throw new Error ( "AutoImportProviderProject is an auto import provider; use `markAsDirty()` instead." ) ;
}
2021-03-26 22:47:07 +01:00
/*@internal*/
onPackageJsonChange ( ) : never {
throw new Error ( "package.json changes should be notified on an AutoImportProvider's host project" ) ;
}
2020-07-22 22:53:30 +02:00
getModuleResolutionHostForAutoImportProvider ( ) : never {
throw new Error ( "AutoImportProviderProject cannot provide its own host; use `hostProject.getModuleResolutionHostForAutomImportProvider()` instead." ) ;
}
getProjectReferences() {
return this . hostProject . getProjectReferences ( ) ;
}
2020-06-23 01:34:27 +02:00
/*@internal*/
includePackageJsonAutoImports() {
2020-08-03 02:47:49 +02:00
return PackageJsonAutoImportPreference . Off ;
2020-06-23 01:34:27 +02:00
}
getTypeAcquisition ( ) : TypeAcquisition {
return { enable : false } ;
}
2020-07-22 22:53:30 +02:00
/*@internal*/
getSymlinkCache() {
return this . hostProject . getSymlinkCache ( ) ;
}
2020-06-23 01:34:27 +02:00
}
2017-05-25 22:30:27 +02:00
/ * *
* If a file is opened , the server will look for a tsconfig ( or jsconfig )
2020-01-30 22:39:54 +01:00
* and if successful create a ConfiguredProject for it .
2017-05-25 22:30:27 +02:00
* Otherwise it will create an InferredProject .
* /
2016-06-23 01:51:09 +02:00
export class ConfiguredProject extends Project {
2017-07-13 08:15:03 +02:00
/* @internal */
2018-11-17 01:02:23 +01:00
pendingReload : ConfigFileProgramReloadLevel | undefined ;
2018-10-05 21:20:48 +02:00
/* @internal */
pendingReloadReason : string | undefined ;
2017-07-13 08:15:03 +02:00
2019-12-11 03:25:10 +01:00
/* @internal */
2021-03-26 21:23:03 +01:00
openFileWatchTriggered = new Map < string , ConfigFileProgramReloadLevel > ( ) ;
2019-12-11 03:25:10 +01:00
2018-09-27 19:04:49 +02:00
/*@internal*/
2018-11-17 01:02:23 +01:00
canConfigFileJsonReportNoInputFiles = false ;
2018-09-27 19:04:49 +02:00
2017-10-12 20:48:25 +02:00
/** Ref count to the project when opened from external project */
private externalProjectRefCount = 0 ;
2016-06-22 02:31:54 +02:00
2019-08-08 20:30:18 +02:00
private projectReferences : readonly ProjectReference [ ] | undefined ;
2018-07-21 01:29:04 +02:00
2019-12-11 21:28:18 +01:00
/** Potential project references before the project is actually loaded (read config file) */
/*@internal*/
2020-06-26 19:12:47 +02:00
potentialProjectReferences : Set < string > | undefined ;
2019-12-11 21:28:18 +01:00
2018-07-21 01:29:04 +02:00
/*@internal*/
projectOptions? : ProjectOptions | true ;
2019-12-11 21:28:18 +01:00
/*@internal*/
isInitialLoadPending : ( ) = > boolean = returnTrue ;
2018-07-25 23:01:48 +02:00
2018-10-05 21:20:48 +02:00
/*@internal*/
sendLoadingProjectFinish = false ;
2020-03-12 21:11:11 +01:00
/*@internal*/
private compilerHost? : CompilerHost ;
2017-10-03 01:14:42 +02:00
/*@internal*/
2017-04-14 01:16:57 +02:00
constructor ( configFileName : NormalizedPath ,
2021-03-26 21:23:03 +01:00
readonly canonicalConfigFilePath : NormalizedPath ,
2016-06-22 02:31:54 +02:00
projectService : ProjectService ,
2017-07-13 22:08:59 +02:00
documentRegistry : DocumentRegistry ,
2018-07-21 01:29:04 +02:00
cachedDirectoryStructureHost : CachedDirectoryStructureHost ) {
2017-09-26 22:34:56 +02:00
super ( configFileName ,
ProjectKind . Configured ,
projectService ,
documentRegistry ,
2018-07-21 01:29:04 +02:00
/*hasExplicitListOfFiles*/ false ,
/*lastFileExceededProgramSize*/ undefined ,
/*compilerOptions*/ { } ,
/*compileOnSaveEnabled*/ false ,
2019-12-11 22:26:44 +01:00
/*watchOptions*/ undefined ,
2017-09-26 22:34:56 +02:00
cachedDirectoryStructureHost ,
2021-06-18 23:46:09 +02:00
getDirectoryPath ( configFileName )
2019-10-29 18:49:14 +01:00
) ;
2016-11-18 00:12:32 +01:00
}
2019-06-21 22:11:39 +02:00
/* @internal */
2020-03-12 21:11:11 +01:00
setCompilerHost ( host : CompilerHost ) {
this . compilerHost = host ;
2019-06-21 22:11:39 +02:00
}
/* @internal */
2020-03-12 21:11:11 +01:00
getCompilerHost ( ) : CompilerHost | undefined {
return this . compilerHost ;
2019-06-21 22:11:39 +02:00
}
2020-03-12 21:11:11 +01:00
/* @internal */
useSourceOfProjectReferenceRedirect() {
return this . languageServiceEnabled ;
2019-06-29 00:49:48 +02:00
}
2019-12-11 22:26:44 +01:00
/* @internal */
2021-03-26 21:23:03 +01:00
getParsedCommandLine ( fileName : string ) {
const configFileName = asNormalizedPath ( normalizePath ( fileName ) ) ;
const canonicalConfigFilePath = asNormalizedPath ( this . projectService . toCanonicalFileName ( configFileName ) ) ;
// Ensure the config file existience info is cached
let configFileExistenceInfo = this . projectService . configFileExistenceInfoCache . get ( canonicalConfigFilePath ) ;
if ( ! configFileExistenceInfo ) {
this . projectService . configFileExistenceInfoCache . set ( canonicalConfigFilePath , configFileExistenceInfo = { exists : this.projectService.host.fileExists ( configFileName ) } ) ;
}
// Ensure we have upto date parsed command line
this . projectService . ensureParsedConfigUptoDate ( configFileName , canonicalConfigFilePath , configFileExistenceInfo , this ) ;
// Watch wild cards if LS is enabled
if ( this . languageServiceEnabled && this . projectService . serverMode === LanguageServiceMode . Semantic ) {
this . projectService . watchWildcards ( configFileName , configFileExistenceInfo , this ) ;
2019-12-11 22:26:44 +01:00
}
2021-03-26 21:23:03 +01:00
return configFileExistenceInfo . exists ? configFileExistenceInfo . config ! . parsedCommandLine : undefined ;
2019-12-11 22:26:44 +01:00
}
/* @internal */
2021-03-26 21:23:03 +01:00
onReleaseParsedCommandLine ( fileName : string ) {
this . releaseParsedConfig ( asNormalizedPath ( this . projectService . toCanonicalFileName ( asNormalizedPath ( normalizePath ( fileName ) ) ) ) ) ;
}
/* @internal */
private releaseParsedConfig ( canonicalConfigFilePath : NormalizedPath ) {
this . projectService . stopWatchingWildCards ( canonicalConfigFilePath , this ) ;
this . projectService . releaseParsedConfig ( canonicalConfigFilePath , this ) ;
2019-12-11 22:26:44 +01:00
}
2017-07-13 08:15:03 +02:00
/ * *
2017-08-18 20:55:47 +02:00
* If the project has reload from disk pending , it reloads ( and then updates graph as part of that ) instead of just updating the graph
2017-07-13 08:15:03 +02:00
* @returns : true if set of files in the project stays the same and false - otherwise .
* /
updateGraph ( ) : boolean {
2020-07-24 22:33:25 +02:00
const isInitialLoad = this . isInitialLoadPending ( ) ;
2018-07-25 23:01:48 +02:00
this . isInitialLoadPending = returnFalse ;
2017-11-06 23:39:23 +01:00
const reloadLevel = this . pendingReload ;
this . pendingReload = ConfigFileProgramReloadLevel . None ;
2018-07-21 01:29:04 +02:00
let result : boolean ;
2017-11-06 23:39:23 +01:00
switch ( reloadLevel ) {
case ConfigFileProgramReloadLevel . Partial :
2019-12-11 03:25:10 +01:00
this . openFileWatchTriggered . clear ( ) ;
2018-07-21 01:29:04 +02:00
result = this . projectService . reloadFileNamesOfConfiguredProject ( this ) ;
break ;
2017-11-06 23:39:23 +01:00
case ConfigFileProgramReloadLevel . Full :
2019-12-11 03:25:10 +01:00
this . openFileWatchTriggered . clear ( ) ;
2020-02-25 03:20:58 +01:00
const reason = Debug . checkDefined ( this . pendingReloadReason ) ;
2018-10-05 21:20:48 +02:00
this . pendingReloadReason = undefined ;
2020-11-04 22:30:06 +01:00
this . projectService . reloadConfiguredProject ( this , reason , isInitialLoad , /*clearSemanticCache*/ false ) ;
2018-07-21 01:29:04 +02:00
result = true ;
break ;
2017-11-06 23:39:23 +01:00
default :
2018-07-21 01:29:04 +02:00
result = super . updateGraph ( ) ;
2017-07-13 08:15:03 +02:00
}
2020-03-12 21:11:11 +01:00
this . compilerHost = undefined ;
2018-10-05 21:20:48 +02:00
this . projectService . sendProjectLoadingFinishEvent ( this ) ;
2018-07-21 01:29:04 +02:00
this . projectService . sendProjectTelemetry ( this ) ;
return result ;
2017-07-13 08:15:03 +02:00
}
2017-08-14 23:59:51 +02:00
/*@internal*/
2017-09-26 20:05:52 +02:00
getCachedDirectoryStructureHost() {
return this . directoryStructureHost as CachedDirectoryStructureHost ;
2017-07-12 01:10:44 +02:00
}
2016-11-18 00:12:32 +01:00
getConfigFilePath() {
2017-07-14 05:08:57 +02:00
return asNormalizedPath ( this . getProjectName ( ) ) ;
2016-06-22 02:31:54 +02:00
}
2019-08-08 20:30:18 +02:00
getProjectReferences ( ) : readonly ProjectReference [ ] | undefined {
2018-09-19 01:21:05 +02:00
return this . projectReferences ;
2018-05-08 00:12:50 +02:00
}
2019-08-08 20:30:18 +02:00
updateReferences ( refs : readonly ProjectReference [ ] | undefined ) {
2018-05-08 00:12:50 +02:00
this . projectReferences = refs ;
2019-12-11 21:28:18 +01:00
this . potentialProjectReferences = undefined ;
2018-05-08 00:12:50 +02:00
}
2018-08-03 00:33:22 +02:00
/*@internal*/
2019-12-11 21:28:18 +01:00
setPotentialProjectReference ( canonicalConfigPath : NormalizedPath ) {
Debug . assert ( this . isInitialLoadPending ( ) ) ;
2020-06-26 19:12:47 +02:00
( this . potentialProjectReferences || ( this . potentialProjectReferences = new Set ( ) ) ) . add ( canonicalConfigPath ) ;
2018-08-03 00:33:22 +02:00
}
2019-06-27 00:57:22 +02:00
/*@internal*/
getResolvedProjectReferenceToRedirect ( fileName : string ) : ResolvedProjectReference | undefined {
const program = this . getCurrentProgram ( ) ;
return program && program . getResolvedProjectReferenceToRedirect ( fileName ) ;
}
2020-10-19 21:59:59 +02:00
/*@internal*/
forEachResolvedProjectReference < T > (
cb : ( resolvedProjectReference : ResolvedProjectReference ) = > T | undefined
) : T | undefined {
return this . getCurrentProgram ( ) ? . forEachResolvedProjectReference ( cb ) ;
}
2018-07-21 01:29:04 +02:00
/*@internal*/
2020-07-02 02:00:26 +02:00
enablePluginsWithOptions ( options : CompilerOptions , pluginConfigOverrides : ESMap < string , any > | undefined ) {
2017-02-14 22:35:16 +01:00
const host = this . projectService . host ;
if ( ! host . require ) {
this . projectService . logger . info ( "Plugins were requested but not running in environment that supports 'require'. Nothing will be loaded" ) ;
return ;
}
2017-04-14 01:16:57 +02:00
// Search our peer node_modules, then any globally-specified probe paths
// ../../.. to walk from X/node_modules/typescript/lib/tsserver.js to X/node_modules/
2017-09-27 02:29:53 +02:00
const searchPaths = [ combinePaths ( this . projectService . getExecutingFilePath ( ) , "../../.." ) , . . . this . projectService . pluginProbeLocations ] ;
2017-04-14 01:16:57 +02:00
2017-05-18 04:52:57 +02:00
if ( this . projectService . allowLocalPluginLoads ) {
const local = getDirectoryPath ( this . canonicalConfigFilePath ) ;
this . projectService . logger . info ( ` Local plugin loading enabled; adding ${ local } to search paths ` ) ;
searchPaths . unshift ( local ) ;
}
2017-04-14 01:16:57 +02:00
// Enable tsconfig-specified plugins
2017-04-14 01:02:24 +02:00
if ( options . plugins ) {
for ( const pluginConfigEntry of options . plugins ) {
2018-10-29 21:56:51 +01:00
this . enablePlugin ( pluginConfigEntry , searchPaths , pluginConfigOverrides ) ;
2017-04-14 01:02:24 +02:00
}
}
2018-10-29 21:56:51 +01:00
this . enableGlobalPlugins ( options , pluginConfigOverrides ) ;
2017-02-14 22:35:16 +01:00
}
2017-08-18 20:55:47 +02:00
/ * *
* Get the errors that dont have any file name associated
* /
2019-08-08 20:30:18 +02:00
getGlobalProjectErrors ( ) : readonly Diagnostic [ ] {
2017-10-04 22:25:56 +02:00
return filter ( this . projectErrors , diagnostic = > ! diagnostic . file ) || emptyArray ;
2017-08-18 20:55:47 +02:00
}
/ * *
* Get all the project errors
* /
2019-08-08 20:30:18 +02:00
getAllProjectErrors ( ) : readonly Diagnostic [ ] {
2017-10-04 22:25:56 +02:00
return this . projectErrors || emptyArray ;
2017-08-18 20:55:47 +02:00
}
2016-08-26 23:37:49 +02:00
setProjectErrors ( projectErrors : Diagnostic [ ] ) {
this . projectErrors = projectErrors ;
}
2016-06-22 02:31:54 +02:00
close() {
2021-03-26 21:23:03 +01:00
this . projectService . configFileExistenceInfoCache . forEach ( ( _configFileExistenceInfo , canonicalConfigFilePath ) = >
this . releaseParsedConfig ( canonicalConfigFilePath ) ) ;
2020-12-09 01:10:05 +01:00
this . projectErrors = undefined ;
2019-12-11 03:25:10 +01:00
this . openFileWatchTriggered . clear ( ) ;
2020-03-12 21:11:11 +01:00
this . compilerHost = undefined ;
2017-10-09 23:25:48 +02:00
super . close ( ) ;
2016-06-22 02:31:54 +02:00
}
2017-10-12 20:48:25 +02:00
/* @internal */
addExternalProjectReference() {
this . externalProjectRefCount ++ ;
2016-06-22 02:31:54 +02:00
}
2017-10-12 20:48:25 +02:00
/* @internal */
deleteExternalProjectReference() {
this . externalProjectRefCount -- ;
2016-06-22 02:31:54 +02:00
}
2016-09-20 01:47:15 +02:00
2020-03-13 18:46:58 +01:00
/* @internal */
isSolution() {
return this . getRootFilesMap ( ) . size === 0 &&
! this . canConfigFileJsonReportNoInputFiles ;
}
/* @internal */
2020-07-22 00:02:10 +02:00
/** Find the configured project from the project references in project which contains the info directly */
getDefaultChildProjectFromProjectWithReferences ( info : ScriptInfo ) {
2020-03-13 18:46:58 +01:00
return forEachResolvedProjectReferenceProject (
this ,
2020-10-19 21:59:59 +02:00
info . path ,
2020-03-13 18:46:58 +01:00
child = > projectContainsInfoDirectly ( child , info ) ?
child :
undefined ,
ProjectReferenceProjectLoadKind . Find
) ;
}
2017-10-12 20:48:25 +02:00
/** Returns true if the project is needed by any of the open script info/external project */
/* @internal */
2017-09-01 02:58:09 +02:00
hasOpenRef() {
2017-10-12 20:48:25 +02:00
if ( ! ! this . externalProjectRefCount ) {
return true ;
}
// Closed project doesnt have any reference
if ( this . isClosed ( ) ) {
return false ;
}
2021-03-26 21:23:03 +01:00
const configFileExistenceInfo = this . projectService . configFileExistenceInfoCache . get ( this . canonicalConfigFilePath ) ! ;
2017-10-12 20:48:25 +02:00
if ( this . projectService . hasPendingProjectUpdate ( this ) ) {
// If there is pending update for this project,
// we dont know if this project would be needed by any of the open files impacted by this config file
// In that case keep the project alive if there are open files impacted by this project
2021-03-26 21:23:03 +01:00
return ! ! configFileExistenceInfo . openFilesImpactedByConfigFile ? . size ;
2017-10-12 20:48:25 +02:00
}
// If there is no pending update for this project,
// We know exact set of open files that get impacted by this configured project as the files in the project
// The project is referenced only if open files impacted by this project are present in this project
2021-03-26 21:23:03 +01:00
return ! ! configFileExistenceInfo . openFilesImpactedByConfigFile && forEachEntry (
2017-10-12 20:48:25 +02:00
configFileExistenceInfo . openFilesImpactedByConfigFile ,
2020-07-21 23:24:15 +02:00
( _value , infoPath ) = > {
const info = this . projectService . getScriptInfoForPath ( infoPath ) ! ;
2020-07-22 00:02:10 +02:00
return this . containsScriptInfo ( info ) ||
2020-07-21 23:24:15 +02:00
! ! forEachResolvedProjectReferenceProject (
this ,
2020-10-19 21:59:59 +02:00
info . path ,
2020-07-21 23:24:15 +02:00
child = > child . containsScriptInfo ( info ) ,
ProjectReferenceProjectLoadKind . Find
2020-07-22 00:02:10 +02:00
) ;
2020-07-21 23:24:15 +02:00
}
2017-10-12 20:48:25 +02:00
) || false ;
2017-09-01 02:58:09 +02:00
}
2018-08-28 23:08:59 +02:00
/*@internal*/
hasExternalProjectRef() {
return ! ! this . externalProjectRefCount ;
}
2016-09-20 01:47:15 +02:00
getEffectiveTypeRoots() {
2017-09-26 20:05:52 +02:00
return getEffectiveTypeRoots ( this . getCompilationSettings ( ) , this . directoryStructureHost ) || [ ] ;
2016-09-20 01:47:15 +02:00
}
2017-08-07 23:47:32 +02:00
/*@internal*/
2020-12-09 01:10:05 +01:00
updateErrorOnNoInputFiles ( fileNames : string [ ] ) {
updateErrorForNoInputFiles ( fileNames , this . getConfigFilePath ( ) , this . getCompilerOptions ( ) . configFile ! . configFileSpecs ! , this . projectErrors ! , this . canConfigFileJsonReportNoInputFiles ) ;
2017-08-07 23:47:32 +02:00
}
2016-06-22 02:31:54 +02:00
}
2017-05-25 22:30:27 +02:00
/ * *
* Project whose configuration is handled externally , such as in a '.csproj' .
* These are created only if a host explicitly calls ` openExternalProject ` .
* /
2016-06-23 01:51:09 +02:00
export class ExternalProject extends Project {
2019-08-08 20:30:18 +02:00
excludedFiles : readonly NormalizedPath [ ] = [ ] ;
2017-10-03 01:14:42 +02:00
/*@internal*/
2017-03-17 20:56:31 +01:00
constructor ( public externalProjectName : string ,
2016-06-22 02:31:54 +02:00
projectService : ProjectService ,
2017-07-13 22:08:59 +02:00
documentRegistry : DocumentRegistry ,
2016-06-22 02:31:54 +02:00
compilerOptions : CompilerOptions ,
2018-02-07 02:11:59 +01:00
lastFileExceededProgramSize : string | undefined ,
2016-09-14 01:20:42 +02:00
public compileOnSaveEnabled : boolean ,
2019-04-03 19:36:04 +02:00
projectFilePath? : string ,
2020-07-02 02:00:26 +02:00
pluginConfigOverrides? : ESMap < string , any > ,
2019-12-11 22:26:44 +01:00
watchOptions? : WatchOptions ) {
2017-09-26 22:34:56 +02:00
super ( externalProjectName ,
ProjectKind . External ,
projectService ,
documentRegistry ,
/*hasExplicitListOfFiles*/ true ,
2018-02-07 02:11:59 +01:00
lastFileExceededProgramSize ,
2017-09-27 02:29:53 +02:00
compilerOptions ,
2017-09-26 22:34:56 +02:00
compileOnSaveEnabled ,
2019-12-11 22:26:44 +01:00
watchOptions ,
2017-09-26 22:34:56 +02:00
projectService . host ,
getDirectoryPath ( projectFilePath || normalizeSlashes ( externalProjectName ) ) ) ;
2019-04-03 19:36:04 +02:00
this . enableGlobalPlugins ( this . getCompilerOptions ( ) , pluginConfigOverrides ) ;
2016-08-24 00:15:12 +02:00
}
2018-07-21 01:29:04 +02:00
updateGraph() {
const result = super . updateGraph ( ) ;
this . projectService . sendProjectTelemetry ( this ) ;
return result ;
}
2017-07-28 01:07:50 +02:00
getExcludedFiles() {
return this . excludedFiles ;
2016-08-24 00:15:12 +02:00
}
2016-06-22 02:31:54 +02:00
}
2019-12-11 03:25:10 +01:00
/* @internal */
export function isInferredProject ( project : Project ) : project is InferredProject {
return project . projectKind === ProjectKind . Inferred ;
}
/* @internal */
export function isConfiguredProject ( project : Project ) : project is ConfiguredProject {
return project . projectKind === ProjectKind . Configured ;
}
/* @internal */
export function isExternalProject ( project : Project ) : project is ExternalProject {
return project . projectKind === ProjectKind . External ;
}
2017-05-24 00:38:03 +02:00
}