Skip to content

Commit

Permalink
timestamp watching for fsWatch is only for files.
Browse files Browse the repository at this point in the history
Directories generate change event in multiple scenarios and should not filter events based on modified time
Fixes #57792
  • Loading branch information
sheetalkamat committed Mar 25, 2024
1 parent 7781e29 commit 98bcd9d
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 8 deletions.
36 changes: 29 additions & 7 deletions src/compiler/sys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -378,16 +378,24 @@ function createDynamicPriorityPollingWatchFile(host: {
}
}

function createUseFsEventsOnParentDirectoryWatchFile(fsWatch: FsWatch, useCaseSensitiveFileNames: boolean): HostWatchFile {
function createUseFsEventsOnParentDirectoryWatchFile(
fsWatch: FsWatch,
useCaseSensitiveFileNames: boolean,
getModifiedTime: NonNullable<System["getModifiedTime"]>,
fsWatchWithTimestamp: boolean | undefined,
): HostWatchFile {
// One file can have multiple watchers
const fileWatcherCallbacks = createMultiMap<string, FileWatcherCallback>();
const fileTimestamps = fsWatchWithTimestamp ? new Map<string, Date>() : undefined;
const dirWatchers = new Map<string, DirectoryWatcher>();
const toCanonicalName = createGetCanonicalFileName(useCaseSensitiveFileNames);
return nonPollingWatchFile;

function nonPollingWatchFile(fileName: string, callback: FileWatcherCallback, _pollingInterval: PollingInterval, fallbackOptions: WatchOptions | undefined): FileWatcher {
const filePath = toCanonicalName(fileName);
fileWatcherCallbacks.add(filePath, callback);
if (fileWatcherCallbacks.add(filePath, callback).length === 1 && fileTimestamps) {
fileTimestamps.set(filePath, getModifiedTime(fileName) || missingFileModifiedTime);
}
const dirPath = getDirectoryPath(filePath) || ".";
const watcher = dirWatchers.get(dirPath) ||
createDirectoryWatcher(getDirectoryPath(fileName) || ".", dirPath, fallbackOptions);
Expand All @@ -410,15 +418,29 @@ function createUseFsEventsOnParentDirectoryWatchFile(fsWatch: FsWatch, useCaseSe
const watcher = fsWatch(
dirName,
FileSystemEntryKind.Directory,
(_eventName: string, relativeFileName, modifiedTime) => {
(eventName: string, relativeFileName) => {
// When files are deleted from disk, the triggered "rename" event would have a relativefileName of "undefined"
if (!isString(relativeFileName)) return;
const fileName = getNormalizedAbsolutePath(relativeFileName, dirName);
const filePath = toCanonicalName(fileName);
// Some applications save a working file via rename operations
const callbacks = fileName && fileWatcherCallbacks.get(toCanonicalName(fileName));
const callbacks = fileName && fileWatcherCallbacks.get(filePath);
if (callbacks) {
let currentModifiedTime;
let eventKind = FileWatcherEventKind.Changed;
if (fileTimestamps) {
const existingTime = fileTimestamps.get(filePath)!;
if (eventName === "change") {
currentModifiedTime = getModifiedTime(fileName) || missingFileModifiedTime;
if (currentModifiedTime.getTime() === existingTime.getTime()) return;
}
currentModifiedTime ||= getModifiedTime(fileName) || missingFileModifiedTime;
fileTimestamps.set(filePath, currentModifiedTime);
if (existingTime === missingFileModifiedTime) eventKind = FileWatcherEventKind.Created;
else if (currentModifiedTime === missingFileModifiedTime) eventKind = FileWatcherEventKind.Deleted;
}
for (const fileCallback of callbacks) {
fileCallback(fileName, FileWatcherEventKind.Changed, modifiedTime);
fileCallback(fileName, eventKind, currentModifiedTime);
}
}
},
Expand Down Expand Up @@ -974,7 +996,7 @@ export function createSystemWatchFunctions({
);
case WatchFileKind.UseFsEventsOnParentDirectory:
if (!nonPollingWatchFile) {
nonPollingWatchFile = createUseFsEventsOnParentDirectoryWatchFile(fsWatch, useCaseSensitiveFileNames);
nonPollingWatchFile = createUseFsEventsOnParentDirectoryWatchFile(fsWatch, useCaseSensitiveFileNames, getModifiedTime, fsWatchWithTimestamp);
}
return nonPollingWatchFile(fileName, callback, pollingInterval, getFallbackOptions(options));
default:
Expand Down Expand Up @@ -1191,7 +1213,7 @@ export function createSystemWatchFunctions({
return watchPresentFileSystemEntryWithFsWatchFile();
}
try {
const presentWatcher = (!fsWatchWithTimestamp ? fsWatchWorker : fsWatchWorkerHandlingTimestamp)(
const presentWatcher = (entryKind === FileSystemEntryKind.Directory || !fsWatchWithTimestamp ? fsWatchWorker : fsWatchWorkerHandlingTimestamp)(
fileOrDirectory,
recursive,
inodeWatching ?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,57 @@ Input::
export const x = 10;export const y = 10;


Before running Timeout callback:: count: 0
Output::
FileWatcher:: Triggered with /user/username/projects/myproject/main.ts 1:: WatchInfo: /user/username/projects/myproject/main.ts 250 {"watchFile":5} Source file
Scheduling update
Elapsed:: *ms FileWatcher:: Triggered with /user/username/projects/myproject/main.ts 1:: WatchInfo: /user/username/projects/myproject/main.ts 250 {"watchFile":5} Source file


Timeout callback:: count: 1
1: timerToUpdateProgram *new*

Before running Timeout callback:: count: 1
1: timerToUpdateProgram

After running Timeout callback:: count: 0
Output::
Synchronizing program
[HH:MM:SS AM] File change detected. Starting incremental compilation...

CreatingProgramWith::
roots: ["/user/username/projects/myproject/main.ts"]
options: {"watch":true,"extendedDiagnostics":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"}
[HH:MM:SS AM] Found 0 errors. Watching for file changes.



//// [/user/username/projects/myproject/main.js]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.y = exports.x = void 0;
exports.x = 10;
exports.y = 10;




Program root files: [
"/user/username/projects/myproject/main.ts"
]
Program options: {
"watch": true,
"extendedDiagnostics": true,
"configFilePath": "/user/username/projects/myproject/tsconfig.json"
}
Program structureReused: Completely
Program files::
/a/lib/lib.d.ts
/user/username/projects/myproject/main.ts

Semantic diagnostics in builder refreshed for::
/user/username/projects/myproject/main.ts

Shape signatures in builder refreshed for::
/user/username/projects/myproject/main.ts (computed .d.ts)

exitCode:: ExitStatus.undefined

0 comments on commit 98bcd9d

Please sign in to comment.