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