From dddd906803675147e4a1ee66c42d6d039566df97 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 24 Jun 2020 13:52:29 -0700 Subject: [PATCH] Handle exclude options in the system watches --- src/compiler/sys.ts | 83 +++++++++++++------ src/harness/virtualFileSystemWithWatch.ts | 1 + ...eDirectories-option-extendedDiagnostics.js | 6 -- 3 files changed, 60 insertions(+), 30 deletions(-) diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 725d2330d214a..484aca07c473e 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -449,6 +449,7 @@ namespace ts { export interface RecursiveDirectoryWatcherHost { watchDirectory: HostWatchDirectory; useCaseSensitiveFileNames: boolean; + getCurrentDirectory: System["getCurrentDirectory"]; getAccessibleSortedChildDirectories(path: string): readonly string[]; directoryExists(dir: string): boolean; realpath(s: string): string; @@ -462,7 +463,16 @@ namespace ts { * (eg on OS that dont support recursive watch using fs.watch use fs.watchFile) */ /*@internal*/ - export function createDirectoryWatcherSupportingRecursive(host: RecursiveDirectoryWatcherHost): HostWatchDirectory { + export function createDirectoryWatcherSupportingRecursive({ + watchDirectory, + useCaseSensitiveFileNames, + getCurrentDirectory, + getAccessibleSortedChildDirectories, + directoryExists, + realpath, + setTimeout, + clearTimeout + }: RecursiveDirectoryWatcherHost): HostWatchDirectory { interface ChildDirectoryWatcher extends FileWatcher { dirName: string; } @@ -478,12 +488,12 @@ namespace ts { const cacheToUpdateChildWatches = createMap<{ dirName: string; options: WatchOptions | undefined; fileNames: string[]; }>(); let timerToUpdateChildWatches: any; - const filePathComparer = getStringComparer(!host.useCaseSensitiveFileNames); - const toCanonicalFilePath = createGetCanonicalFileName(host.useCaseSensitiveFileNames); + const filePathComparer = getStringComparer(!useCaseSensitiveFileNames); + const toCanonicalFilePath = createGetCanonicalFileName(useCaseSensitiveFileNames); return (dirName, callback, recursive, options) => recursive ? createDirectoryWatcher(dirName, options, callback) : - host.watchDirectory(dirName, callback, recursive, options); + watchDirectory(dirName, callback, recursive, options); /** * Create the directory watcher for the dirPath. @@ -496,8 +506,8 @@ namespace ts { } else { directoryWatcher = { - watcher: host.watchDirectory(dirName, fileName => { - if (isIgnoredPath(fileName)) return; + watcher: watchDirectory(dirName, fileName => { + if (isIgnoredPath(fileName, options)) return; if (options?.synchronousWatchDirectory) { // Call the actual callback @@ -578,7 +588,7 @@ namespace ts { function nonSyncUpdateChildWatches(dirName: string, dirPath: Path, fileName: string, options: WatchOptions | undefined) { // Iterate through existing children and update the watches if needed const parentWatcher = cache.get(dirPath); - if (parentWatcher && host.directoryExists(dirName)) { + if (parentWatcher && directoryExists(dirName)) { // Schedule the update and postpone invoke for callbacks scheduleUpdateChildWatches(dirName, dirPath, fileName, options); return; @@ -598,10 +608,10 @@ namespace ts { cacheToUpdateChildWatches.set(dirPath, { dirName, options, fileNames: [fileName] }); } if (timerToUpdateChildWatches) { - host.clearTimeout(timerToUpdateChildWatches); + clearTimeout(timerToUpdateChildWatches); timerToUpdateChildWatches = undefined; } - timerToUpdateChildWatches = host.setTimeout(onTimerToUpdateChildWatches, 1000); + timerToUpdateChildWatches = setTimeout(onTimerToUpdateChildWatches, 1000); } function onTimerToUpdateChildWatches() { @@ -655,11 +665,11 @@ namespace ts { if (!parentWatcher) return false; let newChildWatches: ChildDirectoryWatcher[] | undefined; const hasChanges = enumerateInsertsAndDeletes( - host.directoryExists(parentDir) ? mapDefined(host.getAccessibleSortedChildDirectories(parentDir), child => { + directoryExists(parentDir) ? mapDefined(getAccessibleSortedChildDirectories(parentDir), child => { const childFullName = getNormalizedAbsolutePath(child, parentDir); // Filter our the symbolic link directories since those arent included in recursive watch // which is same behaviour when recursive: true is passed to fs.watch - return !isIgnoredPath(childFullName) && filePathComparer(childFullName, normalizePath(host.realpath(childFullName))) === Comparison.EqualTo ? childFullName : undefined; + return !isIgnoredPath(childFullName, options) && filePathComparer(childFullName, normalizePath(realpath(childFullName))) === Comparison.EqualTo ? childFullName : undefined; }) : emptyArray, parentWatcher.childWatches, (child, childWatcher) => filePathComparer(child, childWatcher.dirName), @@ -686,13 +696,14 @@ namespace ts { } } - function isIgnoredPath(path: string) { - return some(ignoredPaths, searchPath => isInPath(path, searchPath)); + function isIgnoredPath(path: string, options: WatchOptions | undefined) { + return some(ignoredPaths, searchPath => isInPath(path, searchPath)) || + isIgnoredByWatchOptions(path, options, useCaseSensitiveFileNames, getCurrentDirectory); } function isInPath(path: string, searchPath: string) { if (stringContains(path, searchPath)) return true; - if (host.useCaseSensitiveFileNames) return false; + if (useCaseSensitiveFileNames) return false; return stringContains(toCanonicalFilePath(path), searchPath); } } @@ -729,14 +740,35 @@ namespace ts { }; } - function createFsWatchCallbackForDirectoryWatcherCallback(directoryName: string, callback: DirectoryWatcherCallback): FsWatchCallback { + function isIgnoredByWatchOptions( + pathToCheck: string, + options: WatchOptions | undefined, + useCaseSensitiveFileNames: boolean, + getCurrentDirectory: System["getCurrentDirectory"], + ) { + return (options?.excludeDirectories || options?.excludeFiles) && ( + matchesExclude(pathToCheck, options?.excludeFiles, useCaseSensitiveFileNames, getCurrentDirectory()) || + matchesExclude(pathToCheck, options?.excludeDirectories, useCaseSensitiveFileNames, getCurrentDirectory()) + ); + } + + function createFsWatchCallbackForDirectoryWatcherCallback( + directoryName: string, + callback: DirectoryWatcherCallback, + options: WatchOptions | undefined, + useCaseSensitiveFileNames: boolean, + getCurrentDirectory: System["getCurrentDirectory"], + ): FsWatchCallback { return (eventName, relativeFileName) => { // In watchDirectory we only care about adding and removing files (when event name is // "rename"); changes made within files are handled by corresponding fileWatchers (when // event name is "change") if (eventName === "rename") { // When deleting a file, the passed baseFileName is null - callback(!relativeFileName ? directoryName : normalizePath(combinePaths(directoryName, relativeFileName))); + const fileName = !relativeFileName ? directoryName : normalizePath(combinePaths(directoryName, relativeFileName)); + if (!relativeFileName || !isIgnoredByWatchOptions(fileName, options, useCaseSensitiveFileNames, getCurrentDirectory)) { + callback(fileName); + } } }; } @@ -753,6 +785,7 @@ namespace ts { fsWatch: FsWatch; fileExists: System["fileExists"]; useCaseSensitiveFileNames: boolean; + getCurrentDirectory: System["getCurrentDirectory"]; fsSupportsRecursiveFsWatch: boolean; directoryExists: System["directoryExists"]; getAccessibleSortedChildDirectories(path: string): readonly string[]; @@ -772,6 +805,7 @@ namespace ts { fsWatch, fileExists, useCaseSensitiveFileNames, + getCurrentDirectory, fsSupportsRecursiveFsWatch, directoryExists, getAccessibleSortedChildDirectories, @@ -868,7 +902,7 @@ namespace ts { return fsWatch( directoryName, FileSystemEntryKind.Directory, - createFsWatchCallbackForDirectoryWatcherCallback(directoryName, callback), + createFsWatchCallbackForDirectoryWatcherCallback(directoryName, callback, options, useCaseSensitiveFileNames, getCurrentDirectory), recursive, PollingInterval.Medium, getFallbackOptions(options) @@ -878,6 +912,7 @@ namespace ts { if (!hostRecursiveDirectoryWatcher) { hostRecursiveDirectoryWatcher = createDirectoryWatcherSupportingRecursive({ useCaseSensitiveFileNames, + getCurrentDirectory, directoryExists, getAccessibleSortedChildDirectories, watchDirectory: nonRecursiveWatchDirectory, @@ -891,8 +926,8 @@ namespace ts { function nonRecursiveWatchDirectory(directoryName: string, callback: DirectoryWatcherCallback, recursive: boolean, options: WatchOptions | undefined): FileWatcher { Debug.assert(!recursive); - options = updateOptionsForWatchDirectory(options); - const watchDirectoryKind = Debug.checkDefined(options.watchDirectory); + const watchDirectoryOptions = updateOptionsForWatchDirectory(options); + const watchDirectoryKind = Debug.checkDefined(watchDirectoryOptions.watchDirectory); switch (watchDirectoryKind) { case WatchDirectoryKind.FixedPollingInterval: return pollingWatchFile( @@ -912,10 +947,10 @@ namespace ts { return fsWatch( directoryName, FileSystemEntryKind.Directory, - createFsWatchCallbackForDirectoryWatcherCallback(directoryName, callback), + createFsWatchCallbackForDirectoryWatcherCallback(directoryName, callback, options, useCaseSensitiveFileNames, getCurrentDirectory), recursive, PollingInterval.Medium, - getFallbackOptions(options) + getFallbackOptions(watchDirectoryOptions) ); default: Debug.assertNever(watchDirectoryKind); @@ -1161,6 +1196,7 @@ namespace ts { const platform: string = _os.platform(); const useCaseSensitiveFileNames = isFileSystemCaseSensitive(); const fsSupportsRecursiveFsWatch = isNode4OrLater && (process.platform === "win32" || process.platform === "darwin"); + const getCurrentDirectory = memoize(() => process.cwd()); const { watchFile, watchDirectory } = createSystemWatchFunctions({ pollingWatchFile: createSingleFileWatcherPerName(fsWatchFileWorker, useCaseSensitiveFileNames), getModifiedTime, @@ -1168,6 +1204,7 @@ namespace ts { clearTimeout, fsWatch, useCaseSensitiveFileNames, + getCurrentDirectory, fileExists, // Node 4.0 `fs.watch` function supports the "recursive" option on both OSX and Windows // (ref: https://github.com/nodejs/node/pull/2649 and https://github.com/Microsoft/TypeScript/issues/4643) @@ -1214,9 +1251,7 @@ namespace ts { getExecutingFilePath() { return __filename; }, - getCurrentDirectory() { - return process.cwd(); - }, + getCurrentDirectory, getDirectories, getEnvironmentVariable(name: string) { return process.env[name] || ""; diff --git a/src/harness/virtualFileSystemWithWatch.ts b/src/harness/virtualFileSystemWithWatch.ts index 28e7dd1cd85b7..a4ab8644ac4ef 100644 --- a/src/harness/virtualFileSystemWithWatch.ts +++ b/src/harness/virtualFileSystemWithWatch.ts @@ -433,6 +433,7 @@ interface Array { length: number; [n: number]: T; }` fsWatch: this.fsWatch.bind(this), fileExists: this.fileExists.bind(this), useCaseSensitiveFileNames: this.useCaseSensitiveFileNames, + getCurrentDirectory: this.getCurrentDirectory.bind(this), fsSupportsRecursiveFsWatch: tscWatchDirectory ? false : !runWithoutRecursiveWatches, directoryExists: this.directoryExists.bind(this), getAccessibleSortedChildDirectories: path => this.getDirectories(path), diff --git a/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-extendedDiagnostics.js b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-extendedDiagnostics.js index 78c091d1e9a5f..621c69ccf3931 100644 --- a/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-extendedDiagnostics.js +++ b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-extendedDiagnostics.js @@ -123,12 +123,6 @@ Input:: //// [/user/username/projects/myproject/node_modules/bar/fooBar.d.ts] deleted Output:: -DirectoryWatcher:: Triggered with /user/username/projects/myproject/node_modules/bar/fooBar.d.ts :: WatchInfo: /user/username/projects/myproject 1 {"excludeDirectories":["/user/username/projects/myproject/node_modules"]} Wild card directory - -Project: /user/username/projects/myproject/tsconfig.json Detected excluded file: /user/username/projects/myproject/node_modules/bar/fooBar.d.ts - -Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject/node_modules/bar/fooBar.d.ts :: WatchInfo: /user/username/projects/myproject 1 {"excludeDirectories":["/user/username/projects/myproject/node_modules"]} Wild card directory - WatchedFiles:: /user/username/projects/myproject/tsconfig.json: