mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Add test when rename event occurs on mac with ~ appended to file name
This commit is contained in:
@@ -297,6 +297,8 @@ interface Array<T> { length: number; [n: number]: T; }`
|
||||
ignoreDelete: boolean;
|
||||
/** Skip inode check on file or folder create*/
|
||||
skipInodeCheckOnCreate: boolean;
|
||||
/** When invoking rename event on fs watch, send event with file name suffixed with tilde */
|
||||
useTildeAsSuffixInRenameEventFileName: boolean;
|
||||
}
|
||||
|
||||
export enum Tsc_WatchFile {
|
||||
@@ -490,11 +492,11 @@ interface Array<T> { length: number; [n: number]: T; }`
|
||||
if (options && options.invokeDirectoryWatcherInsteadOfFileChanged) {
|
||||
const directoryFullPath = getDirectoryPath(currentEntry.fullPath);
|
||||
this.invokeFileWatcher(directoryFullPath, FileWatcherEventKind.Changed, /*useFileNameInCallback*/ true);
|
||||
this.invokeFsWatchesCallbacks(directoryFullPath, "rename", currentEntry.fullPath);
|
||||
this.invokeRecursiveFsWatches(directoryFullPath, "rename", currentEntry.fullPath);
|
||||
this.invokeFsWatchesCallbacks(directoryFullPath, "rename", currentEntry.fullPath, options.useTildeAsSuffixInRenameEventFileName);
|
||||
this.invokeRecursiveFsWatches(directoryFullPath, "rename", currentEntry.fullPath, options.useTildeAsSuffixInRenameEventFileName);
|
||||
}
|
||||
else {
|
||||
this.invokeFileAndFsWatches(currentEntry.fullPath, FileWatcherEventKind.Changed);
|
||||
this.invokeFileAndFsWatches(currentEntry.fullPath, FileWatcherEventKind.Changed, options?.useTildeAsSuffixInRenameEventFileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -618,8 +620,8 @@ interface Array<T> { length: number; [n: number]: T; }`
|
||||
}
|
||||
const inodeWatching = this.inodeWatching;
|
||||
if (options?.skipInodeCheckOnCreate) this.inodeWatching = false;
|
||||
this.invokeFileAndFsWatches(fileOrDirectory.fullPath, FileWatcherEventKind.Created);
|
||||
this.invokeFileAndFsWatches(folder.fullPath, FileWatcherEventKind.Changed);
|
||||
this.invokeFileAndFsWatches(fileOrDirectory.fullPath, FileWatcherEventKind.Created, options?.useTildeAsSuffixInRenameEventFileName);
|
||||
this.invokeFileAndFsWatches(folder.fullPath, FileWatcherEventKind.Changed, options?.useTildeAsSuffixInRenameEventFileName);
|
||||
this.inodeWatching = inodeWatching;
|
||||
}
|
||||
|
||||
@@ -636,9 +638,9 @@ interface Array<T> { length: number; [n: number]: T; }`
|
||||
if (isFsFolder(fileOrDirectory)) {
|
||||
Debug.assert(fileOrDirectory.entries.length === 0 || isRenaming);
|
||||
}
|
||||
if (!options?.ignoreDelete) this.invokeFileAndFsWatches(fileOrDirectory.fullPath, FileWatcherEventKind.Deleted);
|
||||
if (!options?.ignoreDelete) this.invokeFileAndFsWatches(fileOrDirectory.fullPath, FileWatcherEventKind.Deleted, options?.useTildeAsSuffixInRenameEventFileName);
|
||||
this.inodes?.delete(fileOrDirectory.path);
|
||||
if (!options?.ignoreDelete) this.invokeFileAndFsWatches(baseFolder.fullPath, FileWatcherEventKind.Changed);
|
||||
if (!options?.ignoreDelete) this.invokeFileAndFsWatches(baseFolder.fullPath, FileWatcherEventKind.Changed, options?.useTildeAsSuffixInRenameEventFileName);
|
||||
}
|
||||
|
||||
deleteFile(filePath: string) {
|
||||
@@ -700,45 +702,47 @@ interface Array<T> { length: number; [n: number]: T; }`
|
||||
invokeWatcherCallbacks(this.watchedFiles.get(this.toPath(fileFullPath)), ({ cb, fileName }) => cb(useFileNameInCallback ? fileName : fileFullPath, eventKind));
|
||||
}
|
||||
|
||||
private fsWatchCallback(map: MultiMap<Path, TestFsWatcher>, fullPath: string, eventName: "rename" | "change", entryFullPath?: string) {
|
||||
private fsWatchCallback(map: MultiMap<Path, TestFsWatcher>, fullPath: string, eventName: "rename" | "change", entryFullPath: string | undefined, useTildeSuffix: boolean | undefined) {
|
||||
const path = this.toPath(fullPath);
|
||||
const currentInode = this.inodes?.get(path);
|
||||
invokeWatcherCallbacks(map.get(path), ({ cb, inode }) => {
|
||||
// TODO::
|
||||
if (this.inodeWatching && inode !== undefined && inode !== currentInode) return;
|
||||
cb(eventName, entryFullPath ? this.getRelativePathToDirectory(fullPath, entryFullPath) : "");
|
||||
let relativeFileName = (entryFullPath ? this.getRelativePathToDirectory(fullPath, entryFullPath) : "");
|
||||
if (useTildeSuffix) relativeFileName = (relativeFileName ? relativeFileName : getBaseFileName(fullPath)) + "~";
|
||||
cb(eventName, relativeFileName);
|
||||
});
|
||||
}
|
||||
|
||||
invokeFsWatchesCallbacks(fullPath: string, eventName: "rename" | "change", entryFullPath?: string) {
|
||||
this.fsWatchCallback(this.fsWatches, fullPath, eventName, entryFullPath);
|
||||
invokeFsWatchesCallbacks(fullPath: string, eventName: "rename" | "change", entryFullPath?: string, useTildeSuffix?: boolean) {
|
||||
this.fsWatchCallback(this.fsWatches, fullPath, eventName, entryFullPath, useTildeSuffix);
|
||||
}
|
||||
|
||||
invokeFsWatchesRecursiveCallbacks(fullPath: string, eventName: "rename" | "change", entryFullPath?: string) {
|
||||
this.fsWatchCallback(this.fsWatchesRecursive, fullPath, eventName, entryFullPath);
|
||||
invokeFsWatchesRecursiveCallbacks(fullPath: string, eventName: "rename" | "change", entryFullPath?: string, useTildeSuffix?: boolean) {
|
||||
this.fsWatchCallback(this.fsWatchesRecursive, fullPath, eventName, entryFullPath, useTildeSuffix);
|
||||
}
|
||||
|
||||
private getRelativePathToDirectory(directoryFullPath: string, fileFullPath: string) {
|
||||
return getRelativePathToDirectoryOrUrl(directoryFullPath, fileFullPath, this.currentDirectory, this.getCanonicalFileName, /*isAbsolutePathAnUrl*/ false);
|
||||
}
|
||||
|
||||
private invokeRecursiveFsWatches(fullPath: string, eventName: "rename" | "change", entryFullPath?: string) {
|
||||
this.invokeFsWatchesRecursiveCallbacks(fullPath, eventName, entryFullPath);
|
||||
private invokeRecursiveFsWatches(fullPath: string, eventName: "rename" | "change", entryFullPath?: string, useTildeSuffix?: boolean) {
|
||||
this.invokeFsWatchesRecursiveCallbacks(fullPath, eventName, entryFullPath, useTildeSuffix);
|
||||
const basePath = getDirectoryPath(fullPath);
|
||||
if (this.getCanonicalFileName(fullPath) !== this.getCanonicalFileName(basePath)) {
|
||||
this.invokeRecursiveFsWatches(basePath, eventName, entryFullPath || fullPath);
|
||||
this.invokeRecursiveFsWatches(basePath, eventName, entryFullPath || fullPath, useTildeSuffix);
|
||||
}
|
||||
}
|
||||
|
||||
private invokeFsWatches(fullPath: string, eventName: "rename" | "change") {
|
||||
this.invokeFsWatchesCallbacks(fullPath, eventName);
|
||||
this.invokeFsWatchesCallbacks(getDirectoryPath(fullPath), eventName, fullPath);
|
||||
this.invokeRecursiveFsWatches(fullPath, eventName);
|
||||
private invokeFsWatches(fullPath: string, eventName: "rename" | "change", useTildeSuffix: boolean | undefined) {
|
||||
this.invokeFsWatchesCallbacks(fullPath, eventName, fullPath, useTildeSuffix);
|
||||
this.invokeFsWatchesCallbacks(getDirectoryPath(fullPath), eventName, fullPath, useTildeSuffix);
|
||||
this.invokeRecursiveFsWatches(fullPath, eventName, /*entryFullPath*/ undefined, useTildeSuffix);
|
||||
}
|
||||
|
||||
private invokeFileAndFsWatches(fileOrFolderFullPath: string, eventKind: FileWatcherEventKind) {
|
||||
private invokeFileAndFsWatches(fileOrFolderFullPath: string, eventKind: FileWatcherEventKind, useTildeSuffix?: boolean) {
|
||||
this.invokeFileWatcher(fileOrFolderFullPath, eventKind);
|
||||
this.invokeFsWatches(fileOrFolderFullPath, eventKind === FileWatcherEventKind.Changed ? "change" : "rename");
|
||||
this.invokeFsWatches(fileOrFolderFullPath, eventKind === FileWatcherEventKind.Changed ? "change" : "rename", useTildeSuffix);
|
||||
}
|
||||
|
||||
private toFsEntry(path: string): FSEntryBase {
|
||||
|
||||
@@ -648,6 +648,36 @@ namespace ts.tscWatch {
|
||||
]
|
||||
});
|
||||
|
||||
verifyTscWatch({
|
||||
scenario,
|
||||
subScenario: `fsWatch/when using file watching thats on inode when rename event ends with tilde`,
|
||||
commandLineArgs: ["-w", "--extendedDiagnostics"],
|
||||
sys: () => createWatchedSystem(
|
||||
{
|
||||
[libFile.path]: libFile.content,
|
||||
[`${projectRoot}/main.ts`]: `import { foo } from "./foo"; foo();`,
|
||||
[`${projectRoot}/foo.d.ts`]: `export function foo(): string;`,
|
||||
[`${projectRoot}/tsconfig.json`]: JSON.stringify({ watchOptions: { watchFile: "useFsEvents" }, files: ["foo.d.ts", "main.ts"] }),
|
||||
},
|
||||
{
|
||||
currentDirectory: projectRoot,
|
||||
inodeWatching: true
|
||||
}
|
||||
),
|
||||
changes: [
|
||||
{
|
||||
caption: "Replace file with rename event that introduces error",
|
||||
change: sys => sys.modifyFile(`${projectRoot}/foo.d.ts`, `export function foo2(): string;`, { invokeFileDeleteCreateAsPartInsteadOfChange: true, useTildeAsSuffixInRenameEventFileName: true }),
|
||||
timeouts: sys => sys.checkTimeoutQueueLengthAndRun(1),
|
||||
},
|
||||
{
|
||||
caption: "Replace file with rename event that fixes error",
|
||||
change: sys => sys.modifyFile(`${projectRoot}/foo.d.ts`, `export function foo(): string;`, { invokeFileDeleteCreateAsPartInsteadOfChange: true, useTildeAsSuffixInRenameEventFileName: true }),
|
||||
timeouts: sys => sys.checkTimeoutQueueLengthAndRun(0),
|
||||
},
|
||||
]
|
||||
});
|
||||
|
||||
verifyTscWatch({
|
||||
scenario,
|
||||
subScenario: `fsWatch/when using file watching thats on inode when rename occurs when file is still on the disk`,
|
||||
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
Input::
|
||||
//// [/a/lib/lib.d.ts] Inode:: 3
|
||||
/// <reference no-default-lib="true"/>
|
||||
interface Boolean {}
|
||||
interface Function {}
|
||||
interface CallableFunction {}
|
||||
interface NewableFunction {}
|
||||
interface IArguments {}
|
||||
interface Number { toExponential: any; }
|
||||
interface Object {}
|
||||
interface RegExp {}
|
||||
interface String { charAt: any; }
|
||||
interface Array<T> { length: number; [n: number]: T; }
|
||||
|
||||
//// [/user/username/projects/myproject/main.ts] Inode:: 8
|
||||
import { foo } from "./foo"; foo();
|
||||
|
||||
//// [/user/username/projects/myproject/foo.d.ts] Inode:: 9
|
||||
export function foo(): string;
|
||||
|
||||
//// [/user/username/projects/myproject/tsconfig.json] Inode:: 10
|
||||
{"watchOptions":{"watchFile":"useFsEvents"},"files":["foo.d.ts","main.ts"]}
|
||||
|
||||
|
||||
/a/lib/tsc.js -w --extendedDiagnostics
|
||||
Output::
|
||||
[[90m12:00:23 AM[0m] 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.d.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.d.ts 250 {"watchFile":4} Source file
|
||||
FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/main.ts 250 {"watchFile":4} Source file
|
||||
DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject 0 {"watchFile":4} Failed Lookup Locations
|
||||
Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject 0 {"watchFile":4} Failed Lookup Locations
|
||||
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
|
||||
DirectoryWatcher:: Triggered with /user/username/projects/myproject/main.js :: WatchInfo: /user/username/projects/myproject 0 {"watchFile":4} Failed Lookup Locations
|
||||
Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject/main.js :: WatchInfo: /user/username/projects/myproject 0 {"watchFile":4} Failed Lookup Locations
|
||||
[[90m12:00:26 AM[0m] Found 0 errors. Watching for file changes.
|
||||
|
||||
|
||||
|
||||
Program root files: ["/user/username/projects/myproject/foo.d.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.d.ts
|
||||
/user/username/projects/myproject/main.ts
|
||||
|
||||
Semantic diagnostics in builder refreshed for::
|
||||
/a/lib/lib.d.ts
|
||||
/user/username/projects/myproject/foo.d.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.d.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","inode":10}
|
||||
/user/username/projects/myproject/foo.d.ts:
|
||||
{"directoryName":"/user/username/projects/myproject/foo.d.ts","inode":9}
|
||||
/user/username/projects/myproject/main.ts:
|
||||
{"directoryName":"/user/username/projects/myproject/main.ts","inode":8}
|
||||
/user/username/projects/myproject:
|
||||
{"directoryName":"/user/username/projects/myproject","inode":7}
|
||||
/a/lib/lib.d.ts:
|
||||
{"directoryName":"/a/lib/lib.d.ts","inode":3}
|
||||
|
||||
FsWatchesRecursive::
|
||||
|
||||
exitCode:: ExitStatus.undefined
|
||||
|
||||
//// [/user/username/projects/myproject/main.js] Inode:: 11
|
||||
"use strict";
|
||||
exports.__esModule = true;
|
||||
var foo_1 = require("./foo");
|
||||
(0, foo_1.foo)();
|
||||
|
||||
|
||||
|
||||
Change:: Replace file with rename event that introduces error
|
||||
|
||||
Input::
|
||||
//// [/user/username/projects/myproject/foo.d.ts] Inode:: 12
|
||||
export function foo2(): string;
|
||||
|
||||
|
||||
Output::
|
||||
FileWatcher:: Triggered with /user/username/projects/myproject/foo.d.ts 2:: WatchInfo: /user/username/projects/myproject/foo.d.ts 250 {"watchFile":4} Source file
|
||||
Scheduling update
|
||||
Elapsed:: *ms FileWatcher:: Triggered with /user/username/projects/myproject/foo.d.ts 2:: WatchInfo: /user/username/projects/myproject/foo.d.ts 250 {"watchFile":4} Source file
|
||||
DirectoryWatcher:: Triggered with /user/username/projects/myproject/foo.d.ts~ :: WatchInfo: /user/username/projects/myproject 0 {"watchFile":4} Failed Lookup Locations
|
||||
Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject/foo.d.ts~ :: WatchInfo: /user/username/projects/myproject 0 {"watchFile":4} Failed Lookup Locations
|
||||
DirectoryWatcher:: Triggered with /user/username/projects/myproject/foo.d.ts~ :: WatchInfo: /user/username/projects/myproject 0 {"watchFile":4} Failed Lookup Locations
|
||||
Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject/foo.d.ts~ :: WatchInfo: /user/username/projects/myproject 0 {"watchFile":4} Failed Lookup Locations
|
||||
Synchronizing program
|
||||
[[90m12:00:30 AM[0m] File change detected. Starting incremental compilation...
|
||||
|
||||
CreatingProgramWith::
|
||||
roots: ["/user/username/projects/myproject/foo.d.ts","/user/username/projects/myproject/main.ts"]
|
||||
options: {"watch":true,"extendedDiagnostics":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"}
|
||||
[96mmain.ts[0m:[93m1[0m:[93m10[0m - [91merror[0m[90m TS2724: [0m'"./foo"' has no exported member named 'foo'. Did you mean 'foo2'?
|
||||
|
||||
[7m1[0m import { foo } from "./foo"; foo();
|
||||
[7m [0m [91m ~~~[0m
|
||||
|
||||
[96mfoo.d.ts[0m:[93m1[0m:[93m17[0m
|
||||
[7m1[0m export function foo2(): string;
|
||||
[7m [0m [96m ~~~~[0m
|
||||
'foo2' is declared here.
|
||||
|
||||
[[90m12:00:34 AM[0m] Found 1 error. Watching for file changes.
|
||||
|
||||
|
||||
|
||||
Program root files: ["/user/username/projects/myproject/foo.d.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.d.ts
|
||||
/user/username/projects/myproject/main.ts
|
||||
|
||||
Semantic diagnostics in builder refreshed for::
|
||||
/user/username/projects/myproject/foo.d.ts
|
||||
/user/username/projects/myproject/main.ts
|
||||
|
||||
Shape signatures in builder refreshed for::
|
||||
/user/username/projects/myproject/foo.d.ts (used version)
|
||||
/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","inode":10}
|
||||
/user/username/projects/myproject/foo.d.ts:
|
||||
{"directoryName":"/user/username/projects/myproject/foo.d.ts","inode":9}
|
||||
/user/username/projects/myproject/main.ts:
|
||||
{"directoryName":"/user/username/projects/myproject/main.ts","inode":8}
|
||||
/user/username/projects/myproject:
|
||||
{"directoryName":"/user/username/projects/myproject","inode":7}
|
||||
/a/lib/lib.d.ts:
|
||||
{"directoryName":"/a/lib/lib.d.ts","inode":3}
|
||||
|
||||
FsWatchesRecursive::
|
||||
|
||||
exitCode:: ExitStatus.undefined
|
||||
|
||||
//// [/user/username/projects/myproject/main.js] file written with same contents Inode:: 11
|
||||
|
||||
Change:: Replace file with rename event that fixes error
|
||||
|
||||
Input::
|
||||
//// [/user/username/projects/myproject/foo.d.ts] Inode:: 13
|
||||
export function foo(): string;
|
||||
|
||||
|
||||
Output::
|
||||
DirectoryWatcher:: Triggered with /user/username/projects/myproject/foo.d.ts~ :: WatchInfo: /user/username/projects/myproject 0 {"watchFile":4} Failed Lookup Locations
|
||||
Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject/foo.d.ts~ :: WatchInfo: /user/username/projects/myproject 0 {"watchFile":4} Failed Lookup Locations
|
||||
DirectoryWatcher:: Triggered with /user/username/projects/myproject/foo.d.ts~ :: WatchInfo: /user/username/projects/myproject 0 {"watchFile":4} Failed Lookup Locations
|
||||
Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject/foo.d.ts~ :: WatchInfo: /user/username/projects/myproject 0 {"watchFile":4} Failed Lookup Locations
|
||||
|
||||
|
||||
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","inode":10}
|
||||
/user/username/projects/myproject/foo.d.ts:
|
||||
{"directoryName":"/user/username/projects/myproject/foo.d.ts","inode":9}
|
||||
/user/username/projects/myproject/main.ts:
|
||||
{"directoryName":"/user/username/projects/myproject/main.ts","inode":8}
|
||||
/user/username/projects/myproject:
|
||||
{"directoryName":"/user/username/projects/myproject","inode":7}
|
||||
/a/lib/lib.d.ts:
|
||||
{"directoryName":"/a/lib/lib.d.ts","inode":3}
|
||||
|
||||
FsWatchesRecursive::
|
||||
|
||||
exitCode:: ExitStatus.undefined
|
||||
|
||||
Reference in New Issue
Block a user