Handle when directory watcher is invoked on file change

Fixes #19206
This commit is contained in:
Sheetal Nandi 2017-10-17 14:13:12 -07:00
parent 28509e1732
commit a5861af00e
4 changed files with 72 additions and 7 deletions

View file

@ -2701,8 +2701,14 @@ namespace ts {
export function assertTypeIsNever(_: never): void { }
export interface FileAndDirectoryExistence {
fileExists: boolean;
directoryExists: boolean;
}
export interface CachedDirectoryStructureHost extends DirectoryStructureHost {
addOrDeleteFileOrDirectory(fileOrDirectory: string, fileOrDirectoryPath: Path): void;
/** Returns the queried result for the file exists and directory exists if at all it was done */
addOrDeleteFileOrDirectory(fileOrDirectory: string, fileOrDirectoryPath: Path): FileAndDirectoryExistence | undefined;
addOrDeleteFile(fileName: string, filePath: Path, eventKind: FileWatcherEventKind): void;
clearCache(): void;
}
@ -2872,8 +2878,13 @@ namespace ts {
if (parentResult) {
const baseName = getBaseNameOfFileName(fileOrDirectory);
if (parentResult) {
updateFilesOfFileSystemEntry(parentResult, baseName, host.fileExists(fileOrDirectoryPath));
updateFileSystemEntry(parentResult.directories, baseName, host.directoryExists(fileOrDirectoryPath));
const fsQueryResult: FileAndDirectoryExistence = {
fileExists: host.fileExists(fileOrDirectoryPath),
directoryExists: host.directoryExists(fileOrDirectoryPath)
};
updateFilesOfFileSystemEntry(parentResult, baseName, fsQueryResult.fileExists);
updateFileSystemEntry(parentResult.directories, baseName, fsQueryResult.directoryExists);
return fsQueryResult;
}
}
}

View file

@ -605,8 +605,17 @@ namespace ts {
const fileOrDirectoryPath = toPath(fileOrDirectory);
// Since the file existance changed, update the sourceFiles cache
(directoryStructureHost as CachedDirectoryStructureHost).addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath);
removeSourceFile(fileOrDirectoryPath);
const result = (directoryStructureHost as CachedDirectoryStructureHost).addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath);
// Instead of deleting the file, mark it as changed instead
// Many times node calls add/remove/file when watching directories recursively
const hostSourceFile = sourceFilesCache.get(fileOrDirectoryPath);
if (hostSourceFile && !isString(hostSourceFile) && (result ? result.fileExists : directoryStructureHost.fileExists(fileOrDirectory))) {
hostSourceFile.version++;
}
else {
removeSourceFile(fileOrDirectoryPath);
}
// If the the added or created file or directory is not supported file name, ignore the file
// But when watched directory is added/removed, we need to reload the file list

View file

@ -1819,4 +1819,44 @@ declare module "fs" {
checkOutputErrors(host);
});
});
describe("tsc-watch with when module emit is specified as node", () => {
it("when instead of filechanged recursive directory watcher is invoked", () => {
const configFile: FileOrFolder = {
path: "/a/rootFolder/project/tsconfig.json",
content: JSON.stringify({
"compilerOptions": {
"module": "none",
"allowJs": true,
"outDir": "Static/scripts/"
},
"include": [
"Scripts/**/*"
],
})
};
const outputFolder = "/a/rootFolder/project/Static/scripts/";
const file1: FileOrFolder = {
path: "/a/rootFolder/project/Scripts/TypeScript.ts",
content: "var z = 10;"
};
const file2: FileOrFolder = {
path: "/a/rootFolder/project/Scripts/Javascript.js",
content: "var zz = 10;"
};
const files = [configFile, file1, file2, libFile];
const host = createWatchedSystem(files);
const watch = createWatchModeWithConfigFile(configFile.path, host);
checkProgramActualFiles(watch(), mapDefined(files, f => f === configFile ? undefined : f.path));
file1.content = "var zz30 = 100;";
host.reloadFS(files, /*invokeDirectoryWatcherInsteadOfFileChanged*/ true);
host.runQueuedTimeoutCallbacks();
checkProgramActualFiles(watch(), mapDefined(files, f => f === configFile ? undefined : f.path));
const outputFile1 = changeExtension((outputFolder + getBaseFileName(file1.path)), ".js");
assert.isTrue(host.fileExists(outputFile1));
assert.equal(host.readFile(outputFile1), file1.content + host.newLine);
});
});
}

View file

@ -250,7 +250,7 @@ namespace ts.TestFSWithWatch {
return this.toPath(this.toNormalizedAbsolutePath(s));
}
reloadFS(fileOrFolderList: ReadonlyArray<FileOrFolder>) {
reloadFS(fileOrFolderList: ReadonlyArray<FileOrFolder>, invokeDirectoryWatcherInsteadOfFileChanged?: boolean) {
const mapNewLeaves = createMap<true>();
const isNewFs = this.fs.size === 0;
// always inject safelist file in the list of files
@ -265,7 +265,12 @@ namespace ts.TestFSWithWatch {
// Update file
if (currentEntry.content !== fileOrDirectory.content) {
currentEntry.content = fileOrDirectory.content;
this.invokeFileWatcher(currentEntry.fullPath, FileWatcherEventKind.Changed);
if (invokeDirectoryWatcherInsteadOfFileChanged) {
this.invokeDirectoryWatcher(getDirectoryPath(currentEntry.fullPath), currentEntry.fullPath);
}
else {
this.invokeFileWatcher(currentEntry.fullPath, FileWatcherEventKind.Changed);
}
}
}
else {