• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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