diff --git a/src/compiler/tsbuild.ts b/src/compiler/tsbuild.ts index f1e194e307d..8b06070a925 100644 --- a/src/compiler/tsbuild.ts +++ b/src/compiler/tsbuild.ts @@ -443,6 +443,7 @@ namespace ts { let nextProjectToBuild = 0; let timerToBuildInvalidatedProject: any; let reportFileChangeDetected = false; + const { watchFile, watchFilePath, watchDirectory } = createWatchFactory(host, options); // Watches for the solution const allWatchedWildcardDirectories = createFileMap>(toPath); @@ -542,9 +543,16 @@ namespace ts { function watchConfigFile(resolved: ResolvedConfigFileName) { if (options.watch && !allWatchedConfigFiles.hasKey(resolved)) { - allWatchedConfigFiles.setValue(resolved, hostWithWatch.watchFile(resolved, () => { + allWatchedConfigFiles.setValue(resolved, watchFile( + hostWithWatch, + resolved, + () => { invalidateProjectAndScheduleBuilds(resolved, ConfigFileProgramReloadLevel.Full); - })); + }, + PollingInterval.High, + WatchType.ConfigFile, + resolved + )); } } @@ -554,20 +562,27 @@ namespace ts { getOrCreateValueMapFromConfigFileMap(allWatchedWildcardDirectories, resolved), createMapFromTemplate(parsed.configFileSpecs!.wildcardDirectories), (dir, flags) => { - return hostWithWatch.watchDirectory(dir, fileOrDirectory => { - const fileOrDirectoryPath = toPath(fileOrDirectory); - if (fileOrDirectoryPath !== toPath(dir) && hasExtension(fileOrDirectoryPath) && !isSupportedSourceFileName(fileOrDirectory, parsed.options)) { - // writeLog(`Project: ${configFileName} Detected file add/remove of non supported extension: ${fileOrDirectory}`); - return; - } + return watchDirectory( + hostWithWatch, + dir, + fileOrDirectory => { + const fileOrDirectoryPath = toPath(fileOrDirectory); + if (fileOrDirectoryPath !== toPath(dir) && hasExtension(fileOrDirectoryPath) && !isSupportedSourceFileName(fileOrDirectory, parsed.options)) { + // writeLog(`Project: ${configFileName} Detected file add/remove of non supported extension: ${fileOrDirectory}`); + return; + } - if (isOutputFile(fileOrDirectory, parsed)) { - // writeLog(`${fileOrDirectory} is output file`); - return; - } + if (isOutputFile(fileOrDirectory, parsed)) { + // writeLog(`${fileOrDirectory} is output file`); + return; + } - invalidateProjectAndScheduleBuilds(resolved, ConfigFileProgramReloadLevel.Partial); - }, !!(flags & WatchDirectoryFlags.Recursive)); + invalidateProjectAndScheduleBuilds(resolved, ConfigFileProgramReloadLevel.Partial); + }, + flags, + WatchType.WildcardDirectory, + resolved + ); } ); } @@ -578,9 +593,15 @@ namespace ts { getOrCreateValueMapFromConfigFileMap(allWatchedInputFiles, resolved), arrayToMap(parsed.fileNames, toPath), { - createNewValue: (_key, input) => hostWithWatch.watchFile(input, () => { - invalidateProjectAndScheduleBuilds(resolved, ConfigFileProgramReloadLevel.None); - }), + createNewValue: (path, input) => watchFilePath( + hostWithWatch, + input, + () => invalidateProjectAndScheduleBuilds(resolved, ConfigFileProgramReloadLevel.None), + PollingInterval.Low, + path as Path, + WatchType.SourceFile, + resolved + ), onDeleteValue: closeFileWatcher, } ); diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index 7a5c12cae4e..4112271307c 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -194,6 +194,30 @@ namespace ts { }; } + export const enum WatchType { + ConfigFile = "Config file", + SourceFile = "Source file", + MissingFile = "Missing file", + WildcardDirectory = "Wild card directory", + FailedLookupLocations = "Failed Lookup Locations", + TypeRoots = "Type roots" + } + + interface WatchFactory extends ts.WatchFactory { + watchLogLevel: WatchLogLevel; + writeLog: (s: string) => void; + } + + export function createWatchFactory(host: { trace?(s: string): void; }, options: { extendedDiagnostics?: boolean; diagnostics?: boolean; }) { + const watchLogLevel = host.trace ? options.extendedDiagnostics ? WatchLogLevel.Verbose : + options.diagnostics ? WatchLogLevel.TriggerOnly : WatchLogLevel.None : WatchLogLevel.None; + const writeLog: (s: string) => void = watchLogLevel !== WatchLogLevel.None ? (s => host.trace!(s)) : noop; + const result = getWatchFactory(watchLogLevel, writeLog) as WatchFactory; + result.watchLogLevel = watchLogLevel; + result.writeLog = writeLog; + return result; + } + /** * Creates the watch compiler host that can be extended with config file or root file names and options host */ @@ -224,7 +248,7 @@ namespace ts { watchDirectory, setTimeout, clearTimeout, - trace: s => system.write(s), + trace: s => system.write(s + system.newLine), onWatchStatusChange, createDirectory: path => system.createDirectory(path), writeFile: (path, data, writeByteOrderMark) => system.writeFile(path, data, writeByteOrderMark), @@ -517,17 +541,12 @@ namespace ts { newLine = updateNewLine(); } - const trace = host.trace && ((s: string) => { host.trace!(s + newLine); }); - const watchLogLevel = trace ? compilerOptions.extendedDiagnostics ? WatchLogLevel.Verbose : - compilerOptions.diagnostics ? WatchLogLevel.TriggerOnly : WatchLogLevel.None : WatchLogLevel.None; - const writeLog: (s: string) => void = watchLogLevel !== WatchLogLevel.None ? trace! : noop; // TODO: GH#18217 - const { watchFile, watchFilePath, watchDirectory } = getWatchFactory(watchLogLevel, writeLog); - + const { watchFile, watchFilePath, watchDirectory, watchLogLevel, writeLog } = createWatchFactory(host, compilerOptions); const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames); writeLog(`Current directory: ${currentDirectory} CaseSensitiveFileNames: ${useCaseSensitiveFileNames}`); if (configFileName) { - watchFile(host, configFileName, scheduleProgramReload, PollingInterval.High, "Config file"); + watchFile(host, configFileName, scheduleProgramReload, PollingInterval.High, WatchType.ConfigFile); } const compilerHost: CompilerHost & ResolutionCacheHost = { @@ -543,7 +562,7 @@ namespace ts { getNewLine: () => newLine, fileExists, readFile, - trace, + trace: host.trace && (s => host.trace!(s)), directoryExists: directoryStructureHost.directoryExists && (path => directoryStructureHost.directoryExists!(path)), getDirectories: (directoryStructureHost.getDirectories && ((path: string) => directoryStructureHost.getDirectories!(path)))!, // TODO: GH#18217 realpath: host.realpath && (s => host.realpath!(s)), @@ -553,8 +572,8 @@ namespace ts { // Members for ResolutionCacheHost toPath, getCompilationSettings: () => compilerOptions, - watchDirectoryOfFailedLookupLocation: (dir, cb, flags) => watchDirectory(host, dir, cb, flags, "Failed Lookup Locations"), - watchTypeRootsDirectory: (dir, cb, flags) => watchDirectory(host, dir, cb, flags, "Type roots"), + watchDirectoryOfFailedLookupLocation: (dir, cb, flags) => watchDirectory(host, dir, cb, flags, WatchType.FailedLookupLocations), + watchTypeRootsDirectory: (dir, cb, flags) => watchDirectory(host, dir, cb, flags, WatchType.TypeRoots), getCachedDirectoryStructureHost: () => cachedDirectoryStructureHost, onInvalidatedResolution: scheduleProgramUpdate, onChangedAutomaticTypeDirectiveNames: () => { @@ -719,7 +738,7 @@ namespace ts { (hostSourceFile as FilePresentOnHost).sourceFile = sourceFile; sourceFile.version = hostSourceFile.version.toString(); if (!(hostSourceFile as FilePresentOnHost).fileWatcher) { - (hostSourceFile as FilePresentOnHost).fileWatcher = watchFilePath(host, fileName, onSourceFileChange, PollingInterval.Low, path, "Source file"); + (hostSourceFile as FilePresentOnHost).fileWatcher = watchFilePath(host, fileName, onSourceFileChange, PollingInterval.Low, path, WatchType.SourceFile); } } else { @@ -733,7 +752,7 @@ namespace ts { else { if (sourceFile) { sourceFile.version = initialVersion.toString(); - const fileWatcher = watchFilePath(host, fileName, onSourceFileChange, PollingInterval.Low, path, "Source file"); + const fileWatcher = watchFilePath(host, fileName, onSourceFileChange, PollingInterval.Low, path, WatchType.SourceFile); sourceFilesCache.set(path, { sourceFile, version: initialVersion, fileWatcher }); } else { @@ -907,7 +926,7 @@ namespace ts { } function watchMissingFilePath(missingFilePath: Path) { - return watchFilePath(host, missingFilePath, onMissingFileChange, PollingInterval.Medium, missingFilePath, "Missing file"); + return watchFilePath(host, missingFilePath, onMissingFileChange, PollingInterval.Medium, missingFilePath, WatchType.MissingFile); } function onMissingFileChange(fileName: string, eventKind: FileWatcherEventKind, missingFilePath: Path) { @@ -971,7 +990,7 @@ namespace ts { } }, flags, - "Wild card directories" + WatchType.WildcardDirectory ); } diff --git a/src/compiler/watchUtilities.ts b/src/compiler/watchUtilities.ts index 8fa04e52da3..9bf242e07e8 100644 --- a/src/compiler/watchUtilities.ts +++ b/src/compiler/watchUtilities.ts @@ -343,10 +343,10 @@ namespace ts { export interface WatchDirectoryHost { watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher; } - export type WatchFile = (host: WatchFileHost, file: string, callback: FileWatcherCallback, pollingInterval: PollingInterval, detailInfo1?: X, detailInfo2?: Y) => FileWatcher; + export type WatchFile = (host: WatchFileHost, file: string, callback: FileWatcherCallback, pollingInterval: PollingInterval, detailInfo1: X, detailInfo2?: Y) => FileWatcher; export type FilePathWatcherCallback = (fileName: string, eventKind: FileWatcherEventKind, filePath: Path) => void; - export type WatchFilePath = (host: WatchFileHost, file: string, callback: FilePathWatcherCallback, pollingInterval: PollingInterval, path: Path, detailInfo1?: X, detailInfo2?: Y) => FileWatcher; - export type WatchDirectory = (host: WatchDirectoryHost, directory: string, callback: DirectoryWatcherCallback, flags: WatchDirectoryFlags, detailInfo1?: X, detailInfo2?: Y) => FileWatcher; + export type WatchFilePath = (host: WatchFileHost, file: string, callback: FilePathWatcherCallback, pollingInterval: PollingInterval, path: Path, detailInfo1: X, detailInfo2?: Y) => FileWatcher; + export type WatchDirectory = (host: WatchDirectoryHost, directory: string, callback: DirectoryWatcherCallback, flags: WatchDirectoryFlags, detailInfo1: X, detailInfo2?: Y) => FileWatcher; export interface WatchFactory { watchFile: WatchFile; @@ -444,7 +444,7 @@ namespace ts { } function getWatchInfo(file: string, flags: T, detailInfo1: X, detailInfo2: Y | undefined, getDetailWatchInfo: GetDetailWatchInfo | undefined) { - return `WatchInfo: ${file} ${flags} ${getDetailWatchInfo ? getDetailWatchInfo(detailInfo1, detailInfo2) : detailInfo1}`; + return `WatchInfo: ${file} ${flags} ${getDetailWatchInfo ? getDetailWatchInfo(detailInfo1, detailInfo2) : detailInfo2 === undefined ? detailInfo1 : `${detailInfo1} ${detailInfo2}`}`; } export function closeFileWatcherOf(objWithWatcher: T) { diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 3050c976231..7256e46d577 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -332,19 +332,6 @@ namespace ts.server { } } - /* @internal */ - export const enum WatchType { - ConfigFilePath = "Config file for the program", - MissingFilePath = "Missing file from program", - WildcardDirectories = "Wild card directory", - ClosedScriptInfo = "Closed Script info", - ConfigFileForInferredRoot = "Config file for the inferred project root", - FailedLookupLocation = "Directory of Failed lookup locations in module resolution", - TypeRoots = "Type root directory", - NodeModulesForClosedScriptInfo = "node_modules for closed script infos in them", - MissingSourceMapFile = "Missing source map file" - } - const enum ConfigFileWatcherStatus { ReloadingFiles = "Reloading configured projects for files", ReloadingInferredRootFiles = "Reloading configured projects for only inferred root files", @@ -1035,7 +1022,7 @@ namespace ts.server { } }, flags, - WatchType.WildcardDirectories, + WatchType.WildcardDirectory, project ); } @@ -1338,7 +1325,7 @@ namespace ts.server { watches.push(WatchType.ConfigFileForInferredRoot); } if (this.configuredProjects.has(canonicalConfigFilePath)) { - watches.push(WatchType.ConfigFilePath); + watches.push(WatchType.ConfigFile); } this.logger.info(`ConfigFilePresence:: Current Watches: ${watches}:: File: ${configFileName} Currently impacted open files: RootsOfInferredProjects: ${inferredRoots} OtherOpenFiles: ${otherFiles} Status: ${status}`); } @@ -1705,7 +1692,7 @@ namespace ts.server { configFileName, (_fileName, eventKind) => this.onConfigChangedForConfiguredProject(project, eventKind), PollingInterval.High, - WatchType.ConfigFilePath, + WatchType.ConfigFile, project ); this.configuredProjects.set(project.canonicalConfigFilePath, project); diff --git a/src/server/project.ts b/src/server/project.ts index e3069e1d183..b296668e6ae 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -428,7 +428,7 @@ namespace ts.server { directory, cb, flags, - WatchType.FailedLookupLocation, + WatchType.FailedLookupLocations, this ); } @@ -989,7 +989,7 @@ namespace ts.server { } }, PollingInterval.Medium, - WatchType.MissingFilePath, + WatchType.MissingFile, this ); return fileWatcher; diff --git a/src/server/utilities.ts b/src/server/utilities.ts index 15b217822c0..04ce9f4e420 100644 --- a/src/server/utilities.ts +++ b/src/server/utilities.ts @@ -217,3 +217,14 @@ namespace ts.server { return indentStr + JSON.stringify(json); } } + +/* @internal */ +namespace ts { + // Additional tsserver specific watch information + export const enum WatchType { + ClosedScriptInfo = "Closed Script info", + ConfigFileForInferredRoot = "Config file for the inferred project root", + NodeModulesForClosedScriptInfo = "node_modules for closed script infos in them", + MissingSourceMapFile = "Missing source map file", + } +}