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