• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1namespace ts.projectSystem {
2    describe("unittests:: tsserver:: ConfiguredProjects", () => {
3        it("create configured project without file list", () => {
4            const configFile: File = {
5                path: "/a/b/tsconfig.json",
6                content: `
7                {
8                    "compilerOptions": {},
9                    "exclude": [
10                        "e"
11                    ]
12                }`
13            };
14            const file1: File = {
15                path: "/a/b/c/f1.ts",
16                content: "let x = 1"
17            };
18            const file2: File = {
19                path: "/a/b/d/f2.ts",
20                content: "let y = 1"
21            };
22            const file3: File = {
23                path: "/a/b/e/f3.ts",
24                content: "let z = 1"
25            };
26
27            const host = createServerHost([configFile, libFile, file1, file2, file3]);
28            const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
29            const { configFileName, configFileErrors } = projectService.openClientFile(file1.path);
30
31            assert(configFileName, "should find config file");
32            assert.isTrue(!configFileErrors || configFileErrors.length === 0, `expect no errors in config file, got ${JSON.stringify(configFileErrors)}`);
33
34            baselineTsserverLogs("configuredProjects", "create configured project without file list", projectService);
35        });
36
37        it("create configured project with the file list", () => {
38            const configFile: File = {
39                path: "/a/b/tsconfig.json",
40                content: `
41                {
42                    "compilerOptions": {},
43                    "include": ["*.ts"]
44                }`
45            };
46            const file1: File = {
47                path: "/a/b/f1.ts",
48                content: "let x = 1"
49            };
50            const file2: File = {
51                path: "/a/b/f2.ts",
52                content: "let y = 1"
53            };
54            const file3: File = {
55                path: "/a/b/c/f3.ts",
56                content: "let z = 1"
57            };
58
59            const host = createServerHost([configFile, libFile, file1, file2, file3]);
60            const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
61            const { configFileName, configFileErrors } = projectService.openClientFile(file1.path);
62
63            assert(configFileName, "should find config file");
64            assert.isTrue(!configFileErrors || configFileErrors.length === 0, `expect no errors in config file, got ${JSON.stringify(configFileErrors)}`);
65
66            baselineTsserverLogs("configuredProjects", "create configured project with the file list", projectService);
67        });
68
69        it("add and then remove a config file in a folder with loose files", () => {
70            const configFile: File = {
71                path: `${tscWatch.projectRoot}/tsconfig.json`,
72                content: `{
73                    "files": ["commonFile1.ts"]
74                }`
75            };
76            const commonFile1: File = {
77                path: `${tscWatch.projectRoot}/commonFile1.ts`,
78                content: "let x = 1"
79            };
80            const commonFile2: File = {
81                path: `${tscWatch.projectRoot}/commonFile2.ts`,
82                content: "let y = 1"
83            };
84
85            const host = createServerHost([libFile, commonFile1, commonFile2]);
86
87            const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
88            projectService.openClientFile(commonFile1.path);
89            projectService.openClientFile(commonFile2.path);
90
91            // Add a tsconfig file
92            host.writeFile(configFile.path, configFile.content);
93            host.checkTimeoutQueueLengthAndRun(2); // load configured project from disk + ensureProjectsForOpenFiles
94
95            // remove the tsconfig file
96            host.deleteFile(configFile.path);
97            host.checkTimeoutQueueLengthAndRun(1); // Refresh inferred projects
98
99            baselineTsserverLogs("configuredProjects", "add and then remove a config file in a folder with loose files", projectService);
100        });
101
102        it("add new files to a configured project without file list", () => {
103            const configFile: File = {
104                path: "/a/b/tsconfig.json",
105                content: `{}`
106            };
107            const host = createServerHost([commonFile1, libFile, configFile]);
108            const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
109            projectService.openClientFile(commonFile1.path);
110
111            // add a new ts file
112            host.writeFile(commonFile2.path, commonFile2.content);
113            host.checkTimeoutQueueLengthAndRun(2);
114            baselineTsserverLogs("configuredProjects", "add new files to a configured project without file list", projectService);
115        });
116
117        it("should ignore non-existing files specified in the config file", () => {
118            const configFile: File = {
119                path: "/a/b/tsconfig.json",
120                content: `{
121                    "compilerOptions": {},
122                    "files": [
123                        "commonFile1.ts",
124                        "commonFile3.ts"
125                    ]
126                }`
127            };
128            const host = createServerHost([commonFile1, commonFile2, configFile]);
129            const projectService = createProjectService(host);
130            projectService.openClientFile(commonFile1.path);
131            projectService.openClientFile(commonFile2.path);
132
133            checkNumberOfConfiguredProjects(projectService, 1);
134            const project = configuredProjectAt(projectService, 0);
135            checkProjectRootFiles(project, [commonFile1.path]);
136            checkNumberOfInferredProjects(projectService, 1);
137        });
138
139        it("handle recreated files correctly", () => {
140            const configFile: File = {
141                path: "/a/b/tsconfig.json",
142                content: `{}`
143            };
144            const host = createServerHost([commonFile1, commonFile2, configFile]);
145            const projectService = createProjectService(host);
146            projectService.openClientFile(commonFile1.path);
147
148            checkNumberOfConfiguredProjects(projectService, 1);
149            const project = configuredProjectAt(projectService, 0);
150            checkProjectRootFiles(project, [commonFile1.path, commonFile2.path]);
151
152            // delete commonFile2
153            host.deleteFile(commonFile2.path);
154            host.checkTimeoutQueueLengthAndRun(2);
155            checkProjectRootFiles(project, [commonFile1.path]);
156
157            // re-add commonFile2
158            host.writeFile(commonFile2.path, commonFile2.content);
159            host.checkTimeoutQueueLengthAndRun(2);
160            checkProjectRootFiles(project, [commonFile1.path, commonFile2.path]);
161        });
162
163        it("files explicitly excluded in config file", () => {
164            const configFile: File = {
165                path: "/a/b/tsconfig.json",
166                content: `{
167                    "compilerOptions": {},
168                    "exclude": ["/a/c"]
169                }`
170            };
171            const excludedFile1: File = {
172                path: "/a/c/excluedFile1.ts",
173                content: `let t = 1;`
174            };
175
176            const host = createServerHost([commonFile1, commonFile2, excludedFile1, configFile]);
177            const projectService = createProjectService(host);
178
179            projectService.openClientFile(commonFile1.path);
180            checkNumberOfConfiguredProjects(projectService, 1);
181            const project = configuredProjectAt(projectService, 0);
182            checkProjectRootFiles(project, [commonFile1.path, commonFile2.path]);
183            projectService.openClientFile(excludedFile1.path);
184            checkNumberOfInferredProjects(projectService, 1);
185        });
186
187        it("should properly handle module resolution changes in config file", () => {
188            const file1: File = {
189                path: "/a/b/file1.ts",
190                content: `import { T } from "module1";`
191            };
192            const nodeModuleFile: File = {
193                path: "/a/b/node_modules/module1.ts",
194                content: `export interface T {}`
195            };
196            const classicModuleFile: File = {
197                path: "/a/module1.ts",
198                content: `export interface T {}`
199            };
200            const randomFile: File = {
201                path: "/a/file1.ts",
202                content: `export interface T {}`
203            };
204            const configFile: File = {
205                path: "/a/b/tsconfig.json",
206                content: `{
207                    "compilerOptions": {
208                        "moduleResolution": "node"
209                    },
210                    "files": ["${file1.path}"]
211                }`
212            };
213            const files = [file1, nodeModuleFile, classicModuleFile, configFile, randomFile];
214            const host = createServerHost(files);
215            const projectService = createProjectService(host);
216            projectService.openClientFile(file1.path);
217            projectService.openClientFile(nodeModuleFile.path);
218            projectService.openClientFile(classicModuleFile.path);
219
220            checkNumberOfProjects(projectService, { configuredProjects: 1, inferredProjects: 1 });
221            const project = configuredProjectAt(projectService, 0);
222            const inferredProject0 = projectService.inferredProjects[0];
223            checkProjectActualFiles(project, [file1.path, nodeModuleFile.path, configFile.path]);
224            checkProjectActualFiles(projectService.inferredProjects[0], [classicModuleFile.path]);
225
226            host.writeFile(configFile.path, `{
227                "compilerOptions": {
228                    "moduleResolution": "classic"
229                },
230                "files": ["${file1.path}"]
231            }`);
232            host.checkTimeoutQueueLengthAndRun(2);
233
234            checkNumberOfProjects(projectService, { configuredProjects: 1, inferredProjects: 2 }); // will not remove project 1
235            checkProjectActualFiles(project, [file1.path, classicModuleFile.path, configFile.path]);
236            assert.strictEqual(projectService.inferredProjects[0], inferredProject0);
237            assert.isTrue(projectService.inferredProjects[0].isOrphan());
238            const inferredProject1 = projectService.inferredProjects[1];
239            checkProjectActualFiles(projectService.inferredProjects[1], [nodeModuleFile.path]);
240
241            // Open random file and it will reuse first inferred project
242            projectService.openClientFile(randomFile.path);
243            checkNumberOfProjects(projectService, { configuredProjects: 1, inferredProjects: 2 });
244            checkProjectActualFiles(project, [file1.path, classicModuleFile.path, configFile.path]);
245            assert.strictEqual(projectService.inferredProjects[0], inferredProject0);
246            checkProjectActualFiles(projectService.inferredProjects[0], [randomFile.path]); // Reuses first inferred project
247            assert.strictEqual(projectService.inferredProjects[1], inferredProject1);
248            checkProjectActualFiles(projectService.inferredProjects[1], [nodeModuleFile.path]);
249        });
250
251        it("should keep the configured project when the opened file is referenced by the project but not its root", () => {
252            const file1: File = {
253                path: "/a/b/main.ts",
254                content: "import { objA } from './obj-a';"
255            };
256            const file2: File = {
257                path: "/a/b/obj-a.ts",
258                content: `export const objA = Object.assign({foo: "bar"}, {bar: "baz"});`
259            };
260            const configFile: File = {
261                path: "/a/b/tsconfig.json",
262                content: `{
263                    "compilerOptions": {
264                        "target": "es6"
265                    },
266                    "files": [ "main.ts" ]
267                }`
268            };
269            const host = createServerHost([file1, file2, configFile]);
270            const projectService = createProjectService(host);
271            projectService.openClientFile(file1.path);
272            projectService.closeClientFile(file1.path);
273            projectService.openClientFile(file2.path);
274            checkNumberOfConfiguredProjects(projectService, 1);
275            checkNumberOfInferredProjects(projectService, 0);
276        });
277
278        it("should keep the configured project when the opened file is referenced by the project but not its root", () => {
279            const file1: File = {
280                path: "/a/b/main.ts",
281                content: "import { objA } from './obj-a';"
282            };
283            const file2: File = {
284                path: "/a/b/obj-a.ts",
285                content: `export const objA = Object.assign({foo: "bar"}, {bar: "baz"});`
286            };
287            const configFile: File = {
288                path: "/a/b/tsconfig.json",
289                content: `{
290                    "compilerOptions": {
291                        "target": "es6"
292                    },
293                    "files": [ "main.ts" ]
294                }`
295            };
296            const host = createServerHost([file1, file2, configFile]);
297            const projectService = createProjectService(host);
298            projectService.openClientFile(file1.path);
299            projectService.closeClientFile(file1.path);
300            projectService.openClientFile(file2.path);
301            checkNumberOfConfiguredProjects(projectService, 1);
302            checkNumberOfInferredProjects(projectService, 0);
303        });
304
305        it("should tolerate config file errors and still try to build a project", () => {
306            const configFile: File = {
307                path: "/a/b/tsconfig.json",
308                content: `{
309                    "compilerOptions": {
310                        "target": "es6",
311                        "allowAnything": true
312                    },
313                    "someOtherProperty": {}
314                }`
315            };
316            const host = createServerHost([commonFile1, commonFile2, libFile, configFile]);
317            const projectService = createProjectService(host);
318            projectService.openClientFile(commonFile1.path);
319            checkNumberOfConfiguredProjects(projectService, 1);
320            checkProjectRootFiles(configuredProjectAt(projectService, 0), [commonFile1.path, commonFile2.path]);
321        });
322
323        it("should reuse same project if file is opened from the configured project that has no open files", () => {
324            const file1 = {
325                path: "/a/b/main.ts",
326                content: "let x =1;"
327            };
328            const file2 = {
329                path: "/a/b/main2.ts",
330                content: "let y =1;"
331            };
332            const configFile: File = {
333                path: "/a/b/tsconfig.json",
334                content: `{
335                    "compilerOptions": {
336                        "target": "es6"
337                    },
338                    "files": [ "main.ts", "main2.ts" ]
339                }`
340            };
341            const host = createServerHost([file1, file2, configFile, libFile]);
342            const projectService = createProjectService(host, { useSingleInferredProject: true });
343            projectService.openClientFile(file1.path);
344            checkNumberOfConfiguredProjects(projectService, 1);
345            const project = projectService.configuredProjects.get(configFile.path)!;
346            assert.isTrue(project.hasOpenRef()); // file1
347
348            projectService.closeClientFile(file1.path);
349            checkNumberOfConfiguredProjects(projectService, 1);
350            assert.strictEqual(projectService.configuredProjects.get(configFile.path), project);
351            assert.isFalse(project.hasOpenRef()); // No open files
352            assert.isFalse(project.isClosed());
353
354            projectService.openClientFile(file2.path);
355            checkNumberOfConfiguredProjects(projectService, 1);
356            assert.strictEqual(projectService.configuredProjects.get(configFile.path), project);
357            assert.isTrue(project.hasOpenRef()); // file2
358            assert.isFalse(project.isClosed());
359        });
360
361        it("should not close configured project after closing last open file, but should be closed on next file open if its not the file from same project", () => {
362            const file1 = {
363                path: "/a/b/main.ts",
364                content: "let x =1;"
365            };
366            const configFile: File = {
367                path: "/a/b/tsconfig.json",
368                content: `{
369                    "compilerOptions": {
370                        "target": "es6"
371                    },
372                    "files": [ "main.ts" ]
373                }`
374            };
375            const host = createServerHost([file1, configFile, libFile]);
376            const projectService = createProjectService(host, { useSingleInferredProject: true });
377            projectService.openClientFile(file1.path);
378            checkNumberOfConfiguredProjects(projectService, 1);
379            const project = projectService.configuredProjects.get(configFile.path)!;
380            assert.isTrue(project.hasOpenRef()); // file1
381
382            projectService.closeClientFile(file1.path);
383            checkNumberOfConfiguredProjects(projectService, 1);
384            assert.strictEqual(projectService.configuredProjects.get(configFile.path), project);
385            assert.isFalse(project.hasOpenRef()); // No files
386            assert.isFalse(project.isClosed());
387
388            projectService.openClientFile(libFile.path);
389            checkNumberOfConfiguredProjects(projectService, 0);
390            assert.isFalse(project.hasOpenRef()); // No files + project closed
391            assert.isTrue(project.isClosed());
392        });
393
394        it("open file become a part of configured project if it is referenced from root file", () => {
395            const file1 = {
396                path: `${tscWatch.projectRoot}/a/b/f1.ts`,
397                content: "export let x = 5"
398            };
399            const file2 = {
400                path: `${tscWatch.projectRoot}/a/c/f2.ts`,
401                content: `import {x} from "../b/f1"`
402            };
403            const file3 = {
404                path: `${tscWatch.projectRoot}/a/c/f3.ts`,
405                content: "export let y = 1"
406            };
407            const configFile = {
408                path: `${tscWatch.projectRoot}/a/c/tsconfig.json`,
409                content: JSON.stringify({ compilerOptions: {}, files: ["f2.ts", "f3.ts"] })
410            };
411
412            const host = createServerHost([file1, file2, file3]);
413            const projectService = createProjectService(host);
414
415            projectService.openClientFile(file1.path);
416            checkNumberOfProjects(projectService, { inferredProjects: 1 });
417            checkProjectActualFiles(projectService.inferredProjects[0], [file1.path]);
418
419            projectService.openClientFile(file3.path);
420            checkNumberOfProjects(projectService, { inferredProjects: 2 });
421            checkProjectActualFiles(projectService.inferredProjects[0], [file1.path]);
422            checkProjectActualFiles(projectService.inferredProjects[1], [file3.path]);
423
424            host.writeFile(configFile.path, configFile.content);
425            host.checkTimeoutQueueLengthAndRun(2); // load configured project from disk + ensureProjectsForOpenFiles
426            checkNumberOfProjects(projectService, { configuredProjects: 1, inferredProjects: 2 });
427            checkProjectActualFiles(configuredProjectAt(projectService, 0), [file1.path, file2.path, file3.path, configFile.path]);
428            assert.isTrue(projectService.inferredProjects[0].isOrphan());
429            assert.isTrue(projectService.inferredProjects[1].isOrphan());
430        });
431
432        it("can correctly update configured project when set of root files has changed (new file on disk)", () => {
433            const file1 = {
434                path: "/a/b/f1.ts",
435                content: "let x = 1"
436            };
437            const file2 = {
438                path: "/a/b/f2.ts",
439                content: "let y = 1"
440            };
441            const configFile = {
442                path: "/a/b/tsconfig.json",
443                content: JSON.stringify({ compilerOptions: {} })
444            };
445
446            const host = createServerHost([file1, configFile]);
447            const projectService = createProjectService(host);
448
449            projectService.openClientFile(file1.path);
450            checkNumberOfProjects(projectService, { configuredProjects: 1 });
451            checkProjectActualFiles(configuredProjectAt(projectService, 0), [file1.path, configFile.path]);
452
453            host.writeFile(file2.path, file2.content);
454
455            host.checkTimeoutQueueLengthAndRun(2);
456
457            checkNumberOfProjects(projectService, { configuredProjects: 1 });
458            checkProjectRootFiles(configuredProjectAt(projectService, 0), [file1.path, file2.path]);
459        });
460
461        it("can correctly update configured project when set of root files has changed (new file in list of files)", () => {
462            const file1 = {
463                path: "/a/b/f1.ts",
464                content: "let x = 1"
465            };
466            const file2 = {
467                path: "/a/b/f2.ts",
468                content: "let y = 1"
469            };
470            const configFile = {
471                path: "/a/b/tsconfig.json",
472                content: JSON.stringify({ compilerOptions: {}, files: ["f1.ts"] })
473            };
474
475            const host = createServerHost([file1, file2, configFile]);
476            const projectService = createProjectService(host);
477
478            projectService.openClientFile(file1.path);
479            checkNumberOfProjects(projectService, { configuredProjects: 1 });
480            checkProjectActualFiles(configuredProjectAt(projectService, 0), [file1.path, configFile.path]);
481
482            host.writeFile(configFile.path, JSON.stringify({ compilerOptions: {}, files: ["f1.ts", "f2.ts"] }));
483
484            checkNumberOfProjects(projectService, { configuredProjects: 1 });
485            host.checkTimeoutQueueLengthAndRun(2);
486            checkProjectRootFiles(configuredProjectAt(projectService, 0), [file1.path, file2.path]);
487        });
488
489        it("can update configured project when set of root files was not changed", () => {
490            const file1 = {
491                path: "/a/b/f1.ts",
492                content: "let x = 1"
493            };
494            const file2 = {
495                path: "/a/b/f2.ts",
496                content: "let y = 1"
497            };
498            const configFile = {
499                path: "/a/b/tsconfig.json",
500                content: JSON.stringify({ compilerOptions: {}, files: ["f1.ts", "f2.ts"] })
501            };
502
503            const host = createServerHost([file1, file2, configFile]);
504            const projectService = createProjectService(host);
505
506            projectService.openClientFile(file1.path);
507            checkNumberOfProjects(projectService, { configuredProjects: 1 });
508            checkProjectActualFiles(configuredProjectAt(projectService, 0), [file1.path, file2.path, configFile.path]);
509
510            host.writeFile(configFile.path, JSON.stringify({ compilerOptions: { outFile: "out.js" }, files: ["f1.ts", "f2.ts"] }));
511
512            checkNumberOfProjects(projectService, { configuredProjects: 1 });
513            checkProjectRootFiles(configuredProjectAt(projectService, 0), [file1.path, file2.path]);
514        });
515
516        it("Open ref of configured project when open file gets added to the project as part of configured file update", () => {
517            const file1: File = {
518                path: "/a/b/src/file1.ts",
519                content: "let x = 1;"
520            };
521            const file2: File = {
522                path: "/a/b/src/file2.ts",
523                content: "let y = 1;"
524            };
525            const file3: File = {
526                path: "/a/b/file3.ts",
527                content: "let z = 1;"
528            };
529            const file4: File = {
530                path: "/a/file4.ts",
531                content: "let z = 1;"
532            };
533            const configFile = {
534                path: "/a/b/tsconfig.json",
535                content: JSON.stringify({ files: ["src/file1.ts", "file3.ts"] })
536            };
537
538            const files = [file1, file2, file3, file4];
539            const host = createServerHost(files.concat(configFile));
540            const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
541
542            projectService.openClientFile(file1.path);
543            projectService.openClientFile(file2.path);
544            projectService.openClientFile(file3.path);
545            projectService.openClientFile(file4.path);
546
547            const configProject1 = projectService.configuredProjects.get(configFile.path)!;
548            assert.isTrue(configProject1.hasOpenRef()); // file1 and file3
549
550            host.writeFile(configFile.path, "{}");
551            host.runQueuedTimeoutCallbacks();
552
553            assert.isTrue(configProject1.hasOpenRef()); // file1, file2, file3
554            assert.isTrue(projectService.inferredProjects[0].isOrphan());
555
556            projectService.closeClientFile(file1.path);
557            projectService.closeClientFile(file2.path);
558            projectService.closeClientFile(file4.path);
559
560            assert.isTrue(configProject1.hasOpenRef()); // file3
561            assert.isTrue(projectService.inferredProjects[0].isOrphan());
562            assert.isTrue(projectService.inferredProjects[1].isOrphan());
563
564            projectService.openClientFile(file4.path);
565            assert.isTrue(configProject1.hasOpenRef()); // file3
566            const inferredProject4 = projectService.inferredProjects[0];
567            checkProjectActualFiles(inferredProject4, [file4.path]);
568
569            projectService.closeClientFile(file3.path);
570            assert.isFalse(configProject1.hasOpenRef()); // No open files
571            const inferredProject5 = projectService.inferredProjects[0];
572            checkProjectActualFiles(inferredProject4, [file4.path]);
573            assert.strictEqual(inferredProject5, inferredProject4);
574
575            const file5: File = {
576                path: "/file5.ts",
577                content: "let zz = 1;"
578            };
579            host.writeFile(file5.path, file5.content);
580            projectService.testhost.baselineHost("File5 written");
581            projectService.openClientFile(file5.path);
582
583            baselineTsserverLogs("configuredProjects", "Open ref of configured project when open file gets added to the project as part of configured file update", projectService);
584        });
585
586        it("Open ref of configured project when open file gets added to the project as part of configured file update buts its open file references are all closed when the update happens", () => {
587            const file1: File = {
588                path: "/a/b/src/file1.ts",
589                content: "let x = 1;"
590            };
591            const file2: File = {
592                path: "/a/b/src/file2.ts",
593                content: "let y = 1;"
594            };
595            const file3: File = {
596                path: "/a/b/file3.ts",
597                content: "let z = 1;"
598            };
599            const file4: File = {
600                path: "/a/file4.ts",
601                content: "let z = 1;"
602            };
603            const configFile = {
604                path: "/a/b/tsconfig.json",
605                content: JSON.stringify({ files: ["src/file1.ts", "file3.ts"] })
606            };
607
608            const files = [file1, file2, file3];
609            const hostFiles = files.concat(file4, configFile);
610            const host = createServerHost(hostFiles);
611            const projectService = createProjectService(host);
612
613            projectService.openClientFile(file1.path);
614            projectService.openClientFile(file2.path);
615            projectService.openClientFile(file3.path);
616
617            checkNumberOfProjects(projectService, { configuredProjects: 1, inferredProjects: 1 });
618            const configuredProject = projectService.configuredProjects.get(configFile.path)!;
619            assert.isTrue(configuredProject.hasOpenRef()); // file1 and file3
620            checkProjectActualFiles(configuredProject, [file1.path, file3.path, configFile.path]);
621            const inferredProject1 = projectService.inferredProjects[0];
622            checkProjectActualFiles(inferredProject1, [file2.path]);
623
624            projectService.closeClientFile(file1.path);
625            projectService.closeClientFile(file3.path);
626            assert.isFalse(configuredProject.hasOpenRef()); // No files
627
628            host.writeFile(configFile.path, "{}");
629            // Time out is not yet run so there is project update pending
630            assert.isTrue(configuredProject.hasOpenRef()); // Pending update and file2 might get into the project
631
632            projectService.openClientFile(file4.path);
633
634            checkNumberOfProjects(projectService, { configuredProjects: 1, inferredProjects: 2 });
635            assert.strictEqual(projectService.configuredProjects.get(configFile.path), configuredProject);
636            assert.isTrue(configuredProject.hasOpenRef()); // Pending update and F2 might get into the project
637            assert.strictEqual(projectService.inferredProjects[0], inferredProject1);
638            const inferredProject2 = projectService.inferredProjects[1];
639            checkProjectActualFiles(inferredProject2, [file4.path]);
640
641            host.runQueuedTimeoutCallbacks();
642            checkNumberOfProjects(projectService, { configuredProjects: 1, inferredProjects: 2 });
643            assert.strictEqual(projectService.configuredProjects.get(configFile.path), configuredProject);
644            assert.isTrue(configuredProject.hasOpenRef()); // file2
645            checkProjectActualFiles(configuredProject, [file1.path, file2.path, file3.path, configFile.path]);
646            assert.strictEqual(projectService.inferredProjects[0], inferredProject1);
647            assert.isTrue(inferredProject1.isOrphan());
648            assert.strictEqual(projectService.inferredProjects[1], inferredProject2);
649            checkProjectActualFiles(inferredProject2, [file4.path]);
650        });
651
652        it("files are properly detached when language service is disabled", () => {
653            const f1 = {
654                path: "/a/app.js",
655                content: "var x = 1"
656            };
657            const f2 = {
658                path: "/a/largefile.js",
659                content: ""
660            };
661            const f3 = {
662                path: "/a/lib.js",
663                content: "var x = 1"
664            };
665            const config = {
666                path: "/a/tsconfig.json",
667                content: JSON.stringify({ compilerOptions: { allowJs: true } })
668            };
669            const host = createServerHost([f1, f2, f3, config]);
670            const originalGetFileSize = host.getFileSize;
671            host.getFileSize = (filePath: string) =>
672                filePath === f2.path ? server.maxProgramSizeForNonTsFiles + 1 : originalGetFileSize.call(host, filePath);
673
674            const projectService = createProjectService(host);
675            projectService.openClientFile(f1.path);
676            projectService.checkNumberOfProjects({ configuredProjects: 1 });
677            const project = projectService.configuredProjects.get(config.path)!;
678            assert.isTrue(project.hasOpenRef()); // f1
679            assert.isFalse(project.isClosed());
680
681            projectService.closeClientFile(f1.path);
682            projectService.checkNumberOfProjects({ configuredProjects: 1 });
683            assert.strictEqual(projectService.configuredProjects.get(config.path), project);
684            assert.isFalse(project.hasOpenRef()); // No files
685            assert.isFalse(project.isClosed());
686
687            for (const f of [f1, f2, f3]) {
688                // All the script infos should be present and contain the project since it is still alive.
689                const scriptInfo = projectService.getScriptInfoForNormalizedPath(server.toNormalizedPath(f.path))!;
690                assert.equal(scriptInfo.containingProjects.length, 1, `expect 1 containing projects for '${f.path}'`);
691                assert.equal(scriptInfo.containingProjects[0], project, `expect configured project to be the only containing project for '${f.path}'`);
692            }
693
694            const f4 = {
695                path: "/aa.js",
696                content: "var x = 1"
697            };
698            host.writeFile(f4.path, f4.content);
699            projectService.openClientFile(f4.path);
700            projectService.checkNumberOfProjects({ inferredProjects: 1 });
701            assert.isFalse(project.hasOpenRef()); // No files
702            assert.isTrue(project.isClosed());
703
704            for (const f of [f1, f2, f3]) {
705                // All the script infos should not be present since the project is closed and orphan script infos are collected
706                assert.isUndefined(projectService.getScriptInfoForNormalizedPath(server.toNormalizedPath(f.path)));
707            }
708        });
709
710        it("syntactic features work even if language service is disabled", () => {
711            const f1 = {
712                path: "/a/app.js",
713                content: "let x =   1;"
714            };
715            const f2 = {
716                path: "/a/largefile.js",
717                content: ""
718            };
719            const config = {
720                path: "/a/jsconfig.json",
721                content: "{}"
722            };
723            const host = createServerHost([f1, f2, config]);
724            const originalGetFileSize = host.getFileSize;
725            host.getFileSize = (filePath: string) =>
726                filePath === f2.path ? server.maxProgramSizeForNonTsFiles + 1 : originalGetFileSize.call(host, filePath);
727            const { session, events } = createSessionWithEventTracking<server.ProjectLanguageServiceStateEvent>(host, server.ProjectLanguageServiceStateEvent);
728            session.executeCommand({
729                seq: 0,
730                type: "request",
731                command: "open",
732                arguments: { file: f1.path }
733            } as protocol.OpenRequest);
734
735            const projectService = session.getProjectService();
736            checkNumberOfProjects(projectService, { configuredProjects: 1 });
737            const project = configuredProjectAt(projectService, 0);
738            assert.isFalse(project.languageServiceEnabled, "Language service enabled");
739            assert.equal(events.length, 1, "should receive event");
740            assert.equal(events[0].data.project, project, "project name");
741            assert.isFalse(events[0].data.languageServiceEnabled, "Language service state");
742
743            const options = projectService.getFormatCodeOptions(f1.path as server.NormalizedPath);
744            const edits = project.getLanguageService().getFormattingEditsForDocument(f1.path, options);
745            assert.deepEqual(edits, [{ span: createTextSpan(/*start*/ 7, /*length*/ 3), newText: " " }]);
746        });
747
748        it("when multiple projects are open, detects correct default project", () => {
749            const barConfig: File = {
750                path: `${tscWatch.projectRoot}/bar/tsconfig.json`,
751                content: JSON.stringify({
752                    include: ["index.ts"],
753                    compilerOptions: {
754                        lib: ["dom", "es2017"]
755                    }
756                })
757            };
758            const barIndex: File = {
759                path: `${tscWatch.projectRoot}/bar/index.ts`,
760                content: `
761export function bar() {
762  console.log("hello world");
763}`
764            };
765            const fooConfig: File = {
766                path: `${tscWatch.projectRoot}/foo/tsconfig.json`,
767                content: JSON.stringify({
768                    include: ["index.ts"],
769                    compilerOptions: {
770                        lib: ["es2017"]
771                    }
772                })
773            };
774            const fooIndex: File = {
775                path: `${tscWatch.projectRoot}/foo/index.ts`,
776                content: `
777import { bar } from "bar";
778bar();`
779            };
780            const barSymLink: SymLink = {
781                path: `${tscWatch.projectRoot}/foo/node_modules/bar`,
782                symLink: `${tscWatch.projectRoot}/bar`
783            };
784
785            const lib2017: File = {
786                path: `${getDirectoryPath(libFile.path)}/lib.es2017.d.ts`,
787                content: libFile.content
788            };
789            const libDom: File = {
790                path: `${getDirectoryPath(libFile.path)}/lib.dom.d.ts`,
791                content: `
792declare var console: {
793    log(...args: any[]): void;
794};`
795            };
796            const host = createServerHost([barConfig, barIndex, fooConfig, fooIndex, barSymLink, lib2017, libDom]);
797            const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
798            openFilesForSession([fooIndex, barIndex], session);
799            verifyGetErrRequest({ session, host, files: [barIndex, fooIndex] });
800            baselineTsserverLogs("configuredProjects", "when multiple projects are open detects correct default project", session);
801        });
802
803        it("when file name starts with ^", () => {
804            const file: File = {
805                path: `${tscWatch.projectRoot}/file.ts`,
806                content: "const x = 10;"
807            };
808            const app: File = {
809                path: `${tscWatch.projectRoot}/^app.ts`,
810                content: "const y = 10;"
811            };
812            const tsconfig: File = {
813                path: `${tscWatch.projectRoot}/tsconfig.json`,
814                content: "{}"
815            };
816            const host = createServerHost([file, app, tsconfig, libFile]);
817            const service = createProjectService(host);
818            service.openClientFile(file.path);
819        });
820
821        describe("when creating new file", () => {
822            const foo: File = {
823                path: `${tscWatch.projectRoot}/src/foo.ts`,
824                content: "export function foo() { }"
825            };
826            const bar: File = {
827                path: `${tscWatch.projectRoot}/src/bar.ts`,
828                content: "export function bar() { }"
829            };
830            const config: File = {
831                path: `${tscWatch.projectRoot}/tsconfig.json`,
832                content: JSON.stringify({
833                    include: ["./src"]
834                })
835            };
836            const fooBar: File = {
837                path: `${tscWatch.projectRoot}/src/sub/fooBar.ts`,
838                content: "export function fooBar() { }"
839            };
840            function verifySessionWorker({ withExclude, openFileBeforeCreating }: VerifySession, errorOnNewFileBeforeOldFile: boolean) {
841                const host = createServerHost([
842                    foo, bar, libFile, { path: `${tscWatch.projectRoot}/src/sub` },
843                    withExclude ?
844                        {
845                            path: config.path,
846                            content: JSON.stringify({
847                                include: ["./src"],
848                                exclude: ["./src/sub"]
849                            })
850                        } :
851                        config
852                ]);
853                const session = createSession(host, {
854                    canUseEvents: true,
855                    logger: createLoggerWithInMemoryLogs(host),
856                });
857                session.executeCommandSeq<protocol.OpenRequest>({
858                    command: protocol.CommandTypes.Open,
859                    arguments: {
860                        file: foo.path,
861                        fileContent: foo.content,
862                        projectRootPath: tscWatch.projectRoot
863                    }
864                });
865                if (!openFileBeforeCreating) {
866                    host.writeFile(fooBar.path, fooBar.content);
867                }
868                session.executeCommandSeq<protocol.OpenRequest>({
869                    command: protocol.CommandTypes.Open,
870                    arguments: {
871                        file: fooBar.path,
872                        fileContent: fooBar.content,
873                        projectRootPath: tscWatch.projectRoot
874                    }
875                });
876                if (openFileBeforeCreating) {
877                    host.writeFile(fooBar.path, fooBar.content);
878                }
879                verifyGetErrRequest({
880                    session,
881                    host,
882                    files: errorOnNewFileBeforeOldFile ?
883                        [fooBar, foo] :
884                        [foo, fooBar],
885                    existingTimeouts: withExclude ? 0 : 2
886                });
887                baselineTsserverLogs("configuredProjects", `creating new file and then open it ${openFileBeforeCreating ? "before" : "after"} watcher is invoked, ask errors on it ${errorOnNewFileBeforeOldFile ? "before" : "after"} old one${withExclude ? " without file being in config" : ""}`, session);
888            }
889            interface VerifySession {
890                withExclude?: boolean;
891                openFileBeforeCreating: boolean;
892            }
893            function verifySession(input: VerifySession) {
894                it("when error on new file are asked before old one", () => {
895                    verifySessionWorker(input, /*errorOnNewFileBeforeOldFile*/ true);
896                });
897
898                it("when error on new file are asked after old one", () => {
899                    verifySessionWorker(input, /*errorOnNewFileBeforeOldFile*/ false);
900                });
901            }
902            describe("when new file creation directory watcher is invoked before file is opened in editor", () => {
903                verifySession({
904                    openFileBeforeCreating: false,
905                });
906                describe("when new file is excluded from config", () => {
907                    verifySession({
908                        withExclude: true,
909                        openFileBeforeCreating: false,
910                    });
911                });
912            });
913
914            describe("when new file creation directory watcher is invoked after file is opened in editor", () => {
915                verifySession({
916                    openFileBeforeCreating: true,
917                });
918                describe("when new file is excluded from config", () => {
919                    verifySession({
920                        withExclude: true,
921                        openFileBeforeCreating: true,
922                    });
923                });
924            });
925        });
926
927        it("when default configured project does not contain the file", () => {
928            const barConfig: File = {
929                path: `${tscWatch.projectRoot}/bar/tsconfig.json`,
930                content: "{}"
931            };
932            const barIndex: File = {
933                path: `${tscWatch.projectRoot}/bar/index.ts`,
934                content: `import {foo} from "../foo/lib";
935foo();`
936            };
937            const fooBarConfig: File = {
938                path: `${tscWatch.projectRoot}/foobar/tsconfig.json`,
939                content: barConfig.path
940            };
941            const fooBarIndex: File = {
942                path: `${tscWatch.projectRoot}/foobar/index.ts`,
943                content: barIndex.content
944            };
945            const fooConfig: File = {
946                path: `${tscWatch.projectRoot}/foo/tsconfig.json`,
947                content: JSON.stringify({
948                    include: ["index.ts"],
949                    compilerOptions: {
950                        declaration: true,
951                        outDir: "lib"
952                    }
953                })
954            };
955            const fooIndex: File = {
956                path: `${tscWatch.projectRoot}/foo/index.ts`,
957                content: `export function foo() {}`
958            };
959            const host = createServerHost([barConfig, barIndex, fooBarConfig, fooBarIndex, fooConfig, fooIndex, libFile]);
960            tscWatch.ensureErrorFreeBuild(host, [fooConfig.path]);
961            const fooDts = `${tscWatch.projectRoot}/foo/lib/index.d.ts`;
962            assert.isTrue(host.fileExists(fooDts));
963            const session = createSession(host);
964            const service = session.getProjectService();
965            service.openClientFile(barIndex.path);
966            checkProjectActualFiles(service.configuredProjects.get(barConfig.path)!, [barIndex.path, fooDts, libFile.path, barConfig.path]);
967            service.openClientFile(fooBarIndex.path);
968            checkProjectActualFiles(service.configuredProjects.get(fooBarConfig.path)!, [fooBarIndex.path, fooDts, libFile.path, fooBarConfig.path]);
969            service.openClientFile(fooIndex.path);
970            checkProjectActualFiles(service.configuredProjects.get(fooConfig.path)!, [fooIndex.path, libFile.path, fooConfig.path]);
971            service.openClientFile(fooDts);
972            session.executeCommandSeq<protocol.GetApplicableRefactorsRequest>({
973                command: protocol.CommandTypes.GetApplicableRefactors,
974                arguments: {
975                    file: fooDts,
976                    startLine: 1,
977                    startOffset: 1,
978                    endLine: 1,
979                    endOffset: 1
980                }
981            });
982            assert.equal(service.tryGetDefaultProjectForFile(server.toNormalizedPath(fooDts)), service.configuredProjects.get(barConfig.path));
983        });
984
985        describe("watches extended config files", () => {
986            function getService(additionalFiles?: File[]) {
987                const alphaExtendedConfig: File = {
988                    path: `${tscWatch.projectRoot}/extended/alpha.tsconfig.json`,
989                    content: "{}"
990                };
991                const bravoExtendedConfig: File = {
992                    path: `${tscWatch.projectRoot}/extended/bravo.tsconfig.json`,
993                    content: JSON.stringify({
994                        extends: "./alpha.tsconfig.json"
995                    })
996                };
997                const aConfig: File = {
998                    path: `${tscWatch.projectRoot}/a/tsconfig.json`,
999                    content: JSON.stringify({
1000                        extends: "../extended/alpha.tsconfig.json",
1001                        files: ["a.ts"]
1002                    })
1003                };
1004                const aFile: File = {
1005                    path: `${tscWatch.projectRoot}/a/a.ts`,
1006                    content: `let a = 1;`
1007                };
1008                const bConfig: File = {
1009                    path: `${tscWatch.projectRoot}/b/tsconfig.json`,
1010                    content: JSON.stringify({
1011                        extends: "../extended/bravo.tsconfig.json",
1012                        files: ["b.ts"]
1013                    })
1014                };
1015                const bFile: File = {
1016                    path: `${tscWatch.projectRoot}/b/b.ts`,
1017                    content: `let b = 1;`
1018                };
1019
1020                const host = createServerHost([alphaExtendedConfig, aConfig, aFile, bravoExtendedConfig, bConfig, bFile, ...(additionalFiles || emptyArray)]);
1021                const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
1022                return { host, projectService, aFile, bFile, aConfig, bConfig, alphaExtendedConfig, bravoExtendedConfig };
1023            }
1024
1025            it("should watch the extended configs of multiple projects", () => {
1026                const { host, projectService, aFile, bFile, bConfig, alphaExtendedConfig, bravoExtendedConfig } = getService();
1027
1028                projectService.openClientFile(aFile.path);
1029                projectService.openClientFile(bFile.path);
1030
1031                host.writeFile(alphaExtendedConfig.path, JSON.stringify({
1032                    compilerOptions: {
1033                        strict: true
1034                    }
1035                }));
1036                host.checkTimeoutQueueLengthAndRun(3);
1037
1038                host.writeFile(bravoExtendedConfig.path, JSON.stringify({
1039                    extends: "./alpha.tsconfig.json",
1040                    compilerOptions: {
1041                        strict: false
1042                    }
1043                }));
1044                host.checkTimeoutQueueLengthAndRun(2);
1045
1046                host.writeFile(bConfig.path, JSON.stringify({
1047                    extends: "../extended/alpha.tsconfig.json",
1048                }));
1049                host.checkTimeoutQueueLengthAndRun(2);
1050
1051                host.writeFile(alphaExtendedConfig.path, "{}");
1052                host.checkTimeoutQueueLengthAndRun(3);
1053                baselineTsserverLogs("configuredProjects", "should watch the extended configs of multiple projects", projectService);
1054            });
1055
1056            it("should stop watching the extended configs of closed projects", () => {
1057                const dummy: File = {
1058                    path: `${tscWatch.projectRoot}/dummy/dummy.ts`,
1059                    content: `let dummy = 1;`
1060                };
1061                const dummyConfig: File = {
1062                    path: `${tscWatch.projectRoot}/dummy/tsconfig.json`,
1063                    content: "{}"
1064                };
1065                const { projectService, aFile, bFile } = getService([dummy, dummyConfig]);
1066
1067                projectService.openClientFile(aFile.path);
1068                projectService.openClientFile(bFile.path);
1069                projectService.openClientFile(dummy.path);
1070
1071                projectService.closeClientFile(bFile.path);
1072                projectService.closeClientFile(dummy.path);
1073                projectService.openClientFile(dummy.path);
1074
1075
1076                projectService.closeClientFile(aFile.path);
1077                projectService.closeClientFile(dummy.path);
1078                projectService.openClientFile(dummy.path);
1079                baselineTsserverLogs("configuredProjects", "should stop watching the extended configs of closed projects", projectService);
1080            });
1081        });
1082    });
1083
1084    describe("unittests:: tsserver:: ConfiguredProjects:: non-existing directories listed in config file input array", () => {
1085        it("should be tolerated without crashing the server", () => {
1086            const configFile = {
1087                path: "/a/b/tsconfig.json",
1088                content: `{
1089                    "compilerOptions": {},
1090                    "include": ["app/*", "test/**/*", "something"]
1091                }`
1092            };
1093            const file1 = {
1094                path: "/a/b/file1.ts",
1095                content: "let t = 10;"
1096            };
1097
1098            const host = createServerHost([file1, configFile]);
1099            const projectService = createProjectService(host);
1100            projectService.openClientFile(file1.path);
1101            host.runQueuedTimeoutCallbacks();
1102
1103            // Since file1 refers to config file as the default project, it needs to be kept alive
1104            checkNumberOfProjects(projectService, { inferredProjects: 1, configuredProjects: 1 });
1105            const inferredProject = projectService.inferredProjects[0];
1106            assert.isTrue(inferredProject.containsFile(file1.path as server.NormalizedPath));
1107            assert.isFalse(projectService.configuredProjects.get(configFile.path)!.containsFile(file1.path as server.NormalizedPath));
1108        });
1109
1110        it("should be able to handle @types if input file list is empty", () => {
1111            const f = {
1112                path: "/a/app.ts",
1113                content: "let x = 1"
1114            };
1115            const config = {
1116                path: "/a/tsconfig.json",
1117                content: JSON.stringify({
1118                    compiler: {},
1119                    files: []
1120                })
1121            };
1122            const t1 = {
1123                path: "/a/node_modules/@types/typings/index.d.ts",
1124                content: `export * from "./lib"`
1125            };
1126            const t2 = {
1127                path: "/a/node_modules/@types/typings/lib.d.ts",
1128                content: `export const x: number`
1129            };
1130            const host = createServerHost([f, config, t1, t2], { currentDirectory: getDirectoryPath(f.path) });
1131            const projectService = createProjectService(host);
1132
1133            projectService.openClientFile(f.path);
1134            // Since f refers to config file as the default project, it needs to be kept alive
1135            projectService.checkNumberOfProjects({ configuredProjects: 1, inferredProjects: 1 });
1136        });
1137
1138        it("should tolerate invalid include files that start in subDirectory", () => {
1139            const f = {
1140                path: `${tscWatch.projectRoot}/src/server/index.ts`,
1141                content: "let x = 1"
1142            };
1143            const config = {
1144                path: `${tscWatch.projectRoot}/src/server/tsconfig.json`,
1145                content: JSON.stringify({
1146                    compiler: {
1147                        module: "commonjs",
1148                        outDir: "../../build"
1149                    },
1150                    include: [
1151                        "../src/**/*.ts"
1152                    ]
1153                })
1154            };
1155            const host = createServerHost([f, config, libFile], { useCaseSensitiveFileNames: true });
1156            const projectService = createProjectService(host);
1157
1158            projectService.openClientFile(f.path);
1159            // Since f refers to config file as the default project, it needs to be kept alive
1160            projectService.checkNumberOfProjects({ configuredProjects: 1, inferredProjects: 1 });
1161        });
1162
1163        it("Changed module resolution reflected when specifying files list", () => {
1164            const file1: File = {
1165                path: "/a/b/file1.ts",
1166                content: 'import classc from "file2"'
1167            };
1168            const file2a: File = {
1169                path: "/a/file2.ts",
1170                content: "export classc { method2a() { return 10; } }"
1171            };
1172            const file2: File = {
1173                path: "/a/b/file2.ts",
1174                content: "export classc { method2() { return 10; } }"
1175            };
1176            const configFile: File = {
1177                path: "/a/b/tsconfig.json",
1178                content: JSON.stringify({ files: [file1.path], compilerOptions: { module: "amd" } })
1179            };
1180            const files = [file1, file2a, configFile, libFile];
1181            const host = createServerHost(files);
1182            const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
1183            projectService.openClientFile(file1.path);
1184
1185            host.writeFile(file2.path, file2.content);
1186            host.runQueuedTimeoutCallbacks(); // Scheduled invalidation of resolutions
1187            host.runQueuedTimeoutCallbacks(); // Actual update
1188
1189            // On next file open the files file2a should be closed and not watched any more
1190            projectService.openClientFile(file2.path);
1191            baselineTsserverLogs("configuredProjects", "changed module resolution reflected when specifying files list", projectService);
1192        });
1193
1194        it("Failed lookup locations uses parent most node_modules directory", () => {
1195            const root = "/user/username/rootfolder";
1196            const file1: File = {
1197                path: "/a/b/src/file1.ts",
1198                content: 'import { classc } from "module1"'
1199            };
1200            const module1: File = {
1201                path: "/a/b/node_modules/module1/index.d.ts",
1202                content: `import { class2 } from "module2";
1203                          export classc { method2a(): class2; }`
1204            };
1205            const module2: File = {
1206                path: "/a/b/node_modules/module2/index.d.ts",
1207                content: "export class2 { method2() { return 10; } }"
1208            };
1209            const module3: File = {
1210                path: "/a/b/node_modules/module/node_modules/module3/index.d.ts",
1211                content: "export class3 { method2() { return 10; } }"
1212            };
1213            const configFile: File = {
1214                path: "/a/b/src/tsconfig.json",
1215                content: JSON.stringify({ files: ["file1.ts"] })
1216            };
1217            const nonLibFiles = [file1, module1, module2, module3, configFile];
1218            nonLibFiles.forEach(f => f.path = root + f.path);
1219            const files = nonLibFiles.concat(libFile);
1220            const host = createServerHost(files);
1221            const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
1222            projectService.openClientFile(file1.path);
1223            baselineTsserverLogs("configuredProjects", "failed lookup locations uses parent most node_modules directory", projectService);
1224        });
1225    });
1226
1227    describe("unittests:: tsserver:: ConfiguredProjects:: when reading tsconfig file fails", () => {
1228        it("should be tolerated without crashing the server", () => {
1229            const configFile = {
1230                path: `${tscWatch.projectRoot}/tsconfig.json`,
1231                content: ""
1232            };
1233            const file1 = {
1234                path: `${tscWatch.projectRoot}/file1.ts`,
1235                content: "let t = 10;"
1236            };
1237
1238            const host = createServerHost([file1, libFile, configFile]);
1239            const { session, events } = createSessionWithEventTracking<server.ConfigFileDiagEvent>(host, server.ConfigFileDiagEvent);
1240            const originalReadFile = host.readFile;
1241            host.readFile = f => {
1242                return f === configFile.path ?
1243                    undefined :
1244                    originalReadFile.call(host, f);
1245            };
1246            openFilesForSession([file1], session);
1247
1248            assert.deepEqual(events, [{
1249                eventName: server.ConfigFileDiagEvent,
1250                data: {
1251                    triggerFile: file1.path,
1252                    configFileName: configFile.path,
1253                    diagnostics: [
1254                        createCompilerDiagnostic(Diagnostics.Cannot_read_file_0, configFile.path)
1255                    ]
1256                }
1257            }]);
1258        });
1259    });
1260}
1261