From bb32906dd4861ac985053ee566632c712a636566 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Fri, 6 May 2022 15:27:24 -0700 Subject: [PATCH] Ensure that when doing inode watching watchers is replaces only on disappearance or appearance --- src/compiler/sys.ts | 7 +- .../unittests/tscWatch/watchEnvironment.ts | 35 +++ ...e-occurs-when-file-is-still-on-the-disk.js | 220 ++++++++++++++++++ 3 files changed, 261 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/tscWatch/watchEnvironment/fsWatch/when-using-file-watching-thats-when-rename-occurs-when-file-is-still-on-the-disk.js diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index a2f366e9bf7..c0ab1065755 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -1147,7 +1147,12 @@ namespace ts { (!relativeName || relativeName === lastDirectoryPart || (relativeName.lastIndexOf(lastDirectoryPartWithDirectorySeparator!) !== -1 && relativeName.lastIndexOf(lastDirectoryPartWithDirectorySeparator!) === relativeName.length - lastDirectoryPartWithDirectorySeparator!.length))) { - updateWatcher(!fileSystemEntryExists(fileOrDirectory, entryKind) ? watchMissingFileSystemEntry : watchPresentFileSystemEntry); + if (inodeWatching) { + updateWatcher(!fileSystemEntryExists(fileOrDirectory, entryKind) ? watchMissingFileSystemEntry : watchPresentFileSystemEntry); + } + else if (!fileSystemEntryExists(fileOrDirectory, entryKind)) { + updateWatcher(watchMissingFileSystemEntry); + } } } diff --git a/src/testRunner/unittests/tscWatch/watchEnvironment.ts b/src/testRunner/unittests/tscWatch/watchEnvironment.ts index c050451193e..7428d9d1a15 100644 --- a/src/testRunner/unittests/tscWatch/watchEnvironment.ts +++ b/src/testRunner/unittests/tscWatch/watchEnvironment.ts @@ -582,6 +582,41 @@ namespace ts.tscWatch { }); }); + verifyTscWatch({ + scenario, + subScenario: `fsWatch/when using file watching thats when rename occurs when file is still on the disk`, + commandLineArgs: ["-w", "--extendedDiagnostics"], + sys: () => createWatchedSystem( + { + [libFile.path]: libFile.content, + [`${projectRoot}/main.ts`]: `import { foo } from "./foo"; foo();`, + [`${projectRoot}/foo.ts`]: `export declare function foo(): string;`, + [`${projectRoot}/tsconfig.json`]: JSON.stringify({ + watchOptions: { watchFile: "useFsEvents" }, + files: ["foo.ts", "main.ts"] + }), + }, + { currentDirectory: projectRoot, } + ), + changes: [ + { + caption: "Introduce error such that when callback happens file is already appeared", + // vm's wq generates this kind of event + // Skip delete event so inode changes but when the create's rename occurs file is on disk + change: sys => sys.modifyFile(`${projectRoot}/foo.ts`, `export declare function foo2(): string;`, { + invokeFileDeleteCreateAsPartInsteadOfChange: true, + ignoreDelete: true, + }), + timeouts: sys => sys.checkTimeoutQueueLengthAndRun(1), + }, + { + caption: "Replace file with rename event that fixes error", + change: sys => sys.modifyFile(`${projectRoot}/foo.ts`, `export declare function foo(): string;`, { invokeFileDeleteCreateAsPartInsteadOfChange: true, }), + timeouts: sys => sys.checkTimeoutQueueLengthAndRun(1), + }, + ] + }); + describe("with fsWatch on inodes", () => { verifyTscWatch({ scenario, diff --git a/tests/baselines/reference/tscWatch/watchEnvironment/fsWatch/when-using-file-watching-thats-when-rename-occurs-when-file-is-still-on-the-disk.js b/tests/baselines/reference/tscWatch/watchEnvironment/fsWatch/when-using-file-watching-thats-when-rename-occurs-when-file-is-still-on-the-disk.js new file mode 100644 index 00000000000..129e3ec2f79 --- /dev/null +++ b/tests/baselines/reference/tscWatch/watchEnvironment/fsWatch/when-using-file-watching-thats-when-rename-occurs-when-file-is-still-on-the-disk.js @@ -0,0 +1,220 @@ +Input:: +//// [/a/lib/lib.d.ts] +/// +interface Boolean {} +interface Function {} +interface CallableFunction {} +interface NewableFunction {} +interface IArguments {} +interface Number { toExponential: any; } +interface Object {} +interface RegExp {} +interface String { charAt: any; } +interface Array { length: number; [n: number]: T; } + +//// [/user/username/projects/myproject/main.ts] +import { foo } from "./foo"; foo(); + +//// [/user/username/projects/myproject/foo.ts] +export declare function foo(): string; + +//// [/user/username/projects/myproject/tsconfig.json] +{"watchOptions":{"watchFile":"useFsEvents"},"files":["foo.ts","main.ts"]} + + +/a/lib/tsc.js -w --extendedDiagnostics +Output:: +[12:00:23 AM] Starting compilation in watch mode... + +Current directory: /user/username/projects/myproject CaseSensitiveFileNames: false +FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/tsconfig.json 2000 {"watchFile":4} Config file +Synchronizing program +CreatingProgramWith:: + roots: ["/user/username/projects/myproject/foo.ts","/user/username/projects/myproject/main.ts"] + options: {"watch":true,"extendedDiagnostics":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"} +FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/foo.ts 250 {"watchFile":4} Source file +FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/main.ts 250 {"watchFile":4} Source file +FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 250 {"watchFile":4} Source file +DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/@types 1 {"watchFile":4} Type roots +Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/@types 1 {"watchFile":4} Type roots +[12:00:28 AM] Found 0 errors. Watching for file changes. + + + +Program root files: ["/user/username/projects/myproject/foo.ts","/user/username/projects/myproject/main.ts"] +Program options: {"watch":true,"extendedDiagnostics":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"} +Program structureReused: Not +Program files:: +/a/lib/lib.d.ts +/user/username/projects/myproject/foo.ts +/user/username/projects/myproject/main.ts + +Semantic diagnostics in builder refreshed for:: +/a/lib/lib.d.ts +/user/username/projects/myproject/foo.ts +/user/username/projects/myproject/main.ts + +Shape signatures in builder refreshed for:: +/a/lib/lib.d.ts (used version) +/user/username/projects/myproject/foo.ts (used version) +/user/username/projects/myproject/main.ts (used version) + +WatchedFiles:: +/user/username/projects/myproject/node_modules/@types: + {"fileName":"/user/username/projects/myproject/node_modules/@types","pollingInterval":500} + +FsWatches:: +/user/username/projects/myproject/tsconfig.json: + {"directoryName":"/user/username/projects/myproject/tsconfig.json"} +/user/username/projects/myproject/foo.ts: + {"directoryName":"/user/username/projects/myproject/foo.ts"} +/user/username/projects/myproject/main.ts: + {"directoryName":"/user/username/projects/myproject/main.ts"} +/a/lib/lib.d.ts: + {"directoryName":"/a/lib/lib.d.ts"} + +FsWatchesRecursive:: + +exitCode:: ExitStatus.undefined + +//// [/user/username/projects/myproject/foo.js] +"use strict"; +exports.__esModule = true; + + +//// [/user/username/projects/myproject/main.js] +"use strict"; +exports.__esModule = true; +var foo_1 = require("./foo"); +(0, foo_1.foo)(); + + + +Change:: Introduce error such that when callback happens file is already appeared + +Input:: +//// [/user/username/projects/myproject/foo.ts] +export declare function foo2(): string; + + +Output:: +FileWatcher:: Triggered with /user/username/projects/myproject/foo.ts 0:: WatchInfo: /user/username/projects/myproject/foo.ts 250 {"watchFile":4} Source file +Scheduling update +Elapsed:: *ms FileWatcher:: Triggered with /user/username/projects/myproject/foo.ts 0:: WatchInfo: /user/username/projects/myproject/foo.ts 250 {"watchFile":4} Source file +Synchronizing program +[12:00:32 AM] File change detected. Starting incremental compilation... + +CreatingProgramWith:: + roots: ["/user/username/projects/myproject/foo.ts","/user/username/projects/myproject/main.ts"] + options: {"watch":true,"extendedDiagnostics":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"} +main.ts:1:10 - error TS2724: '"./foo"' has no exported member named 'foo'. Did you mean 'foo2'? + +1 import { foo } from "./foo"; foo(); +   ~~~ + + foo.ts:1:25 + 1 export declare function foo2(): string; +    ~~~~ + 'foo2' is declared here. + +[12:00:39 AM] Found 1 error. Watching for file changes. + + + +Program root files: ["/user/username/projects/myproject/foo.ts","/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/foo.ts +/user/username/projects/myproject/main.ts + +Semantic diagnostics in builder refreshed for:: +/user/username/projects/myproject/foo.ts +/user/username/projects/myproject/main.ts + +Shape signatures in builder refreshed for:: +/user/username/projects/myproject/foo.ts (computed .d.ts) +/user/username/projects/myproject/main.ts (computed .d.ts) + +WatchedFiles:: +/user/username/projects/myproject/node_modules/@types: + {"fileName":"/user/username/projects/myproject/node_modules/@types","pollingInterval":500} + +FsWatches:: +/user/username/projects/myproject/tsconfig.json: + {"directoryName":"/user/username/projects/myproject/tsconfig.json"} +/user/username/projects/myproject/foo.ts: + {"directoryName":"/user/username/projects/myproject/foo.ts"} +/user/username/projects/myproject/main.ts: + {"directoryName":"/user/username/projects/myproject/main.ts"} +/a/lib/lib.d.ts: + {"directoryName":"/a/lib/lib.d.ts"} + +FsWatchesRecursive:: + +exitCode:: ExitStatus.undefined + +//// [/user/username/projects/myproject/foo.js] file written with same contents +//// [/user/username/projects/myproject/main.js] file written with same contents + +Change:: Replace file with rename event that fixes error + +Input:: +//// [/user/username/projects/myproject/foo.ts] +export declare function foo(): string; + + +Output:: +FileWatcher:: Triggered with /user/username/projects/myproject/foo.ts 2:: WatchInfo: /user/username/projects/myproject/foo.ts 250 {"watchFile":4} Source file +Scheduling update +Elapsed:: *ms FileWatcher:: Triggered with /user/username/projects/myproject/foo.ts 2:: WatchInfo: /user/username/projects/myproject/foo.ts 250 {"watchFile":4} Source file +FileWatcher:: Triggered with /user/username/projects/myproject/foo.ts 0:: WatchInfo: /user/username/projects/myproject/foo.ts 250 {"watchFile":4} Source file +Scheduling update +Elapsed:: *ms FileWatcher:: Triggered with /user/username/projects/myproject/foo.ts 0:: WatchInfo: /user/username/projects/myproject/foo.ts 250 {"watchFile":4} Source file +Synchronizing program +[12:00:43 AM] File change detected. Starting incremental compilation... + +CreatingProgramWith:: + roots: ["/user/username/projects/myproject/foo.ts","/user/username/projects/myproject/main.ts"] + options: {"watch":true,"extendedDiagnostics":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"} +[12:00:50 AM] Found 0 errors. Watching for file changes. + + + +Program root files: ["/user/username/projects/myproject/foo.ts","/user/username/projects/myproject/main.ts"] +Program options: {"watch":true,"extendedDiagnostics":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"} +Program structureReused: SafeModules +Program files:: +/a/lib/lib.d.ts +/user/username/projects/myproject/foo.ts +/user/username/projects/myproject/main.ts + +Semantic diagnostics in builder refreshed for:: +/user/username/projects/myproject/foo.ts +/user/username/projects/myproject/main.ts + +Shape signatures in builder refreshed for:: +/user/username/projects/myproject/foo.ts (computed .d.ts) +/user/username/projects/myproject/main.ts (computed .d.ts) + +WatchedFiles:: +/user/username/projects/myproject/node_modules/@types: + {"fileName":"/user/username/projects/myproject/node_modules/@types","pollingInterval":500} + +FsWatches:: +/user/username/projects/myproject/tsconfig.json: + {"directoryName":"/user/username/projects/myproject/tsconfig.json"} +/user/username/projects/myproject/foo.ts: + {"directoryName":"/user/username/projects/myproject/foo.ts"} +/user/username/projects/myproject/main.ts: + {"directoryName":"/user/username/projects/myproject/main.ts"} +/a/lib/lib.d.ts: + {"directoryName":"/a/lib/lib.d.ts"} + +FsWatchesRecursive:: + +exitCode:: ExitStatus.undefined + +//// [/user/username/projects/myproject/foo.js] file written with same contents +//// [/user/username/projects/myproject/main.js] file written with same contents