1import * as ts from "../../_namespaces/ts"; 2 3import projectsLocation = ts.TestFSWithWatch.tsbuildProjectsLocation; 4describe("unittests:: tsbuildWatch:: watchMode:: program updates", () => { 5 const enum SubProject { 6 core = "core", 7 logic = "logic", 8 tests = "tests", 9 ui = "ui" 10 } 11 type ReadonlyFile = Readonly<ts.tscWatch.File>; 12 /** [tsconfig, index] | [tsconfig, index, anotherModule, someDecl] */ 13 type SubProjectFiles = [tsconfig: ReadonlyFile, index: ReadonlyFile] | [tsconfig: ReadonlyFile, index: ReadonlyFile, anotherModule: ReadonlyFile, someDecl: ReadonlyFile]; 14 function projectFilePath(subProject: SubProject, baseFileName: string) { 15 return `${ts.TestFSWithWatch.getTsBuildProjectFilePath("sample1", subProject)}/${baseFileName.toLowerCase()}`; 16 } 17 18 function projectFile(subProject: SubProject, baseFileName: string): ts.tscWatch.File { 19 return ts.TestFSWithWatch.getTsBuildProjectFile("sample1", `${subProject}/${baseFileName}`); 20 } 21 22 function subProjectFiles(subProject: SubProject, anotherModuleAndSomeDecl?: true): SubProjectFiles { 23 const tsconfig = projectFile(subProject, "tsconfig.json"); 24 const index = projectFile(subProject, "index.ts"); 25 if (!anotherModuleAndSomeDecl) { 26 return [tsconfig, index]; 27 } 28 const anotherModule = projectFile(SubProject.core, "anotherModule.ts"); 29 const someDecl = projectFile(SubProject.core, "some_decl.ts"); 30 return [tsconfig, index, anotherModule, someDecl]; 31 } 32 33 function changeFile(fileName: string | (() => string), content: string | (() => string), caption: string): ts.tscWatch.TscWatchCompileChange { 34 return { 35 caption, 36 change: sys => sys.writeFile(ts.isString(fileName) ? fileName : fileName(), ts.isString(content) ? content : content()), 37 timeouts: ts.tscWatch.checkSingleTimeoutQueueLengthAndRun, // Builds core 38 }; 39 } 40 41 function changeCore(content: () => string, caption: string) { 42 return changeFile(() => core[1].path, content, caption); 43 } 44 45 let core: SubProjectFiles; 46 let logic: SubProjectFiles; 47 let tests: SubProjectFiles; 48 let ui: SubProjectFiles; 49 let allFiles: readonly ts.tscWatch.File[]; 50 51 before(() => { 52 core = subProjectFiles(SubProject.core, /*anotherModuleAndSomeDecl*/ true); 53 logic = subProjectFiles(SubProject.logic); 54 tests = subProjectFiles(SubProject.tests); 55 ui = subProjectFiles(SubProject.ui); 56 allFiles = [ts.tscWatch.libFile, ...core, ...logic, ...tests, ...ui]; 57 }); 58 59 after(() => { 60 core = undefined!; 61 logic = undefined!; 62 tests = undefined!; 63 ui = undefined!; 64 allFiles = undefined!; 65 }); 66 67 ts.tscWatch.verifyTscWatch({ 68 scenario: "programUpdates", 69 subScenario: "creates solution in watch mode", 70 commandLineArgs: ["-b", "-w", `sample1/${SubProject.tests}`], 71 sys: () => ts.tscWatch.createWatchedSystem(allFiles, { currentDirectory: projectsLocation }), 72 changes: ts.emptyArray 73 }); 74 75 it("verify building references watches only those projects", () => { 76 const { sys, baseline, oldSnap, cb, getPrograms } = ts.tscWatch.createBaseline(ts.tscWatch.createWatchedSystem(allFiles, { currentDirectory: projectsLocation })); 77 const host = ts.tscWatch.createSolutionBuilderWithWatchHostForBaseline(sys, cb); 78 const solutionBuilder = ts.createSolutionBuilderWithWatch(host, [`sample1/${SubProject.tests}`], { watch: true }); 79 solutionBuilder.buildReferences(`sample1/${SubProject.tests}`); 80 ts.tscWatch.runWatchBaseline({ 81 scenario: "programUpdates", 82 subScenario: "verify building references watches only those projects", 83 commandLineArgs: ["--b", "--w"], 84 sys, 85 baseline, 86 oldSnap, 87 getPrograms, 88 changes: ts.emptyArray, 89 watchOrSolution: solutionBuilder 90 }); 91 }); 92 93 describe("validates the changes and watched files", () => { 94 const newFileWithoutExtension = "newFile"; 95 const newFile: ts.tscWatch.File = { 96 path: projectFilePath(SubProject.core, `${newFileWithoutExtension}.ts`), 97 content: `export const newFileConst = 30;` 98 }; 99 100 function verifyProjectChanges(subScenario: string, allFilesGetter: () => readonly ts.tscWatch.File[]) { 101 const buildLogicAndTests: ts.tscWatch.TscWatchCompileChange = { 102 caption: "Build logic and tests", 103 change: ts.noop, 104 timeouts: ts.tscWatch.checkSingleTimeoutQueueLengthAndRunAndVerifyNoTimeout, 105 }; 106 107 ts.tscWatch.verifyTscWatch({ 108 scenario: "programUpdates", 109 subScenario: `${subScenario}/change builds changes and reports found errors message`, 110 commandLineArgs: ["-b", "-w", `sample1/${SubProject.tests}`], 111 sys: () => ts.tscWatch.createWatchedSystem( 112 allFilesGetter(), 113 { currentDirectory: projectsLocation } 114 ), 115 changes: [ 116 changeCore(() => `${core[1].content} 117export class someClass { }`, "Make change to core"), 118 buildLogicAndTests, 119 // Another change requeues and builds it 120 changeCore(() => core[1].content, "Revert core file"), 121 buildLogicAndTests, 122 { 123 caption: "Make two changes", 124 change: sys => { 125 const change1 = `${core[1].content} 126export class someClass { }`; 127 sys.writeFile(core[1].path, change1); 128 assert.equal(sys.writtenFiles.size, 1); 129 sys.writtenFiles.clear(); 130 sys.writeFile(core[1].path, `${change1} 131export class someClass2 { }`); 132 }, 133 timeouts: ts.tscWatch.checkSingleTimeoutQueueLengthAndRun, // Builds core 134 }, 135 buildLogicAndTests, 136 ] 137 }); 138 139 ts.tscWatch.verifyTscWatch({ 140 scenario: "programUpdates", 141 subScenario: `${subScenario}/non local change does not start build of referencing projects`, 142 commandLineArgs: ["-b", "-w", `sample1/${SubProject.tests}`], 143 sys: () => ts.tscWatch.createWatchedSystem( 144 allFilesGetter(), 145 { currentDirectory: projectsLocation } 146 ), 147 changes: [ 148 changeCore(() => `${core[1].content} 149function foo() { }`, "Make local change to core"), 150 ] 151 }); 152 153 function changeNewFile(newFileContent: string) { 154 return changeFile(newFile.path, newFileContent, "Change to new File and build core"); 155 } 156 ts.tscWatch.verifyTscWatch({ 157 scenario: "programUpdates", 158 subScenario: `${subScenario}/builds when new file is added, and its subsequent updates`, 159 commandLineArgs: ["-b", "-w", `sample1/${SubProject.tests}`], 160 sys: () => ts.tscWatch.createWatchedSystem( 161 allFilesGetter(), 162 { currentDirectory: projectsLocation } 163 ), 164 changes: [ 165 changeNewFile(newFile.content), 166 buildLogicAndTests, 167 changeNewFile(`${newFile.content} 168export class someClass2 { }`), 169 buildLogicAndTests, 170 ] 171 }); 172 } 173 174 describe("with simple project reference graph", () => { 175 verifyProjectChanges( 176 "with simple project reference graph", 177 () => allFiles 178 ); 179 }); 180 181 describe("with circular project reference", () => { 182 verifyProjectChanges( 183 "with circular project reference", 184 () => { 185 const [coreTsconfig, ...otherCoreFiles] = core; 186 const circularCoreConfig: ts.tscWatch.File = { 187 path: coreTsconfig.path, 188 content: JSON.stringify({ 189 compilerOptions: { composite: true, declaration: true }, 190 references: [{ path: "../tests", circular: true }] 191 }) 192 }; 193 return [ts.tscWatch.libFile, circularCoreConfig, ...otherCoreFiles, ...logic, ...tests]; 194 } 195 ); 196 }); 197 }); 198 199 ts.tscWatch.verifyTscWatch({ 200 scenario: "programUpdates", 201 subScenario: "watches config files that are not present", 202 commandLineArgs: ["-b", "-w", `sample1/${SubProject.tests}`], 203 sys: () => ts.tscWatch.createWatchedSystem( 204 [ts.tscWatch.libFile, ...core, logic[1], ...tests], 205 { currentDirectory: projectsLocation } 206 ), 207 changes: [ 208 { 209 caption: "Write logic tsconfig and build logic", 210 change: sys => sys.writeFile(logic[0].path, logic[0].content), 211 timeouts: ts.tscWatch.checkSingleTimeoutQueueLengthAndRun, // Builds logic 212 }, 213 { 214 caption: "Build Tests", 215 change: ts.noop, 216 // Build tests 217 timeouts: ts.tscWatch.checkSingleTimeoutQueueLengthAndRunAndVerifyNoTimeout, 218 } 219 ] 220 }); 221 222 describe("when referenced using prepend, builds referencing project even for non local change", () => { 223 let coreIndex: ts.tscWatch.File; 224 before(() => { 225 coreIndex = { 226 path: core[1].path, 227 content: `function foo() { return 10; }` 228 }; 229 }); 230 after(() => { 231 coreIndex = undefined!; 232 }); 233 const buildLogic: ts.tscWatch.TscWatchCompileChange = { 234 caption: "Build logic", 235 change: ts.noop, 236 // Builds logic 237 timeouts: ts.tscWatch.checkSingleTimeoutQueueLengthAndRunAndVerifyNoTimeout, 238 }; 239 ts.tscWatch.verifyTscWatch({ 240 scenario: "programUpdates", 241 subScenario: "when referenced using prepend builds referencing project even for non local change", 242 commandLineArgs: ["-b", "-w", `sample1/${SubProject.logic}`], 243 sys: () => { 244 const coreTsConfig: ts.tscWatch.File = { 245 path: core[0].path, 246 content: JSON.stringify({ 247 compilerOptions: { composite: true, declaration: true, outFile: "index.js" } 248 }) 249 }; 250 const logicTsConfig: ts.tscWatch.File = { 251 path: logic[0].path, 252 content: JSON.stringify({ 253 compilerOptions: { composite: true, declaration: true, outFile: "index.js" }, 254 references: [{ path: "../core", prepend: true }] 255 }) 256 }; 257 const logicIndex: ts.tscWatch.File = { 258 path: logic[1].path, 259 content: `function bar() { return foo() + 1 };` 260 }; 261 return ts.tscWatch.createWatchedSystem([ts.tscWatch.libFile, coreTsConfig, coreIndex, logicTsConfig, logicIndex], { currentDirectory: projectsLocation }); 262 }, 263 changes: [ 264 changeCore(() => `${coreIndex.content} 265function myFunc() { return 10; }`, "Make non local change and build core"), 266 buildLogic, 267 changeCore(() => `${coreIndex.content} 268function myFunc() { return 100; }`, "Make local change and build core"), 269 buildLogic, 270 ] 271 }); 272 }); 273 274 describe("when referenced project change introduces error in the down stream project and then fixes it", () => { 275 const subProjectLibrary = `${projectsLocation}/sample1/Library`; 276 const libraryTs: ts.tscWatch.File = { 277 path: `${subProjectLibrary}/library.ts`, 278 content: ` 279interface SomeObject 280{ 281 message: string; 282} 283 284export function createSomeObject(): SomeObject 285{ 286 return { 287 message: "new Object" 288 }; 289}` 290 }; 291 ts.tscWatch.verifyTscWatch({ 292 scenario: "programUpdates", 293 subScenario: "when referenced project change introduces error in the down stream project and then fixes it", 294 commandLineArgs: ["-b", "-w", "App"], 295 sys: () => { 296 const libraryTsconfig: ts.tscWatch.File = { 297 path: `${subProjectLibrary}/tsconfig.json`, 298 content: JSON.stringify({ compilerOptions: { composite: true } }) 299 }; 300 const subProjectApp = `${projectsLocation}/sample1/App`; 301 const appTs: ts.tscWatch.File = { 302 path: `${subProjectApp}/app.ts`, 303 content: `import { createSomeObject } from "../Library/library"; 304createSomeObject().message;` 305 }; 306 const appTsconfig: ts.tscWatch.File = { 307 path: `${subProjectApp}/tsconfig.json`, 308 content: JSON.stringify({ references: [{ path: "../Library" }] }) 309 }; 310 311 const files = [ts.tscWatch.libFile, libraryTs, libraryTsconfig, appTs, appTsconfig]; 312 return ts.tscWatch.createWatchedSystem(files, { currentDirectory: `${projectsLocation}/sample1` }); 313 }, 314 changes: [ 315 { 316 caption: "Introduce error", 317 // Change message in library to message2 318 change: sys => sys.writeFile(libraryTs.path, libraryTs.content.replace(/message/g, "message2")), 319 timeouts: sys => { 320 sys.checkTimeoutQueueLengthAndRun(1); // Build library 321 sys.checkTimeoutQueueLengthAndRun(1); // Build App 322 }, 323 }, 324 { 325 caption: "Fix error", 326 // Revert library changes 327 change: sys => sys.writeFile(libraryTs.path, libraryTs.content), 328 timeouts: sys => { 329 sys.checkTimeoutQueueLengthAndRun(1); // Build library 330 sys.checkTimeoutQueueLengthAndRun(1); // Build App 331 }, 332 }, 333 ] 334 }); 335 336 }); 337 338 describe("reports errors in all projects on incremental compile", () => { 339 function verifyIncrementalErrors(subScenario: string, buildOptions: readonly string[]) { 340 ts.tscWatch.verifyTscWatch({ 341 scenario: "programUpdates", 342 subScenario: `reportErrors/${subScenario}`, 343 commandLineArgs: ["-b", "-w", `sample1/${SubProject.tests}`, ...buildOptions], 344 sys: () => ts.tscWatch.createWatchedSystem(allFiles, { currentDirectory: projectsLocation }), 345 changes: [ 346 { 347 caption: "change logic", 348 change: sys => sys.writeFile(logic[1].path, `${logic[1].content} 349let y: string = 10;`), 350 // Builds logic 351 timeouts: ts.tscWatch.checkSingleTimeoutQueueLengthAndRunAndVerifyNoTimeout, 352 }, 353 { 354 caption: "change core", 355 change: sys => sys.writeFile(core[1].path, `${core[1].content} 356let x: string = 10;`), 357 // Builds core 358 timeouts: ts.tscWatch.checkSingleTimeoutQueueLengthAndRunAndVerifyNoTimeout, 359 } 360 ] 361 }); 362 } 363 verifyIncrementalErrors("when preserveWatchOutput is not used", ts.emptyArray); 364 verifyIncrementalErrors("when preserveWatchOutput is passed on command line", ["--preserveWatchOutput"]); 365 366 describe("when declaration emit errors are present", () => { 367 const solution = "solution"; 368 const subProject = "app"; 369 const subProjectLocation = `${projectsLocation}/${solution}/${subProject}`; 370 const fileWithError: ts.tscWatch.File = { 371 path: `${subProjectLocation}/fileWithError.ts`, 372 content: `export var myClassWithError = class { 373 tags() { } 374 private p = 12 375 };` 376 }; 377 const fileWithFixedError: ts.tscWatch.File = { 378 path: fileWithError.path, 379 content: fileWithError.content.replace("private p = 12", "") 380 }; 381 const fileWithoutError: ts.tscWatch.File = { 382 path: `${subProjectLocation}/fileWithoutError.ts`, 383 content: `export class myClass { }` 384 }; 385 const tsconfig: ts.tscWatch.File = { 386 path: `${subProjectLocation}/tsconfig.json`, 387 content: JSON.stringify({ compilerOptions: { composite: true } }) 388 }; 389 390 function incrementalBuild(sys: ts.tscWatch.WatchedSystem) { 391 sys.checkTimeoutQueueLengthAndRun(1); // Build the app 392 sys.checkTimeoutQueueLength(0); 393 } 394 395 const fixError: ts.tscWatch.TscWatchCompileChange = { 396 caption: "Fix error in fileWithError", 397 // Fix error 398 change: sys => sys.writeFile(fileWithError.path, fileWithFixedError.content), 399 timeouts: incrementalBuild 400 }; 401 402 const changeFileWithoutError: ts.tscWatch.TscWatchCompileChange = { 403 caption: "Change fileWithoutError", 404 change: sys => sys.writeFile(fileWithoutError.path, fileWithoutError.content.replace(/myClass/g, "myClass2")), 405 timeouts: incrementalBuild 406 }; 407 408 ts.tscWatch.verifyTscWatch({ 409 scenario: "programUpdates", 410 subScenario: "reportErrors/declarationEmitErrors/when fixing error files all files are emitted", 411 commandLineArgs: ["-b", "-w", subProject], 412 sys: () => ts.tscWatch.createWatchedSystem( 413 [ts.tscWatch.libFile, fileWithError, fileWithoutError, tsconfig], 414 { currentDirectory: `${projectsLocation}/${solution}` } 415 ), 416 changes: [ 417 fixError 418 ] 419 }); 420 421 ts.tscWatch.verifyTscWatch({ 422 scenario: "programUpdates", 423 subScenario: "reportErrors/declarationEmitErrors/when file with no error changes", 424 commandLineArgs: ["-b", "-w", subProject], 425 sys: () => ts.tscWatch.createWatchedSystem( 426 [ts.tscWatch.libFile, fileWithError, fileWithoutError, tsconfig], 427 { currentDirectory: `${projectsLocation}/${solution}` } 428 ), 429 changes: [ 430 changeFileWithoutError 431 ] 432 }); 433 434 describe("when reporting errors on introducing error", () => { 435 const introduceError: ts.tscWatch.TscWatchCompileChange = { 436 caption: "Introduce error", 437 change: sys => sys.writeFile(fileWithError.path, fileWithError.content), 438 timeouts: incrementalBuild, 439 }; 440 441 ts.tscWatch.verifyTscWatch({ 442 scenario: "programUpdates", 443 subScenario: "reportErrors/declarationEmitErrors/introduceError/when fixing errors only changed file is emitted", 444 commandLineArgs: ["-b", "-w", subProject], 445 sys: () => ts.tscWatch.createWatchedSystem( 446 [ts.tscWatch.libFile, fileWithFixedError, fileWithoutError, tsconfig], 447 { currentDirectory: `${projectsLocation}/${solution}` } 448 ), 449 changes: [ 450 introduceError, 451 fixError 452 ] 453 }); 454 455 ts.tscWatch.verifyTscWatch({ 456 scenario: "programUpdates", 457 subScenario: "reportErrors/declarationEmitErrors/introduceError/when file with no error changes", 458 commandLineArgs: ["-b", "-w", subProject], 459 sys: () => ts.tscWatch.createWatchedSystem( 460 [ts.tscWatch.libFile, fileWithFixedError, fileWithoutError, tsconfig], 461 { currentDirectory: `${projectsLocation}/${solution}` } 462 ), 463 changes: [ 464 introduceError, 465 changeFileWithoutError 466 ] 467 }); 468 }); 469 }); 470 }); 471 472 ts.tscWatch.verifyTscWatch({ 473 scenario: "programUpdates", 474 subScenario: "incremental updates in verbose mode", 475 commandLineArgs: ["-b", "-w", `sample1/${SubProject.tests}`, "-verbose"], 476 sys: () => ts.tscWatch.createWatchedSystem(allFiles, { currentDirectory: projectsLocation }), 477 changes: [ 478 { 479 caption: "Make non dts change", 480 change: sys => sys.writeFile(logic[1].path, `${logic[1].content} 481function someFn() { }`), 482 timeouts: sys => { 483 sys.checkTimeoutQueueLengthAndRun(1); // build logic and updates tests 484 sys.checkTimeoutQueueLength(0); 485 }, 486 }, 487 { 488 caption: "Make dts change", 489 change: sys => sys.writeFile(logic[1].path, `${logic[1].content} 490export function someFn() { }`), 491 timeouts: sys => { 492 sys.checkTimeoutQueueLengthAndRun(1); // build logic 493 sys.checkTimeoutQueueLengthAndRun(1); // build tests 494 }, 495 } 496 ], 497 }); 498 499 ts.tscWatch.verifyTscWatch({ 500 scenario: "programUpdates", 501 subScenario: "works when noUnusedParameters changes to false", 502 commandLineArgs: ["-b", "-w"], 503 sys: () => { 504 const index: ts.tscWatch.File = { 505 path: `${ts.tscWatch.projectRoot}/index.ts`, 506 content: `const fn = (a: string, b: string) => b;` 507 }; 508 const configFile: ts.tscWatch.File = { 509 path: `${ts.tscWatch.projectRoot}/tsconfig.json`, 510 content: JSON.stringify({ 511 compilerOptions: { 512 noUnusedParameters: true 513 } 514 }) 515 }; 516 return ts.tscWatch.createWatchedSystem([index, configFile, ts.tscWatch.libFile], { currentDirectory: ts.tscWatch.projectRoot }); 517 }, 518 changes: [ 519 { 520 caption: "Change tsconfig to set noUnusedParameters to false", 521 change: sys => sys.writeFile(`${ts.tscWatch.projectRoot}/tsconfig.json`, JSON.stringify({ 522 compilerOptions: { 523 noUnusedParameters: false 524 } 525 })), 526 timeouts: ts.tscWatch.runQueuedTimeoutCallbacks, 527 }, 528 ] 529 }); 530 531 ts.tscWatch.verifyTscWatch({ 532 scenario: "programUpdates", 533 subScenario: "should not trigger recompilation because of program emit", 534 commandLineArgs: ["-b", "-w", `sample1/${SubProject.core}`, "-verbose"], 535 sys: () => ts.tscWatch.createWatchedSystem([ts.tscWatch.libFile, ...core], { currentDirectory: projectsLocation }), 536 changes: [ 537 ts.tscWatch.noopChange, 538 { 539 caption: "Add new file", 540 change: sys => sys.writeFile(`sample1/${SubProject.core}/file3.ts`, `export const y = 10;`), 541 timeouts: ts.tscWatch.checkSingleTimeoutQueueLengthAndRun 542 }, 543 ts.tscWatch.noopChange, 544 ] 545 }); 546 547 ts.tscWatch.verifyTscWatch({ 548 scenario: "programUpdates", 549 subScenario: "should not trigger recompilation because of program emit with outDir specified", 550 commandLineArgs: ["-b", "-w", `sample1/${SubProject.core}`, "-verbose"], 551 sys: () => { 552 const [coreConfig, ...rest] = core; 553 const newCoreConfig: ts.tscWatch.File = { path: coreConfig.path, content: JSON.stringify({ compilerOptions: { composite: true, outDir: "outDir" } }) }; 554 return ts.tscWatch.createWatchedSystem([ts.tscWatch.libFile, newCoreConfig, ...rest], { currentDirectory: projectsLocation }); 555 }, 556 changes: [ 557 ts.tscWatch.noopChange, 558 { 559 caption: "Add new file", 560 change: sys => sys.writeFile(`sample1/${SubProject.core}/file3.ts`, `export const y = 10;`), 561 timeouts: ts.tscWatch.checkSingleTimeoutQueueLengthAndRun 562 }, 563 ts.tscWatch.noopChange 564 ] 565 }); 566 567 ts.tscWatch.verifyTscWatch({ 568 scenario: "programUpdates", 569 subScenario: "works with extended source files", 570 commandLineArgs: ["-b", "-w", "-v", "project1.tsconfig.json", "project2.tsconfig.json"], 571 sys: () => { 572 const alphaExtendedConfigFile: ts.tscWatch.File = { 573 path: "/a/b/alpha.tsconfig.json", 574 content: "{}" 575 }; 576 const project1Config: ts.tscWatch.File = { 577 path: "/a/b/project1.tsconfig.json", 578 content: JSON.stringify({ 579 extends: "./alpha.tsconfig.json", 580 compilerOptions: { 581 composite: true, 582 }, 583 files: [ts.tscWatch.commonFile1.path, ts.tscWatch.commonFile2.path] 584 }) 585 }; 586 const bravoExtendedConfigFile: ts.tscWatch.File = { 587 path: "/a/b/bravo.tsconfig.json", 588 content: JSON.stringify({ 589 extends: "./alpha.tsconfig.json" 590 }) 591 }; 592 const otherFile: ts.tscWatch.File = { 593 path: "/a/b/other.ts", 594 content: "let z = 0;", 595 }; 596 const project2Config: ts.tscWatch.File = { 597 path: "/a/b/project2.tsconfig.json", 598 content: JSON.stringify({ 599 extends: "./bravo.tsconfig.json", 600 compilerOptions: { 601 composite: true, 602 }, 603 files: [otherFile.path] 604 }) 605 }; 606 return ts.tscWatch.createWatchedSystem([ 607 ts.tscWatch.libFile, 608 alphaExtendedConfigFile, project1Config, ts.tscWatch.commonFile1, ts.tscWatch.commonFile2, 609 bravoExtendedConfigFile, project2Config, otherFile 610 ], { currentDirectory: "/a/b" }); 611 }, 612 changes: [ 613 { 614 caption: "Modify alpha config", 615 change: sys => sys.writeFile("/a/b/alpha.tsconfig.json", JSON.stringify({ 616 compilerOptions: { strict: true } 617 })), 618 timeouts: ts.tscWatch.checkSingleTimeoutQueueLengthAndRun // Build project1 619 }, 620 { 621 caption: "Build project 2", 622 change: ts.noop, 623 timeouts: ts.tscWatch.checkSingleTimeoutQueueLengthAndRunAndVerifyNoTimeout // Build project2 624 }, 625 { 626 caption: "change bravo config", 627 change: sys => sys.writeFile("/a/b/bravo.tsconfig.json", JSON.stringify({ 628 extends: "./alpha.tsconfig.json", 629 compilerOptions: { strict: false } 630 })), 631 timeouts: ts.tscWatch.checkSingleTimeoutQueueLengthAndRunAndVerifyNoTimeout // Build project2 632 }, 633 { 634 caption: "project 2 extends alpha", 635 change: sys => sys.writeFile("/a/b/project2.tsconfig.json", JSON.stringify({ 636 extends: "./alpha.tsconfig.json", 637 })), 638 timeouts: ts.tscWatch.checkSingleTimeoutQueueLengthAndRunAndVerifyNoTimeout // Build project2 639 }, 640 { 641 caption: "update aplha config", 642 change: sys => sys.writeFile("/a/b/alpha.tsconfig.json", "{}"), 643 timeouts: ts.tscWatch.checkSingleTimeoutQueueLengthAndRun, // build project1 644 }, 645 { 646 caption: "Build project 2", 647 change: ts.noop, 648 timeouts: ts.tscWatch.checkSingleTimeoutQueueLengthAndRunAndVerifyNoTimeout // Build project2 649 }, 650 ] 651 }); 652 653 ts.tscWatch.verifyTscWatch({ 654 scenario: "programUpdates", 655 subScenario: "works correctly when project with extended config is removed", 656 commandLineArgs: ["-b", "-w", "-v"], 657 sys: () => { 658 const alphaExtendedConfigFile: ts.tscWatch.File = { 659 path: "/a/b/alpha.tsconfig.json", 660 content: JSON.stringify({ 661 compilerOptions: { 662 strict: true 663 } 664 }) 665 }; 666 const project1Config: ts.tscWatch.File = { 667 path: "/a/b/project1.tsconfig.json", 668 content: JSON.stringify({ 669 extends: "./alpha.tsconfig.json", 670 compilerOptions: { 671 composite: true, 672 }, 673 files: [ts.tscWatch.commonFile1.path, ts.tscWatch.commonFile2.path] 674 }) 675 }; 676 const bravoExtendedConfigFile: ts.tscWatch.File = { 677 path: "/a/b/bravo.tsconfig.json", 678 content: JSON.stringify({ 679 compilerOptions: { 680 strict: true 681 } 682 }) 683 }; 684 const otherFile: ts.tscWatch.File = { 685 path: "/a/b/other.ts", 686 content: "let z = 0;", 687 }; 688 const project2Config: ts.tscWatch.File = { 689 path: "/a/b/project2.tsconfig.json", 690 content: JSON.stringify({ 691 extends: "./bravo.tsconfig.json", 692 compilerOptions: { 693 composite: true, 694 }, 695 files: [otherFile.path] 696 }) 697 }; 698 const configFile: ts.tscWatch.File = { 699 path: "/a/b/tsconfig.json", 700 content: JSON.stringify({ 701 references: [ 702 { 703 path: "./project1.tsconfig.json", 704 }, 705 { 706 path: "./project2.tsconfig.json", 707 }, 708 ], 709 files: [], 710 }) 711 }; 712 return ts.tscWatch.createWatchedSystem([ 713 ts.tscWatch.libFile, configFile, 714 alphaExtendedConfigFile, project1Config, ts.tscWatch.commonFile1, ts.tscWatch.commonFile2, 715 bravoExtendedConfigFile, project2Config, otherFile 716 ], { currentDirectory: "/a/b" }); 717 }, 718 changes: [ 719 { 720 caption: "Remove project2 from base config", 721 change: sys => sys.modifyFile("/a/b/tsconfig.json", JSON.stringify({ 722 references: [ 723 { 724 path: "./project1.tsconfig.json", 725 }, 726 ], 727 files: [], 728 })), 729 timeouts: ts.tscWatch.checkSingleTimeoutQueueLengthAndRunAndVerifyNoTimeout, 730 } 731 ] 732 }); 733 734 ts.tscWatch.verifyTscWatch({ 735 scenario: "programUpdates", 736 subScenario: "tsbuildinfo has error", 737 sys: () => ts.tscWatch.createWatchedSystem({ 738 "/src/project/main.ts": "export const x = 10;", 739 "/src/project/tsconfig.json": "{}", 740 "/src/project/tsconfig.tsbuildinfo": "Some random string", 741 [ts.tscWatch.libFile.path]: ts.tscWatch.libFile.content, 742 }), 743 commandLineArgs: ["--b", "src/project", "-i", "-w"], 744 changes: ts.emptyArray 745 }); 746});