• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1namespace ts.tscWatch {
2    describe("unittests:: tsc-watch:: program updates", () => {
3        const scenario = "programUpdates";
4        const configFilePath = "/a/b/tsconfig.json";
5        const configFile: File = {
6            path: configFilePath,
7            content: `{}`
8        };
9        verifyTscWatch({
10            scenario,
11            subScenario: "create watch without config file",
12            commandLineArgs: ["-w", "/a/b/c/app.ts"],
13            sys: () => {
14                const appFile: File = {
15                    path: "/a/b/c/app.ts",
16                    content: `
17                import {f} from "./module"
18                console.log(f)
19                `
20                };
21
22                const moduleFile: File = {
23                    path: "/a/b/c/module.d.ts",
24                    content: `export let x: number`
25                };
26                return createWatchedSystem([appFile, moduleFile, libFile]);
27            },
28            changes: emptyArray
29        });
30
31        verifyTscWatch({
32            scenario,
33            subScenario: "can handle tsconfig file name with difference casing",
34            commandLineArgs: ["-w", "-p", "/A/B/tsconfig.json"],
35            sys: () => {
36                const f1 = {
37                    path: "/a/b/app.ts",
38                    content: "let x = 1"
39                };
40                const config = {
41                    path: configFilePath,
42                    content: JSON.stringify({
43                        include: ["app.ts"]
44                    })
45                };
46                return createWatchedSystem([f1, libFile, config], { useCaseSensitiveFileNames: false });
47            },
48            changes: emptyArray
49        });
50
51        verifyTscWatch({
52            scenario,
53            subScenario: "create configured project without file list",
54            commandLineArgs: ["-w", "-p", configFilePath],
55            sys: () => {
56                const configFile: File = {
57                    path: configFilePath,
58                    content: `
59                {
60                    "compilerOptions": {},
61                    "exclude": [
62                        "e"
63                    ]
64                }`
65                };
66                const file1: File = {
67                    path: "/a/b/c/f1.ts",
68                    content: "let x = 1"
69                };
70                const file2: File = {
71                    path: "/a/b/d/f2.ts",
72                    content: "let y = 1"
73                };
74                const file3: File = {
75                    path: "/a/b/e/f3.ts",
76                    content: "let z = 1"
77                };
78                return createWatchedSystem([configFile, libFile, file1, file2, file3]);
79            },
80            changes: emptyArray
81        });
82
83        verifyTscWatch({
84            scenario,
85            subScenario: "add new files to a configured program without file list",
86            commandLineArgs: ["-w", "-p", configFilePath],
87            sys: () => createWatchedSystem([commonFile1, libFile, configFile]),
88            changes: [
89                {
90                    caption: "Create commonFile2",
91                    change: sys => sys.writeFile(commonFile2.path, commonFile2.content),
92                    timeouts: checkSingleTimeoutQueueLengthAndRun,
93                }
94            ]
95        });
96
97        verifyTscWatch({
98            scenario,
99            subScenario: "should ignore non-existing files specified in the config file",
100            commandLineArgs: ["-w", "-p", configFilePath],
101            sys: () => {
102                const configFile: File = {
103                    path: configFilePath,
104                    content: `{
105                    "compilerOptions": {},
106                    "files": [
107                        "commonFile1.ts",
108                        "commonFile3.ts"
109                    ]
110                }`
111                };
112                return createWatchedSystem([commonFile1, commonFile2, libFile, configFile]);
113            },
114            changes: emptyArray
115        });
116
117        verifyTscWatch({
118            scenario,
119            subScenario: "handle recreated files correctly",
120            commandLineArgs: ["-w", "-p", configFilePath, "--explainFiles"],
121            sys: () => {
122                return createWatchedSystem([libFile, commonFile1, commonFile2, configFile]);
123            },
124            changes: [
125                {
126                    caption: "change file to ensure signatures are updated",
127                    change: sys => sys.appendFile(commonFile2.path, ";let xy = 10;"),
128                    timeouts: checkSingleTimeoutQueueLengthAndRun,
129                },
130                {
131                    caption: "delete file2",
132                    change: sys => sys.deleteFile(commonFile2.path),
133                    timeouts: checkSingleTimeoutQueueLengthAndRun,
134                },
135                {
136                    caption: "recreate file2",
137                    change: sys => sys.writeFile(commonFile2.path, commonFile2.content),
138                    timeouts: checkSingleTimeoutQueueLengthAndRun,
139                }
140            ]
141        });
142
143        verifyTscWatch({
144            scenario,
145            subScenario: "handles the missing files - that were added to program because they were added with tripleSlashRefs",
146            commandLineArgs: ["-w", "/a/b/commonFile1.ts"],
147            sys: () => {
148                const file1: File = {
149                    path: commonFile1.path,
150                    content: `/// <reference path="commonFile2.ts"/>
151                    let x = y`
152                };
153                return createWatchedSystem([file1, libFile]);
154            },
155            changes: [
156                {
157                    caption: "create file2",
158                    change: sys => sys.writeFile(commonFile2.path, commonFile2.content),
159                    timeouts: checkSingleTimeoutQueueLengthAndRun,
160                }
161            ]
162        });
163
164        verifyTscWatch({
165            scenario,
166            subScenario: "should reflect change in config file",
167            commandLineArgs: ["-w", "-p", configFilePath, "--explainFiles"],
168            sys: () => {
169                const configFile: File = {
170                    path: configFilePath,
171                    content: `{
172                    "compilerOptions": {},
173                    "files": ["${commonFile1.path}", "${commonFile2.path}"]
174                }`
175                };
176                return createWatchedSystem([libFile, commonFile1, commonFile2, configFile]);
177            },
178            changes: [
179                {
180                    caption: "change file to ensure signatures are updated",
181                    change: sys => sys.appendFile(commonFile2.path, ";let xy = 10;"),
182                    timeouts: checkSingleTimeoutQueueLengthAndRun,
183                },
184                {
185                    caption: "Change config",
186                    change: sys => sys.writeFile(configFilePath, `{
187                        "compilerOptions": {},
188                        "files": ["${commonFile1.path}"]
189                    }`),
190                    timeouts: checkSingleTimeoutQueueLengthAndRun,
191                }
192            ]
193        });
194
195        verifyTscWatch({
196            scenario,
197            subScenario: "works correctly when config file is changed but its content havent",
198            commandLineArgs: ["-w", "-p", configFilePath],
199            sys: () => {
200                const configFile: File = {
201                    path: configFilePath,
202                    content: `{
203                        "compilerOptions": {},
204                        "files": ["${commonFile1.path}", "${commonFile2.path}"]
205                    }`
206                };
207                return createWatchedSystem([libFile, commonFile1, commonFile2, configFile]);
208            },
209            changes: [
210                {
211                    caption: "Modify config without changing content",
212                    change: sys => sys.modifyFile(configFilePath, `{
213                        "compilerOptions": {},
214                        "files": ["${commonFile1.path}", "${commonFile2.path}"]
215                    }`),
216                    timeouts: checkSingleTimeoutQueueLengthAndRun,
217                }
218            ]
219        });
220
221        verifyTscWatch({
222            scenario,
223            subScenario: "Updates diagnostics when '--noUnusedLabels' changes",
224            commandLineArgs: ["-w", "-p", "/tsconfig.json"],
225            sys: () => {
226                const aTs: File = {
227                    path: "/a.ts",
228                    content: "label: while (1) {}"
229                };
230                const tsconfig: File = {
231                    path: "/tsconfig.json",
232                    content: JSON.stringify({
233                        compilerOptions: { allowUnusedLabels: true }
234                    })
235                };
236                return createWatchedSystem([libFile, aTs, tsconfig]);
237            },
238            changes: [
239                {
240                    caption: "Disable  allowUnsusedLabels",
241                    change: sys => sys.modifyFile("/tsconfig.json", JSON.stringify({
242                        compilerOptions: { allowUnusedLabels: false }
243                    })),
244                    timeouts: checkSingleTimeoutQueueLengthAndRun
245                },
246                {
247                    caption: "Enable  allowUnsusedLabels",
248                    change: sys => sys.modifyFile("/tsconfig.json", JSON.stringify({
249                        compilerOptions: { allowUnusedLabels: true }
250                    })),
251                    timeouts: checkSingleTimeoutQueueLengthAndRun,
252                }
253            ]
254        });
255
256        verifyTscWatch({
257            scenario,
258            subScenario: "updates diagnostics and emit for decorators",
259            commandLineArgs: ["-w"],
260            sys: () => {
261                const aTs: File = {
262                    path: "/a.ts",
263                    content: `import {B} from './b'
264@((_) => {})
265export class A {
266    constructor(p: B) {}
267}`,
268                };
269                const bTs: File = {
270                    path: "/b.ts",
271                    content: `export class B {}`,
272                };
273                const tsconfig: File = {
274                    path: "/tsconfig.json",
275                    content: JSON.stringify({
276                        compilerOptions: { target: "es6", importsNotUsedAsValues: "error" }
277                    })
278                };
279                return createWatchedSystem([libFile, aTs, bTs, tsconfig]);
280            },
281            changes: [
282                {
283                    caption: "Enable experimentalDecorators",
284                    change: sys => sys.modifyFile("/tsconfig.json", JSON.stringify({
285                        compilerOptions: { target: "es6", importsNotUsedAsValues: "error", experimentalDecorators: true }
286                    })),
287                    timeouts: checkSingleTimeoutQueueLengthAndRun,
288
289                },
290                {
291                    caption: "Enable emitDecoratorMetadata",
292                    change: sys => sys.modifyFile("/tsconfig.json", JSON.stringify({
293                        compilerOptions: { target: "es6", importsNotUsedAsValues: "error", experimentalDecorators: true, emitDecoratorMetadata: true }
294                    })),
295                    timeouts: checkSingleTimeoutQueueLengthAndRun,
296                }
297            ]
298        });
299
300        verifyTscWatch({
301            scenario,
302            subScenario: "files explicitly excluded in config file",
303            commandLineArgs: ["-w", "-p", configFilePath],
304            sys: () => {
305                const configFile: File = {
306                    path: configFilePath,
307                    content: `{
308                    "compilerOptions": {},
309                    "exclude": ["/a/c"]
310                }`
311                };
312                const excludedFile1: File = {
313                    path: "/a/c/excluedFile1.ts",
314                    content: `let t = 1;`
315                };
316                return createWatchedSystem([libFile, commonFile1, commonFile2, excludedFile1, configFile]);
317            },
318            changes: emptyArray
319        });
320
321        verifyTscWatch({
322            scenario,
323            subScenario: "should properly handle module resolution changes in config file",
324            commandLineArgs: ["-w", "-p", configFilePath],
325            sys: () => {
326                const file1: File = {
327                    path: "/a/b/file1.ts",
328                    content: `import { T } from "module1";`
329                };
330                const nodeModuleFile: File = {
331                    path: "/a/b/node_modules/module1.ts",
332                    content: `export interface T {}`
333                };
334                const classicModuleFile: File = {
335                    path: "/a/module1.ts",
336                    content: `export interface T {}`
337                };
338                const configFile: File = {
339                    path: configFilePath,
340                    content: `{
341                    "compilerOptions": {
342                        "moduleResolution": "node"
343                    },
344                    "files": ["${file1.path}"]
345                }`
346                };
347                return createWatchedSystem([libFile, file1, nodeModuleFile, classicModuleFile, configFile]);
348            },
349            changes: [
350                {
351                    caption: "Change module resolution to classic",
352                    change: sys => sys.writeFile(configFile.path, `{
353                        "compilerOptions": {
354                            "moduleResolution": "classic"
355                        },
356                        "files": ["/a/b/file1.ts"]
357                    }`),
358                    timeouts: checkSingleTimeoutQueueLengthAndRun,
359                }
360            ]
361        });
362
363        verifyTscWatch({
364            scenario,
365            subScenario: "should tolerate config file errors and still try to build a project",
366            commandLineArgs: ["-w", "-p", configFilePath],
367            sys: () => {
368                const configFile: File = {
369                    path: configFilePath,
370                    content: `{
371                        "compilerOptions": {
372                            "module": "none",
373                            "allowAnything": true
374                        },
375                        "someOtherProperty": {}
376                    }`
377                };
378                return createWatchedSystem([commonFile1, commonFile2, libFile, configFile]);
379            },
380            changes: emptyArray
381        });
382
383        verifyTscWatch({
384            scenario,
385            subScenario: "changes in files are reflected in project structure",
386            commandLineArgs: ["-w", "/a/b/f1.ts", "--explainFiles"],
387            sys: () => {
388                const file1 = {
389                    path: "/a/b/f1.ts",
390                    content: `export * from "./f2"`
391                };
392                const file2 = {
393                    path: "/a/b/f2.ts",
394                    content: `export let x = 1`
395                };
396                const file3 = {
397                    path: "/a/c/f3.ts",
398                    content: `export let y = 1;`
399                };
400                return createWatchedSystem([file1, file2, file3, libFile]);
401            },
402            changes: [
403                {
404                    caption: "Modify f2 to include f3",
405                    // now inferred project should inclule file3
406                    change: sys => sys.modifyFile("/a/b/f2.ts", `export * from "../c/f3"`),
407                    timeouts: checkSingleTimeoutQueueLengthAndRun,
408                }
409            ]
410        });
411
412        verifyTscWatch({
413            scenario,
414            subScenario: "deleted files affect project structure",
415            commandLineArgs: ["-w", "/a/b/f1.ts", "--noImplicitAny"],
416            sys: () => {
417                const file1 = {
418                    path: "/a/b/f1.ts",
419                    content: `export * from "./f2"`
420                };
421                const file2 = {
422                    path: "/a/b/f2.ts",
423                    content: `export * from "../c/f3"`
424                };
425                const file3 = {
426                    path: "/a/c/f3.ts",
427                    content: `export let y = 1;`
428                };
429                return createWatchedSystem([file1, file2, file3, libFile]);
430            },
431            changes: [
432                {
433                    caption: "Delete f2",
434                    change: sys => sys.deleteFile("/a/b/f2.ts"),
435                    timeouts: checkSingleTimeoutQueueLengthAndRun,
436                }
437            ]
438        });
439
440        verifyTscWatch({
441            scenario,
442            subScenario: "deleted files affect project structure-2",
443            commandLineArgs: ["-w", "/a/b/f1.ts", "/a/c/f3.ts", "--noImplicitAny"],
444            sys: () => {
445                const file1 = {
446                    path: "/a/b/f1.ts",
447                    content: `export * from "./f2"`
448                };
449                const file2 = {
450                    path: "/a/b/f2.ts",
451                    content: `export * from "../c/f3"`
452                };
453                const file3 = {
454                    path: "/a/c/f3.ts",
455                    content: `export let y = 1;`
456                };
457                return createWatchedSystem([file1, file2, file3, libFile]);
458            },
459            changes: [
460                {
461                    caption: "Delete f2",
462                    change: sys => sys.deleteFile("/a/b/f2.ts"),
463                    timeouts: checkSingleTimeoutQueueLengthAndRun,
464                }
465            ]
466        });
467
468        verifyTscWatch({
469            scenario,
470            subScenario: "config file includes the file",
471            commandLineArgs: ["-w", "-p", "/a/c/tsconfig.json"],
472            sys: () => {
473                const file1 = {
474                    path: "/a/b/f1.ts",
475                    content: "export let x = 5"
476                };
477                const file2 = {
478                    path: "/a/c/f2.ts",
479                    content: `import {x} from "../b/f1"`
480                };
481                const file3 = {
482                    path: "/a/c/f3.ts",
483                    content: "export let y = 1"
484                };
485                const configFile = {
486                    path: "/a/c/tsconfig.json",
487                    content: JSON.stringify({ compilerOptions: {}, files: ["f2.ts", "f3.ts"] })
488                };
489                return createWatchedSystem([file1, file2, file3, libFile, configFile]);
490            },
491            changes: emptyArray
492        });
493
494        verifyTscWatch({
495            scenario,
496            subScenario: "change module to none",
497            commandLineArgs: ["-w", "-p", configFilePath],
498            sys: () => {
499                const file1 = {
500                    path: "/a/b/f1.ts",
501                    content: "export {}\ndeclare global {}"
502                };
503                return createWatchedSystem([file1, libFile, configFile]);
504            },
505            changes: [{
506                caption: "change `module` to 'none'",
507                timeouts: checkSingleTimeoutQueueLengthAndRun,
508                change: sys => {
509                    sys.writeFile(configFilePath, JSON.stringify({ compilerOptions: { module: "none" } }));
510                }
511            }]
512        });
513
514        it("two watch programs are not affected by each other", () => {
515            const file1 = {
516                path: "/a/b/f1.ts",
517                content: `
518                export * from "../c/f2";
519                export * from "../d/f3";`
520            };
521            const file2 = {
522                path: "/a/c/f2.ts",
523                content: "export let x = 1;"
524            };
525            const file3 = {
526                path: "/a/d/f3.ts",
527                content: "export let y = 1;"
528            };
529            const { sys, baseline, oldSnap, cb, getPrograms } = createBaseline(createWatchedSystem([libFile, file1, file2, file3]));
530            const host = createWatchCompilerHostOfFilesAndCompilerOptionsForBaseline({
531                rootFiles: [file2.path, file3.path],
532                system: sys,
533                options: { allowNonTsExtensions: true },
534                cb,
535                watchOptions: undefined
536            });
537            createWatchProgram(host);
538            baseline.push(`${sys.getExecutingFilePath()} --w ${file2.path} ${file3.path}`);
539            watchBaseline({
540                baseline,
541                getPrograms,
542                oldPrograms: emptyArray,
543                sys,
544                oldSnap,
545            });
546
547            const {cb: cb2, getPrograms: getPrograms2 } = commandLineCallbacks(sys);
548            const oldSnap2 = sys.snap();
549            baseline.push("createing separate watcher");
550            createWatchProgram(createWatchCompilerHostOfFilesAndCompilerOptionsForBaseline({
551                rootFiles:[file1.path],
552                system: sys,
553                options: { allowNonTsExtensions: true },
554                cb: cb2,
555                watchOptions: undefined
556            }));
557            watchBaseline({
558                baseline,
559                getPrograms: getPrograms2,
560                oldPrograms: emptyArray,
561                sys,
562                oldSnap: oldSnap2,
563            });
564
565            sys.checkTimeoutQueueLength(0);
566            baseline.push(`First program is not updated:: ${getPrograms() === emptyArray}`);
567            baseline.push(`Second program is not updated:: ${getPrograms2() === emptyArray}`);
568            Harness.Baseline.runBaseline(`tscWatch/${scenario}/two-watch-programs-are-not-affected-by-each-other.js`, baseline.join("\r\n"));
569        });
570
571        verifyTscWatch({
572            scenario,
573            subScenario: "can correctly update configured project when set of root files has changed (new file on disk)",
574            commandLineArgs: ["-w", "-p", configFilePath],
575            sys: () => {
576                const file1 = {
577                    path: "/a/b/f1.ts",
578                    content: "let x = 1"
579                };
580                return createWatchedSystem([file1, libFile, configFile]);
581            },
582            changes: [
583                {
584                    caption: "Write f2",
585                    change: sys => sys.writeFile("/a/b/f2.ts", "let y = 1"),
586                    timeouts: checkSingleTimeoutQueueLengthAndRun,
587                }
588            ]
589        });
590
591        verifyTscWatch({
592            scenario,
593            subScenario: "can correctly update configured project when set of root files has changed (new file in list of files)",
594            commandLineArgs: ["-w", "-p", configFilePath],
595            sys: () => {
596                const file1 = {
597                    path: "/a/b/f1.ts",
598                    content: "let x = 1"
599                };
600                const file2 = {
601                    path: "/a/b/f2.ts",
602                    content: "let y = 1"
603                };
604                const configFile = {
605                    path: configFilePath,
606                    content: JSON.stringify({ compilerOptions: {}, files: ["f1.ts"] })
607                };
608                return createWatchedSystem([file1, file2, libFile, configFile]);
609            },
610            changes: [
611                {
612                    caption: "Modify config to make f2 as root too",
613                    change: sys => sys.writeFile(configFilePath, JSON.stringify({ compilerOptions: {}, files: ["f1.ts", "f2.ts"] })),
614                    timeouts: checkSingleTimeoutQueueLengthAndRun,
615                }
616            ]
617        });
618
619        verifyTscWatch({
620            scenario,
621            subScenario: "correctly parses wild card directories from implicit glob when two keys differ only in directory seperator",
622            commandLineArgs: ["-w", "--extendedDiagnostics"],
623            sys: () => {
624                const file1 = {
625                    path: `${projectRoot}/f1.ts`,
626                    content: "export const x = 1"
627                };
628                const file2 = {
629                    path: `${projectRoot}/f2.ts`,
630                    content: "export const y = 1"
631                };
632                const configFile = {
633                    path: `${projectRoot}/tsconfig.json`,
634                    content: JSON.stringify({ compilerOptions: { composite: true }, include: ["./", "./**/*.json"] })
635                };
636                return createWatchedSystem([file1, file2, libFile, configFile], { currentDirectory: projectRoot });
637            },
638            changes: [
639                {
640                    caption: "Add new file",
641                    change: sys => sys.writeFile(`${projectRoot}/new-file.ts`, "export const z = 1;"),
642                    timeouts: sys => sys.checkTimeoutQueueLengthAndRun(1),
643                },
644                {
645                    caption: "Import new file",
646                    change: sys => sys.prependFile(`${projectRoot}/f1.ts`, `import { z } from "./new-file";`),
647                    timeouts: sys => sys.checkTimeoutQueueLengthAndRun(1),
648                }
649            ]
650        });
651
652        verifyTscWatch({
653            scenario,
654            subScenario: "can correctly update configured project when set of root files has changed through include",
655            commandLineArgs: ["-w", "-p", "."],
656            sys: () => {
657                const file1 = {
658                    path: `${projectRoot}/Project/file1.ts`,
659                    content: "export const x = 10;"
660                };
661                const configFile = {
662                    path: `${projectRoot}/Project/tsconfig.json`,
663                    content: JSON.stringify({ include: [".", "./**/*.json"] })
664                };
665                return createWatchedSystem([file1, libFile, configFile], { currentDirectory: `${projectRoot}/Project` });
666            },
667            changes: [
668                {
669                    caption: "Write file2",
670                    change: sys => sys.writeFile(`${projectRoot}/Project/file2.ts`, "export const y = 10;"),
671                    timeouts: checkSingleTimeoutQueueLengthAndRun
672                }
673            ]
674        });
675
676        verifyTscWatch({
677            scenario,
678            subScenario: "can update configured project when set of root files was not changed",
679            commandLineArgs: ["-w", "-p", configFilePath],
680            sys: () => {
681                const file1 = {
682                    path: "/a/b/f1.ts",
683                    content: "let x = 1"
684                };
685                const file2 = {
686                    path: "/a/b/f2.ts",
687                    content: "let y = 1"
688                };
689                const configFile = {
690                    path: configFilePath,
691                    content: JSON.stringify({ compilerOptions: {}, files: ["f1.ts", "f2.ts"] })
692                };
693                return createWatchedSystem([file1, file2, libFile, configFile]);
694            },
695            changes: [
696                {
697                    caption: "Modify config to set outFile option",
698                    change: sys => sys.writeFile(configFilePath, JSON.stringify({ compilerOptions: { outFile: "out.js" }, files: ["f1.ts", "f2.ts"] })),
699                    timeouts: checkSingleTimeoutQueueLengthAndRun,
700                }
701            ]
702        });
703
704        verifyTscWatch({
705            scenario,
706            subScenario: "file in files is deleted",
707            commandLineArgs: ["-w", "-p", configFilePath],
708            sys: () => {
709                const file1 = {
710                    path: "/a/b/f1.ts",
711                    content: "let x = 1"
712                };
713                const file2 = {
714                    path: "/a/b/f2.ts",
715                    content: "let y = 1"
716                };
717                const configFile = {
718                    path: configFilePath,
719                    content: JSON.stringify({ compilerOptions: {}, files: ["f1.ts", "f2.ts"] })
720                };
721                return createWatchedSystem([file1, file2, libFile, configFile]);
722            },
723            changes: [
724                {
725                    caption: "Delete f2",
726                    change: sys => sys.deleteFile("/a/b/f2.ts"),
727                    timeouts: checkSingleTimeoutQueueLengthAndRun,
728                }
729            ]
730        });
731
732        verifyTscWatch({
733            scenario,
734            subScenario: "config file is deleted",
735            commandLineArgs: ["-w", "-p", configFilePath],
736            sys: () => {
737                const file1 = {
738                    path: "/a/b/f1.ts",
739                    content: "let x = 1;"
740                };
741                const file2 = {
742                    path: "/a/b/f2.ts",
743                    content: "let y = 2;"
744                };
745                return createWatchedSystem([file1, file2, libFile, configFile]);
746            },
747            changes: [
748                {
749                    caption: "Delete config file",
750                    change: sys => sys.deleteFile(configFilePath),
751                    timeouts: checkSingleTimeoutQueueLengthAndRun,
752                }
753            ]
754        });
755
756        verifyTscWatch({
757            scenario,
758            subScenario: "Proper errors document is not contained in project",
759            commandLineArgs: ["-w", "-p", configFilePath],
760            sys: () => {
761                const file1 = {
762                    path: "/a/b/app.ts",
763                    content: ""
764                };
765                const corruptedConfig = {
766                    path: configFilePath,
767                    content: "{"
768                };
769                return createWatchedSystem([file1, libFile, corruptedConfig]);
770            },
771            changes: emptyArray
772        });
773
774        verifyTscWatch({
775            scenario,
776            subScenario: "correctly handles changes in lib section of config file",
777            commandLineArgs: ["-w", "-p", "/src/tsconfig.json"],
778            sys: () => {
779                const libES5 = {
780                    path: "/compiler/lib.es5.d.ts",
781                    content: `${libFile.content}
782declare const eval: any`
783                };
784                const libES2015Promise = {
785                    path: "/compiler/lib.es2015.promise.d.ts",
786                    content: `declare class Promise<T> {}`
787                };
788                const app = {
789                    path: "/src/app.ts",
790                    content: "var x: Promise<string>;"
791                };
792                const config1 = {
793                    path: "/src/tsconfig.json",
794                    content: JSON.stringify(
795                        {
796                            compilerOptions: {
797                                module: "commonjs",
798                                target: "es5",
799                                noImplicitAny: true,
800                                sourceMap: false,
801                                lib: [
802                                    "es5"
803                                ]
804                            }
805                        })
806                };
807                return createWatchedSystem([libES5, libES2015Promise, app, config1], { executingFilePath: "/compiler/tsc.js" });
808            },
809            changes: [
810                {
811                    caption: "Change the lib in config",
812                    change: sys => sys.writeFile("/src/tsconfig.json", JSON.stringify(
813                        {
814                            compilerOptions: {
815                                module: "commonjs",
816                                target: "es5",
817                                noImplicitAny: true,
818                                sourceMap: false,
819                                lib: [
820                                    "es5",
821                                    "es2015.promise"
822                                ]
823                            }
824                        })
825                    ),
826                    timeouts: checkSingleTimeoutQueueLengthAndRun,
827                }
828            ]
829        });
830
831        verifyTscWatch({
832            scenario,
833            subScenario: "should handle non-existing directories in config file",
834            commandLineArgs: ["-w", "-p", "/a/tsconfig.json"],
835            sys: () => {
836                const f = {
837                    path: "/a/src/app.ts",
838                    content: "let x = 1;"
839                };
840                const config = {
841                    path: "/a/tsconfig.json",
842                    content: JSON.stringify({
843                        compilerOptions: {},
844                        include: [
845                            "src/**/*",
846                            "notexistingfolder/*"
847                        ]
848                    })
849                };
850                return createWatchedSystem([f, config, libFile]);
851            },
852            changes: emptyArray
853        });
854
855        function runQueuedTimeoutCallbacksTwice(sys: WatchedSystem) {
856            sys.runQueuedTimeoutCallbacks(); // Scheduled invalidation of resolutions
857            sys.runQueuedTimeoutCallbacks(); // Actual update
858        }
859
860        const changeModuleFileToModuleFile1: TscWatchCompileChange = {
861            caption: "Rename moduleFile to moduleFile1",
862            change: sys => {
863                sys.renameFile("/a/b/moduleFile.ts", "/a/b/moduleFile1.ts");
864                sys.deleteFile("/a/b/moduleFile.js");
865            },
866            timeouts: runQueuedTimeoutCallbacksTwice
867        };
868        const changeModuleFile1ToModuleFile: TscWatchCompileChange = {
869            caption: "Rename moduleFile1 back to moduleFile",
870            change: sys => sys.renameFile("/a/b/moduleFile1.ts", "/a/b/moduleFile.ts"),
871            timeouts: runQueuedTimeoutCallbacksTwice,
872        };
873
874        verifyTscWatch({
875            scenario,
876            subScenario: "rename a module file and rename back should restore the states for inferred projects",
877            commandLineArgs: ["-w", "/a/b/file1.ts"],
878            sys: () => {
879                const moduleFile = {
880                    path: "/a/b/moduleFile.ts",
881                    content: "export function bar() { };"
882                };
883                const file1 = {
884                    path: "/a/b/file1.ts",
885                    content: 'import * as T from "./moduleFile"; T.bar();'
886                };
887                return createWatchedSystem([moduleFile, file1, libFile]);
888            },
889            changes: [
890                changeModuleFileToModuleFile1,
891                changeModuleFile1ToModuleFile
892            ]
893        });
894
895        verifyTscWatch({
896            scenario,
897            subScenario: "rename a module file and rename back should restore the states for configured projects",
898            commandLineArgs: ["-w", "-p", configFilePath],
899            sys: () => {
900                const moduleFile = {
901                    path: "/a/b/moduleFile.ts",
902                    content: "export function bar() { };"
903                };
904                const file1 = {
905                    path: "/a/b/file1.ts",
906                    content: 'import * as T from "./moduleFile"; T.bar();'
907                };
908                return createWatchedSystem([moduleFile, file1, configFile, libFile]);
909            },
910            changes: [
911                changeModuleFileToModuleFile1,
912                changeModuleFile1ToModuleFile
913            ]
914        });
915
916        verifyTscWatch({
917            scenario,
918            subScenario: "types should load from config file path if config exists",
919            commandLineArgs: ["-w", "-p", configFilePath],
920            sys: () => {
921                const f1 = {
922                    path: "/a/b/app.ts",
923                    content: "let x = 1"
924                };
925                const config = {
926                    path: configFilePath,
927                    content: JSON.stringify({ compilerOptions: { types: ["node"], typeRoots: [] } })
928                };
929                const node = {
930                    path: "/a/b/node_modules/@types/node/index.d.ts",
931                    content: "declare var process: any"
932                };
933                const cwd = {
934                    path: "/a/c"
935                };
936                return createWatchedSystem([f1, config, node, cwd, libFile], { currentDirectory: cwd.path });
937            },
938            changes: emptyArray
939        });
940
941        verifyTscWatch({
942            scenario,
943            subScenario: "add the missing module file for inferred project-should remove the module not found error",
944            commandLineArgs: ["-w", "/a/b/file1.ts"],
945            sys: () => {
946                const file1 = {
947                    path: "/a/b/file1.ts",
948                    content: 'import * as T from "./moduleFile"; T.bar();'
949                };
950                return createWatchedSystem([file1, libFile]);
951            },
952            changes: [
953                {
954                    caption: "Create module file",
955                    change: sys => sys.writeFile("/a/b/moduleFile.ts", "export function bar() { }"),
956                    timeouts: runQueuedTimeoutCallbacksTwice,
957                }
958            ]
959        });
960
961        verifyTscWatch({
962            scenario,
963            subScenario: "Configure file diagnostics events are generated when the config file has errors",
964            commandLineArgs: ["-w", "-p", configFilePath],
965            sys: () => {
966                const file = {
967                    path: "/a/b/app.ts",
968                    content: "let x = 10"
969                };
970                const configFile = {
971                    path: configFilePath,
972                    content: `{
973                        "compilerOptions": {
974                            "foo": "bar",
975                            "allowJS": true
976                        }
977                    }`
978                };
979                return createWatchedSystem([file, configFile, libFile]);
980            },
981            changes: emptyArray
982        });
983
984        verifyTscWatch({
985            scenario,
986            subScenario: "if config file doesnt have errors, they are not reported",
987            commandLineArgs: ["-w", "-p", configFilePath],
988            sys: () => {
989                const file = {
990                    path: "/a/b/app.ts",
991                    content: "let x = 10"
992                };
993                const configFile = {
994                    path: configFilePath,
995                    content: `{
996                        "compilerOptions": {}
997                    }`
998                };
999                return createWatchedSystem([file, configFile, libFile]);
1000            },
1001            changes: emptyArray
1002        });
1003
1004        verifyTscWatch({
1005            scenario,
1006            subScenario: "Reports errors when the config file changes",
1007            commandLineArgs: ["-w", "-p", configFilePath],
1008            sys: () => {
1009                const file = {
1010                    path: "/a/b/app.ts",
1011                    content: "let x = 10"
1012                };
1013                return createWatchedSystem([file, configFile, libFile]);
1014            },
1015            changes: [
1016                {
1017                    caption: "change config file to add error",
1018                    change: sys => sys.writeFile(configFilePath, `{
1019                        "compilerOptions": {
1020                            "haha": 123
1021                        }
1022                    }`),
1023                    timeouts: runQueuedTimeoutCallbacks,
1024                },
1025                {
1026                    caption: "change config file to remove error",
1027                    change: sys => sys.writeFile(configFilePath, `{
1028                        "compilerOptions": {
1029                        }
1030                    }`),
1031                    timeouts: runQueuedTimeoutCallbacks,
1032                }
1033            ]
1034        });
1035
1036        verifyTscWatch({
1037            scenario,
1038            subScenario: "non-existing directories listed in config file input array should be tolerated without crashing the server",
1039            commandLineArgs: ["-w", "-p", configFilePath],
1040            sys: () => {
1041                const configFile = {
1042                    path: configFilePath,
1043                    content: `{
1044                        "compilerOptions": {},
1045                        "include": ["app/*", "test/**/*", "something"]
1046                    }`
1047                };
1048                const file1 = {
1049                    path: "/a/b/file1.ts",
1050                    content: "let t = 10;"
1051                };
1052                return createWatchedSystem([file1, configFile, libFile]);
1053            },
1054            changes: emptyArray
1055        });
1056
1057        verifyTscWatch({
1058            scenario,
1059            subScenario: "non-existing directories listed in config file input array should be able to handle @types if input file list is empty",
1060            commandLineArgs: ["-w", "-p", "/a/tsconfig.json"],
1061            sys: () => {
1062                const f = {
1063                    path: "/a/app.ts",
1064                    content: "let x = 1"
1065                };
1066                const config = {
1067                    path: "/a/tsconfig.json",
1068                    content: JSON.stringify({
1069                        compiler: {},
1070                        files: []
1071                    })
1072                };
1073                const t1 = {
1074                    path: "/a/node_modules/@types/typings/index.d.ts",
1075                    content: `export * from "./lib"`
1076                };
1077                const t2 = {
1078                    path: "/a/node_modules/@types/typings/lib.d.ts",
1079                    content: `export const x: number`
1080                };
1081                return createWatchedSystem([f, config, t1, t2, libFile], { currentDirectory: getDirectoryPath(f.path) });
1082            },
1083            changes: emptyArray
1084        });
1085
1086        it("should support files without extensions", () => {
1087            const f = {
1088                path: "/a/compile",
1089                content: "let x = 1"
1090            };
1091            const { sys, baseline, oldSnap, cb, getPrograms } = createBaseline(createWatchedSystem([f, libFile]));
1092            const watch = createWatchProgram(createWatchCompilerHostOfFilesAndCompilerOptionsForBaseline({
1093                rootFiles: [f.path],
1094                system: sys,
1095                options: { allowNonTsExtensions: true },
1096                cb,
1097                watchOptions: undefined
1098            }));
1099            runWatchBaseline({
1100                scenario,
1101                subScenario: "should support files without extensions",
1102                commandLineArgs: ["--w", f.path],
1103                sys,
1104                baseline,
1105                oldSnap,
1106                getPrograms,
1107                changes: emptyArray,
1108                watchOrSolution: watch
1109            });
1110        });
1111
1112        verifyTscWatch({
1113            scenario,
1114            subScenario: "Options Diagnostic locations reported correctly with changes in configFile contents when options change",
1115            commandLineArgs: ["-w", "-p", configFilePath],
1116            sys: () => {
1117                const file = {
1118                    path: "/a/b/app.ts",
1119                    content: "let x = 10"
1120                };
1121                const configFile = {
1122                    path: configFilePath,
1123                    content: `
1124{
1125    // comment
1126    // More comment
1127    "compilerOptions": {
1128        "inlineSourceMap": true,
1129        "mapRoot": "./"
1130    }
1131}`
1132                };
1133                return createWatchedSystem([file, libFile, configFile]);
1134            },
1135            changes: [
1136                {
1137                    caption: "Remove the comment from config file",
1138                    change: sys => sys.writeFile(configFilePath, `
1139{
1140    "compilerOptions": {
1141        "inlineSourceMap": true,
1142        "mapRoot": "./"
1143    }
1144}`),
1145                    timeouts: runQueuedTimeoutCallbacks,
1146                }
1147            ]
1148        });
1149
1150        describe("should not trigger recompilation because of program emit", () => {
1151            function verifyWithOptions(subScenario: string, options: CompilerOptions) {
1152                verifyTscWatch({
1153                    scenario,
1154                    subScenario: `should not trigger recompilation because of program emit/${subScenario}`,
1155                    commandLineArgs: ["-w", "-p", `${projectRoot}/tsconfig.json`],
1156                    sys: () => {
1157                        const file1: File = {
1158                            path: `${projectRoot}/file1.ts`,
1159                            content: "export const c = 30;"
1160                        };
1161                        const file2: File = {
1162                            path: `${projectRoot}/src/file2.ts`,
1163                            content: `import {c} from "file1"; export const d = 30;`
1164                        };
1165                        const tsconfig: File = {
1166                            path: `${projectRoot}/tsconfig.json`,
1167                            content: generateTSConfig(options, emptyArray, "\n")
1168                        };
1169                        return createWatchedSystem([file1, file2, libFile, tsconfig], { currentDirectory: projectRoot });
1170                    },
1171                    changes: [
1172                        noopChange,
1173                        {
1174                            caption: "Add new file",
1175                            change: sys => sys.writeFile(`${projectRoot}/src/file3.ts`, `export const y = 10;`),
1176                            timeouts: sys => sys.checkTimeoutQueueLengthAndRun(2), // To update program and failed lookups
1177                        },
1178                        noopChange,
1179                    ]
1180                });
1181            }
1182
1183            verifyWithOptions(
1184                "without outDir or outFile is specified",
1185                { module: ModuleKind.AMD }
1186            );
1187
1188            verifyWithOptions(
1189                "with outFile",
1190                { module: ModuleKind.AMD, outFile: "build/outFile.js" }
1191            );
1192
1193            verifyWithOptions(
1194                "when outDir is specified",
1195                { module: ModuleKind.AMD, outDir: "build" }
1196            );
1197
1198            verifyWithOptions(
1199                "without outDir or outFile is specified with declaration enabled",
1200                { module: ModuleKind.AMD, declaration: true }
1201            );
1202
1203            verifyWithOptions(
1204                "when outDir and declarationDir is specified",
1205                { module: ModuleKind.AMD, outDir: "build", declaration: true, declarationDir: "decls" }
1206            );
1207
1208            verifyWithOptions(
1209                "declarationDir is specified",
1210                { module: ModuleKind.AMD, declaration: true, declarationDir: "decls" }
1211            );
1212        });
1213
1214        verifyTscWatch({
1215            scenario,
1216            subScenario: "shouldnt report error about unused function incorrectly when file changes from global to module",
1217            commandLineArgs: ["-w", "/a/b/file.ts", "--noUnusedLocals"],
1218            sys: () => {
1219                const file: File = {
1220                    path: "/a/b/file.ts",
1221                    content: `function one() {}
1222function two() {
1223    return function three() {
1224        one();
1225    }
1226}`
1227                };
1228                return createWatchedSystem([file, libFile]);
1229            },
1230            changes: [
1231                {
1232                    caption: "Change file to module",
1233                    change: sys => sys.writeFile("/a/b/file.ts", `function one() {}
1234export function two() {
1235    return function three() {
1236        one();
1237    }
1238}`),
1239                    timeouts: runQueuedTimeoutCallbacks,
1240
1241                }
1242            ]
1243        });
1244
1245        verifyTscWatch({
1246            scenario,
1247            subScenario: "watched files when file is deleted and new file is added as part of change",
1248            commandLineArgs: ["-w", "-p", "/home/username/project/tsconfig.json"],
1249            sys: () => {
1250                const projectLocation = "/home/username/project";
1251                const file: File = {
1252                    path: `${projectLocation}/src/file1.ts`,
1253                    content: "var a = 10;"
1254                };
1255                const configFile: File = {
1256                    path: `${projectLocation}/tsconfig.json`,
1257                    content: "{}"
1258                };
1259                return createWatchedSystem([file, libFile, configFile]);
1260            },
1261            changes: [
1262                {
1263                    caption: "Rename file1 to file2",
1264                    change: sys => sys.renameFile("/home/username/project/src/file1.ts", "/home/username/project/src/file2.ts"),
1265                    timeouts: runQueuedTimeoutCallbacks,
1266                }
1267            ]
1268        });
1269
1270        function changeParameterTypeOfBFile(parameterName: string, toType: string): TscWatchCompileChange {
1271            return {
1272                caption: `Changed ${parameterName} type to ${toType}`,
1273                change: sys => replaceFileText(sys, `${projectRoot}/b.ts`, new RegExp(`${parameterName}\: [a-z]*`), `${parameterName}: ${toType}`),
1274                timeouts: runQueuedTimeoutCallbacks,
1275            };
1276        }
1277
1278        verifyTscWatch({
1279            scenario,
1280            subScenario: "updates errors correctly when declaration emit is disabled in compiler options",
1281            commandLineArgs: ["-w"],
1282            sys: () => {
1283                const aFile: File = {
1284                    path: `${projectRoot}/a.ts`,
1285                    content: `import test from './b';
1286test(4, 5);`
1287                };
1288                const bFile: File = {
1289                    path: `${projectRoot}/b.ts`,
1290                    content: `function test(x: number, y: number) {
1291    return x + y / 5;
1292}
1293export default test;`
1294                };
1295                const tsconfigFile: File = {
1296                    path: `${projectRoot}/tsconfig.json`,
1297                    content: JSON.stringify({
1298                        compilerOptions: {
1299                            module: "commonjs",
1300                            noEmit: true,
1301                            strict: true,
1302                        }
1303                    })
1304                };
1305                return createWatchedSystem([aFile, bFile, libFile, tsconfigFile], { currentDirectory: projectRoot });
1306            },
1307            changes: [
1308                changeParameterTypeOfBFile("x", "string"),
1309                changeParameterTypeOfBFile("x", "number"),
1310                changeParameterTypeOfBFile("y", "string"),
1311                changeParameterTypeOfBFile("y", "number"),
1312            ]
1313        });
1314
1315        verifyTscWatch({
1316            scenario,
1317            subScenario: "updates errors when strictNullChecks changes",
1318            commandLineArgs: ["-w"],
1319            sys: () => {
1320                const aFile: File = {
1321                    path: `${projectRoot}/a.ts`,
1322                    content: `declare function foo(): null | { hello: any };
1323foo().hello`
1324                };
1325                const config: File = {
1326                    path: `${projectRoot}/tsconfig.json`,
1327                    content: JSON.stringify({ compilerOptions: {} })
1328                };
1329                return createWatchedSystem([aFile, config, libFile], { currentDirectory: projectRoot });
1330            },
1331            changes: [
1332                {
1333                    caption: "Enable strict null checks",
1334                    change: sys => sys.writeFile(`${projectRoot}/tsconfig.json`, JSON.stringify({ compilerOptions: { strictNullChecks: true } })),
1335                    timeouts: runQueuedTimeoutCallbacks,
1336                },
1337                {
1338                    caption: "Set always strict false",
1339                    change: sys => sys.writeFile(`${projectRoot}/tsconfig.json`, JSON.stringify({ compilerOptions: { strict: true, alwaysStrict: false } })), // Avoid changing 'alwaysStrict' or must re-bind
1340                    timeouts: runQueuedTimeoutCallbacks,
1341                },
1342                {
1343                    caption: "Disable strict",
1344                    change: sys => sys.writeFile(`${projectRoot}/tsconfig.json`, JSON.stringify({ compilerOptions: {} })),
1345                    timeouts: runQueuedTimeoutCallbacks,
1346                },
1347            ]
1348        });
1349
1350        verifyTscWatch({
1351            scenario,
1352            subScenario: "updates errors when noErrorTruncation changes",
1353            commandLineArgs: ["-w"],
1354            sys: () => {
1355                const aFile: File = {
1356                    path: `${projectRoot}/a.ts`,
1357                    content: `declare var v: {
1358    reallyLongPropertyName1: string | number | boolean | object | symbol | bigint;
1359    reallyLongPropertyName2: string | number | boolean | object | symbol | bigint;
1360    reallyLongPropertyName3: string | number | boolean | object | symbol | bigint;
1361    reallyLongPropertyName4: string | number | boolean | object | symbol | bigint;
1362    reallyLongPropertyName5: string | number | boolean | object | symbol | bigint;
1363    reallyLongPropertyName6: string | number | boolean | object | symbol | bigint;
1364    reallyLongPropertyName7: string | number | boolean | object | symbol | bigint;
1365};
1366v === 'foo';`
1367                };
1368                const config: File = {
1369                    path: `${projectRoot}/tsconfig.json`,
1370                    content: JSON.stringify({ compilerOptions: {} })
1371                };
1372                return createWatchedSystem([aFile, config, libFile], { currentDirectory: projectRoot });
1373            },
1374            changes: [
1375                {
1376                    caption: "Enable noErrorTruncation",
1377                    change: sys => sys.writeFile(`${projectRoot}/tsconfig.json`, JSON.stringify({ compilerOptions: { noErrorTruncation: true } })),
1378                    timeouts: runQueuedTimeoutCallbacks,
1379                },
1380            ]
1381        });
1382
1383        verifyTscWatch({
1384            scenario,
1385            subScenario: "updates diagnostics and emit when useDefineForClassFields changes",
1386            commandLineArgs: ["-w"],
1387            sys: () => {
1388                const aFile: File = {
1389                    path: `/a.ts`,
1390                    content: `class C { get prop() { return 1; } }
1391class D extends C { prop = 1; }`
1392                };
1393                const config: File = {
1394                    path: `/tsconfig.json`,
1395                    content: JSON.stringify({ compilerOptions: { target: "es6" } })
1396                };
1397                return createWatchedSystem([aFile, config, libFile]);
1398            },
1399            changes: [
1400                {
1401                    caption: "Enable useDefineForClassFields",
1402                    change: sys => sys.writeFile(`/tsconfig.json`, JSON.stringify({ compilerOptions: { target: "es6", useDefineForClassFields: true } })),
1403                    timeouts: runQueuedTimeoutCallbacks,
1404                },
1405            ]
1406        });
1407
1408        verifyTscWatch({
1409            scenario,
1410            subScenario: "updates errors and emit when importsNotUsedAsValues changes",
1411            commandLineArgs: ["-w"],
1412            sys: () => {
1413                const aFile: File = {
1414                    path: `${projectRoot}/a.ts`,
1415                    content: `export class C {}`
1416                };
1417                const bFile: File = {
1418                    path: `${projectRoot}/b.ts`,
1419                    content: `import {C} from './a';
1420export function f(p: C) { return p; }`
1421                };
1422                const config: File = {
1423                    path: `${projectRoot}/tsconfig.json`,
1424                    content: JSON.stringify({ compilerOptions: {} })
1425                };
1426                return createWatchedSystem([aFile, bFile, config, libFile], { currentDirectory: projectRoot });
1427            },
1428            changes: [
1429                {
1430                    caption: 'Set to "remove"',
1431                    change: sys => sys.writeFile(`${projectRoot}/tsconfig.json`, JSON.stringify({ compilerOptions: { importsNotUsedAsValues: "remove" } })),
1432                    timeouts: runQueuedTimeoutCallbacks,
1433                },
1434                {
1435                    caption: 'Set to "error"',
1436                    change: sys => sys.writeFile(`${projectRoot}/tsconfig.json`, JSON.stringify({ compilerOptions: { importsNotUsedAsValues: "error" } })),
1437                    timeouts: runQueuedTimeoutCallbacks,
1438                },
1439                {
1440                    caption: 'Set to "preserve"',
1441                    change: sys => sys.writeFile(`${projectRoot}/tsconfig.json`, JSON.stringify({ compilerOptions: { importsNotUsedAsValues: "preserve" } })),
1442                    timeouts: runQueuedTimeoutCallbacks,
1443                },
1444            ]
1445        });
1446
1447
1448        verifyTscWatch({
1449            scenario,
1450            subScenario: "updates errors when forceConsistentCasingInFileNames changes",
1451            commandLineArgs: ["-w"],
1452            sys: () => {
1453                const aFile: File = {
1454                    path: `/a.ts`,
1455                    content: `export class C {}`
1456                };
1457                const bFile: File = {
1458                    path: `/b.ts`,
1459                    content: `import {C} from './a'; import * as A from './A';`
1460                };
1461                const config: File = {
1462                    path: `/tsconfig.json`,
1463                    content: JSON.stringify({ compilerOptions: {} })
1464                };
1465                return createWatchedSystem([aFile, bFile, config, libFile], { useCaseSensitiveFileNames: false });
1466            },
1467            changes: [
1468                {
1469                    caption: "Enable forceConsistentCasingInFileNames",
1470                    change: sys => sys.writeFile(`/tsconfig.json`, JSON.stringify({ compilerOptions: { forceConsistentCasingInFileNames: true } })),
1471                    timeouts: runQueuedTimeoutCallbacks,
1472                },
1473            ]
1474        });
1475
1476        verifyTscWatch({
1477            scenario,
1478            subScenario: "updates moduleResolution when resolveJsonModule changes",
1479            commandLineArgs: ["-w"],
1480            sys: () => {
1481                const aFile: File = {
1482                    path: `${projectRoot}/a.ts`,
1483                    content: `import * as data from './data.json'`
1484                };
1485                const jsonFile: File = {
1486                    path: `${projectRoot}/data.json`,
1487                    content: `{ "foo": 1 }`
1488                };
1489                const config: File = {
1490                    path: `${projectRoot}/tsconfig.json`,
1491                    content: JSON.stringify({ compilerOptions: { moduleResolution: "node" } })
1492                };
1493                return createWatchedSystem([aFile, jsonFile, config, libFile], { currentDirectory: projectRoot });
1494            },
1495            changes: [
1496                {
1497                    caption: "Enable resolveJsonModule",
1498                    change: sys => sys.writeFile(`${projectRoot}/tsconfig.json`, JSON.stringify({ compilerOptions: { moduleResolution: "node", resolveJsonModule: true } })),
1499                    timeouts: runQueuedTimeoutCallbacks,
1500                },
1501            ]
1502        });
1503
1504        verifyTscWatch({
1505            scenario,
1506            subScenario: "updates errors when ambient modules of program changes",
1507            commandLineArgs: ["-w"],
1508            sys: () => {
1509                const aFile: File = {
1510                    path: `${projectRoot}/a.ts`,
1511                    content: `declare module 'a' {
1512  type foo = number;
1513}`
1514                };
1515                const config: File = {
1516                    path: `${projectRoot}/tsconfig.json`,
1517                    content: "{}"
1518                };
1519                return createWatchedSystem([aFile, config, libFile], { currentDirectory: projectRoot });
1520            },
1521            changes: [
1522                {
1523                    caption: "Create b.ts with same content",
1524                    // Create bts with same file contents
1525                    change: sys => sys.writeFile(`${projectRoot}/b.ts`, `declare module 'a' {
1526  type foo = number;
1527}`),
1528                    timeouts: runQueuedTimeoutCallbacks,
1529                },
1530                {
1531                    caption: "Delete b.ts",
1532                    change: sys => sys.deleteFile(`${projectRoot}/b.ts`),
1533                    timeouts: runQueuedTimeoutCallbacks,
1534                },
1535            ]
1536        });
1537
1538        describe("updates errors in lib file", () => {
1539            const field = "fullscreen";
1540            const fieldWithoutReadonly = `interface Document {
1541    ${field}: boolean;
1542}`;
1543
1544            const libFileWithDocument: File = {
1545                path: libFile.path,
1546                content: `${libFile.content}
1547interface Document {
1548    readonly ${field}: boolean;
1549}`
1550            };
1551
1552            function verifyLibFileErrorsWith(subScenario: string, aFile: File) {
1553                function verifyLibErrors(subScenario: string, commandLineOptions: readonly string[]) {
1554                    verifyTscWatch({
1555                        scenario,
1556                        subScenario: `updates errors in lib file/${subScenario}`,
1557                        commandLineArgs: ["-w", aFile.path, ...commandLineOptions],
1558                        sys: () => createWatchedSystem([aFile, libFileWithDocument], { currentDirectory: projectRoot }),
1559                        changes: [
1560                            {
1561                                caption: "Remove document declaration from file",
1562                                change: sys => sys.writeFile(aFile.path, aFile.content.replace(fieldWithoutReadonly, "var x: string;")),
1563                                timeouts: runQueuedTimeoutCallbacks,
1564                            },
1565                            {
1566                                caption: "Rever the file to contain document declaration",
1567                                change: sys => sys.writeFile(aFile.path, aFile.content),
1568                                timeouts: runQueuedTimeoutCallbacks,
1569                            },
1570                        ]
1571                    });
1572                }
1573
1574                verifyLibErrors(`${subScenario}/with default options`, emptyArray);
1575                verifyLibErrors(`${subScenario}/with skipLibCheck`, ["--skipLibCheck"]);
1576                verifyLibErrors(`${subScenario}/with skipDefaultLibCheck`, ["--skipDefaultLibCheck"]);
1577            }
1578
1579            describe("when non module file changes", () => {
1580                const aFile: File = {
1581                    path: `${projectRoot}/a.ts`,
1582                    content: `${fieldWithoutReadonly}
1583var y: number;`
1584                };
1585                verifyLibFileErrorsWith("when non module file changes", aFile);
1586            });
1587
1588            describe("when module file with global definitions changes", () => {
1589                const aFile: File = {
1590                    path: `${projectRoot}/a.ts`,
1591                    content: `export {}
1592declare global {
1593${fieldWithoutReadonly}
1594var y: number;
1595}`
1596                };
1597                verifyLibFileErrorsWith("when module file with global definitions changes", aFile);
1598            });
1599        });
1600
1601        function changeWhenLibCheckChanges(compilerOptions: CompilerOptions): TscWatchCompileChange {
1602            const configFileContent = JSON.stringify({ compilerOptions });
1603            return {
1604                caption: `Changing config to ${configFileContent}`,
1605                change: sys => sys.writeFile(`${projectRoot}/tsconfig.json`, configFileContent),
1606                timeouts: runQueuedTimeoutCallbacks,
1607            };
1608        }
1609
1610        verifyTscWatch({
1611            scenario,
1612            subScenario: "when skipLibCheck and skipDefaultLibCheck changes",
1613            commandLineArgs: ["-w"],
1614            sys: () => {
1615                const field = "fullscreen";
1616                const aFile: File = {
1617                    path: `${projectRoot}/a.ts`,
1618                    content: `interface Document {
1619    ${field}: boolean;
1620}`
1621                };
1622                const bFile: File = {
1623                    path: `${projectRoot}/b.d.ts`,
1624                    content: `interface Document {
1625    ${field}: boolean;
1626}`
1627                };
1628                const libFileWithDocument: File = {
1629                    path: libFile.path,
1630                    content: `${libFile.content}
1631interface Document {
1632    readonly ${field}: boolean;
1633}`
1634                };
1635                const configFile: File = {
1636                    path: `${projectRoot}/tsconfig.json`,
1637                    content: "{}"
1638                };
1639                return createWatchedSystem([aFile, bFile, configFile, libFileWithDocument], { currentDirectory: projectRoot });
1640            },
1641            changes: [
1642                changeWhenLibCheckChanges({ skipLibCheck: true }),
1643                changeWhenLibCheckChanges({ skipDefaultLibCheck: true }),
1644                changeWhenLibCheckChanges({}),
1645                changeWhenLibCheckChanges({ skipDefaultLibCheck: true }),
1646                changeWhenLibCheckChanges({ skipLibCheck: true }),
1647                changeWhenLibCheckChanges({}),
1648            ]
1649        });
1650
1651        verifyTscWatch({
1652            scenario,
1653            subScenario: "reports errors correctly with isolatedModules",
1654            commandLineArgs: ["-w"],
1655            sys: () => {
1656                const aFile: File = {
1657                    path: `${projectRoot}/a.ts`,
1658                    content: `export const a: string = "";`
1659                };
1660                const bFile: File = {
1661                    path: `${projectRoot}/b.ts`,
1662                    content: `import { a } from "./a";
1663const b: string = a;`
1664                };
1665                const configFile: File = {
1666                    path: `${projectRoot}/tsconfig.json`,
1667                    content: JSON.stringify({
1668                        compilerOptions: {
1669                            isolatedModules: true
1670                        }
1671                    })
1672                };
1673                return createWatchedSystem([aFile, bFile, configFile, libFile], { currentDirectory: projectRoot });
1674            },
1675            changes: [
1676                {
1677                    caption: "Change shape of a",
1678                    change: sys => sys.writeFile(`${projectRoot}/a.ts`, `export const a: number = 1`),
1679                    timeouts: runQueuedTimeoutCallbacks,
1680                },
1681            ]
1682        });
1683
1684        verifyTscWatch({
1685            scenario,
1686            subScenario: "reports errors correctly with file not in rootDir",
1687            commandLineArgs: ["-w"],
1688            sys: () => {
1689                const aFile: File = {
1690                    path: `${projectRoot}/a.ts`,
1691                    content: `import { x } from "../b";`
1692                };
1693                const bFile: File = {
1694                    path: `/user/username/projects/b.ts`,
1695                    content: `export const x = 10;`
1696                };
1697                const configFile: File = {
1698                    path: `${projectRoot}/tsconfig.json`,
1699                    content: JSON.stringify({
1700                        compilerOptions: {
1701                            rootDir: ".",
1702                            outDir: "lib"
1703                        }
1704                    })
1705                };
1706                return createWatchedSystem([aFile, bFile, configFile, libFile], { currentDirectory: projectRoot });
1707            },
1708            changes: [
1709                {
1710                    caption: "Make changes to file a",
1711                    change: sys => sys.writeFile(`${projectRoot}/a.ts`, `
1712
1713import { x } from "../b";`),
1714                    timeouts: runQueuedTimeoutCallbacks,
1715                },
1716            ]
1717        });
1718
1719        verifyTscWatch({
1720            scenario,
1721            subScenario: "updates emit on jsx option change",
1722            commandLineArgs: ["-w"],
1723            sys: () => {
1724                const index: File = {
1725                    path: `${projectRoot}/index.tsx`,
1726                    content: `declare var React: any;\nconst d = <div />;`
1727                };
1728                const configFile: File = {
1729                    path: `${projectRoot}/tsconfig.json`,
1730                    content: JSON.stringify({
1731                        compilerOptions: {
1732                            jsx: "preserve"
1733                        }
1734                    })
1735                };
1736                return createWatchedSystem([index, configFile, libFile], { currentDirectory: projectRoot });
1737            },
1738            changes: [
1739                {
1740                    caption: "Update 'jsx' to 'react'",
1741                    change: sys => sys.writeFile(`${projectRoot}/tsconfig.json`, '{ "compilerOptions": { "jsx": "react" } }'),
1742                    timeouts: runQueuedTimeoutCallbacks,
1743                },
1744            ]
1745        });
1746
1747        verifyTscWatch({
1748            scenario,
1749            subScenario: "extended source files are watched",
1750            commandLineArgs: ["-w", "-p", configFilePath],
1751            sys: () => {
1752                const firstExtendedConfigFile: File = {
1753                    path: "/a/b/first.tsconfig.json",
1754                    content: JSON.stringify({
1755                        compilerOptions: {
1756                            strict: true
1757                        }
1758                    })
1759                };
1760                const secondExtendedConfigFile: File = {
1761                    path: "/a/b/second.tsconfig.json",
1762                    content: JSON.stringify({
1763                        extends: "./first.tsconfig.json"
1764                    })
1765                };
1766                const configFile: File = {
1767                    path: configFilePath,
1768                    content: JSON.stringify({
1769                        compilerOptions: {},
1770                        files: [commonFile1.path, commonFile2.path]
1771                    })
1772                };
1773                return createWatchedSystem([
1774                    libFile, commonFile1, commonFile2, configFile, firstExtendedConfigFile, secondExtendedConfigFile
1775                ]);
1776            },
1777            changes: [
1778                {
1779                    caption: "Change config to extend another config",
1780                    change: sys => sys.modifyFile(configFilePath, JSON.stringify({
1781                        extends: "./second.tsconfig.json",
1782                        compilerOptions: {},
1783                        files: [commonFile1.path, commonFile2.path]
1784                    })),
1785                    timeouts: checkSingleTimeoutQueueLengthAndRun,
1786                },
1787                {
1788                    caption: "Change first extended config",
1789                    change: sys => sys.modifyFile("/a/b/first.tsconfig.json", JSON.stringify({
1790                        compilerOptions: {
1791                            strict: false,
1792                        }
1793                    })),
1794                    timeouts: checkSingleTimeoutQueueLengthAndRun,
1795                },
1796                {
1797                    caption: "Change second extended config",
1798                    change: sys => sys.modifyFile("/a/b/second.tsconfig.json", JSON.stringify({
1799                        extends: "./first.tsconfig.json",
1800                        compilerOptions: {
1801                            strictNullChecks: true,
1802                        }
1803                    })),
1804                    timeouts: checkSingleTimeoutQueueLengthAndRun,
1805                },
1806                {
1807                    caption: "Change config to stop extending another config",
1808                    change: sys => sys.modifyFile(configFilePath, JSON.stringify({
1809                        compilerOptions: {},
1810                        files: [commonFile1.path, commonFile2.path]
1811                    })),
1812                    timeouts: checkSingleTimeoutQueueLengthAndRun,
1813                },
1814            ]
1815        });
1816
1817        verifyTscWatch({
1818            scenario,
1819            subScenario: "when creating new file in symlinked folder",
1820            commandLineArgs: ["-w", "-p", ".", "--extendedDiagnostics"],
1821            sys: () => {
1822                const module1: File = {
1823                    path: `${projectRoot}/client/folder1/module1.ts`,
1824                    content: `export class Module1Class { }`
1825                };
1826                const module2: File = {
1827                    path: `${projectRoot}/folder2/module2.ts`,
1828                    content: `import * as M from "folder1/module1";`
1829                };
1830                const symlink: SymLink = {
1831                    path: `${projectRoot}/client/linktofolder2`,
1832                    symLink: `${projectRoot}/folder2`,
1833                };
1834                const config: File = {
1835                    path: `${projectRoot}/tsconfig.json`,
1836                    content: JSON.stringify({
1837                        compilerOptions: {
1838                            baseUrl: "client",
1839                            paths: { "*": ["*"] },
1840                        },
1841                        include: ["client/**/*", "folder2"]
1842                    })
1843                };
1844                return createWatchedSystem([module1, module2, symlink, config, libFile], { currentDirectory: projectRoot });
1845            },
1846            changes: [
1847                {
1848                    caption: "Add module3 to folder2",
1849                    change: sys => sys.writeFile(`${projectRoot}/client/linktofolder2/module3.ts`, `import * as M from "folder1/module1";`),
1850                    timeouts: checkSingleTimeoutQueueLengthAndRun,
1851                },
1852            ]
1853        });
1854
1855        verifyTscWatch({
1856            scenario,
1857            subScenario: "when new file is added to the referenced project",
1858            commandLineArgs: ["-w", "-p", `${projectRoot}/projects/project2/tsconfig.json`, "--extendedDiagnostics"],
1859            sys: () => {
1860                const config1: File = {
1861                    path: `${projectRoot}/projects/project1/tsconfig.json`,
1862                    content: JSON.stringify({
1863                        compilerOptions: {
1864                            module: "none",
1865                            composite: true
1866                        },
1867                        exclude: ["temp"]
1868                    })
1869                };
1870                const class1: File = {
1871                    path: `${projectRoot}/projects/project1/class1.ts`,
1872                    content: `class class1 {}`
1873                };
1874                // Built file
1875                const class1Dt: File = {
1876                    path: `${projectRoot}/projects/project1/class1.d.ts`,
1877                    content: `declare class class1 {}`
1878                };
1879                const config2: File = {
1880                    path: `${projectRoot}/projects/project2/tsconfig.json`,
1881                    content: JSON.stringify({
1882                        compilerOptions: {
1883                            module: "none",
1884                            composite: true
1885                        },
1886                        references: [
1887                            { path: "../project1" }
1888                        ]
1889                    })
1890                };
1891                const class2: File = {
1892                    path: `${projectRoot}/projects/project2/class2.ts`,
1893                    content: `class class2 {}`
1894                };
1895                return createWatchedSystem([config1, class1, config2, class2, libFile, class1Dt]);
1896            },
1897            changes: [
1898                {
1899                    caption: "Add class3 to project1",
1900                    change: sys => sys.writeFile(`${projectRoot}/projects/project1/class3.ts`, `class class3 {}`),
1901                    timeouts: checkSingleTimeoutQueueLengthAndRun,
1902                },
1903                {
1904                    caption: "Add output of class3",
1905                    change: sys => sys.writeFile(`${projectRoot}/projects/project1/class3.d.ts`, `declare class class3 {}`),
1906                    timeouts: checkSingleTimeoutQueueLengthAndRun,
1907                },
1908                {
1909                    caption: "Add excluded file to project1",
1910                    change: sys => sys.ensureFileOrFolder({ path: `${projectRoot}/projects/project1/temp/file.d.ts`, content: `declare class file {}` }),
1911                    timeouts: sys => sys.checkTimeoutQueueLength(0),
1912                },
1913                {
1914                    caption: "Delete output of class3",
1915                    change: sys => sys.deleteFile(`${projectRoot}/projects/project1/class3.d.ts`),
1916                    timeouts: checkSingleTimeoutQueueLengthAndRun,
1917                },
1918                {
1919                    caption: "Add output of class3",
1920                    change: sys => sys.writeFile(`${projectRoot}/projects/project1/class3.d.ts`, `declare class class3 {}`),
1921                    timeouts: checkSingleTimeoutQueueLengthAndRun,
1922                },
1923            ]
1924        });
1925
1926        verifyTscWatch({
1927            scenario,
1928            subScenario: "when creating extensionless file",
1929            commandLineArgs: ["-w", "-p", ".", "--extendedDiagnostics"],
1930            sys: () => {
1931                const module1: File = {
1932                    path: `${projectRoot}/index.ts`,
1933                    content: ``
1934                };
1935                const config: File = {
1936                    path: `${projectRoot}/tsconfig.json`,
1937                    content: `{}`
1938                };
1939                return createWatchedSystem([module1, config, libFile], { currentDirectory: projectRoot });
1940            },
1941            changes: [
1942                {
1943                    caption: "Create foo in project root",
1944                    change: sys => sys.writeFile(`${projectRoot}/foo`, ``),
1945                    timeouts: checkSingleTimeoutQueueLengthAndRun,
1946                },
1947            ]
1948        });
1949    });
1950}
1951