Skip to content

Commit 30f611b

Browse files
authored
Add survey event (#26455)
* Start adding survey event * Add surveyReady event * Remove old notes * Move event, simplify type, add test 1. Move the survey event to sendProjectTelemetry so that it happens on open instead of on editing tsconfig. 2. Remove URL from the survey type; VS code should control this information. 3. Add test based on large files event test. I'm not sure it's in the right place, though. * Fix tests and update API baseline * Split survey sending from telemetry Based on tests requested during review. * Add additional assertion
1 parent cff04e6 commit 30f611b

File tree

6 files changed

+185
-3
lines changed

6 files changed

+185
-3
lines changed

src/server/editorServices.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ namespace ts.server {
55

66
// tslint:disable variable-name
77
export const ProjectsUpdatedInBackgroundEvent = "projectsUpdatedInBackground";
8+
export const SurveyReady = "surveyReady";
89
export const LargeFileReferencedEvent = "largeFileReferenced";
910
export const ConfigFileDiagEvent = "configFileDiag";
1011
export const ProjectLanguageServiceStateEvent = "projectLanguageServiceState";
@@ -17,6 +18,11 @@ namespace ts.server {
1718
data: { openFiles: string[]; };
1819
}
1920

21+
export interface SurveyReady {
22+
eventName: typeof SurveyReady;
23+
data: { surveyId: string; };
24+
}
25+
2026
export interface LargeFileReferencedEvent {
2127
eventName: typeof LargeFileReferencedEvent;
2228
data: { file: string; fileSize: number; maxFileSize: number; };
@@ -98,7 +104,7 @@ namespace ts.server {
98104
readonly checkJs: boolean;
99105
}
100106

101-
export type ProjectServiceEvent = LargeFileReferencedEvent | ProjectsUpdatedInBackgroundEvent | ConfigFileDiagEvent | ProjectLanguageServiceStateEvent | ProjectInfoTelemetryEvent | OpenFileInfoTelemetryEvent;
107+
export type ProjectServiceEvent = LargeFileReferencedEvent | SurveyReady | ProjectsUpdatedInBackgroundEvent | ConfigFileDiagEvent | ProjectLanguageServiceStateEvent | ProjectInfoTelemetryEvent | OpenFileInfoTelemetryEvent;
102108

103109
export type ProjectServiceEventHandler = (event: ProjectServiceEvent) => void;
104110

@@ -462,6 +468,9 @@ namespace ts.server {
462468
/** Tracks projects that we have already sent telemetry for. */
463469
private readonly seenProjects = createMap<true>();
464470

471+
/** Tracks projects that we have already sent survey events for. */
472+
private readonly seenSurveyProjects = createMap<true>();
473+
465474
/*@internal*/
466475
readonly watchFactory: WatchFactory<WatchType, Project>;
467476

@@ -663,6 +672,14 @@ namespace ts.server {
663672
this.eventHandler(event);
664673
}
665674

675+
/* @internal */
676+
sendSurveyReadyEvent(surveyId: string) {
677+
if (!this.eventHandler) {
678+
return;
679+
}
680+
this.eventHandler({ eventName: SurveyReady, data: { surveyId } });
681+
}
682+
666683
/* @internal */
667684
sendLargeFileReferencedEvent(file: string, fileSize: number) {
668685
if (!this.eventHandler) {
@@ -1477,6 +1494,20 @@ namespace ts.server {
14771494
return project;
14781495
}
14791496

1497+
/*@internal*/
1498+
sendSurveyReady(project: ExternalProject | ConfiguredProject): void {
1499+
if (this.seenSurveyProjects.has(project.projectName)) {
1500+
return;
1501+
}
1502+
1503+
if (project.getCompilerOptions().checkJs !== undefined) {
1504+
const name = "checkJs";
1505+
this.logger.info(`Survey ${name} is ready`);
1506+
this.sendSurveyReadyEvent(name);
1507+
this.seenSurveyProjects.set(project.projectName, true);
1508+
}
1509+
}
1510+
14801511
/*@internal*/
14811512
sendProjectTelemetry(project: ExternalProject | ConfiguredProject): void {
14821513
if (this.seenProjects.has(project.projectName)) {

src/server/project.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1366,6 +1366,7 @@ namespace ts.server {
13661366
result = super.updateGraph();
13671367
}
13681368
this.projectService.sendProjectTelemetry(this);
1369+
this.projectService.sendSurveyReady(this);
13691370
return result;
13701371
}
13711372

@@ -1570,6 +1571,7 @@ namespace ts.server {
15701571
updateGraph() {
15711572
const result = super.updateGraph();
15721573
this.projectService.sendProjectTelemetry(this);
1574+
this.projectService.sendSurveyReady(this);
15731575
return result;
15741576
}
15751577

src/server/protocol.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2436,6 +2436,18 @@ namespace ts.server.protocol {
24362436
openFiles: string[];
24372437
}
24382438

2439+
export type SurveyReadyEventName = "surveyReady";
2440+
2441+
export interface SurveyReadyEvent extends Event {
2442+
event: SurveyReadyEventName;
2443+
body: SurveyReadyEventBody;
2444+
}
2445+
2446+
export interface SurveyReadyEventBody {
2447+
/** Name of the survey. This is an internal machine- and programmer-friendly name */
2448+
surveyId: string;
2449+
}
2450+
24392451
export type LargeFileReferencedEventName = "largeFileReferenced";
24402452
export interface LargeFileReferencedEvent extends Event {
24412453
event: LargeFileReferencedEventName;

src/server/session.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,10 @@ namespace ts.server {
572572
diagnostics: bakedDiags
573573
}, "configFileDiag");
574574
break;
575+
case SurveyReady:
576+
const { surveyId } = event.data;
577+
this.event<protocol.SurveyReadyEventBody>({ surveyId }, "surveyReady");
578+
break;
575579
case ProjectLanguageServiceStateEvent: {
576580
const eventName: protocol.ProjectLanguageServiceStateEventName = "projectLanguageServiceState";
577581
this.event<protocol.ProjectLanguageServiceStateEventBody>({

src/testRunner/unittests/tsserverProjectSystem.ts

Lines changed: 116 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2863,7 +2863,7 @@ namespace ts.projectSystem {
28632863
const session = createSession(host, {
28642864
canUseEvents: true,
28652865
eventHandler: e => {
2866-
if (e.eventName === server.ConfigFileDiagEvent || e.eventName === server.ProjectsUpdatedInBackgroundEvent || e.eventName === server.ProjectInfoTelemetryEvent || e.eventName === server.OpenFileInfoTelemetryEvent || e.eventName === server.LargeFileReferencedEvent) {
2866+
if (e.eventName === server.ConfigFileDiagEvent || e.eventName === server.ProjectsUpdatedInBackgroundEvent || e.eventName === server.ProjectInfoTelemetryEvent || e.eventName === server.OpenFileInfoTelemetryEvent || e.eventName === server.LargeFileReferencedEvent || e.eventName === server.SurveyReady) {
28672867
return;
28682868
}
28692869
assert.equal(e.eventName, server.ProjectLanguageServiceStateEvent);
@@ -3539,6 +3539,121 @@ namespace ts.projectSystem {
35393539
}
35403540
});
35413541

3542+
function createSessionWithEventHandler(host: TestServerHost) {
3543+
const surveyEvents: server.SurveyReady[] = [];
3544+
const session = createSession(host, {
3545+
eventHandler: e => {
3546+
if (e.eventName === server.SurveyReady) {
3547+
surveyEvents.push(e);
3548+
}
3549+
}
3550+
});
3551+
3552+
return { session, verifySurveyReadyEvent };
3553+
3554+
function verifySurveyReadyEvent(numberOfEvents: number) {
3555+
assert.equal(surveyEvents.length, numberOfEvents);
3556+
const expectedEvents = numberOfEvents === 0 ? [] : [{
3557+
eventName: server.SurveyReady,
3558+
data: { surveyId: "checkJs" }
3559+
}];
3560+
assert.deepEqual(surveyEvents, expectedEvents);
3561+
}
3562+
}
3563+
3564+
it("doesn't log an event when checkJs isn't set", () => {
3565+
const projectRoot = "/user/username/projects/project";
3566+
const file: File = {
3567+
path: `${projectRoot}/src/file.ts`,
3568+
content: "export var y = 10;"
3569+
};
3570+
const tsconfig: File = {
3571+
path: `${projectRoot}/tsconfig.json`,
3572+
content: JSON.stringify({ compilerOptions: { } }),
3573+
};
3574+
const host = createServerHost([file, tsconfig]);
3575+
const { session, verifySurveyReadyEvent } = createSessionWithEventHandler(host);
3576+
const service = session.getProjectService();
3577+
openFilesForSession([file], session);
3578+
checkNumberOfProjects(service, { configuredProjects: 1 });
3579+
const project = service.configuredProjects.get(tsconfig.path)!;
3580+
checkProjectActualFiles(project, [file.path, tsconfig.path]);
3581+
3582+
verifySurveyReadyEvent(0);
3583+
});
3584+
3585+
it("logs an event when checkJs is set", () => {
3586+
const projectRoot = "/user/username/projects/project";
3587+
const file: File = {
3588+
path: `${projectRoot}/src/file.ts`,
3589+
content: "export var y = 10;"
3590+
};
3591+
const tsconfig: File = {
3592+
path: `${projectRoot}/tsconfig.json`,
3593+
content: JSON.stringify({ compilerOptions: { checkJs: true } }),
3594+
};
3595+
const host = createServerHost([file, tsconfig]);
3596+
const { session, verifySurveyReadyEvent } = createSessionWithEventHandler(host);
3597+
openFilesForSession([file], session);
3598+
3599+
verifySurveyReadyEvent(1);
3600+
});
3601+
3602+
it("logs an event when checkJs is set, only the first time", () => {
3603+
const projectRoot = "/user/username/projects/project";
3604+
const file: File = {
3605+
path: `${projectRoot}/src/file.ts`,
3606+
content: "export var y = 10;"
3607+
};
3608+
const rando: File = {
3609+
path: `/rando/calrissian.ts`,
3610+
content: "export function f() { }"
3611+
};
3612+
const tsconfig: File = {
3613+
path: `${projectRoot}/tsconfig.json`,
3614+
content: JSON.stringify({ compilerOptions: { checkJs: true } }),
3615+
};
3616+
const host = createServerHost([file, tsconfig]);
3617+
const { session, verifySurveyReadyEvent } = createSessionWithEventHandler(host);
3618+
openFilesForSession([file], session);
3619+
3620+
verifySurveyReadyEvent(1);
3621+
3622+
closeFilesForSession([file], session);
3623+
openFilesForSession([rando], session);
3624+
openFilesForSession([file], session);
3625+
3626+
verifySurveyReadyEvent(1);
3627+
});
3628+
3629+
it("logs an event when checkJs is set after closing and reopening", () => {
3630+
const projectRoot = "/user/username/projects/project";
3631+
const file: File = {
3632+
path: `${projectRoot}/src/file.ts`,
3633+
content: "export var y = 10;"
3634+
};
3635+
const rando: File = {
3636+
path: `/rando/calrissian.ts`,
3637+
content: "export function f() { }"
3638+
};
3639+
const tsconfig: File = {
3640+
path: `${projectRoot}/tsconfig.json`,
3641+
content: JSON.stringify({ }),
3642+
};
3643+
const host = createServerHost([file, tsconfig]);
3644+
const { session, verifySurveyReadyEvent } = createSessionWithEventHandler(host);
3645+
openFilesForSession([file], session);
3646+
3647+
verifySurveyReadyEvent(0);
3648+
3649+
closeFilesForSession([file], session);
3650+
openFilesForSession([rando], session);
3651+
host.writeFile(tsconfig.path, JSON.stringify({ compilerOptions: { checkJs: true } }));
3652+
openFilesForSession([file], session);
3653+
3654+
verifySurveyReadyEvent(1);
3655+
});
3656+
35423657
describe("CompileOnSaveAffectedFileListRequest with and without projectFileName in request", () => {
35433658
const projectRoot = "/user/username/projects/myproject";
35443659
const core: File = {

tests/baselines/reference/api/tsserverlibrary.d.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7462,6 +7462,15 @@ declare namespace ts.server.protocol {
74627462
*/
74637463
openFiles: string[];
74647464
}
7465+
type SurveyReadyEventName = "surveyReady";
7466+
interface SurveyReadyEvent extends Event {
7467+
event: SurveyReadyEventName;
7468+
body: SurveyReadyEventBody;
7469+
}
7470+
interface SurveyReadyEventBody {
7471+
/** Name of the survey. This is an internal machine- and programmer-friendly name */
7472+
surveyId: string;
7473+
}
74657474
type LargeFileReferencedEventName = "largeFileReferenced";
74667475
interface LargeFileReferencedEvent extends Event {
74677476
event: LargeFileReferencedEventName;
@@ -8204,6 +8213,7 @@ declare namespace ts.server {
82048213
declare namespace ts.server {
82058214
const maxProgramSizeForNonTsFiles: number;
82068215
const ProjectsUpdatedInBackgroundEvent = "projectsUpdatedInBackground";
8216+
const SurveyReady = "surveyReady";
82078217
const LargeFileReferencedEvent = "largeFileReferenced";
82088218
const ConfigFileDiagEvent = "configFileDiag";
82098219
const ProjectLanguageServiceStateEvent = "projectLanguageServiceState";
@@ -8215,6 +8225,12 @@ declare namespace ts.server {
82158225
openFiles: string[];
82168226
};
82178227
}
8228+
interface SurveyReady {
8229+
eventName: typeof SurveyReady;
8230+
data: {
8231+
surveyId: string;
8232+
};
8233+
}
82188234
interface LargeFileReferencedEvent {
82198235
eventName: typeof LargeFileReferencedEvent;
82208236
data: {
@@ -8293,7 +8309,7 @@ declare namespace ts.server {
82938309
interface OpenFileInfo {
82948310
readonly checkJs: boolean;
82958311
}
8296-
type ProjectServiceEvent = LargeFileReferencedEvent | ProjectsUpdatedInBackgroundEvent | ConfigFileDiagEvent | ProjectLanguageServiceStateEvent | ProjectInfoTelemetryEvent | OpenFileInfoTelemetryEvent;
8312+
type ProjectServiceEvent = LargeFileReferencedEvent | SurveyReady | ProjectsUpdatedInBackgroundEvent | ConfigFileDiagEvent | ProjectLanguageServiceStateEvent | ProjectInfoTelemetryEvent | OpenFileInfoTelemetryEvent;
82978313
type ProjectServiceEventHandler = (event: ProjectServiceEvent) => void;
82988314
interface SafeList {
82998315
[name: string]: {
@@ -8412,6 +8428,8 @@ declare namespace ts.server {
84128428
readonly syntaxOnly?: boolean;
84138429
/** Tracks projects that we have already sent telemetry for. */
84148430
private readonly seenProjects;
8431+
/** Tracks projects that we have already sent survey events for. */
8432+
private readonly seenSurveyProjects;
84158433
constructor(opts: ProjectServiceOptions);
84168434
toPath(fileName: string): Path;
84178435
private loadTypesMap;

0 commit comments

Comments
 (0)