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 ,
External
}
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 ;
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 ,
* or it could be just normalized path if root wasnt present on the host ( only for non inferred project )
* /
/* @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 ;
}
type GeneratedFileWatcherMap = GeneratedFileWatcher | Map < GeneratedFileWatcher > ;
function isGeneratedFileWatcher ( watch : GeneratedFileWatcherMap ) : watch is GeneratedFileWatcher {
return ( watch as GeneratedFileWatcher ) . generatedFilePath !== undefined ;
}
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 [ ] = [ ] ;
2019-12-11 03:25:10 +01:00
private rootFilesMap = createMap < ProjectRootFile > ( ) ;
2018-11-17 01:02:23 +01:00
private program : Program | undefined ;
private externalFiles : SortedReadonlyArray < string > | undefined ;
private missingFilesMap : Map < 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
2019-09-27 22:38:31 +02:00
/*@internal*/
private packageJsonFilesMap : Map < FileWatcher > | undefined ;
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
* /
2019-08-08 20:30:18 +02:00
cachedUnresolvedImportsPerFile = createMap < 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 ;
2016-10-26 00:24:21 +02:00
2018-02-07 02:11:59 +01:00
private lastFileExceededProgramSize : string | undefined ;
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 .
* /
2018-05-22 23:46:57 +02:00
private updatedFileNames : Map < true > | undefined ;
2016-06-23 01:51:09 +02:00
/ * *
* Set of files that was returned from the last call to getChangesSinceVersion .
* /
2018-11-17 01:02:23 +01:00
private lastReportedFileNames : Map < true > | 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 ;
2018-07-25 23:01:48 +02:00
protected isInitialLoadPending : ( ) = > boolean = returnFalse ;
2018-02-12 23:55:58 +01:00
/*@internal*/
dirty = false ;
2017-09-26 22:34:56 +02:00
/*@internal*/
hasChangedAutomaticTypeDirectiveNames = false ;
2016-08-12 20:04:43 +02:00
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*/
originalConfiguredProjects : Map < true > | undefined ;
2019-06-27 00:57:22 +02:00
/*@internal*/
getResolvedProjectReferenceToRedirect ( _fileName : string ) : ResolvedProjectReference | undefined {
return 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*/
readonly packageJsonCache : PackageJsonCache ;
/*@internal*/
private importSuggestionsCache = Completions . createImportSuggestionsForFileCache ( ) ;
/*@internal*/
private dirtyFilesForSuggestions : Map < true > | undefined ;
/*@internal*/
private symlinks : ReadonlyMap < string > | undefined ;
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 ,
customRealpath ? : ( s : string ) = > string ) {
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 ;
}
2018-04-06 02:30:04 +02:00
else if ( hasExplicitListOfFiles || this . compilerOptions . allowJs || 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 ;
}
2018-04-03 22:40:48 +02:00
this . languageServiceEnabled = ! projectService . syntaxOnly ;
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
}
2016-11-04 05:13:41 +01:00
2017-08-24 20:50:27 +02:00
if ( host . realpath ) {
2019-10-29 18:49:14 +01:00
this . realpath = customRealpath || ( path = > host . realpath ! ( path ) ) ;
2017-08-24 20:50:27 +02:00
}
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
2017-12-05 00:08:56 +01:00
this . resolutionCache = createResolutionCache ( this , currentDirectory && this . currentDirectory , /*logChangesWhenResolvingModule*/ true ) ;
2018-04-03 22:40:48 +02:00
this . languageService = createLanguageService ( this , this . documentRegistry , projectService . syntaxOnly ) ;
2018-02-07 02:11:59 +01:00
if ( lastFileExceededProgramSize ) {
this . disableLanguageService ( lastFileExceededProgramSize ) ;
2016-06-22 02:31:54 +02:00
}
this . markAsDirty ( ) ;
2018-02-12 23:55:58 +01:00
this . projectService . pendingEnsureProjectForOpenFiles = true ;
2019-09-27 22:38:31 +02:00
this . packageJsonCache = createPackageJsonCache ( this ) ;
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*/
getProbableSymlinks ( files : readonly SourceFile [ ] ) : ReadonlyMap < string > {
return this . symlinks || ( this . symlinks = discoverProbableSymlinks (
files ,
this . getCanonicalFileName ,
this . getCurrentDirectory ( ) ) ) ;
}
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 ) {
2017-09-26 22:34:56 +02:00
const info = this . getOrCreateScriptInfoAndAttachToProject ( filename ) ;
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 {
2019-06-21 22:11:39 +02:00
return this . fileExistsWithCache ( file ) ;
}
/* @internal */
fileExistsWithCache ( file : string ) : boolean {
2017-08-24 20:50:27 +02:00
// 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
}
2018-10-22 21:23:04 +02:00
resolveModuleNames ( module Names : string [ ] , containingFile : string , reusedNames? : string [ ] , redirectedReference? : ResolvedProjectReference ) : ( ResolvedModuleFull | undefined ) [ ] {
2018-10-02 00:30:06 +02:00
return this . resolutionCache . resolveModuleNames ( module Names , containingFile , reusedNames , redirectedReference ) ;
2017-08-24 20:50:27 +02:00
}
2018-05-22 23:46:57 +02:00
getResolvedModuleWithFailedLookupLocationsFromCache ( module Name : string , containingFile : string ) : ResolvedModuleWithFailedLookupLocations | undefined {
2018-05-19 01:42:42 +02:00
return this . resolutionCache . getResolvedModuleWithFailedLookupLocationsFromCache ( module Name , containingFile ) ;
}
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
this . projectService . host ,
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
) ;
}
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
this . projectService . host ,
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
) ;
}
/*@internal*/
onChangedAutomaticTypeDirectiveNames() {
2017-09-26 22:34:56 +02:00
this . hasChangedAutomaticTypeDirectiveNames = true ;
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 [ ] {
2017-08-18 20:55:47 +02:00
return emptyArray ;
2017-05-24 00:38:03 +02:00
}
2019-08-08 20:30:18 +02:00
getAllProjectErrors ( ) : readonly Diagnostic [ ] {
2017-08-18 20:55:47 +02:00
return emptyArray ;
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 ( ) ;
}
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 ) ;
2018-11-17 01:02:23 +01:00
this . builderState = BuilderState . create ( this . program ! , this . projectService . toCanonicalFileName , this . builderState ) ;
return mapDefined ( BuilderState . getFilesAffectedBy ( this . builderState , this . program ! , scriptInfo . path , this . cancellationToken , data = > this . projectService . host . createHash ! ( data ) ) , // TODO: GH#18217
2019-07-09 00:55:35 +02:00
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
* /
emitFile ( scriptInfo : ScriptInfo , writeFile : ( path : string , data : string , writeByteOrderMark? : boolean ) = > void ) : boolean {
2017-10-04 00:06:56 +02:00
if ( ! this . languageServiceEnabled || ! this . shouldEmitFile ( scriptInfo ) ) {
return false ;
}
const { emitSkipped , outputFiles } = this . getLanguageService ( /*ensureSynchronized*/ false ) . 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 ) ;
}
}
return ! emitSkipped ;
2016-08-24 01:11:52 +02:00
}
2016-06-22 02:31:54 +02:00
enableLanguageService() {
2018-04-03 22:40:48 +02:00
if ( this . languageServiceEnabled || this . projectService . syntaxOnly ) {
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 ;
}
2018-04-03 22:40:48 +02:00
Debug . assert ( ! this . projectService . syntaxOnly ) ;
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 ;
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-11-19 02:46:06 +01:00
abstract getTypeAcquisition ( ) : TypeAcquisition ;
2016-06-22 02:31:54 +02:00
2017-12-06 22:17:21 +01:00
protected removeLocalTypingsFromTypeAcquisition ( newTypeAcquisition : TypeAcquisition ) : TypeAcquisition {
if ( ! newTypeAcquisition || ! newTypeAcquisition . include ) {
// Nothing to filter out, so just return as-is
return newTypeAcquisition ;
}
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
}
2018-10-02 23:10:19 +02:00
this . program . forEachResolvedProjectReference ( ref = > {
if ( ref ) {
this . detachScriptInfoFromProject ( ref . sourceFile . fileName ) ;
2018-08-03 00:33:22 +02:00
}
2018-10-02 23:10:19 +02:00
} ) ;
2016-06-22 02:31:54 +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 ! ;
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 ! ;
this . directoryStructureHost = 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 ) ;
2018-05-22 23:46:57 +02:00
this . missingFilesMap = undefined ! ;
2017-08-07 23:47:32 +02:00
}
2019-09-27 22:38:31 +02:00
if ( this . packageJsonFilesMap ) {
clearMap ( this . packageJsonFilesMap , closeFileWatcher ) ;
this . packageJsonFilesMap = undefined ;
}
2019-06-25 21:15:04 +02:00
this . clearGeneratedFileWatch ( ) ;
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. ` ) ;
2018-05-22 23:46:57 +02: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
}
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 ) {
2017-07-07 19:34:36 +02:00
( this . updatedFileNames || ( this . updatedFileNames = createMap < true > ( ) ) ) . set ( fileName , true ) ;
2016-12-09 02:56:08 +01:00
}
2019-09-27 22:38:31 +02:00
/*@internal*/
markFileAsDirty ( changedFile : Path ) {
this . markAsDirty ( ) ;
if ( ! this . importSuggestionsCache . isEmpty ( ) ) {
( this . dirtyFilesForSuggestions || ( this . dirtyFilesForSuggestions = createMap ( ) ) ) . set ( changedFile , true ) ;
}
}
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
}
2018-04-17 23:17:15 +02:00
/* @internal */
2018-04-18 20:05:56 +02:00
onFileAddedOrRemoved() {
this . hasAddedorRemovedFiles = true ;
2018-04-17 23:17:15 +02:00
}
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 ;
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
if ( this . languageServiceEnabled ) {
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
}
2018-04-17 23:17:15 +02:00
if ( hasNewProgram ) {
this . projectProgramVersion ++ ;
2016-08-12 20:04:43 +02:00
}
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 > ) {
2018-10-30 16:41:31 +01:00
enumerateInsertsAndDeletes < string , string > ( typingFiles , this . typingFiles , getStringComparer ( ! this . useCaseSensitiveFileNames ( ) ) ,
2018-04-19 19:59:23 +02:00
/*inserted*/ noop ,
removed = > this . detachScriptInfoFromProject ( removed )
) ;
2018-04-13 01:47:40 +02:00
this . typingFiles = typingFiles ;
// Invalidate files with unresolved imports
this . resolutionCache . setFilesWithInvalidatedNonRelativeUnresolvedImports ( this . cachedUnresolvedImportsPerFile ) ;
}
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 ( ) ;
2018-05-22 23:46:57 +02: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.
2018-05-22 23:46:57 +02:00
const hasNewProgram = this . program && ( ! oldProgram || ( this . program !== oldProgram && ! ( oldProgram . structureIsReused ! & StructureIsReused . Completely ) ) ) ;
2017-09-26 22:34:56 +02:00
this . hasChangedAutomaticTypeDirectiveNames = false ;
2018-04-17 23:17:15 +02:00
if ( hasNewProgram ) {
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
oldProgram . forEachResolvedProjectReference ( ( resolvedProjectReference , resolvedProjectReferencePath ) = > {
2018-11-17 01:02:23 +01:00
if ( resolvedProjectReference && ! this . program ! . getResolvedProjectReferenceByPath ( resolvedProjectReferencePath ) ) {
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 ,
2017-08-07 23:47:32 +02:00
this . missingFilesMap || ( this . missingFilesMap = createMap ( ) ) ,
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 ) {
const outPath = this . compilerOptions . outFile && this . compilerOptions . out ;
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 ) = > {
const sourceFile = this . program ! . getSourceFileByPath ( source as Path ) ;
if ( ! sourceFile ||
sourceFile . resolvedPath !== source ||
! this . isValidGeneratedFileWatcher (
getDeclarationEmitOutputFilePathWorker ( sourceFile . fileName , this . compilerOptions , this . currentDirectory , this . program ! . getCommonSourceDirectory ( ) , this . getCanonicalFileName ) ,
watcher
) ) {
closeFileWatcherOf ( watcher ) ;
( this . generatedFilesMap as Map < GeneratedFileWatcher > ) . delete ( source ) ;
}
} ) ;
}
}
}
2017-08-29 02:09:07 +02:00
// Watch the type locations that would be added to program as part of automatic type resolutions
2017-08-30 20:49:58 +02:00
if ( this . languageServiceEnabled ) {
this . resolutionCache . updateTypeRootsWatch ( ) ;
}
2017-06-21 00:54:43 +02:00
}
2019-09-27 22:38:31 +02:00
if ( ! this . importSuggestionsCache . isEmpty ( ) ) {
if ( this . hasAddedorRemovedFiles || oldProgram && ! oldProgram . structureIsReused ) {
this . importSuggestionsCache . clear ( ) ;
}
else if ( this . dirtyFilesForSuggestions && oldProgram && this . program ) {
forEachKey ( this . dirtyFilesForSuggestions , fileName = > {
const oldSourceFile = oldProgram . getSourceFile ( fileName ) ;
const sourceFile = this . program ! . getSourceFile ( fileName ) ;
if ( this . sourceFileHasChangedOwnImportSuggestions ( oldSourceFile , sourceFile ) ) {
this . importSuggestionsCache . clear ( ) ;
return true ;
}
} ) ;
}
}
if ( this . dirtyFilesForSuggestions ) {
this . dirtyFilesForSuggestions . clear ( ) ;
}
if ( this . hasAddedorRemovedFiles ) {
this . symlinks = undefined ;
}
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 = > {
2018-05-22 23:46:57 +02:00
const scriptInfo = this . projectService . getOrCreateScriptInfoNotOpenedByClient ( inserted , this . currentDirectory , this . directoryStructureHost ) ! ;
2017-04-22 00:20:55 +02:00
scriptInfo . attachToProject ( this ) ;
} ,
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 ;
2018-04-17 23:17:15 +02:00
this . writeLog ( ` Finishing updateGraphWorker: Project: ${ this . getProjectName ( ) } Version: ${ this . getProjectVersion ( ) } structureChanged: ${ hasNewProgram } Elapsed: ${ elapsed } ms ` ) ;
2019-08-12 22:13:28 +02:00
if ( this . hasAddedorRemovedFiles ) {
2019-04-03 23:05:49 +02:00
this . print ( ) ;
}
2019-08-12 22:13:28 +02:00
else if ( this . program !== oldProgram ) {
this . writeLog ( ` Different program with same set of files:: oldProgram.structureIsReused:: ${ oldProgram && oldProgram . structureIsReused } ` ) ;
}
2018-04-17 23:17:15 +02:00
return hasNewProgram ;
2016-06-22 02:31:54 +02:00
}
2019-09-27 22:38:31 +02:00
/*@internal*/
private sourceFileHasChangedOwnImportSuggestions ( oldSourceFile : SourceFile | undefined , newSourceFile : SourceFile | undefined ) {
if ( ! oldSourceFile && ! newSourceFile ) {
return false ;
}
// Probably shouldn’ t get this far, but on the off chance the file was added or removed,
// we can’ t reliably tell anything about it.
if ( ! oldSourceFile || ! newSourceFile ) {
return true ;
}
Debug . assertEqual ( oldSourceFile . fileName , newSourceFile . fileName ) ;
// If ATA is enabled, auto-imports uses existing imports to guess whether you want auto-imports from node.
// Adding or removing imports from node could change the outcome of that guess, so could change the suggestions list.
if ( this . getTypeAcquisition ( ) . enable && consumesNodeCoreModules ( oldSourceFile ) !== consumesNodeCoreModules ( newSourceFile ) ) {
return true ;
}
// Module agumentation and ambient module changes can add or remove exports available to be auto-imported.
// Changes elsewhere in the file can change the *type* of an export in a module augmentation,
// but type info is gathered in getCompletionEntryDetails, which doesn’ t use the cache.
if (
! arrayIsEqualTo ( oldSourceFile . module Augmentations , newSourceFile . module Augmentations ) ||
! this . ambientModuleDeclarationsAreEqual ( oldSourceFile , newSourceFile )
) {
return true ;
}
return false ;
}
/*@internal*/
private ambientModuleDeclarationsAreEqual ( oldSourceFile : SourceFile , newSourceFile : SourceFile ) {
if ( ! arrayIsEqualTo ( oldSourceFile . ambientModuleNames , newSourceFile . ambientModuleNames ) ) {
return false ;
}
let oldFileStatementIndex = - 1 ;
let newFileStatementIndex = - 1 ;
for ( const ambientModuleName of newSourceFile . ambientModuleNames ) {
const isMatchingModuleDeclaration = ( node : Statement ) = > isNonGlobalAmbientModule ( node ) && node . name . text === ambientModuleName ;
oldFileStatementIndex = findIndex ( oldSourceFile . statements , isMatchingModuleDeclaration , oldFileStatementIndex + 1 ) ;
newFileStatementIndex = findIndex ( newSourceFile . statements , isMatchingModuleDeclaration , newFileStatementIndex + 1 ) ;
if ( oldSourceFile . statements [ oldFileStatementIndex ] !== newSourceFile . statements [ newFileStatementIndex ] ) {
return false ;
}
}
return true ;
}
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 ) {
2017-12-18 22:20:42 +01:00
const fileWatcher = this . projectService . watchFactory . watchFile (
2017-08-23 22:34:41 +02:00
this . projectService . host ,
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 ) {
if ( this . compilerOptions . outFile || this . compilerOptions . out ) {
// 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 {
this . generatedFilesMap = createMap ( ) ;
}
this . generatedFilesMap . set ( path , this . createGeneratedFileWatcher ( generatedFile ) ) ;
}
}
private createGeneratedFileWatcher ( generatedFile : string ) : GeneratedFileWatcher {
return {
generatedFilePath : this.toPath ( generatedFile ) ,
watcher : this.projectService.watchFactory.watchFile (
this . projectService . host ,
generatedFile ,
( ) = > this . projectService . delayUpdateProjectGraphAndEnsureProjectStructureForOpenFiles ( this ) ,
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 ` ;
}
2016-06-22 02:31:54 +02:00
}
return strBuilder ;
}
2018-11-08 22:01:22 +01:00
/*@internal*/
2018-10-30 00:32:58 +01:00
print ( counter? : number ) {
this . writeLog ( ` Project ' ${ this . projectName } ' ( ${ ProjectKind [ this . projectKind ] } ) ${ counter === undefined ? "" : counter } ` ) ;
this . writeLog ( this . filesToString ( this . projectService . logger . hasLevel ( LogLevel . verbose ) ) ) ;
this . writeLog ( "-----------------------------------------------" ) ;
}
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 ( ) ;
}
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 ;
}
2017-01-05 00:56:16 +01:00
/* @internal */
2016-08-26 23:37:49 +02:00
getChangesSinceVersion ( lastKnownVersion? : number ) : ProjectFilesWithTSDiagnostics {
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 ;
2017-08-29 19:22:36 +02:00
const externalFiles = this . getExternalFiles ( ) . map ( f = > toNormalizedPath ( f ) ) ;
const currentFiles = arrayToSet ( this . getFileNames ( ) . concat ( externalFiles ) ) ;
2016-06-22 02:31:54 +02:00
const added : string [ ] = [ ] ;
const removed : string [ ] = [ ] ;
2017-05-25 23:00:11 +02:00
const updated : string [ ] = updatedFileNames ? arrayFrom ( updatedFileNames . keys ( ) ) : [ ] ;
2016-12-12 16:50:09 +01:00
2016-12-28 18:16:38 +01:00
forEachKey ( currentFiles , id = > {
2016-12-05 23:13:32 +01:00
if ( ! lastReportedFileNames . has ( id ) ) {
2016-06-22 02:31:54 +02:00
added . push ( id ) ;
}
2016-12-05 23:13:32 +01:00
} ) ;
2016-12-28 18:16:38 +01:00
forEachKey ( lastReportedFileNames , id = > {
2016-12-05 23:13:32 +01:00
if ( ! currentFiles . has ( id ) ) {
2016-06-22 02:31:54 +02:00
removed . push ( id ) ;
}
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 ;
2017-05-24 00:38:03 +02:00
return { info , changes : { added , removed , updated } , projectErrors : this.getGlobalProjectErrors ( ) } ;
2016-06-22 02:31:54 +02:00
}
else {
// unknown version - return everything
const projectFileNames = this . getFileNames ( ) ;
2017-08-29 19:22:36 +02:00
const externalFiles = this . getExternalFiles ( ) . map ( f = > toNormalizedPath ( f ) ) ;
2017-09-20 19:52:56 +02:00
const allFiles = projectFileNames . concat ( externalFiles ) ;
this . lastReportedFileNames = arrayToSet ( allFiles ) ;
2018-04-17 23:17:15 +02:00
this . lastReportedVersion = this . projectProgramVersion ;
2017-09-20 19:52:56 +02:00
return { info , files : allFiles , 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 ) ;
}
2018-10-29 21:56:51 +01: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
}
}
}
2018-10-29 21:56:51 +01: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 ( "," ) } ` ) ;
2019-05-25 00:04:42 +02:00
const log = ( message : string ) = > this . projectService . logger . info ( message ) ;
let errorLogs : string [ ] | undefined ;
const logError = ( message : string ) = > { ( errorLogs || ( errorLogs = [ ] ) ) . push ( message ) ; } ;
2018-05-03 00:37:22 +02:00
const resolvedModule = firstDefined ( searchPaths , searchPath = >
2019-05-25 00:04:42 +02:00
< PluginModuleFactory | undefined > Project . resolveModule ( pluginConfigEntry . name , searchPath , this . projectService . host , log , logError ) ) ;
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 ,
serverHost : this.projectService.host
} ;
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 ] ;
}
}
this . projectService . logger . info ( ` Plugin validation succeded ` ) ;
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 [ ] {
const packageJsonCache = this . packageJsonCache ;
const watchPackageJsonFile = this . watchPackageJsonFile . bind ( this ) ;
const toPath = this . toPath . bind ( this ) ;
const rootPath = rootDir && toPath ( rootDir ) ;
const filePath = toPath ( fileName ) ;
const result : PackageJsonInfo [ ] = [ ] ;
forEachAncestorDirectory ( getDirectoryPath ( filePath ) , function processDirectory ( directory ) : boolean | undefined {
switch ( packageJsonCache . directoryHasPackageJson ( directory ) ) {
// Sync and check same directory again
case Ternary . Maybe :
packageJsonCache . searchDirectoryAndAncestors ( directory ) ;
return processDirectory ( directory ) ;
// Check package.json
case Ternary . True :
const packageJsonFileName = combinePaths ( directory , "package.json" ) ;
watchPackageJsonFile ( packageJsonFileName ) ;
2019-11-04 23:31:45 +01:00
const info = packageJsonCache . getInDirectory ( directory ) ;
if ( info ) result . push ( info ) ;
2019-09-27 22:38:31 +02:00
}
if ( rootPath && rootPath === toPath ( directory ) ) {
return true ;
}
} ) ;
return result ;
}
/*@internal*/
onAddPackageJson ( path : Path ) {
this . packageJsonCache . addOrUpdate ( path ) ;
this . watchPackageJsonFile ( path ) ;
}
/*@internal*/
getImportSuggestionsCache() {
return this . importSuggestionsCache ;
}
private watchPackageJsonFile ( path : Path ) {
const watchers = this . packageJsonFilesMap || ( this . packageJsonFilesMap = createMap ( ) ) ;
if ( ! watchers . has ( path ) ) {
watchers . set ( path , this . projectService . watchFactory . watchFile (
this . projectService . host ,
path ,
( fileName , eventKind ) = > {
const path = this . toPath ( fileName ) ;
switch ( eventKind ) {
case FileWatcherEventKind . Created :
return Debug . fail ( ) ;
case FileWatcherEventKind . Changed :
this . packageJsonCache . addOrUpdate ( path ) ;
break ;
case FileWatcherEventKind . Deleted :
this . packageJsonCache . delete ( path ) ;
watchers . get ( path ) ! . close ( ) ;
watchers . delete ( path ) ;
}
} ,
PollingInterval . Low ,
2019-12-11 22:26:44 +01:00
this . projectService . getWatchOptions ( this ) ,
2019-09-27 22:38:31 +02:00
WatchType . PackageJsonFile ,
) ) ;
}
}
2016-06-22 02:31:54 +02:00
}
2019-08-08 20:30:18 +02:00
function getUnresolvedImports ( program : Program , cachedUnresolvedImportsPerFile : Map < 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 ) ) ) ;
}
2019-08-08 20:30:18 +02:00
function extractUnresolvedImportsFromSourceFile ( file : SourceFile , ambientModules : readonly string [ ] , cachedUnresolvedImportsPerFile : Map < 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 {
2017-07-20 17:54:47 +02:00
private static readonly newName = ( ( ) = > {
2016-11-18 00:12:32 +01:00
let nextId = 1 ;
return ( ) = > {
const id = nextId ;
nextId ++ ;
return makeInferredProjectName ( id ) ;
2017-02-10 21:24:10 +01:00
} ;
2016-11-18 00:12:32 +01:00
} ) ( ) ;
2016-06-23 01:51:09 +02:00
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 ,
pluginConfigOverrides : Map < any > | undefined ) {
2016-11-18 00:12:32 +01:00
super ( InferredProject . newName ( ) ,
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 ) ;
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 {
2016-08-24 00:15:12 +02:00
return {
2016-11-19 02:46:06 +01:00
enable : allRootFilesAreJsOrDts ( this ) ,
2016-08-24 00:15:12 +02:00
include : [ ] ,
exclude : [ ]
} ;
}
2016-06-23 01:51:09 +02:00
}
2019-10-29 18:49:14 +01:00
/*@internal*/
interface SymlinkedDirectory {
real : string ;
realPath : Path ;
}
2017-05-25 22:30:27 +02:00
/ * *
* If a file is opened , the server will look for a tsconfig ( or jsconfig )
* and if successfull create a ConfiguredProject for it .
* Otherwise it will create an InferredProject .
* /
2016-06-23 01:51:09 +02:00
export class ConfiguredProject extends Project {
2018-11-17 01:02:23 +01:00
private typeAcquisition ! : TypeAcquisition ; // TODO: GH#18217
2017-07-14 05:08:57 +02:00
/* @internal */
2018-05-22 23:46:57 +02:00
configFileWatcher : FileWatcher | undefined ;
2017-08-07 23:47:32 +02:00
private directoriesWatchedForWildcards : Map < WildcardDirectoryWatcher > | undefined ;
2016-11-23 21:34:00 +01:00
readonly canonicalConfigFilePath : NormalizedPath ;
2019-06-29 00:49:48 +02:00
private projectReferenceCallbacks : ResolvedProjectReferenceCallbacks | undefined ;
private mapOfDeclarationDirectories : Map < true > | undefined ;
2019-10-29 18:49:14 +01:00
private symlinkedDirectories : Map < SymlinkedDirectory | false > | undefined ;
private symlinkedFiles : Map < string > | undefined ;
2016-09-02 00:17:38 +02:00
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 */
openFileWatchTriggered = createMap < true > ( ) ;
2017-07-11 22:38:12 +02:00
/*@internal*/
2018-05-22 23:46:57 +02:00
configFileSpecs : ConfigFileSpecs | undefined ;
2017-07-11 22:38:12 +02: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
2018-05-22 23:46:57 +02:00
private projectErrors : Diagnostic [ ] | undefined ;
2017-08-18 20:55:47 +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*/
potentialProjectReferences : Map < true > | undefined ;
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 ;
2017-10-03 01:14:42 +02:00
/*@internal*/
2017-04-14 01:16:57 +02:00
constructor ( configFileName : 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 ,
2019-10-29 18:49:14 +01:00
getDirectoryPath ( configFileName ) ,
projectService . host . realpath && ( s = > this . getRealpath ( s ) )
) ;
2016-11-23 21:34:00 +01:00
this . canonicalConfigFilePath = asNormalizedPath ( projectService . toCanonicalFileName ( configFileName ) ) ;
2016-11-18 00:12:32 +01:00
}
2019-06-21 22:11:39 +02:00
/* @internal */
2019-06-29 00:49:48 +02:00
setResolvedProjectReferenceCallbacks ( projectReferenceCallbacks : ResolvedProjectReferenceCallbacks ) {
this . projectReferenceCallbacks = projectReferenceCallbacks ;
2019-06-21 22:11:39 +02:00
}
/* @internal */
2019-07-11 00:21:24 +02:00
useSourceOfProjectReferenceRedirect = ( ) = > ! ! this . languageServiceEnabled &&
! this . getCompilerOptions ( ) . disableSourceOfProjectReferenceRedirect ;
2019-06-21 22:11:39 +02:00
2019-10-29 18:49:14 +01:00
private fileExistsIfProjectReferenceDts ( file : string ) {
const source = this . projectReferenceCallbacks ! . getSourceOfProjectReferenceRedirect ( file ) ;
return source !== undefined ?
isString ( source ) ? super . fileExists ( source ) : true :
undefined ;
}
2019-09-23 22:54:12 +02:00
/ * *
* This implementation of fileExists checks if the file being requested is
* . d . ts file for the referenced Project .
* If it is it returns true irrespective of whether that file exists on host
* /
2019-06-21 22:11:39 +02:00
fileExists ( file : string ) : boolean {
2019-10-29 18:49:14 +01:00
if ( super . fileExists ( file ) ) return true ;
if ( ! this . useSourceOfProjectReferenceRedirect ( ) || ! this . projectReferenceCallbacks ) return false ;
if ( ! isDeclarationFileName ( file ) ) return false ;
2019-06-21 22:11:39 +02:00
// Project references go to source file instead of .d.ts file
2019-10-29 18:49:14 +01:00
return this . fileOrDirectoryExistsUsingSource ( file , /*isFile*/ true ) ;
}
private directoryExistsIfProjectReferenceDeclDir ( dir : string ) {
const dirPath = this . toPath ( dir ) ;
const dirPathWithTrailingDirectorySeparator = ` ${ dirPath } ${ directorySeparator } ` ;
return forEachKey (
this . mapOfDeclarationDirectories ! ,
2019-11-27 18:36:52 +01:00
declDirPath = > dirPath === declDirPath ||
// Any parent directory of declaration dir
startsWith ( declDirPath , dirPathWithTrailingDirectorySeparator ) ||
// Any directory inside declaration dir
startsWith ( dirPath , ` ${ declDirPath } / ` )
2019-10-29 18:49:14 +01:00
) ;
2019-06-21 22:11:39 +02:00
}
2019-09-23 22:54:12 +02:00
/ * *
* This implementation of directoryExists checks if the directory being requested is
* directory of . d . ts file for the referenced Project .
* If it is it returns true irrespective of whether that directory exists on host
* /
2019-06-29 00:49:48 +02:00
directoryExists ( path : string ) : boolean {
2019-10-29 18:49:14 +01:00
if ( super . directoryExists ( path ) ) {
this . handleDirectoryCouldBeSymlink ( path ) ;
return true ;
}
2019-07-11 00:21:24 +02:00
if ( ! this . useSourceOfProjectReferenceRedirect ( ) || ! this . projectReferenceCallbacks ) return false ;
2019-06-29 00:49:48 +02:00
if ( ! this . mapOfDeclarationDirectories ) {
this . mapOfDeclarationDirectories = createMap ( ) ;
this . projectReferenceCallbacks . forEachResolvedProjectReference ( ref = > {
if ( ! ref ) return ;
2019-10-29 18:49:14 +01:00
const out = ref . commandLine . options . outFile || ref . commandLine . options . out ;
2019-06-29 00:49:48 +02:00
if ( out ) {
this . mapOfDeclarationDirectories ! . set ( getDirectoryPath ( this . toPath ( out ) ) , true ) ;
}
else {
// Set declaration's in different locations only, if they are next to source the directory present doesnt change
const declarationDir = ref . commandLine . options . declarationDir || ref . commandLine . options . outDir ;
if ( declarationDir ) {
this . mapOfDeclarationDirectories ! . set ( this . toPath ( declarationDir ) , true ) ;
}
}
} ) ;
}
2019-10-29 18:49:14 +01:00
return this . fileOrDirectoryExistsUsingSource ( path , /*isFile*/ false ) ;
}
2019-11-27 18:36:52 +01:00
/ * *
* Call super . getDirectories only if directory actually present on the host
* This is needed to ensure that we arent getting directories that we fake about presence for
* /
getDirectories ( path : string ) : string [ ] {
return ! this . useSourceOfProjectReferenceRedirect ( ) || ! this . projectReferenceCallbacks || super . directoryExists ( path ) ?
super . getDirectories ( path ) :
[ ] ;
}
2019-10-29 18:49:14 +01:00
private realpathIfSymlinkedProjectReferenceDts ( s : string ) : string | undefined {
return this . symlinkedFiles && this . symlinkedFiles . get ( this . toPath ( s ) ) ;
}
private getRealpath ( s : string ) : string {
return this . realpathIfSymlinkedProjectReferenceDts ( s ) ||
this . projectService . host . realpath ! ( s ) ;
}
private handleDirectoryCouldBeSymlink ( directory : string ) {
if ( ! this . useSourceOfProjectReferenceRedirect ( ) || ! this . projectReferenceCallbacks ) return ;
// Because we already watch node_modules, handle symlinks in there
if ( ! this . realpath || ! stringContains ( directory , nodeModulesPathPart ) ) return ;
if ( ! this . symlinkedDirectories ) this . symlinkedDirectories = createMap ( ) ;
const directoryPath = ensureTrailingDirectorySeparator ( this . toPath ( directory ) ) ;
if ( this . symlinkedDirectories . has ( directoryPath ) ) return ;
2019-11-27 18:36:52 +01:00
const real = normalizePath ( this . projectService . host . realpath ! ( directory ) ) ;
2019-10-29 18:49:14 +01:00
let realPath : Path ;
if ( real === directory ||
( realPath = ensureTrailingDirectorySeparator ( this . toPath ( real ) ) ) === directoryPath ) {
// not symlinked
this . symlinkedDirectories . set ( directoryPath , false ) ;
return ;
}
this . symlinkedDirectories . set ( directoryPath , {
real : ensureTrailingDirectorySeparator ( real ) ,
realPath
} ) ;
}
private fileOrDirectoryExistsUsingSource ( fileOrDirectory : string , isFile : boolean ) : boolean {
const fileOrDirectoryExistsUsingSource = isFile ?
( file : string ) = > this . fileExistsIfProjectReferenceDts ( file ) :
( dir : string ) = > this . directoryExistsIfProjectReferenceDeclDir ( dir ) ;
// Check current directory or file
const result = fileOrDirectoryExistsUsingSource ( fileOrDirectory ) ;
if ( result !== undefined ) return result ;
if ( ! this . symlinkedDirectories ) return false ;
const fileOrDirectoryPath = this . toPath ( fileOrDirectory ) ;
if ( ! stringContains ( fileOrDirectoryPath , nodeModulesPathPart ) ) return false ;
if ( isFile && this . symlinkedFiles && this . symlinkedFiles . has ( fileOrDirectoryPath ) ) return true ;
// If it contains node_modules check if its one of the symlinked path we know of
return firstDefinedIterator (
this . symlinkedDirectories . entries ( ) ,
( [ directoryPath , symlinkedDirectory ] ) = > {
if ( ! symlinkedDirectory || ! startsWith ( fileOrDirectoryPath , directoryPath ) ) return undefined ;
const result = fileOrDirectoryExistsUsingSource ( fileOrDirectoryPath . replace ( directoryPath , symlinkedDirectory . realPath ) ) ;
if ( isFile && result ) {
if ( ! this . symlinkedFiles ) this . symlinkedFiles = createMap ( ) ;
// Store the real path for the file'
const absolutePath = getNormalizedAbsolutePath ( fileOrDirectory , this . currentDirectory ) ;
this . symlinkedFiles . set (
fileOrDirectoryPath ,
` ${ symlinkedDirectory . real } ${ absolutePath . replace ( new RegExp ( directoryPath , "i" ) , "" ) } `
) ;
}
return result ;
}
) || false ;
2019-06-29 00:49:48 +02:00
}
2019-12-11 22:26:44 +01:00
/* @internal */
setWatchOptions ( watchOptions : WatchOptions | undefined ) {
const oldOptions = this . getWatchOptions ( ) ;
super . setWatchOptions ( watchOptions ) ;
// If watch options different than older options
if ( this . isInitialLoadPending ( ) &&
! isJsonEqual ( oldOptions , this . getWatchOptions ( ) ) ) {
const oldWatcher = this . configFileWatcher ;
this . createConfigFileWatcher ( ) ;
if ( oldWatcher ) oldWatcher . close ( ) ;
}
}
/* @internal */
createConfigFileWatcher() {
this . configFileWatcher = this . projectService . watchFactory . watchFile (
this . projectService . host ,
this . getConfigFilePath ( ) ,
( _fileName , eventKind ) = > this . projectService . onConfigChangedForConfiguredProject ( this , eventKind ) ,
PollingInterval . High ,
this . projectService . getWatchOptions ( this ) ,
WatchType . ConfigFile ,
this
) ;
}
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 {
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 ;
2019-06-29 00:49:48 +02:00
this . projectReferenceCallbacks = undefined ;
this . mapOfDeclarationDirectories = undefined ;
2019-10-29 18:49:14 +01:00
this . symlinkedDirectories = undefined ;
this . symlinkedFiles = undefined ;
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 ( ) ;
2018-10-05 21:20:48 +02:00
const reason = Debug . assertDefined ( this . pendingReloadReason ) ;
this . pendingReloadReason = undefined ;
this . projectService . reloadConfiguredProject ( this , reason ) ;
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
}
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 ( ) ) ;
( this . potentialProjectReferences || ( this . potentialProjectReferences = createMap ( ) ) ) . set ( canonicalConfigPath , true ) ;
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 ) ;
}
2018-07-21 01:29:04 +02:00
/*@internal*/
2018-10-29 21:56:51 +01:00
enablePluginsWithOptions ( options : CompilerOptions , pluginConfigOverrides : Map < 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-11-19 02:46:06 +01:00
setTypeAcquisition ( newTypeAcquisition : TypeAcquisition ) : void {
2017-12-06 22:17:21 +01:00
this . typeAcquisition = this . removeLocalTypingsFromTypeAcquisition ( newTypeAcquisition ) ;
2016-08-24 00:15:12 +02:00
}
2016-11-19 02:46:06 +01:00
getTypeAcquisition() {
return this . typeAcquisition ;
2016-08-12 20:04:43 +02:00
}
2017-08-18 20:55:47 +02:00
/*@internal*/
2017-07-07 23:43:00 +02:00
watchWildcards ( wildcardDirectories : Map < WatchDirectoryFlags > ) {
2017-08-13 00:31:19 +02:00
updateWatchingWildcardDirectories (
2017-08-07 23:47:32 +02:00
this . directoriesWatchedForWildcards || ( this . directoriesWatchedForWildcards = createMap ( ) ) ,
2017-07-25 01:57:49 +02:00
wildcardDirectories ,
// Create new directory watcher
2017-08-24 20:50:27 +02:00
( directory , flags ) = > this . projectService . watchWildcardDirectory ( directory as Path , flags , this ) ,
2017-07-14 05:08:57 +02:00
) ;
}
2017-08-18 20:55:47 +02:00
/*@internal*/
2017-08-23 22:34:41 +02:00
stopWatchingWildCards() {
2017-08-07 23:47:32 +02:00
if ( this . directoriesWatchedForWildcards ) {
2017-08-23 22:34:41 +02:00
clearMap ( this . directoriesWatchedForWildcards , closeFileWatcherOf ) ;
2017-08-07 23:47:32 +02:00
this . directoriesWatchedForWildcards = undefined ;
}
2017-07-14 05:08:57 +02:00
}
2016-06-22 02:31:54 +02:00
close() {
2017-07-07 23:43:00 +02:00
if ( this . configFileWatcher ) {
2017-08-23 22:34:41 +02:00
this . configFileWatcher . close ( ) ;
2017-07-07 23:43:00 +02:00
this . configFileWatcher = undefined ;
2016-06-22 02:31:54 +02:00
}
2017-08-23 22:34:41 +02:00
this . stopWatchingWildCards ( ) ;
2017-10-04 22:25:56 +02:00
this . projectErrors = undefined ;
this . configFileSpecs = undefined ;
2019-06-29 00:49:48 +02:00
this . projectReferenceCallbacks = undefined ;
this . mapOfDeclarationDirectories = undefined ;
2019-10-29 18:49:14 +01:00
this . symlinkedDirectories = undefined ;
this . symlinkedFiles = undefined ;
2019-12-11 03:25:10 +01:00
this . openFileWatchTriggered . clear ( ) ;
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
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 ;
}
const configFileExistenceInfo = this . projectService . getConfigFileExistenceInfo ( this ) ;
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
return ! ! configFileExistenceInfo . openFilesImpactedByConfigFile . size ;
}
// 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
return forEachEntry (
configFileExistenceInfo . openFilesImpactedByConfigFile ,
2018-05-22 23:46:57 +02:00
( _value , infoPath ) = > this . containsScriptInfo ( this . projectService . getScriptInfoForPath ( infoPath as Path ) ! )
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*/
2018-09-27 19:04:49 +02:00
updateErrorOnNoInputFiles ( fileNameResult : ExpandResult ) {
updateErrorForNoInputFiles ( fileNameResult , this . getConfigFilePath ( ) , this . 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 [ ] = [ ] ;
2018-11-17 01:02:23 +01:00
private typeAcquisition ! : TypeAcquisition ; // TODO: GH#18217
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 ,
2019-12-11 22:26:44 +01:00
pluginConfigOverrides? : Map < any > ,
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-11-19 02:46:06 +01:00
getTypeAcquisition() {
return this . typeAcquisition ;
2016-08-24 00:15:12 +02:00
}
2016-11-19 02:46:06 +01:00
setTypeAcquisition ( newTypeAcquisition : TypeAcquisition ) : void {
2017-11-16 21:37:40 +01:00
Debug . assert ( ! ! newTypeAcquisition , "newTypeAcquisition may not be null/undefined" ) ;
Debug . assert ( ! ! newTypeAcquisition . include , "newTypeAcquisition.include may not be null/undefined" ) ;
Debug . assert ( ! ! newTypeAcquisition . exclude , "newTypeAcquisition.exclude may not be null/undefined" ) ;
Debug . assert ( typeof newTypeAcquisition . enable === "boolean" , "newTypeAcquisition.enable may not be null/undefined" ) ;
2017-12-06 22:17:21 +01:00
this . typeAcquisition = this . removeLocalTypingsFromTypeAcquisition ( newTypeAcquisition ) ;
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
}