Skip to content

Commit 89d2d4c

Browse files
committed
Passdown modified time if queried
1 parent dd96e33 commit 89d2d4c

File tree

4 files changed

+60
-59
lines changed

4 files changed

+60
-59
lines changed

src/compiler/sys.ts

+31-29
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ namespace ts {
3333
Deleted
3434
}
3535

36-
export type FileWatcherCallback = (fileName: string, eventKind: FileWatcherEventKind) => void;
36+
export type FileWatcherCallback = (fileName: string, eventKind: FileWatcherEventKind, modifiedTime?: Date) => void;
3737
export type DirectoryWatcherCallback = (fileName: string) => void;
3838
/*@internal*/
3939
export interface WatchedFile {
@@ -364,15 +364,15 @@ namespace ts {
364364
const watcher = fsWatch(
365365
dirName,
366366
FileSystemEntryKind.Directory,
367-
(_eventName: string, relativeFileName) => {
367+
(_eventName: string, relativeFileName, modifiedTime) => {
368368
// When files are deleted from disk, the triggered "rename" event would have a relativefileName of "undefined"
369369
if (!isString(relativeFileName)) return;
370370
const fileName = getNormalizedAbsolutePath(relativeFileName, dirName);
371371
// Some applications save a working file via rename operations
372372
const callbacks = fileName && fileWatcherCallbacks.get(toCanonicalName(fileName));
373373
if (callbacks) {
374374
for (const fileCallback of callbacks) {
375-
fileCallback(fileName, FileWatcherEventKind.Changed);
375+
fileCallback(fileName, FileWatcherEventKind.Changed, modifiedTime);
376376
}
377377
}
378378
},
@@ -446,9 +446,9 @@ namespace ts {
446446
cache.set(path, {
447447
watcher: watchFile(
448448
fileName,
449-
(fileName, eventKind) => forEach(
449+
(fileName, eventKind, modifiedTime) => forEach(
450450
callbacksCache.get(path),
451-
cb => cb(fileName, eventKind)
451+
cb => cb(fileName, eventKind, modifiedTime)
452452
),
453453
pollingInterval,
454454
options
@@ -480,7 +480,7 @@ namespace ts {
480480
const newTime = modifiedTime.getTime();
481481
if (oldTime !== newTime) {
482482
watchedFile.mtime = modifiedTime;
483-
watchedFile.callback(watchedFile.fileName, getFileWatcherEventKind(oldTime, newTime));
483+
watchedFile.callback(watchedFile.fileName, getFileWatcherEventKind(oldTime, newTime), modifiedTime);
484484
return true;
485485
}
486486

@@ -776,7 +776,7 @@ namespace ts {
776776
}
777777

778778
/*@internal*/
779-
export type FsWatchCallback = (eventName: "rename" | "change", relativeFileName: string | undefined) => void;
779+
export type FsWatchCallback = (eventName: "rename" | "change", relativeFileName: string | undefined, modifiedTime?: Date) => void;
780780
/*@internal*/
781781
export type FsWatch = (fileOrDirectory: string, entryKind: FileSystemEntryKind, callback: FsWatchCallback, recursive: boolean, fallbackPollingInterval: PollingInterval, fallbackOptions: WatchOptions | undefined) => FileWatcher;
782782

@@ -788,21 +788,22 @@ namespace ts {
788788

789789
/*@internal*/
790790
export function createFileWatcherCallback(callback: FsWatchCallback): FileWatcherCallback {
791-
return (_fileName, eventKind) => callback(eventKind === FileWatcherEventKind.Changed ? "change" : "rename", "");
791+
return (_fileName, eventKind, modifiedTime) => callback(eventKind === FileWatcherEventKind.Changed ? "change" : "rename", "", modifiedTime);
792792
}
793793

794794
function createFsWatchCallbackForFileWatcherCallback(
795795
fileName: string,
796796
callback: FileWatcherCallback,
797-
fileExists: System["fileExists"]
797+
getModifiedTime: NonNullable<System["getModifiedTime"]>
798798
): FsWatchCallback {
799-
return eventName => {
799+
return (eventName, _relativeFileName, modifiedTime) => {
800800
if (eventName === "rename") {
801-
callback(fileName, fileExists(fileName) ? FileWatcherEventKind.Created : FileWatcherEventKind.Deleted);
801+
modifiedTime ||= getModifiedTime(fileName) || missingFileModifiedTime;
802+
callback(fileName, modifiedTime !== missingFileModifiedTime ? FileWatcherEventKind.Created : FileWatcherEventKind.Deleted, modifiedTime);
802803
}
803804
else {
804805
// Change
805-
callback(fileName, FileWatcherEventKind.Changed);
806+
callback(fileName, FileWatcherEventKind.Changed, modifiedTime);
806807
}
807808
};
808809
}
@@ -850,7 +851,6 @@ namespace ts {
850851
clearTimeout: NonNullable<System["clearTimeout"]>;
851852
// For fs events :
852853
fsWatch: FsWatch;
853-
fileExists: System["fileExists"];
854854
useCaseSensitiveFileNames: boolean;
855855
getCurrentDirectory: System["getCurrentDirectory"];
856856
fsSupportsRecursiveFsWatch: boolean;
@@ -871,7 +871,6 @@ namespace ts {
871871
setTimeout,
872872
clearTimeout,
873873
fsWatch,
874-
fileExists,
875874
useCaseSensitiveFileNames,
876875
getCurrentDirectory,
877876
fsSupportsRecursiveFsWatch,
@@ -908,7 +907,7 @@ namespace ts {
908907
return fsWatch(
909908
fileName,
910909
FileSystemEntryKind.File,
911-
createFsWatchCallbackForFileWatcherCallback(fileName, callback, fileExists),
910+
createFsWatchCallbackForFileWatcherCallback(fileName, callback, getModifiedTime),
912911
/*recursive*/ false,
913912
pollingInterval,
914913
getFallbackOptions(options)
@@ -1297,7 +1296,6 @@ namespace ts {
12971296
fsWatch,
12981297
useCaseSensitiveFileNames,
12991298
getCurrentDirectory,
1300-
fileExists,
13011299
// Node 4.0 `fs.watch` function supports the "recursive" option on both OSX and Windows
13021300
// (ref: https://github.com/nodejs/node/pull/2649 and https://github.com/Microsoft/TypeScript/issues/4643)
13031301
fsSupportsRecursiveFsWatch,
@@ -1542,7 +1540,7 @@ namespace ts {
15421540
close: () => _fs.unwatchFile(fileName, fileChanged)
15431541
};
15441542

1545-
function fileChanged(curr: any, prev: any) {
1543+
function fileChanged(curr: import("fs").Stats, prev: import("fs").Stats) {
15461544
// previous event kind check is to ensure we recongnize the file as previously also missing when it is restored or renamed twice (that is it disappears and reappears)
15471545
// In such case, prevTime returned is same as prev time of event when file was deleted as per node documentation
15481546
const isPreviouslyDeleted = +prev.mtime === 0 || eventKind === FileWatcherEventKind.Deleted;
@@ -1564,7 +1562,7 @@ namespace ts {
15641562
// File changed
15651563
eventKind = FileWatcherEventKind.Changed;
15661564
}
1567-
callback(fileName, eventKind);
1565+
callback(fileName, eventKind, curr.mtime);
15681566
}
15691567
}
15701568

@@ -1599,10 +1597,10 @@ namespace ts {
15991597
* Invoke the callback with rename and update the watcher if not closed
16001598
* @param createWatcher
16011599
*/
1602-
function invokeCallbackAndUpdateWatcher(createWatcher: () => FileWatcher) {
1600+
function invokeCallbackAndUpdateWatcher(createWatcher: () => FileWatcher, modifiedTime?: Date) {
16031601
sysLog(`sysLog:: ${fileOrDirectory}:: Changing watcher to ${createWatcher === watchPresentFileSystemEntry ? "Present" : "Missing"}FileSystemEntryWatcher`);
16041602
// Call the callback for current directory
1605-
callback("rename", "");
1603+
callback("rename", "", modifiedTime);
16061604

16071605
// If watcher is not closed, update it
16081606
if (watcher) {
@@ -1656,13 +1654,14 @@ namespace ts {
16561654
function callbackChangingToMissingFileSystemEntry(event: "rename" | "change", relativeName: string | undefined) {
16571655
// because relativeName is not guaranteed to be correct we need to check on each rename with few combinations
16581656
// Eg on ubuntu while watching app/node_modules the relativeName is "node_modules" which is neither relative nor full path
1657+
const modifiedTime = getModifiedTime(fileOrDirectory) || missingFileModifiedTime;
16591658
return event === "rename" &&
16601659
(!relativeName ||
16611660
relativeName === lastDirectoryPart ||
16621661
(relativeName.lastIndexOf(lastDirectoryPartWithDirectorySeparator!) !== -1 && relativeName.lastIndexOf(lastDirectoryPartWithDirectorySeparator!) === relativeName.length - lastDirectoryPartWithDirectorySeparator!.length)) &&
1663-
!fileSystemEntryExists(fileOrDirectory, entryKind) ?
1664-
invokeCallbackAndUpdateWatcher(watchMissingFileSystemEntry) :
1665-
callback(event, relativeName);
1662+
modifiedTime === missingFileModifiedTime ?
1663+
invokeCallbackAndUpdateWatcher(watchMissingFileSystemEntry, modifiedTime) :
1664+
callback(event, relativeName, modifiedTime);
16661665
}
16671666

16681667
/**
@@ -1685,12 +1684,15 @@ namespace ts {
16851684
function watchMissingFileSystemEntry(): FileWatcher {
16861685
return watchFile(
16871686
fileOrDirectory,
1688-
(_fileName, eventKind) => {
1689-
if (eventKind === FileWatcherEventKind.Created && fileSystemEntryExists(fileOrDirectory, entryKind)) {
1690-
// Call the callback for current file or directory
1691-
// For now it could be callback for the inner directory creation,
1692-
// but just return current directory, better than current no-op
1693-
invokeCallbackAndUpdateWatcher(watchPresentFileSystemEntry);
1687+
(_fileName, eventKind, modifiedTime) => {
1688+
if (eventKind === FileWatcherEventKind.Created) {
1689+
modifiedTime ||= getModifiedTime(fileOrDirectory) || missingFileModifiedTime;
1690+
if (modifiedTime !== missingFileModifiedTime) {
1691+
// Call the callback for current file or directory
1692+
// For now it could be callback for the inner directory creation,
1693+
// but just return current directory, better than current no-op
1694+
invokeCallbackAndUpdateWatcher(watchPresentFileSystemEntry, modifiedTime);
1695+
}
16941696
}
16951697
},
16961698
fallbackPollingInterval,

src/harness/virtualFileSystemWithWatch.ts

+27-28
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,6 @@ interface Array<T> { length: number; [n: number]: T; }`
433433
setTimeout: this.setTimeout.bind(this),
434434
clearTimeout: this.clearTimeout.bind(this),
435435
fsWatch: this.fsWatch.bind(this),
436-
fileExists: this.fileExists.bind(this),
437436
useCaseSensitiveFileNames: this.useCaseSensitiveFileNames,
438437
getCurrentDirectory: this.getCurrentDirectory.bind(this),
439438
fsSupportsRecursiveFsWatch: tscWatchDirectory ? false : !runWithoutRecursiveWatches,
@@ -514,7 +513,7 @@ interface Array<T> { length: number; [n: number]: T; }`
514513
else {
515514
// Folder update: Nothing to do.
516515
currentEntry.modifiedTime = this.now();
517-
this.invokeFsWatches(currentEntry.fullPath, "change");
516+
this.invokeFsWatches(currentEntry.fullPath, "change", currentEntry.modifiedTime);
518517
}
519518
}
520519
}
@@ -541,12 +540,12 @@ interface Array<T> { length: number; [n: number]: T; }`
541540
this.fs.get(getDirectoryPath(currentEntry.path))!.modifiedTime = this.now();
542541
if (options && options.invokeDirectoryWatcherInsteadOfFileChanged) {
543542
const directoryFullPath = getDirectoryPath(currentEntry.fullPath);
544-
this.invokeFileWatcher(directoryFullPath, FileWatcherEventKind.Changed, /*useFileNameInCallback*/ true);
545-
this.invokeFsWatchesCallbacks(directoryFullPath, "rename", currentEntry.fullPath);
546-
this.invokeRecursiveFsWatches(directoryFullPath, "rename", currentEntry.fullPath);
543+
this.invokeFileWatcher(directoryFullPath, FileWatcherEventKind.Changed, currentEntry.modifiedTime, /*useFileNameInCallback*/ true);
544+
this.invokeFsWatchesCallbacks(directoryFullPath, "rename", currentEntry.modifiedTime, currentEntry.fullPath);
545+
this.invokeRecursiveFsWatches(directoryFullPath, "rename", currentEntry.modifiedTime, currentEntry.fullPath);
547546
}
548547
else {
549-
this.invokeFileAndFsWatches(currentEntry.fullPath, FileWatcherEventKind.Changed);
548+
this.invokeFileAndFsWatches(currentEntry.fullPath, FileWatcherEventKind.Changed, currentEntry.modifiedTime);
550549
}
551550
}
552551
}
@@ -665,8 +664,8 @@ interface Array<T> { length: number; [n: number]: T; }`
665664
if (ignoreWatch) {
666665
return;
667666
}
668-
this.invokeFileAndFsWatches(fileOrDirectory.fullPath, FileWatcherEventKind.Created);
669-
this.invokeFileAndFsWatches(folder.fullPath, FileWatcherEventKind.Changed);
667+
this.invokeFileAndFsWatches(fileOrDirectory.fullPath, FileWatcherEventKind.Created, fileOrDirectory.modifiedTime);
668+
this.invokeFileAndFsWatches(folder.fullPath, FileWatcherEventKind.Changed, folder.modifiedTime);
670669
}
671670

672671
private removeFileOrFolder(fileOrDirectory: FsFile | FsFolder | FsSymLink, isRemovableLeafFolder: (folder: FsFolder) => boolean, isRenaming = false) {
@@ -683,7 +682,7 @@ interface Array<T> { length: number; [n: number]: T; }`
683682
Debug.assert(fileOrDirectory.entries.length === 0 || isRenaming);
684683
}
685684
this.invokeFileAndFsWatches(fileOrDirectory.fullPath, FileWatcherEventKind.Deleted);
686-
this.invokeFileAndFsWatches(baseFolder.fullPath, FileWatcherEventKind.Changed);
685+
this.invokeFileAndFsWatches(baseFolder.fullPath, FileWatcherEventKind.Changed, baseFolder.modifiedTime);
687686
if (basePath !== fileOrDirectory.path &&
688687
baseFolder.entries.length === 0 &&
689688
isRemovableLeafFolder(baseFolder)) {
@@ -750,43 +749,43 @@ interface Array<T> { length: number; [n: number]: T; }`
750749
);
751750
}
752751

753-
invokeFileWatcher(fileFullPath: string, eventKind: FileWatcherEventKind, useFileNameInCallback?: boolean) {
754-
invokeWatcherCallbacks(this.watchedFiles.get(this.toPath(fileFullPath)), ({ cb, fileName }) => cb(useFileNameInCallback ? fileName : fileFullPath, eventKind));
752+
invokeFileWatcher(fileFullPath: string, eventKind: FileWatcherEventKind, modifiedTime?: Date, useFileNameInCallback?: boolean) {
753+
invokeWatcherCallbacks(this.watchedFiles.get(this.toPath(fileFullPath)), ({ cb, fileName }) => cb(useFileNameInCallback ? fileName : fileFullPath, eventKind, modifiedTime));
755754
}
756755

757-
private fsWatchCallback(map: MultiMap<Path, TestFsWatcher>, fullPath: string, eventName: "rename" | "change", entryFullPath?: string) {
758-
invokeWatcherCallbacks(map.get(this.toPath(fullPath)), ({ cb }) => cb(eventName, entryFullPath ? this.getRelativePathToDirectory(fullPath, entryFullPath) : ""));
756+
private fsWatchCallback(map: MultiMap<Path, TestFsWatcher>, fullPath: string, eventName: "rename" | "change", modifiedTime?: Date, entryFullPath?: string) {
757+
invokeWatcherCallbacks(map.get(this.toPath(fullPath)), ({ cb }) => cb(eventName, entryFullPath ? this.getRelativePathToDirectory(fullPath, entryFullPath) : "", modifiedTime));
759758
}
760759

761-
invokeFsWatchesCallbacks(fullPath: string, eventName: "rename" | "change", entryFullPath?: string) {
762-
this.fsWatchCallback(this.fsWatches, fullPath, eventName, entryFullPath);
760+
invokeFsWatchesCallbacks(fullPath: string, eventName: "rename" | "change", modifiedTime?: Date, entryFullPath?: string) {
761+
this.fsWatchCallback(this.fsWatches, fullPath, eventName, modifiedTime, entryFullPath);
763762
}
764763

765-
invokeFsWatchesRecursiveCallbacks(fullPath: string, eventName: "rename" | "change", entryFullPath?: string) {
766-
this.fsWatchCallback(this.fsWatchesRecursive, fullPath, eventName, entryFullPath);
764+
invokeFsWatchesRecursiveCallbacks(fullPath: string, eventName: "rename" | "change", modifiedTime?: Date, entryFullPath?: string) {
765+
this.fsWatchCallback(this.fsWatchesRecursive, fullPath, eventName, modifiedTime, entryFullPath);
767766
}
768767

769768
private getRelativePathToDirectory(directoryFullPath: string, fileFullPath: string) {
770769
return getRelativePathToDirectoryOrUrl(directoryFullPath, fileFullPath, this.currentDirectory, this.getCanonicalFileName, /*isAbsolutePathAnUrl*/ false);
771770
}
772771

773-
private invokeRecursiveFsWatches(fullPath: string, eventName: "rename" | "change", entryFullPath?: string) {
774-
this.invokeFsWatchesRecursiveCallbacks(fullPath, eventName, entryFullPath);
772+
private invokeRecursiveFsWatches(fullPath: string, eventName: "rename" | "change", modifiedTime?: Date, entryFullPath?: string) {
773+
this.invokeFsWatchesRecursiveCallbacks(fullPath, eventName, modifiedTime, entryFullPath);
775774
const basePath = getDirectoryPath(fullPath);
776775
if (this.getCanonicalFileName(fullPath) !== this.getCanonicalFileName(basePath)) {
777-
this.invokeRecursiveFsWatches(basePath, eventName, entryFullPath || fullPath);
776+
this.invokeRecursiveFsWatches(basePath, eventName, modifiedTime, entryFullPath || fullPath);
778777
}
779778
}
780779

781-
private invokeFsWatches(fullPath: string, eventName: "rename" | "change") {
782-
this.invokeFsWatchesCallbacks(fullPath, eventName);
783-
this.invokeFsWatchesCallbacks(getDirectoryPath(fullPath), eventName, fullPath);
784-
this.invokeRecursiveFsWatches(fullPath, eventName);
780+
private invokeFsWatches(fullPath: string, eventName: "rename" | "change", modifiedTime?: Date) {
781+
this.invokeFsWatchesCallbacks(fullPath, eventName, modifiedTime);
782+
this.invokeFsWatchesCallbacks(getDirectoryPath(fullPath), eventName, modifiedTime, fullPath);
783+
this.invokeRecursiveFsWatches(fullPath, eventName, modifiedTime);
785784
}
786785

787-
private invokeFileAndFsWatches(fileOrFolderFullPath: string, eventKind: FileWatcherEventKind) {
788-
this.invokeFileWatcher(fileOrFolderFullPath, eventKind);
789-
this.invokeFsWatches(fileOrFolderFullPath, eventKind === FileWatcherEventKind.Changed ? "change" : "rename");
786+
private invokeFileAndFsWatches(fileOrFolderFullPath: string, eventKind: FileWatcherEventKind, modifiedTime?: Date) {
787+
this.invokeFileWatcher(fileOrFolderFullPath, eventKind, modifiedTime);
788+
this.invokeFsWatches(fileOrFolderFullPath, eventKind === FileWatcherEventKind.Changed ? "change" : "rename", modifiedTime);
790789
}
791790

792791
private toFsEntry(path: string): FSEntryBase {
@@ -879,7 +878,7 @@ interface Array<T> { length: number; [n: number]: T; }`
879878
const fsEntry = this.fs.get(path);
880879
if (fsEntry) {
881880
fsEntry.modifiedTime = date;
882-
this.invokeFileAndFsWatches(fsEntry.fullPath, FileWatcherEventKind.Changed);
881+
this.invokeFileAndFsWatches(fsEntry.fullPath, FileWatcherEventKind.Changed, fsEntry.modifiedTime);
883882
}
884883
}
885884

0 commit comments

Comments
 (0)