mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-17 21:06:50 -05:00
Directories dont check modified time when sending "change" event (#57938)
This commit is contained in:
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -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:
|
||||
@@ -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 ?
|
||||
|
||||
@@ -501,12 +501,12 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
|
||||
else {
|
||||
currentEntry.content = content;
|
||||
currentEntry.modifiedTime = this.now();
|
||||
this.fs.get(getDirectoryPath(currentEntry.path))!.modifiedTime = this.now();
|
||||
if (options && options.invokeDirectoryWatcherInsteadOfFileChanged) {
|
||||
const directoryFullPath = getDirectoryPath(currentEntry.fullPath);
|
||||
this.invokeFileWatcher(directoryFullPath, FileWatcherEventKind.Changed, currentEntry.modifiedTime);
|
||||
this.invokeFsWatchesCallbacks(directoryFullPath, "rename", currentEntry.modifiedTime, currentEntry.fullPath, options.useTildeAsSuffixInRenameEventFileName);
|
||||
this.invokeRecursiveFsWatches(directoryFullPath, "rename", currentEntry.modifiedTime, currentEntry.fullPath, options.useTildeAsSuffixInRenameEventFileName);
|
||||
this.fs.get(getDirectoryPath(currentEntry.path))!.modifiedTime = this.now();
|
||||
this.invokeFileWatcher(directoryFullPath, FileWatcherEventKind.Changed, /*modifiedTime*/ undefined);
|
||||
this.invokeFsWatchesCallbacks(directoryFullPath, "rename", /*modifiedTime*/ undefined, currentEntry.fullPath, options.useTildeAsSuffixInRenameEventFileName);
|
||||
this.invokeRecursiveFsWatches(directoryFullPath, "rename", /*modifiedTime*/ undefined, currentEntry.fullPath, options.useTildeAsSuffixInRenameEventFileName);
|
||||
}
|
||||
else {
|
||||
this.invokeFileAndFsWatches(currentEntry.fullPath, FileWatcherEventKind.Changed, currentEntry.modifiedTime, options?.useTildeAsSuffixInRenameEventFileName);
|
||||
@@ -634,7 +634,7 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
|
||||
const inodeWatching = this.inodeWatching;
|
||||
if (options?.skipInodeCheckOnCreate) this.inodeWatching = false;
|
||||
this.invokeFileAndFsWatches(fileOrDirectory.fullPath, FileWatcherEventKind.Created, fileOrDirectory.modifiedTime, options?.useTildeAsSuffixInRenameEventFileName);
|
||||
this.invokeFileAndFsWatches(folder.fullPath, FileWatcherEventKind.Changed, fileOrDirectory.modifiedTime, options?.useTildeAsSuffixInRenameEventFileName);
|
||||
this.invokeFileAndFsWatches(folder.fullPath, FileWatcherEventKind.Changed, folder.modifiedTime, options?.useTildeAsSuffixInRenameEventFileName);
|
||||
this.inodeWatching = inodeWatching;
|
||||
}
|
||||
|
||||
@@ -741,13 +741,13 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
|
||||
this.invokeFsWatchesRecursiveCallbacks(fullPath, eventName, modifiedTime, entryFullPath, useTildeSuffix);
|
||||
const basePath = getDirectoryPath(fullPath);
|
||||
if (this.getCanonicalFileName(fullPath) !== this.getCanonicalFileName(basePath)) {
|
||||
this.invokeRecursiveFsWatches(basePath, eventName, modifiedTime, entryFullPath || fullPath, useTildeSuffix);
|
||||
this.invokeRecursiveFsWatches(basePath, eventName, /*modifiedTime*/ undefined, entryFullPath || fullPath, useTildeSuffix);
|
||||
}
|
||||
}
|
||||
|
||||
invokeFsWatches(fullPath: string, eventName: "rename" | "change", modifiedTime: Date | undefined, useTildeSuffix: boolean | undefined) {
|
||||
this.invokeFsWatchesCallbacks(fullPath, eventName, modifiedTime, fullPath, useTildeSuffix);
|
||||
this.invokeFsWatchesCallbacks(getDirectoryPath(fullPath), eventName, modifiedTime, fullPath, useTildeSuffix);
|
||||
this.invokeFsWatchesCallbacks(getDirectoryPath(fullPath), eventName, /*modifiedTime*/ undefined, fullPath, useTildeSuffix);
|
||||
this.invokeRecursiveFsWatches(fullPath, eventName, modifiedTime, /*entryFullPath*/ undefined, useTildeSuffix);
|
||||
}
|
||||
|
||||
|
||||
@@ -690,11 +690,11 @@ describe("unittests:: tsc-watch:: watchEnvironment:: tsc-watch with different po
|
||||
});
|
||||
|
||||
describe("with fsWatch with fsWatchWithTimestamp", () => {
|
||||
function verify(fsWatchWithTimestamp: boolean) {
|
||||
function verify(fsWatchWithTimestamp: boolean, watchFile?: "useFsEventsOnParentDirectory") {
|
||||
verifyTscWatch({
|
||||
scenario,
|
||||
subScenario: `fsWatch/fsWatchWithTimestamp ${fsWatchWithTimestamp}`,
|
||||
commandLineArgs: ["-w", "--extendedDiagnostics"],
|
||||
subScenario: `fsWatch/fsWatchWithTimestamp ${fsWatchWithTimestamp}${watchFile ? ` ${watchFile}` : ""}`,
|
||||
commandLineArgs: ["-w", "--extendedDiagnostics", ...(watchFile ? ["--watchFile", watchFile] : [])],
|
||||
sys: () =>
|
||||
createWatchedSystem(
|
||||
{
|
||||
@@ -723,6 +723,8 @@ describe("unittests:: tsc-watch:: watchEnvironment:: tsc-watch with different po
|
||||
}
|
||||
verify(/*fsWatchWithTimestamp*/ true);
|
||||
verify(/*fsWatchWithTimestamp*/ false);
|
||||
verify(/*fsWatchWithTimestamp*/ true, "useFsEventsOnParentDirectory");
|
||||
verify(/*fsWatchWithTimestamp*/ false, "useFsEventsOnParentDirectory");
|
||||
});
|
||||
|
||||
verifyTscWatch({
|
||||
|
||||
Reference in New Issue
Block a user