Skip to content

Commit e99e933

Browse files
authored
Merge pull request #19263 from Microsoft/directoryWatcherInsteadOfFileWatch
This fixes the issue with tsc --watch when module emit kind is none and directory watcher gets invoked instead of file
2 parents d05443b + bd0c210 commit e99e933

File tree

4 files changed

+72
-7
lines changed

4 files changed

+72
-7
lines changed

src/compiler/core.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2701,8 +2701,14 @@ namespace ts {
27012701

27022702
export function assertTypeIsNever(_: never): void { }
27032703

2704+
export interface FileAndDirectoryExistence {
2705+
fileExists: boolean;
2706+
directoryExists: boolean;
2707+
}
2708+
27042709
export interface CachedDirectoryStructureHost extends DirectoryStructureHost {
2705-
addOrDeleteFileOrDirectory(fileOrDirectory: string, fileOrDirectoryPath: Path): void;
2710+
/** Returns the queried result for the file exists and directory exists if at all it was done */
2711+
addOrDeleteFileOrDirectory(fileOrDirectory: string, fileOrDirectoryPath: Path): FileAndDirectoryExistence | undefined;
27062712
addOrDeleteFile(fileName: string, filePath: Path, eventKind: FileWatcherEventKind): void;
27072713
clearCache(): void;
27082714
}
@@ -2872,8 +2878,13 @@ namespace ts {
28722878
if (parentResult) {
28732879
const baseName = getBaseNameOfFileName(fileOrDirectory);
28742880
if (parentResult) {
2875-
updateFilesOfFileSystemEntry(parentResult, baseName, host.fileExists(fileOrDirectoryPath));
2876-
updateFileSystemEntry(parentResult.directories, baseName, host.directoryExists(fileOrDirectoryPath));
2881+
const fsQueryResult: FileAndDirectoryExistence = {
2882+
fileExists: host.fileExists(fileOrDirectoryPath),
2883+
directoryExists: host.directoryExists(fileOrDirectoryPath)
2884+
};
2885+
updateFilesOfFileSystemEntry(parentResult, baseName, fsQueryResult.fileExists);
2886+
updateFileSystemEntry(parentResult.directories, baseName, fsQueryResult.directoryExists);
2887+
return fsQueryResult;
28772888
}
28782889
}
28792890
}

src/compiler/watch.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -605,8 +605,17 @@ namespace ts {
605605
const fileOrDirectoryPath = toPath(fileOrDirectory);
606606

607607
// Since the file existance changed, update the sourceFiles cache
608-
(directoryStructureHost as CachedDirectoryStructureHost).addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath);
609-
removeSourceFile(fileOrDirectoryPath);
608+
const result = (directoryStructureHost as CachedDirectoryStructureHost).addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath);
609+
610+
// Instead of deleting the file, mark it as changed instead
611+
// Many times node calls add/remove/file when watching directories recursively
612+
const hostSourceFile = sourceFilesCache.get(fileOrDirectoryPath);
613+
if (hostSourceFile && !isString(hostSourceFile) && (result ? result.fileExists : directoryStructureHost.fileExists(fileOrDirectory))) {
614+
hostSourceFile.version++;
615+
}
616+
else {
617+
removeSourceFile(fileOrDirectoryPath);
618+
}
610619

611620
// If the the added or created file or directory is not supported file name, ignore the file
612621
// But when watched directory is added/removed, we need to reload the file list

src/harness/unittests/tscWatchMode.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1819,4 +1819,44 @@ declare module "fs" {
18191819
checkOutputErrors(host);
18201820
});
18211821
});
1822+
1823+
describe("tsc-watch with when module emit is specified as node", () => {
1824+
it("when instead of filechanged recursive directory watcher is invoked", () => {
1825+
const configFile: FileOrFolder = {
1826+
path: "/a/rootFolder/project/tsconfig.json",
1827+
content: JSON.stringify({
1828+
"compilerOptions": {
1829+
"module": "none",
1830+
"allowJs": true,
1831+
"outDir": "Static/scripts/"
1832+
},
1833+
"include": [
1834+
"Scripts/**/*"
1835+
],
1836+
})
1837+
};
1838+
const outputFolder = "/a/rootFolder/project/Static/scripts/";
1839+
const file1: FileOrFolder = {
1840+
path: "/a/rootFolder/project/Scripts/TypeScript.ts",
1841+
content: "var z = 10;"
1842+
};
1843+
const file2: FileOrFolder = {
1844+
path: "/a/rootFolder/project/Scripts/Javascript.js",
1845+
content: "var zz = 10;"
1846+
};
1847+
const files = [configFile, file1, file2, libFile];
1848+
const host = createWatchedSystem(files);
1849+
const watch = createWatchModeWithConfigFile(configFile.path, host);
1850+
1851+
checkProgramActualFiles(watch(), mapDefined(files, f => f === configFile ? undefined : f.path));
1852+
file1.content = "var zz30 = 100;";
1853+
host.reloadFS(files, /*invokeDirectoryWatcherInsteadOfFileChanged*/ true);
1854+
host.runQueuedTimeoutCallbacks();
1855+
1856+
checkProgramActualFiles(watch(), mapDefined(files, f => f === configFile ? undefined : f.path));
1857+
const outputFile1 = changeExtension((outputFolder + getBaseFileName(file1.path)), ".js");
1858+
assert.isTrue(host.fileExists(outputFile1));
1859+
assert.equal(host.readFile(outputFile1), file1.content + host.newLine);
1860+
});
1861+
});
18221862
}

src/harness/virtualFileSystemWithWatch.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ namespace ts.TestFSWithWatch {
263263
return s;
264264
}
265265

266-
reloadFS(fileOrFolderList: ReadonlyArray<FileOrFolder>) {
266+
reloadFS(fileOrFolderList: ReadonlyArray<FileOrFolder>, invokeDirectoryWatcherInsteadOfFileChanged?: boolean) {
267267
const mapNewLeaves = createMap<true>();
268268
const isNewFs = this.fs.size === 0;
269269
fileOrFolderList = fileOrFolderList.concat(this.withSafeList ? safeList : []);
@@ -284,7 +284,12 @@ namespace ts.TestFSWithWatch {
284284
// Update file
285285
if (currentEntry.content !== fileOrDirectory.content) {
286286
currentEntry.content = fileOrDirectory.content;
287-
this.invokeFileWatcher(currentEntry.fullPath, FileWatcherEventKind.Changed);
287+
if (invokeDirectoryWatcherInsteadOfFileChanged) {
288+
this.invokeDirectoryWatcher(getDirectoryPath(currentEntry.fullPath), currentEntry.fullPath);
289+
}
290+
else {
291+
this.invokeFileWatcher(currentEntry.fullPath, FileWatcherEventKind.Changed);
292+
}
288293
}
289294
}
290295
else {

0 commit comments

Comments
 (0)