mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-06-11 10:46:28 -05:00
Watch events enhancements (#57950)
This commit is contained in:
@@ -4,6 +4,7 @@ import {
|
||||
} from "../../../../harness/tsserverLogger";
|
||||
import {
|
||||
createWatchUtils,
|
||||
Watches,
|
||||
WatchUtils,
|
||||
} from "../../../../harness/watchUtils";
|
||||
import * as ts from "../../../_namespaces/ts";
|
||||
@@ -60,11 +61,11 @@ describe("unittests:: tsserver:: events:: watchEvents", () => {
|
||||
}
|
||||
|
||||
function watchDirectory(data: ts.server.protocol.CreateDirectoryWatcherEventBody) {
|
||||
logger.log(`Custom watchDirectory: ${data.id}: ${data.path} ${data.recursive}`);
|
||||
logger.log(`Custom watchDirectory: ${data.id}: ${data.path} ${data.recursive} ${data.ignoreUpdate}`);
|
||||
ts.Debug.assert(!idToClose.has(data.id));
|
||||
const result = host.factoryData.watchUtils.fsWatch(data.path, data.recursive, data);
|
||||
idToClose.set(data.id, () => {
|
||||
logger.log(`Custom watchDirectory:: Close:: ${data.id}: ${data.path} ${data.recursive}`);
|
||||
logger.log(`Custom watchDirectory:: Close:: ${data.id}: ${data.path} ${data.recursive} ${data.ignoreUpdate}`);
|
||||
result.close();
|
||||
});
|
||||
}
|
||||
@@ -89,37 +90,132 @@ describe("unittests:: tsserver:: events:: watchEvents", () => {
|
||||
}
|
||||
}
|
||||
|
||||
function updateFileOnHost(session: TestSession, file: string, log: string) {
|
||||
function updateFileOnHost(session: TestSession, file: string, log: string, content?: string) {
|
||||
// Change b.ts
|
||||
session.logger.log(log);
|
||||
session.host.writeFile(file, session.host.readFile("/user/username/projects/myproject/a.ts")!);
|
||||
session.logger.log(`${log}: ${file}`);
|
||||
if (content) session.host.appendFile(file, content);
|
||||
else session.host.writeFile(file, session.host.readFile("/user/username/projects/myproject/a.ts")!);
|
||||
session.host.runQueuedTimeoutCallbacks();
|
||||
}
|
||||
|
||||
function collectWatchChanges<T extends ts.server.protocol.CreateFileWatcherEventBody | ts.server.protocol.CreateDirectoryWatcherEventBody>(
|
||||
session: TestSession,
|
||||
watches: Watches<T>,
|
||||
path: string,
|
||||
eventPath: string,
|
||||
eventType: "created" | "deleted" | "updated",
|
||||
ignoreUpdate?: (data: T) => boolean,
|
||||
) {
|
||||
session.logger.log(`Custom watch:: ${path} ${eventPath} ${eventType}`);
|
||||
let result: ts.server.protocol.WatchChangeRequestArgs[] | undefined;
|
||||
watches.forEach(
|
||||
path,
|
||||
data => {
|
||||
if (ignoreUpdate?.(data)) return;
|
||||
switch (eventType) {
|
||||
case "created":
|
||||
(result ??= []).push({ id: data.id, created: [eventPath] });
|
||||
break;
|
||||
case "deleted":
|
||||
(result ??= []).push({ id: data.id, deleted: [eventPath] });
|
||||
break;
|
||||
case "updated":
|
||||
(result ??= []).push({ id: data.id, updated: [eventPath] });
|
||||
break;
|
||||
default:
|
||||
ts.Debug.assertNever(eventType);
|
||||
}
|
||||
},
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
function collectDirectoryWatcherChanges(
|
||||
session: TestSession,
|
||||
dir: string,
|
||||
eventPath: string,
|
||||
eventType: "created" | "deleted" | "updated",
|
||||
) {
|
||||
return collectWatchChanges(
|
||||
session,
|
||||
(session.logger.host as TestServerHostWithCustomWatch).factoryData.watchUtils.fsWatchesRecursive,
|
||||
dir,
|
||||
eventPath,
|
||||
eventType,
|
||||
data => !!data.ignoreUpdate && eventType === "updated",
|
||||
);
|
||||
}
|
||||
|
||||
function collectFileWatcherChanges(
|
||||
session: TestSession,
|
||||
file: string,
|
||||
eventType: "created" | "deleted" | "updated",
|
||||
) {
|
||||
return collectWatchChanges(
|
||||
session,
|
||||
(session.logger.host as TestServerHostWithCustomWatch).factoryData.watchUtils.pollingWatches,
|
||||
file,
|
||||
file,
|
||||
eventType,
|
||||
);
|
||||
}
|
||||
|
||||
function invokeWatchChange(
|
||||
session: TestSession,
|
||||
...args: (ts.server.protocol.WatchChangeRequestArgs[] | undefined)[]
|
||||
) {
|
||||
let result: Map<number, ts.server.protocol.WatchChangeRequestArgs> | undefined;
|
||||
args.forEach(arg =>
|
||||
arg?.forEach(value => {
|
||||
result ??= new Map();
|
||||
const valueInResult = result.get(value.id);
|
||||
if (!valueInResult) result.set(value.id, value);
|
||||
else {
|
||||
valueInResult.created = ts.combine(valueInResult.created, value.created);
|
||||
valueInResult.deleted = ts.combine(valueInResult.deleted, value.deleted);
|
||||
valueInResult.updated = ts.combine(valueInResult.updated, value.updated);
|
||||
}
|
||||
})
|
||||
);
|
||||
if (result) {
|
||||
session.executeCommandSeq<ts.server.protocol.WatchChangeRequest>({
|
||||
command: ts.server.protocol.CommandTypes.WatchChange,
|
||||
arguments: ts.singleOrMany(ts.arrayFrom(result.values())),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function addFile(session: TestSession, path: string) {
|
||||
updateFileOnHost(session, path, "Add file");
|
||||
session.logger.log("Custom watch");
|
||||
(session.logger.host as TestServerHostWithCustomWatch).factoryData.watchUtils.fsWatchesRecursive.forEach(
|
||||
"/user/username/projects/myproject",
|
||||
data =>
|
||||
session.executeCommandSeq<ts.server.protocol.WatchChangeRequest>({
|
||||
command: ts.server.protocol.CommandTypes.WatchChange,
|
||||
arguments: { id: data.id, path, eventType: "create" },
|
||||
}),
|
||||
invokeWatchChange(
|
||||
session,
|
||||
collectDirectoryWatcherChanges(session, "/user/username/projects/myproject", path, "created"),
|
||||
);
|
||||
session.host.runQueuedTimeoutCallbacks();
|
||||
}
|
||||
|
||||
function changeFile(session: TestSession, path: string) {
|
||||
updateFileOnHost(session, path, "Change File");
|
||||
session.logger.log("Custom watch");
|
||||
(session.logger.host as TestServerHostWithCustomWatch).factoryData.watchUtils.pollingWatches.forEach(
|
||||
path,
|
||||
data =>
|
||||
session.executeCommandSeq<ts.server.protocol.WatchChangeRequest>({
|
||||
command: ts.server.protocol.CommandTypes.WatchChange,
|
||||
arguments: { id: data.id, path, eventType: "update" },
|
||||
}),
|
||||
function changeFile(session: TestSession, path: string, content?: string) {
|
||||
updateFileOnHost(session, path, "Change File", content);
|
||||
invokeWatchChange(
|
||||
session,
|
||||
collectFileWatcherChanges(session, path, "updated"),
|
||||
collectDirectoryWatcherChanges(session, ts.getDirectoryPath(path), path, "updated"),
|
||||
);
|
||||
session.host.runQueuedTimeoutCallbacks();
|
||||
}
|
||||
|
||||
function npmInstall(session: TestSession) {
|
||||
session.logger.log("update with npm install");
|
||||
session.host.appendFile("/user/username/projects/myproject/node_modules/something/index.d.ts", `export const y = 20;`);
|
||||
session.host.runQueuedTimeoutCallbacks();
|
||||
invokeWatchChange(
|
||||
session,
|
||||
collectDirectoryWatcherChanges(
|
||||
session,
|
||||
"/user/username/projects/myproject/node_modules",
|
||||
"/user/username/projects/myproject/node_modules/something/index.d.ts",
|
||||
"updated",
|
||||
),
|
||||
);
|
||||
session.host.runQueuedTimeoutCallbacks();
|
||||
}
|
||||
@@ -129,6 +225,8 @@ describe("unittests:: tsserver:: events:: watchEvents", () => {
|
||||
"/user/username/projects/myproject/tsconfig.json": "{}",
|
||||
"/user/username/projects/myproject/a.ts": `export class a { prop = "hello"; foo() { return this.prop; } }`,
|
||||
"/user/username/projects/myproject/b.ts": `export class b { prop = "hello"; foo() { return this.prop; } }`,
|
||||
"/user/username/projects/myproject/m.ts": `import { x } from "something"`,
|
||||
"/user/username/projects/myproject/node_modules/something/index.d.ts": `export const x = 10;`,
|
||||
[libFile.path]: libFile.content,
|
||||
});
|
||||
const logger = createLoggerWithInMemoryLogs(inputHost);
|
||||
@@ -153,6 +251,26 @@ describe("unittests:: tsserver:: events:: watchEvents", () => {
|
||||
// Re watch
|
||||
closeFilesForSession(["/user/username/projects/myproject/b.ts"], session);
|
||||
|
||||
// Update c.ts
|
||||
changeFile(session, "/user/username/projects/myproject/c.ts", `export const y = 20;`);
|
||||
|
||||
// Update with npm install
|
||||
npmInstall(session);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
|
||||
// Add and change multiple files - combine and send multiple requests together
|
||||
updateFileOnHost(session, "/user/username/projects/myproject/d.ts", "Add file");
|
||||
updateFileOnHost(session, "/user/username/projects/myproject/c.ts", "Change File", `export const z = 30;`);
|
||||
updateFileOnHost(session, "/user/username/projects/myproject/e.ts", "Add File");
|
||||
invokeWatchChange(
|
||||
session,
|
||||
collectDirectoryWatcherChanges(session, "/user/username/projects/myproject", "/user/username/projects/myproject/d.ts", "created"),
|
||||
collectFileWatcherChanges(session, "/user/username/projects/myproject/c.ts", "updated"),
|
||||
collectDirectoryWatcherChanges(session, "/user/username/projects/myproject", "/user/username/projects/myproject/c.ts", "updated"),
|
||||
collectDirectoryWatcherChanges(session, "/user/username/projects/myproject", "/user/username/projects/myproject/e.ts", "created"),
|
||||
);
|
||||
session.host.runQueuedTimeoutCallbacks();
|
||||
|
||||
baselineTsserverLogs("events/watchEvents", `canUseWatchEvents`, session);
|
||||
function handleWatchEvents(event: ts.server.ProjectServiceEvent) {
|
||||
switch (event.eventName) {
|
||||
@@ -192,9 +310,15 @@ describe("unittests:: tsserver:: events:: watchEvents", () => {
|
||||
logger.msg = (s, type) => logger.info(`${type}:: ${s}`);
|
||||
session.executeCommandSeq<ts.server.protocol.WatchChangeRequest>({
|
||||
command: ts.server.protocol.CommandTypes.WatchChange,
|
||||
arguments: { id: 1, path: "/user/username/projects/myproject/b.ts", eventType: "update" },
|
||||
arguments: { id: 1, updated: ["/user/username/projects/myproject/b.ts"] },
|
||||
});
|
||||
|
||||
// Update c.ts
|
||||
changeFile(session, "/user/username/projects/myproject/c.ts", `export const y = 20;`);
|
||||
|
||||
// Update with npm install
|
||||
npmInstall(session);
|
||||
|
||||
baselineTsserverLogs("events/watchEvents", `canUseWatchEvents without canUseEvents`, session);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user