1namespace ts { 2 export interface ReadBuildProgramHost { 3 useCaseSensitiveFileNames(): boolean; 4 getCurrentDirectory(): string; 5 readFile(fileName: string): string | undefined; 6 /*@internal*/ 7 getBuildInfo?(fileName: string, configFilePath: string | undefined): BuildInfo | undefined; 8 getLastCompiledProgram?(): Program; 9 } 10 export function readBuilderProgram(compilerOptions: CompilerOptions, host: ReadBuildProgramHost, isForLinter?: boolean) { 11 let buildInfoPath = getTsBuildInfoEmitOutputFilePath(compilerOptions); 12 13 if (!buildInfoPath) return undefined; 14 15 if (isForLinter) { 16 buildInfoPath = getTsBuildInfoEmitOutputFilePathForLinter(buildInfoPath); 17 } 18 19 let buildInfo; 20 if (host.getBuildInfo) { 21 // host provides buildinfo, get it from there. This allows host to cache it 22 buildInfo = host.getBuildInfo(buildInfoPath, compilerOptions.configFilePath); 23 } 24 else { 25 const content = host.readFile(buildInfoPath); 26 if (!content) return undefined; 27 buildInfo = getBuildInfo(buildInfoPath, content); 28 } 29 if (!buildInfo || buildInfo.version !== version || !buildInfo.program) return undefined; 30 return createBuilderProgramUsingProgramBuildInfo(buildInfo.program, buildInfoPath, host); 31 } 32 33 export function createIncrementalCompilerHost(options: CompilerOptions, system = sys): CompilerHost { 34 const host = createCompilerHostWorker(options, /*setParentNodes*/ undefined, system); 35 host.createHash = maybeBind(system, system.createHash); 36 host.disableUseFileVersionAsSignature = system.disableUseFileVersionAsSignature; 37 host.storeFilesChangingSignatureDuringEmit = system.storeFilesChangingSignatureDuringEmit; 38 setGetSourceFileAsHashVersioned(host); 39 changeCompilerHostLikeToUseCache(host, fileName => toPath(fileName, host.getCurrentDirectory(), host.getCanonicalFileName)); 40 return host; 41 } 42 43 export interface IncrementalProgramOptions<T extends BuilderProgram> { 44 rootNames: readonly string[]; 45 options: CompilerOptions; 46 configFileParsingDiagnostics?: readonly Diagnostic[]; 47 projectReferences?: readonly ProjectReference[]; 48 host?: CompilerHost; 49 createProgram?: CreateProgram<T>; 50 } 51 52 export function createIncrementalProgram<T extends BuilderProgram = EmitAndSemanticDiagnosticsBuilderProgram>({ 53 rootNames, options, configFileParsingDiagnostics, projectReferences, host, createProgram 54 }: IncrementalProgramOptions<T>): T { 55 host = host || createIncrementalCompilerHost(options); 56 createProgram = createProgram || createEmitAndSemanticDiagnosticsBuilderProgram as any as CreateProgram<T>; 57 const oldProgram = readBuilderProgram(options, host) as any as T; 58 return createProgram(rootNames, options, host, oldProgram, configFileParsingDiagnostics, projectReferences); 59 } 60 61 export function createIncrementalProgramForArkTs({ 62 rootNames, options, configFileParsingDiagnostics, projectReferences, host 63 }: IncrementalProgramOptions<EmitAndSemanticDiagnosticsBuilderProgram>): EmitAndSemanticDiagnosticsBuilderProgram { 64 host = host || createIncrementalCompilerHost(options); 65 const oldProgram = readBuilderProgram(options, host) as any as EmitAndSemanticDiagnosticsBuilderProgram; 66 return createEmitAndSemanticDiagnosticsBuilderProgramForArkTs( 67 rootNames, options, host, oldProgram, configFileParsingDiagnostics, projectReferences); 68 } 69 70 export type WatchStatusReporter = (diagnostic: Diagnostic, newLine: string, options: CompilerOptions, errorCount?: number) => void; 71 /** Create the program with rootNames and options, if they are undefined, oldProgram and new configFile diagnostics create new program */ 72 export type CreateProgram<T extends BuilderProgram> = (rootNames: readonly string[] | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: T, configFileParsingDiagnostics?: readonly Diagnostic[], projectReferences?: readonly ProjectReference[] | undefined) => T; 73 74 /** Host that has watch functionality used in --watch mode */ 75 export interface WatchHost { 76 /** If provided, called with Diagnostic message that informs about change in watch status */ 77 onWatchStatusChange?(diagnostic: Diagnostic, newLine: string, options: CompilerOptions, errorCount?: number): void; 78 79 /** Used to watch changes in source files, missing files needed to update the program or config file */ 80 watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number, options?: WatchOptions): FileWatcher; 81 /** Used to watch resolved module's failed lookup locations, config file specs, type roots where auto type reference directives are added */ 82 watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean, options?: WatchOptions): FileWatcher; 83 /** If provided, will be used to set delayed compilation, so that multiple changes in short span are compiled together */ 84 setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any; 85 /** If provided, will be used to reset existing delayed compilation */ 86 clearTimeout?(timeoutId: any): void; 87 } 88 export interface ProgramHost<T extends BuilderProgram> { 89 /** 90 * Used to create the program when need for program creation or recreation detected 91 */ 92 createProgram: CreateProgram<T>; 93 94 // Sub set of compiler host methods to read and generate new program 95 useCaseSensitiveFileNames(): boolean; 96 getNewLine(): string; 97 getCurrentDirectory(): string; 98 getDefaultLibFileName(options: CompilerOptions): string; 99 getDefaultLibLocation?(): string; 100 createHash?(data: string): string; 101 102 /** 103 * Use to check file presence for source files and 104 * if resolveModuleNames is not provided (complier is in charge of module resolution) then module files as well 105 */ 106 fileExists(path: string): boolean; 107 /** 108 * Use to read file text for source files and 109 * if resolveModuleNames is not provided (complier is in charge of module resolution) then module files as well 110 */ 111 readFile(path: string, encoding?: string): string | undefined; 112 113 /** If provided, used for module resolution as well as to handle directory structure */ 114 directoryExists?(path: string): boolean; 115 /** If provided, used in resolutions as well as handling directory structure */ 116 getDirectories?(path: string): string[]; 117 /** If provided, used to cache and handle directory structure modifications */ 118 readDirectory?(path: string, extensions?: readonly string[], exclude?: readonly string[], include?: readonly string[], depth?: number): string[]; 119 120 /** Symbol links resolution */ 121 realpath?(path: string): string; 122 /** If provided would be used to write log about compilation */ 123 trace?(s: string): void; 124 /** If provided is used to get the environment variable */ 125 getEnvironmentVariable?(name: string): string | undefined; 126 127 /** If provided, used to resolve the module names, otherwise typescript's default module resolution */ 128 resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingSourceFile?: SourceFile): (ResolvedModule | undefined)[]; 129 /** If provided, used to resolve type reference directives, otherwise typescript's default resolution */ 130 resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[] | readonly FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined): (ResolvedTypeReferenceDirective | undefined)[]; 131 /** If provided along with custom resolveModuleNames or resolveTypeReferenceDirectives, used to determine if unchanged file path needs to re-resolve modules/type reference directives */ 132 hasInvalidatedResolutions?(filePath: Path): boolean; 133 /** 134 * Returns the module resolution cache used by a provided `resolveModuleNames` implementation so that any non-name module resolution operations (eg, package.json lookup) can reuse it 135 */ 136 getModuleResolutionCache?(): ModuleResolutionCache | undefined; 137 } 138 /** Internal interface used to wire emit through same host */ 139 140 /*@internal*/ 141 export interface ProgramHost<T extends BuilderProgram> { 142 // TODO: GH#18217 Optional methods are frequently asserted 143 createDirectory?(path: string): void; 144 writeFile?(path: string, data: string, writeByteOrderMark?: boolean): void; 145 // For testing 146 disableUseFileVersionAsSignature?: boolean; 147 storeFilesChangingSignatureDuringEmit?: boolean; 148 now?(): Date; 149 } 150 151 export interface WatchCompilerHost<T extends BuilderProgram> extends ProgramHost<T>, WatchHost { 152 /** Instead of using output d.ts file from project reference, use its source file */ 153 useSourceOfProjectReferenceRedirect?(): boolean; 154 155 /** If provided, use this method to get parsed command lines for referenced projects */ 156 getParsedCommandLine?(fileName: string): ParsedCommandLine | undefined; 157 158 /** If provided, callback to invoke after every new program creation */ 159 afterProgramCreate?(program: T): void; 160 } 161 162 /** 163 * Host to create watch with root files and options 164 */ 165 export interface WatchCompilerHostOfFilesAndCompilerOptions<T extends BuilderProgram> extends WatchCompilerHost<T> { 166 /** root files to use to generate program */ 167 rootFiles: string[]; 168 169 /** Compiler options */ 170 options: CompilerOptions; 171 172 watchOptions?: WatchOptions; 173 174 /** Project References */ 175 projectReferences?: readonly ProjectReference[]; 176 } 177 178 /** 179 * Host to create watch with config file 180 */ 181 export interface WatchCompilerHostOfConfigFile<T extends BuilderProgram> extends WatchCompilerHost<T>, ConfigFileDiagnosticsReporter { 182 /** Name of the config file to compile */ 183 configFileName: string; 184 185 /** Options to extend */ 186 optionsToExtend?: CompilerOptions; 187 188 watchOptionsToExtend?: WatchOptions; 189 190 extraFileExtensions?: readonly FileExtensionInfo[] 191 192 /** 193 * Used to generate source file names from the config file and its include, exclude, files rules 194 * and also to cache the directory stucture 195 */ 196 readDirectory(path: string, extensions?: readonly string[], exclude?: readonly string[], include?: readonly string[], depth?: number): string[]; 197 } 198 199 /** 200 * Host to create watch with config file that is already parsed (from tsc) 201 */ 202 /*@internal*/ 203 export interface WatchCompilerHostOfConfigFile<T extends BuilderProgram> extends WatchCompilerHost<T> { 204 configFileParsingResult?: ParsedCommandLine; 205 extendedConfigCache?: Map<ExtendedConfigCacheEntry>; 206 } 207 208 export interface Watch<T> { 209 /** Synchronize with host and get updated program */ 210 getProgram(): T; 211 /** Gets the existing program without synchronizing with changes on host */ 212 /*@internal*/ 213 getCurrentProgram(): T; 214 /** Closes the watch */ 215 close(): void; 216 } 217 218 /** 219 * Creates the watch what generates program using the config file 220 */ 221 export interface WatchOfConfigFile<T> extends Watch<T> { 222 } 223 224 /** 225 * Creates the watch that generates program using the root files and compiler options 226 */ 227 export interface WatchOfFilesAndCompilerOptions<T> extends Watch<T> { 228 /** Updates the root files in the program, only if this is not config file compilation */ 229 updateRootFileNames(fileNames: string[]): void; 230 } 231 232 /** 233 * Create the watch compiler host for either configFile or fileNames and its options 234 */ 235 export function createWatchCompilerHost<T extends BuilderProgram>(configFileName: string, optionsToExtend: CompilerOptions | undefined, system: System, createProgram?: CreateProgram<T>, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter, watchOptionsToExtend?: WatchOptions, extraFileExtensions?: readonly FileExtensionInfo[]): WatchCompilerHostOfConfigFile<T>; 236 export function createWatchCompilerHost<T extends BuilderProgram>(rootFiles: string[], options: CompilerOptions, system: System, createProgram?: CreateProgram<T>, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter, projectReferences?: readonly ProjectReference[], watchOptions?: WatchOptions): WatchCompilerHostOfFilesAndCompilerOptions<T>; 237 export function createWatchCompilerHost<T extends BuilderProgram>(rootFilesOrConfigFileName: string | string[], options: CompilerOptions | undefined, system: System, createProgram?: CreateProgram<T>, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter, projectReferencesOrWatchOptionsToExtend?: readonly ProjectReference[] | WatchOptions, watchOptionsOrExtraFileExtensions?: WatchOptions | readonly FileExtensionInfo[]): WatchCompilerHostOfFilesAndCompilerOptions<T> | WatchCompilerHostOfConfigFile<T> { 238 if (isArray(rootFilesOrConfigFileName)) { 239 return createWatchCompilerHostOfFilesAndCompilerOptions({ 240 rootFiles: rootFilesOrConfigFileName, 241 options: options!, 242 watchOptions: watchOptionsOrExtraFileExtensions as WatchOptions, 243 projectReferences: projectReferencesOrWatchOptionsToExtend as readonly ProjectReference[], 244 system, 245 createProgram, 246 reportDiagnostic, 247 reportWatchStatus, 248 }); 249 } 250 else { 251 return createWatchCompilerHostOfConfigFile({ 252 configFileName: rootFilesOrConfigFileName, 253 optionsToExtend: options, 254 watchOptionsToExtend: projectReferencesOrWatchOptionsToExtend as WatchOptions, 255 extraFileExtensions: watchOptionsOrExtraFileExtensions as readonly FileExtensionInfo[], 256 system, 257 createProgram, 258 reportDiagnostic, 259 reportWatchStatus, 260 }); 261 } 262 } 263 264 interface ParsedConfig { 265 /** ParsedCommandLine for the config file if present */ 266 parsedCommandLine: ParsedCommandLine | undefined; 267 /** File watcher of the config file */ 268 watcher?: FileWatcher; 269 /** Wild card directories watched from this config file */ 270 watchedDirectories?: Map<WildcardDirectoryWatcher>; 271 /** Reload to be done for this config file */ 272 reloadLevel?: ConfigFileProgramReloadLevel.Partial | ConfigFileProgramReloadLevel.Full; 273 } 274 275 /** 276 * Creates the watch from the host for root files and compiler options 277 */ 278 export function createWatchProgram<T extends BuilderProgram>(host: WatchCompilerHostOfFilesAndCompilerOptions<T>): WatchOfFilesAndCompilerOptions<T>; 279 /** 280 * Creates the watch from the host for config file 281 */ 282 export function createWatchProgram<T extends BuilderProgram>(host: WatchCompilerHostOfConfigFile<T>): WatchOfConfigFile<T>; 283 export function createWatchProgram<T extends BuilderProgram>(host: WatchCompilerHostOfFilesAndCompilerOptions<T> & WatchCompilerHostOfConfigFile<T>): WatchOfFilesAndCompilerOptions<T> | WatchOfConfigFile<T> { 284 interface FilePresentOnHost { 285 version: string; 286 sourceFile: SourceFile; 287 fileWatcher: FileWatcher; 288 } 289 type FileMissingOnHost = false; 290 interface FilePresenceUnknownOnHost { 291 version: false; 292 fileWatcher?: FileWatcher; 293 } 294 type FileMayBePresentOnHost = FilePresentOnHost | FilePresenceUnknownOnHost; 295 type HostFileInfo = FilePresentOnHost | FileMissingOnHost | FilePresenceUnknownOnHost; 296 297 let builderProgram: T; 298 let reloadLevel: ConfigFileProgramReloadLevel; // level to indicate if the program needs to be reloaded from config file/just filenames etc 299 let missingFilesMap: ESMap<Path, FileWatcher>; // Map of file watchers for the missing files 300 let watchedWildcardDirectories: ESMap<string, WildcardDirectoryWatcher>; // map of watchers for the wild card directories in the config file 301 let timerToUpdateProgram: any; // timer callback to recompile the program 302 let timerToInvalidateFailedLookupResolutions: any; // timer callback to invalidate resolutions for changes in failed lookup locations 303 let parsedConfigs: ESMap<Path, ParsedConfig> | undefined; // Parsed commandline and watching cached for referenced projects 304 let sharedExtendedConfigFileWatchers: ESMap<Path, SharedExtendedConfigFileWatcher<Path>>; // Map of file watchers for extended files, shared between different referenced projects 305 let extendedConfigCache = host.extendedConfigCache; // Cache for extended config evaluation 306 let reportFileChangeDetectedOnCreateProgram = false; // True if synchronizeProgram should report "File change detected..." when a new program is created 307 308 const sourceFilesCache = new Map<string, HostFileInfo>(); // Cache that stores the source file and version info 309 let missingFilePathsRequestedForRelease: Path[] | undefined; // These paths are held temporarily so that we can remove the entry from source file cache if the file is not tracked by missing files 310 let hasChangedCompilerOptions = false; // True if the compiler options have changed between compilations 311 312 const useCaseSensitiveFileNames = host.useCaseSensitiveFileNames(); 313 const currentDirectory = host.getCurrentDirectory(); 314 const { configFileName, optionsToExtend: optionsToExtendForConfigFile = {}, watchOptionsToExtend, extraFileExtensions, createProgram } = host; 315 let { rootFiles: rootFileNames, options: compilerOptions, watchOptions, projectReferences } = host; 316 let wildcardDirectories: MapLike<WatchDirectoryFlags> | undefined; 317 let configFileParsingDiagnostics: Diagnostic[] | undefined; 318 let canConfigFileJsonReportNoInputFiles = false; 319 let hasChangedConfigFileParsingErrors = false; 320 321 const cachedDirectoryStructureHost = configFileName === undefined ? undefined : createCachedDirectoryStructureHost(host, currentDirectory, useCaseSensitiveFileNames); 322 const directoryStructureHost: DirectoryStructureHost = cachedDirectoryStructureHost || host; 323 const parseConfigFileHost = parseConfigHostFromCompilerHostLike(host, directoryStructureHost); 324 325 // From tsc we want to get already parsed result and hence check for rootFileNames 326 let newLine = updateNewLine(); 327 if (configFileName && host.configFileParsingResult) { 328 setConfigFileParsingResult(host.configFileParsingResult); 329 newLine = updateNewLine(); 330 } 331 reportWatchDiagnostic(Diagnostics.Starting_compilation_in_watch_mode); 332 if (configFileName && !host.configFileParsingResult) { 333 newLine = getNewLineCharacter(optionsToExtendForConfigFile, () => host.getNewLine()); 334 Debug.assert(!rootFileNames); 335 parseConfigFile(); 336 newLine = updateNewLine(); 337 } 338 339 const { watchFile, watchDirectory, writeLog } = createWatchFactory(host, compilerOptions); 340 const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames); 341 342 writeLog(`Current directory: ${currentDirectory} CaseSensitiveFileNames: ${useCaseSensitiveFileNames}`); 343 let configFileWatcher: FileWatcher | undefined; 344 if (configFileName) { 345 configFileWatcher = watchFile(configFileName, scheduleProgramReload, PollingInterval.High, watchOptions, WatchType.ConfigFile); 346 } 347 348 const compilerHost = createCompilerHostFromProgramHost(host, () => compilerOptions, directoryStructureHost) as CompilerHost & ResolutionCacheHost; 349 setGetSourceFileAsHashVersioned(compilerHost); 350 // Members for CompilerHost 351 const getNewSourceFile = compilerHost.getSourceFile; 352 compilerHost.getSourceFile = (fileName, ...args) => getVersionedSourceFileByPath(fileName, toPath(fileName), ...args); 353 compilerHost.getSourceFileByPath = getVersionedSourceFileByPath; 354 compilerHost.getNewLine = () => newLine; 355 compilerHost.fileExists = fileExists; 356 compilerHost.onReleaseOldSourceFile = onReleaseOldSourceFile; 357 compilerHost.onReleaseParsedCommandLine = onReleaseParsedCommandLine; 358 // Members for ResolutionCacheHost 359 compilerHost.toPath = toPath; 360 compilerHost.getCompilationSettings = () => compilerOptions; 361 compilerHost.useSourceOfProjectReferenceRedirect = maybeBind(host, host.useSourceOfProjectReferenceRedirect); 362 compilerHost.watchDirectoryOfFailedLookupLocation = (dir, cb, flags) => watchDirectory(dir, cb, flags, watchOptions, WatchType.FailedLookupLocations); 363 compilerHost.watchAffectingFileLocation = (file, cb) => watchFile(file, cb, PollingInterval.High, watchOptions, WatchType.AffectingFileLocation); 364 compilerHost.watchTypeRootsDirectory = (dir, cb, flags) => watchDirectory(dir, cb, flags, watchOptions, WatchType.TypeRoots); 365 compilerHost.getCachedDirectoryStructureHost = () => cachedDirectoryStructureHost; 366 compilerHost.scheduleInvalidateResolutionsOfFailedLookupLocations = scheduleInvalidateResolutionsOfFailedLookupLocations; 367 compilerHost.onInvalidatedResolution = scheduleProgramUpdate; 368 compilerHost.onChangedAutomaticTypeDirectiveNames = scheduleProgramUpdate; 369 compilerHost.fileIsOpen = returnFalse; 370 compilerHost.getCurrentProgram = getCurrentProgram; 371 compilerHost.writeLog = writeLog; 372 compilerHost.getParsedCommandLine = getParsedCommandLine; 373 374 // Cache for the module resolution 375 const resolutionCache = createResolutionCache(compilerHost, 376 configFileName ? 377 getDirectoryPath(getNormalizedAbsolutePath(configFileName, currentDirectory)) : 378 currentDirectory, 379 /*logChangesWhenResolvingModule*/ false 380 ); 381 // Resolve module using host module resolution strategy if provided otherwise use resolution cache to resolve module names 382 compilerHost.resolveModuleNames = host.resolveModuleNames ? 383 ((...args) => host.resolveModuleNames!(...args)) : 384 ((moduleNames, containingFile, reusedNames, redirectedReference, _options, sourceFile) => resolutionCache.resolveModuleNames(moduleNames, containingFile, reusedNames, redirectedReference, sourceFile)); 385 compilerHost.resolveTypeReferenceDirectives = host.resolveTypeReferenceDirectives ? 386 ((...args) => host.resolveTypeReferenceDirectives!(...args)) : 387 ((typeDirectiveNames, containingFile, redirectedReference, _options, containingFileMode) => resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile, redirectedReference, containingFileMode)); 388 compilerHost.getModuleResolutionCache = host.resolveModuleNames ? 389 maybeBind(host, host.getModuleResolutionCache) : 390 (() => resolutionCache.getModuleResolutionCache()); 391 const userProvidedResolution = !!host.resolveModuleNames || !!host.resolveTypeReferenceDirectives; 392 // All resolutions are invalid if user provided resolutions and didnt supply hasInvalidatedResolutions 393 const customHasInvalidatedResolutions = userProvidedResolution ? 394 maybeBind(host, host.hasInvalidatedResolutions) || returnTrue : 395 returnFalse; 396 397 builderProgram = readBuilderProgram(compilerOptions, compilerHost) as any as T; 398 synchronizeProgram(); 399 400 // Update the wild card directory watch 401 watchConfigFileWildCardDirectories(); 402 403 // Update extended config file watch 404 if (configFileName) updateExtendedConfigFilesWatches(toPath(configFileName), compilerOptions, watchOptions, WatchType.ExtendedConfigFile); 405 406 return configFileName ? 407 { getCurrentProgram: getCurrentBuilderProgram, getProgram: updateProgram, close } : 408 { getCurrentProgram: getCurrentBuilderProgram, getProgram: updateProgram, updateRootFileNames, close }; 409 410 function close() { 411 clearInvalidateResolutionsOfFailedLookupLocations(); 412 resolutionCache.clear(); 413 clearMap(sourceFilesCache, value => { 414 if (value && value.fileWatcher) { 415 value.fileWatcher.close(); 416 value.fileWatcher = undefined; 417 } 418 }); 419 if (configFileWatcher) { 420 configFileWatcher.close(); 421 configFileWatcher = undefined; 422 } 423 extendedConfigCache?.clear(); 424 extendedConfigCache = undefined; 425 if (sharedExtendedConfigFileWatchers) { 426 clearMap(sharedExtendedConfigFileWatchers, closeFileWatcherOf); 427 sharedExtendedConfigFileWatchers = undefined!; 428 } 429 if (watchedWildcardDirectories) { 430 clearMap(watchedWildcardDirectories, closeFileWatcherOf); 431 watchedWildcardDirectories = undefined!; 432 } 433 if (missingFilesMap) { 434 clearMap(missingFilesMap, closeFileWatcher); 435 missingFilesMap = undefined!; 436 } 437 if (parsedConfigs) { 438 clearMap(parsedConfigs, config => { 439 config.watcher?.close(); 440 config.watcher = undefined; 441 if (config.watchedDirectories) clearMap(config.watchedDirectories, closeFileWatcherOf); 442 config.watchedDirectories = undefined; 443 }); 444 parsedConfigs = undefined; 445 } 446 } 447 448 function getCurrentBuilderProgram() { 449 return builderProgram; 450 } 451 452 function getCurrentProgram() { 453 return builderProgram && builderProgram.getProgramOrUndefined(); 454 } 455 456 function synchronizeProgram() { 457 writeLog(`Synchronizing program`); 458 clearInvalidateResolutionsOfFailedLookupLocations(); 459 460 const program = getCurrentBuilderProgram(); 461 if (hasChangedCompilerOptions) { 462 newLine = updateNewLine(); 463 if (program && changesAffectModuleResolution(program.getCompilerOptions(), compilerOptions)) { 464 resolutionCache.clear(); 465 } 466 } 467 468 const hasInvalidatedResolutions = resolutionCache.createHasInvalidatedResolutions(customHasInvalidatedResolutions); 469 const { 470 originalReadFile, originalFileExists, originalDirectoryExists, 471 originalCreateDirectory, originalWriteFile, readFileWithCache, 472 } = changeCompilerHostLikeToUseCache(compilerHost, toPath); 473 if (isProgramUptoDate(getCurrentProgram(), rootFileNames, compilerOptions, path => getSourceVersion(path, readFileWithCache), fileName => compilerHost.fileExists(fileName), hasInvalidatedResolutions, hasChangedAutomaticTypeDirectiveNames, getParsedCommandLine, projectReferences)) { 474 if (hasChangedConfigFileParsingErrors) { 475 if (reportFileChangeDetectedOnCreateProgram) { 476 reportWatchDiagnostic(Diagnostics.File_change_detected_Starting_incremental_compilation); 477 } 478 builderProgram = createProgram(/*rootNames*/ undefined, /*options*/ undefined, compilerHost, builderProgram, configFileParsingDiagnostics, projectReferences); 479 hasChangedConfigFileParsingErrors = false; 480 } 481 } 482 else { 483 if (reportFileChangeDetectedOnCreateProgram) { 484 reportWatchDiagnostic(Diagnostics.File_change_detected_Starting_incremental_compilation); 485 } 486 createNewProgram(hasInvalidatedResolutions); 487 } 488 489 reportFileChangeDetectedOnCreateProgram = false; 490 if (host.afterProgramCreate && program !== builderProgram) { 491 host.afterProgramCreate(builderProgram); 492 } 493 494 compilerHost.readFile = originalReadFile; 495 compilerHost.fileExists = originalFileExists; 496 compilerHost.directoryExists = originalDirectoryExists; 497 compilerHost.createDirectory = originalCreateDirectory; 498 compilerHost.writeFile = originalWriteFile!; 499 500 return builderProgram; 501 } 502 503 function createNewProgram(hasInvalidatedResolutions: HasInvalidatedResolutions) { 504 // Compile the program 505 writeLog("CreatingProgramWith::"); 506 writeLog(` roots: ${JSON.stringify(rootFileNames)}`); 507 writeLog(` options: ${JSON.stringify(compilerOptions)}`); 508 if (projectReferences) writeLog(` projectReferences: ${JSON.stringify(projectReferences)}`); 509 510 const needsUpdateInTypeRootWatch = hasChangedCompilerOptions || !getCurrentProgram(); 511 hasChangedCompilerOptions = false; 512 hasChangedConfigFileParsingErrors = false; 513 resolutionCache.startCachingPerDirectoryResolution(); 514 compilerHost.hasInvalidatedResolutions = hasInvalidatedResolutions; 515 compilerHost.hasChangedAutomaticTypeDirectiveNames = hasChangedAutomaticTypeDirectiveNames; 516 const oldProgram = getCurrentProgram(); 517 builderProgram = createProgram(rootFileNames, compilerOptions, compilerHost, builderProgram, configFileParsingDiagnostics, projectReferences); 518 resolutionCache.finishCachingPerDirectoryResolution(builderProgram.getProgram(), oldProgram); 519 520 // Update watches 521 updateMissingFilePathsWatch(builderProgram.getProgram(), missingFilesMap || (missingFilesMap = new Map()), watchMissingFilePath); 522 if (needsUpdateInTypeRootWatch) { 523 resolutionCache.updateTypeRootsWatch(); 524 } 525 526 if (missingFilePathsRequestedForRelease) { 527 // These are the paths that program creater told us as not in use any more but were missing on the disk. 528 // We didnt remove the entry for them from sourceFiles cache so that we dont have to do File IO, 529 // if there is already watcher for it (for missing files) 530 // At this point our watches were updated, hence now we know that these paths are not tracked and need to be removed 531 // so that at later time we have correct result of their presence 532 for (const missingFilePath of missingFilePathsRequestedForRelease) { 533 if (!missingFilesMap.has(missingFilePath)) { 534 sourceFilesCache.delete(missingFilePath); 535 } 536 } 537 missingFilePathsRequestedForRelease = undefined; 538 } 539 } 540 541 function updateRootFileNames(files: string[]) { 542 Debug.assert(!configFileName, "Cannot update root file names with config file watch mode"); 543 rootFileNames = files; 544 scheduleProgramUpdate(); 545 } 546 547 function updateNewLine() { 548 return getNewLineCharacter(compilerOptions || optionsToExtendForConfigFile, () => host.getNewLine()); 549 } 550 551 function toPath(fileName: string) { 552 return ts.toPath(fileName, currentDirectory, getCanonicalFileName); 553 } 554 555 function isFileMissingOnHost(hostSourceFile: HostFileInfo | undefined): hostSourceFile is FileMissingOnHost { 556 return typeof hostSourceFile === "boolean"; 557 } 558 559 function isFilePresenceUnknownOnHost(hostSourceFile: FileMayBePresentOnHost): hostSourceFile is FilePresenceUnknownOnHost { 560 return typeof (hostSourceFile as FilePresenceUnknownOnHost).version === "boolean"; 561 } 562 563 function fileExists(fileName: string) { 564 const path = toPath(fileName); 565 // If file is missing on host from cache, we can definitely say file doesnt exist 566 // otherwise we need to ensure from the disk 567 if (isFileMissingOnHost(sourceFilesCache.get(path))) { 568 return false; 569 } 570 571 return directoryStructureHost.fileExists(fileName); 572 } 573 574 function getVersionedSourceFileByPath(fileName: string, path: Path, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined { 575 const hostSourceFile = sourceFilesCache.get(path); 576 // No source file on the host 577 if (isFileMissingOnHost(hostSourceFile)) { 578 return undefined; 579 } 580 581 // Create new source file if requested or the versions dont match 582 if (hostSourceFile === undefined || shouldCreateNewSourceFile || isFilePresenceUnknownOnHost(hostSourceFile)) { 583 const sourceFile = getNewSourceFile(fileName, languageVersionOrOptions, onError); 584 if (hostSourceFile) { 585 if (sourceFile) { 586 // Set the source file and create file watcher now that file was present on the disk 587 (hostSourceFile as FilePresentOnHost).sourceFile = sourceFile; 588 hostSourceFile.version = sourceFile.version; 589 if (!hostSourceFile.fileWatcher) { 590 hostSourceFile.fileWatcher = watchFilePath(path, fileName, onSourceFileChange, PollingInterval.Low, watchOptions, WatchType.SourceFile); 591 } 592 } 593 else { 594 // There is no source file on host any more, close the watch, missing file paths will track it 595 if (hostSourceFile.fileWatcher) { 596 hostSourceFile.fileWatcher.close(); 597 } 598 sourceFilesCache.set(path, false); 599 } 600 } 601 else { 602 if (sourceFile) { 603 const fileWatcher = watchFilePath(path, fileName, onSourceFileChange, PollingInterval.Low, watchOptions, WatchType.SourceFile); 604 sourceFilesCache.set(path, { sourceFile, version: sourceFile.version, fileWatcher }); 605 } 606 else { 607 sourceFilesCache.set(path, false); 608 } 609 } 610 return sourceFile; 611 } 612 return hostSourceFile.sourceFile; 613 } 614 615 function nextSourceFileVersion(path: Path) { 616 const hostSourceFile = sourceFilesCache.get(path); 617 if (hostSourceFile !== undefined) { 618 if (isFileMissingOnHost(hostSourceFile)) { 619 // The next version, lets set it as presence unknown file 620 sourceFilesCache.set(path, { version: false }); 621 } 622 else { 623 (hostSourceFile as FilePresenceUnknownOnHost).version = false; 624 } 625 } 626 } 627 628 function getSourceVersion(path: Path, readFileWithCache: (fileName: string) => string | undefined): string | undefined { 629 const hostSourceFile = sourceFilesCache.get(path); 630 if (!hostSourceFile) return undefined; 631 if (hostSourceFile.version) return hostSourceFile.version; 632 // Read file and get new version 633 const text = readFileWithCache(path); 634 return text !== undefined ? (compilerHost.createHash || generateDjb2Hash)(text) : undefined; 635 } 636 637 function onReleaseOldSourceFile(oldSourceFile: SourceFile, _oldOptions: CompilerOptions, hasSourceFileByPath: boolean) { 638 const hostSourceFileInfo = sourceFilesCache.get(oldSourceFile.resolvedPath); 639 // If this is the source file thats in the cache and new program doesnt need it, 640 // remove the cached entry. 641 // Note we arent deleting entry if file became missing in new program or 642 // there was version update and new source file was created. 643 if (hostSourceFileInfo !== undefined) { 644 // record the missing file paths so they can be removed later if watchers arent tracking them 645 if (isFileMissingOnHost(hostSourceFileInfo)) { 646 (missingFilePathsRequestedForRelease || (missingFilePathsRequestedForRelease = [])).push(oldSourceFile.path); 647 } 648 else if ((hostSourceFileInfo as FilePresentOnHost).sourceFile === oldSourceFile) { 649 if (hostSourceFileInfo.fileWatcher) { 650 hostSourceFileInfo.fileWatcher.close(); 651 } 652 sourceFilesCache.delete(oldSourceFile.resolvedPath); 653 if (!hasSourceFileByPath) { 654 resolutionCache.removeResolutionsOfFile(oldSourceFile.path); 655 } 656 } 657 } 658 } 659 660 function reportWatchDiagnostic(message: DiagnosticMessage) { 661 if (host.onWatchStatusChange) { 662 host.onWatchStatusChange(createCompilerDiagnostic(message), newLine, compilerOptions || optionsToExtendForConfigFile); 663 } 664 } 665 666 function hasChangedAutomaticTypeDirectiveNames() { 667 return resolutionCache.hasChangedAutomaticTypeDirectiveNames(); 668 } 669 670 function clearInvalidateResolutionsOfFailedLookupLocations() { 671 if (!timerToInvalidateFailedLookupResolutions) return false; 672 host.clearTimeout!(timerToInvalidateFailedLookupResolutions); 673 timerToInvalidateFailedLookupResolutions = undefined; 674 return true; 675 } 676 677 function scheduleInvalidateResolutionsOfFailedLookupLocations() { 678 if (!host.setTimeout || !host.clearTimeout) { 679 return resolutionCache.invalidateResolutionsOfFailedLookupLocations(); 680 } 681 const pending = clearInvalidateResolutionsOfFailedLookupLocations(); 682 writeLog(`Scheduling invalidateFailedLookup${pending ? ", Cancelled earlier one" : ""}`); 683 timerToInvalidateFailedLookupResolutions = host.setTimeout(invalidateResolutionsOfFailedLookup, 250); 684 } 685 686 function invalidateResolutionsOfFailedLookup() { 687 timerToInvalidateFailedLookupResolutions = undefined; 688 if (resolutionCache.invalidateResolutionsOfFailedLookupLocations()) { 689 scheduleProgramUpdate(); 690 } 691 } 692 693 // Upon detecting a file change, wait for 250ms and then perform a recompilation. This gives batch 694 // operations (such as saving all modified files in an editor) a chance to complete before we kick 695 // off a new compilation. 696 function scheduleProgramUpdate() { 697 if (!host.setTimeout || !host.clearTimeout) { 698 return; 699 } 700 701 if (timerToUpdateProgram) { 702 host.clearTimeout(timerToUpdateProgram); 703 } 704 writeLog("Scheduling update"); 705 timerToUpdateProgram = host.setTimeout(updateProgramWithWatchStatus, 250); 706 } 707 708 function scheduleProgramReload() { 709 Debug.assert(!!configFileName); 710 reloadLevel = ConfigFileProgramReloadLevel.Full; 711 scheduleProgramUpdate(); 712 } 713 714 function updateProgramWithWatchStatus() { 715 timerToUpdateProgram = undefined; 716 reportFileChangeDetectedOnCreateProgram = true; 717 updateProgram(); 718 } 719 720 function updateProgram() { 721 switch (reloadLevel) { 722 case ConfigFileProgramReloadLevel.Partial: 723 perfLogger.logStartUpdateProgram("PartialConfigReload"); 724 reloadFileNamesFromConfigFile(); 725 break; 726 case ConfigFileProgramReloadLevel.Full: 727 perfLogger.logStartUpdateProgram("FullConfigReload"); 728 reloadConfigFile(); 729 break; 730 default: 731 perfLogger.logStartUpdateProgram("SynchronizeProgram"); 732 synchronizeProgram(); 733 break; 734 } 735 perfLogger.logStopUpdateProgram("Done"); 736 return getCurrentBuilderProgram(); 737 } 738 739 function reloadFileNamesFromConfigFile() { 740 writeLog("Reloading new file names and options"); 741 reloadLevel = ConfigFileProgramReloadLevel.None; 742 rootFileNames = getFileNamesFromConfigSpecs(compilerOptions.configFile!.configFileSpecs!, getNormalizedAbsolutePath(getDirectoryPath(configFileName), currentDirectory), compilerOptions, parseConfigFileHost, extraFileExtensions); 743 if (updateErrorForNoInputFiles(rootFileNames, getNormalizedAbsolutePath(configFileName, currentDirectory), compilerOptions.configFile!.configFileSpecs!, configFileParsingDiagnostics!, canConfigFileJsonReportNoInputFiles)) { 744 hasChangedConfigFileParsingErrors = true; 745 } 746 747 // Update the program 748 synchronizeProgram(); 749 } 750 751 function reloadConfigFile() { 752 writeLog(`Reloading config file: ${configFileName}`); 753 reloadLevel = ConfigFileProgramReloadLevel.None; 754 755 if (cachedDirectoryStructureHost) { 756 cachedDirectoryStructureHost.clearCache(); 757 } 758 parseConfigFile(); 759 hasChangedCompilerOptions = true; 760 synchronizeProgram(); 761 762 // Update the wild card directory watch 763 watchConfigFileWildCardDirectories(); 764 765 // Update extended config file watch 766 updateExtendedConfigFilesWatches(toPath(configFileName), compilerOptions, watchOptions, WatchType.ExtendedConfigFile); 767 } 768 769 function parseConfigFile() { 770 setConfigFileParsingResult(getParsedCommandLineOfConfigFile( 771 configFileName, 772 optionsToExtendForConfigFile, 773 parseConfigFileHost, 774 extendedConfigCache ||= new Map(), 775 watchOptionsToExtend, 776 extraFileExtensions 777 )!); // TODO: GH#18217 778 } 779 780 function setConfigFileParsingResult(configFileParseResult: ParsedCommandLine) { 781 rootFileNames = configFileParseResult.fileNames; 782 compilerOptions = configFileParseResult.options; 783 watchOptions = configFileParseResult.watchOptions; 784 projectReferences = configFileParseResult.projectReferences; 785 wildcardDirectories = configFileParseResult.wildcardDirectories; 786 configFileParsingDiagnostics = getConfigFileParsingDiagnostics(configFileParseResult).slice(); 787 canConfigFileJsonReportNoInputFiles = canJsonReportNoInputFiles(configFileParseResult.raw); 788 hasChangedConfigFileParsingErrors = true; 789 } 790 791 function getParsedCommandLine(configFileName: string): ParsedCommandLine | undefined { 792 const configPath = toPath(configFileName); 793 let config = parsedConfigs?.get(configPath); 794 if (config) { 795 if (!config.reloadLevel) return config.parsedCommandLine; 796 // With host implementing getParsedCommandLine we cant just update file names 797 if (config.parsedCommandLine && config.reloadLevel === ConfigFileProgramReloadLevel.Partial && !host.getParsedCommandLine) { 798 writeLog("Reloading new file names and options"); 799 const fileNames = getFileNamesFromConfigSpecs( 800 config.parsedCommandLine.options.configFile!.configFileSpecs!, 801 getNormalizedAbsolutePath(getDirectoryPath(configFileName), currentDirectory), 802 compilerOptions, 803 parseConfigFileHost, 804 ); 805 config.parsedCommandLine = { ...config.parsedCommandLine, fileNames }; 806 config.reloadLevel = undefined; 807 return config.parsedCommandLine; 808 } 809 } 810 811 writeLog(`Loading config file: ${configFileName}`); 812 const parsedCommandLine = host.getParsedCommandLine ? 813 host.getParsedCommandLine(configFileName) : 814 getParsedCommandLineFromConfigFileHost(configFileName); 815 if (config) { 816 config.parsedCommandLine = parsedCommandLine; 817 config.reloadLevel = undefined; 818 } 819 else { 820 (parsedConfigs ||= new Map()).set(configPath, config = { parsedCommandLine }); 821 } 822 watchReferencedProject(configFileName, configPath, config); 823 return parsedCommandLine; 824 } 825 826 function getParsedCommandLineFromConfigFileHost(configFileName: string) { 827 // Ignore the file absent errors 828 const onUnRecoverableConfigFileDiagnostic = parseConfigFileHost.onUnRecoverableConfigFileDiagnostic; 829 parseConfigFileHost.onUnRecoverableConfigFileDiagnostic = noop; 830 const parsedCommandLine = getParsedCommandLineOfConfigFile( 831 configFileName, 832 /*optionsToExtend*/ undefined, 833 parseConfigFileHost, 834 extendedConfigCache ||= new Map(), 835 watchOptionsToExtend 836 ); 837 parseConfigFileHost.onUnRecoverableConfigFileDiagnostic = onUnRecoverableConfigFileDiagnostic; 838 return parsedCommandLine; 839 } 840 841 function onReleaseParsedCommandLine(fileName: string) { 842 const path = toPath(fileName); 843 const config = parsedConfigs?.get(path); 844 if (!config) return; 845 846 parsedConfigs!.delete(path); 847 if (config.watchedDirectories) clearMap(config.watchedDirectories, closeFileWatcherOf); 848 config.watcher?.close(); 849 clearSharedExtendedConfigFileWatcher(path, sharedExtendedConfigFileWatchers); 850 } 851 852 function watchFilePath( 853 path: Path, 854 file: string, 855 callback: (fileName: string, eventKind: FileWatcherEventKind, filePath: Path) => void, 856 pollingInterval: PollingInterval, 857 options: WatchOptions | undefined, 858 watchType: WatchType 859 ): FileWatcher { 860 return watchFile(file, (fileName, eventKind) => callback(fileName, eventKind, path), pollingInterval, options, watchType); 861 } 862 863 function onSourceFileChange(fileName: string, eventKind: FileWatcherEventKind, path: Path) { 864 updateCachedSystemWithFile(fileName, path, eventKind); 865 866 // Update the source file cache 867 if (eventKind === FileWatcherEventKind.Deleted && sourceFilesCache.has(path)) { 868 resolutionCache.invalidateResolutionOfFile(path); 869 } 870 nextSourceFileVersion(path); 871 872 // Update the program 873 scheduleProgramUpdate(); 874 } 875 876 function updateCachedSystemWithFile(fileName: string, path: Path, eventKind: FileWatcherEventKind) { 877 if (cachedDirectoryStructureHost) { 878 cachedDirectoryStructureHost.addOrDeleteFile(fileName, path, eventKind); 879 } 880 } 881 882 function watchMissingFilePath(missingFilePath: Path) { 883 // If watching missing referenced config file, we are already watching it so no need for separate watcher 884 return parsedConfigs?.has(missingFilePath) ? 885 noopFileWatcher : 886 watchFilePath(missingFilePath, missingFilePath, onMissingFileChange, PollingInterval.Medium, watchOptions, WatchType.MissingFile); 887 } 888 889 function onMissingFileChange(fileName: string, eventKind: FileWatcherEventKind, missingFilePath: Path) { 890 updateCachedSystemWithFile(fileName, missingFilePath, eventKind); 891 892 if (eventKind === FileWatcherEventKind.Created && missingFilesMap.has(missingFilePath)) { 893 missingFilesMap.get(missingFilePath)!.close(); 894 missingFilesMap.delete(missingFilePath); 895 896 // Delete the entry in the source files cache so that new source file is created 897 nextSourceFileVersion(missingFilePath); 898 899 // When a missing file is created, we should update the graph. 900 scheduleProgramUpdate(); 901 } 902 } 903 904 function watchConfigFileWildCardDirectories() { 905 if (wildcardDirectories) { 906 updateWatchingWildcardDirectories( 907 watchedWildcardDirectories || (watchedWildcardDirectories = new Map()), 908 new Map(getEntries(wildcardDirectories)), 909 watchWildcardDirectory 910 ); 911 } 912 else if (watchedWildcardDirectories) { 913 clearMap(watchedWildcardDirectories, closeFileWatcherOf); 914 } 915 } 916 917 function watchWildcardDirectory(directory: string, flags: WatchDirectoryFlags) { 918 return watchDirectory( 919 directory, 920 fileOrDirectory => { 921 Debug.assert(!!configFileName); 922 923 const fileOrDirectoryPath = toPath(fileOrDirectory); 924 925 // Since the file existence changed, update the sourceFiles cache 926 if (cachedDirectoryStructureHost) { 927 cachedDirectoryStructureHost.addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath); 928 } 929 nextSourceFileVersion(fileOrDirectoryPath); 930 931 if (isIgnoredFileFromWildCardWatching({ 932 watchedDirPath: toPath(directory), 933 fileOrDirectory, 934 fileOrDirectoryPath, 935 configFileName, 936 extraFileExtensions, 937 options: compilerOptions, 938 program: getCurrentBuilderProgram() || rootFileNames, 939 currentDirectory, 940 useCaseSensitiveFileNames, 941 writeLog, 942 toPath, 943 })) return; 944 945 // Reload is pending, do the reload 946 if (reloadLevel !== ConfigFileProgramReloadLevel.Full) { 947 reloadLevel = ConfigFileProgramReloadLevel.Partial; 948 949 // Schedule Update the program 950 scheduleProgramUpdate(); 951 } 952 }, 953 flags, 954 watchOptions, 955 WatchType.WildcardDirectory 956 ); 957 } 958 959 function updateExtendedConfigFilesWatches(forProjectPath: Path, options: CompilerOptions | undefined, watchOptions: WatchOptions | undefined, watchType: WatchTypeRegistry["ExtendedConfigFile"] | WatchTypeRegistry["ExtendedConfigOfReferencedProject"]) { 960 updateSharedExtendedConfigFileWatcher( 961 forProjectPath, 962 options, 963 sharedExtendedConfigFileWatchers ||= new Map(), 964 (extendedConfigFileName, extendedConfigFilePath) => watchFile( 965 extendedConfigFileName, 966 (_fileName, eventKind) => { 967 updateCachedSystemWithFile(extendedConfigFileName, extendedConfigFilePath, eventKind); 968 // Update extended config cache 969 if (extendedConfigCache) cleanExtendedConfigCache(extendedConfigCache, extendedConfigFilePath, toPath); 970 // Update projects 971 const projects = sharedExtendedConfigFileWatchers.get(extendedConfigFilePath)?.projects; 972 // If there are no referenced projects this extended config file watcher depend on ignore 973 if (!projects?.size) return; 974 projects.forEach(projectPath => { 975 if (toPath(configFileName) === projectPath) { 976 // If this is the config file of the project, reload completely 977 reloadLevel = ConfigFileProgramReloadLevel.Full; 978 } 979 else { 980 // Reload config for the referenced projects and remove the resolutions from referenced projects since the config file changed 981 const config = parsedConfigs?.get(projectPath); 982 if (config) config.reloadLevel = ConfigFileProgramReloadLevel.Full; 983 resolutionCache.removeResolutionsFromProjectReferenceRedirects(projectPath); 984 } 985 scheduleProgramUpdate(); 986 }); 987 }, 988 PollingInterval.High, 989 watchOptions, 990 watchType 991 ), 992 toPath, 993 ); 994 } 995 996 function watchReferencedProject(configFileName: string, configPath: Path, commandLine: ParsedConfig) { 997 // Watch file 998 commandLine.watcher ||= watchFile( 999 configFileName, 1000 (_fileName, eventKind) => { 1001 updateCachedSystemWithFile(configFileName, configPath, eventKind); 1002 const config = parsedConfigs?.get(configPath); 1003 if (config) config.reloadLevel = ConfigFileProgramReloadLevel.Full; 1004 resolutionCache.removeResolutionsFromProjectReferenceRedirects(configPath); 1005 scheduleProgramUpdate(); 1006 }, 1007 PollingInterval.High, 1008 commandLine.parsedCommandLine?.watchOptions || watchOptions, 1009 WatchType.ConfigFileOfReferencedProject 1010 ); 1011 // Watch Wild card 1012 if (commandLine.parsedCommandLine?.wildcardDirectories) { 1013 updateWatchingWildcardDirectories( 1014 commandLine.watchedDirectories ||= new Map(), 1015 new Map(getEntries(commandLine.parsedCommandLine?.wildcardDirectories)), 1016 (directory, flags) => watchDirectory( 1017 directory, 1018 fileOrDirectory => { 1019 const fileOrDirectoryPath = toPath(fileOrDirectory); 1020 // Since the file existence changed, update the sourceFiles cache 1021 if (cachedDirectoryStructureHost) { 1022 cachedDirectoryStructureHost.addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath); 1023 } 1024 nextSourceFileVersion(fileOrDirectoryPath); 1025 1026 const config = parsedConfigs?.get(configPath); 1027 if (!config?.parsedCommandLine) return; 1028 if (isIgnoredFileFromWildCardWatching({ 1029 watchedDirPath: toPath(directory), 1030 fileOrDirectory, 1031 fileOrDirectoryPath, 1032 configFileName, 1033 options: config.parsedCommandLine.options, 1034 program: config.parsedCommandLine.fileNames, 1035 currentDirectory, 1036 useCaseSensitiveFileNames, 1037 writeLog, 1038 toPath, 1039 })) return; 1040 1041 // Reload is pending, do the reload 1042 if (config.reloadLevel !== ConfigFileProgramReloadLevel.Full) { 1043 config.reloadLevel = ConfigFileProgramReloadLevel.Partial; 1044 1045 // Schedule Update the program 1046 scheduleProgramUpdate(); 1047 } 1048 }, 1049 flags, 1050 commandLine.parsedCommandLine?.watchOptions || watchOptions, 1051 WatchType.WildcardDirectoryOfReferencedProject 1052 ) 1053 ); 1054 } 1055 else if (commandLine.watchedDirectories) { 1056 clearMap(commandLine.watchedDirectories, closeFileWatcherOf); 1057 commandLine.watchedDirectories = undefined; 1058 } 1059 // Watch extended config files 1060 updateExtendedConfigFilesWatches( 1061 configPath, 1062 commandLine.parsedCommandLine?.options, 1063 commandLine.parsedCommandLine?.watchOptions || watchOptions, 1064 WatchType.ExtendedConfigOfReferencedProject 1065 ); 1066 } 1067 } 1068} 1069