Skip to content

Commit b84e65d

Browse files
authored
Merge pull request #32745 from microsoft/fsWatchInode
Change to missing file watcher on linux and darwin explicitly to avoid watching deleted inode
2 parents 825d8bb + 850ff78 commit b84e65d

File tree

2 files changed

+30
-2
lines changed

2 files changed

+30
-2
lines changed

Diff for: src/compiler/sys.ts

+27-2
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,9 @@ namespace ts {
379379
/*@internal*/
380380
export const ignoredPaths = ["/node_modules/.", "/.git", "/.#"];
381381

382+
/*@internal*/
383+
export let sysLog: (s: string) => void = noop;
384+
382385
/*@internal*/
383386
export interface RecursiveDirectoryWatcherHost {
384387
watchDirectory: HostWatchDirectory;
@@ -730,6 +733,7 @@ namespace ts {
730733

731734
const nodeVersion = getNodeMajorVersion();
732735
const isNode4OrLater = nodeVersion! >= 4;
736+
const isLinuxOrMacOs = process.platform === "linux" || process.platform === "darwin";
733737

734738
const platform: string = _os.platform();
735739
const useCaseSensitiveFileNames = isFileSystemCaseSensitive();
@@ -1029,6 +1033,12 @@ namespace ts {
10291033

10301034
function fsWatch(fileOrDirectory: string, entryKind: FileSystemEntryKind.File | FileSystemEntryKind.Directory, callback: FsWatchCallback, recursive: boolean, fallbackPollingWatchFile: HostWatchFile, pollingInterval?: number): FileWatcher {
10311035
let options: any;
1036+
let lastDirectoryPartWithDirectorySeparator: string | undefined;
1037+
let lastDirectoryPart: string | undefined;
1038+
if (isLinuxOrMacOs) {
1039+
lastDirectoryPartWithDirectorySeparator = fileOrDirectory.substr(fileOrDirectory.lastIndexOf(directorySeparator));
1040+
lastDirectoryPart = lastDirectoryPartWithDirectorySeparator.slice(directorySeparator.length);
1041+
}
10321042
/** Watcher for the file system entry depending on whether it is missing or present */
10331043
let watcher = !fileSystemEntryExists(fileOrDirectory, entryKind) ?
10341044
watchMissingFileSystemEntry() :
@@ -1046,6 +1056,7 @@ namespace ts {
10461056
* @param createWatcher
10471057
*/
10481058
function invokeCallbackAndUpdateWatcher(createWatcher: () => FileWatcher) {
1059+
sysLog(`sysLog:: ${fileOrDirectory}:: Changing watcher to ${createWatcher === watchPresentFileSystemEntry ? "Present" : "Missing"}FileSystemEntryWatcher`);
10491060
// Call the callback for current directory
10501061
callback("rename", "");
10511062

@@ -1072,11 +1083,12 @@ namespace ts {
10721083
}
10731084
}
10741085
try {
1075-
10761086
const presentWatcher = _fs.watch(
10771087
fileOrDirectory,
10781088
options,
1079-
callback
1089+
isLinuxOrMacOs ?
1090+
callbackChangingToMissingFileSystemEntry :
1091+
callback
10801092
);
10811093
// Watch the missing file or directory or error
10821094
presentWatcher.on("error", () => invokeCallbackAndUpdateWatcher(watchMissingFileSystemEntry));
@@ -1090,11 +1102,24 @@ namespace ts {
10901102
}
10911103
}
10921104

1105+
function callbackChangingToMissingFileSystemEntry(event: "rename" | "change", relativeName: string | undefined) {
1106+
// because relativeName is not guaranteed to be correct we need to check on each rename with few combinations
1107+
// Eg on ubuntu while watching app/node_modules the relativeName is "node_modules" which is neither relative nor full path
1108+
return event === "rename" &&
1109+
(!relativeName ||
1110+
relativeName === lastDirectoryPart ||
1111+
relativeName.lastIndexOf(lastDirectoryPartWithDirectorySeparator!) === relativeName.length - lastDirectoryPartWithDirectorySeparator!.length) &&
1112+
!fileSystemEntryExists(fileOrDirectory, entryKind) ?
1113+
invokeCallbackAndUpdateWatcher(watchMissingFileSystemEntry) :
1114+
callback(event, relativeName);
1115+
}
1116+
10931117
/**
10941118
* Watch the file or directory using fs.watchFile since fs.watch threw exception
10951119
* Eg. on linux the number of watches are limited and one could easily exhaust watches and the exception ENOSPC is thrown when creating watcher at that point
10961120
*/
10971121
function watchPresentFileSystemEntryWithFsWatchFile(): FileWatcher {
1122+
sysLog(`sysLog:: ${fileOrDirectory}:: Changing to fsWatchFile`);
10981123
return fallbackPollingWatchFile(fileOrDirectory, createFileWatcherCallback(callback), pollingInterval);
10991124
}
11001125

Diff for: src/compiler/watchUtilities.ts

+3
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,9 @@ namespace ts {
370370
const createFileWatcher: CreateFileWatcher<WatchFileHost, PollingInterval, FileWatcherEventKind, never, X, Y> = getCreateFileWatcher(watchLogLevel, watchFile);
371371
const createFilePathWatcher: CreateFileWatcher<WatchFileHost, PollingInterval, FileWatcherEventKind, Path, X, Y> = watchLogLevel === WatchLogLevel.None ? watchFilePath : createFileWatcher;
372372
const createDirectoryWatcher: CreateFileWatcher<WatchDirectoryHost, WatchDirectoryFlags, undefined, never, X, Y> = getCreateFileWatcher(watchLogLevel, watchDirectory);
373+
if (watchLogLevel === WatchLogLevel.Verbose && sysLog === noop) {
374+
sysLog = s => log(s);
375+
}
373376
return {
374377
watchFile: (host, file, callback, pollingInterval, detailInfo1, detailInfo2) =>
375378
createFileWatcher(host, file, callback, pollingInterval, /*passThrough*/ undefined, detailInfo1, detailInfo2, watchFile, log, "FileWatcher", getDetailWatchInfo),

0 commit comments

Comments
 (0)