1namespace ts { 2 describe("unittests:: tsbuild:: outFile::", () => { 3 let outFileFs: vfs.FileSystem; 4 const enum Ext { js, jsmap, dts, dtsmap, buildinfo } 5 const enum Project { first, second, third } 6 type OutputFile = [string, string, string, string, string]; 7 function relName(path: string) { return path.slice(1); } 8 const outputFiles: [OutputFile, OutputFile, OutputFile] = [ 9 [ 10 "/src/first/bin/first-output.js", 11 "/src/first/bin/first-output.js.map", 12 "/src/first/bin/first-output.d.ts", 13 "/src/first/bin/first-output.d.ts.map", 14 "/src/first/bin/first-output.tsbuildinfo" 15 ], 16 [ 17 "/src/2/second-output.js", 18 "/src/2/second-output.js.map", 19 "/src/2/second-output.d.ts", 20 "/src/2/second-output.d.ts.map", 21 "/src/2/second-output.tsbuildinfo" 22 ], 23 [ 24 "/src/third/thirdjs/output/third-output.js", 25 "/src/third/thirdjs/output/third-output.js.map", 26 "/src/third/thirdjs/output/third-output.d.ts", 27 "/src/third/thirdjs/output/third-output.d.ts.map", 28 "/src/third/thirdjs/output/third-output.tsbuildinfo" 29 ] 30 ]; 31 const relOutputFiles = outputFiles.map(v => v.map(relName)) as [OutputFile, OutputFile, OutputFile]; 32 type Sources = [string, readonly string[]]; 33 const enum Source { config, ts } 34 const enum Part { one, two, three } 35 const sources: [Sources, Sources, Sources] = [ 36 [ 37 "/src/first/tsconfig.json", 38 [ 39 "/src/first/first_PART1.ts", 40 "/src/first/first_part2.ts", 41 "/src/first/first_part3.ts" 42 ] 43 ], 44 [ 45 "/src/second/tsconfig.json", 46 [ 47 "/src/second/second_part1.ts", 48 "/src/second/second_part2.ts" 49 ] 50 ], 51 [ 52 "/src/third/tsconfig.json", 53 [ 54 "/src/third/third_part1.ts" 55 ] 56 ] 57 ]; 58 const relSources = sources.map(([config, sources]) => [relName(config), sources.map(relName)]) as any as [Sources, Sources, Sources]; 59 let initialExpectedDiagnostics: readonly fakes.ExpectedDiagnostic[] = [ 60 getExpectedDiagnosticForProjectsInBuild(relSources[Project.first][Source.config], relSources[Project.second][Source.config], relSources[Project.third][Source.config]), 61 [Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, relSources[Project.first][Source.config], relOutputFiles[Project.first][Ext.js]], 62 [Diagnostics.Building_project_0, sources[Project.first][Source.config]], 63 [Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, relSources[Project.second][Source.config], relOutputFiles[Project.second][Ext.js]], 64 [Diagnostics.Building_project_0, sources[Project.second][Source.config]], 65 [Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, relSources[Project.third][Source.config], relOutputFiles[Project.third][Ext.js]], 66 [Diagnostics.Building_project_0, sources[Project.third][Source.config]] 67 ]; 68 before(() => { 69 outFileFs = loadProjectFromDisk("tests/projects/outfile-concat"); 70 }); 71 after(() => { 72 outFileFs = undefined!; 73 initialExpectedDiagnostics = undefined!; 74 }); 75 76 function createSolutionBuilder(host: fakes.SolutionBuilderHost, baseOptions?: BuildOptions) { 77 return ts.createSolutionBuilder(host, ["/src/third"], { dry: false, force: false, verbose: true, ...(baseOptions || {}) }); 78 } 79 80 interface VerifyOutFileScenarioInput { 81 subScenario: string; 82 modifyFs?: (fs: vfs.FileSystem) => void; 83 modifyAgainFs?: (fs: vfs.FileSystem) => void; 84 ignoreDtsChanged?: true; 85 ignoreDtsUnchanged?: true; 86 baselineOnly?: true; 87 additionalCommandLineArgs?: string[]; 88 } 89 90 function verifyOutFileScenario({ 91 subScenario, 92 modifyFs, 93 modifyAgainFs, 94 ignoreDtsChanged, 95 ignoreDtsUnchanged, 96 baselineOnly, 97 additionalCommandLineArgs, 98 }: VerifyOutFileScenarioInput) { 99 const incrementalScenarios: TscIncremental[] = []; 100 if (!ignoreDtsChanged) { 101 incrementalScenarios.push({ 102 buildKind: BuildKind.IncrementalDtsChange, 103 modifyFs: fs => replaceText(fs, relSources[Project.first][Source.ts][Part.one], "Hello", "Hola"), 104 }); 105 } 106 if (!ignoreDtsUnchanged) { 107 incrementalScenarios.push({ 108 buildKind: BuildKind.IncrementalDtsUnchanged, 109 modifyFs: fs => appendText(fs, relSources[Project.first][Source.ts][Part.one], "console.log(s);"), 110 }); 111 } 112 if (modifyAgainFs) { 113 incrementalScenarios.push({ 114 buildKind: BuildKind.IncrementalHeadersChange, 115 modifyFs: modifyAgainFs 116 }); 117 } 118 const input: VerifyTsBuildInput = { 119 subScenario, 120 fs: () => outFileFs, 121 scenario: "outfile-concat", 122 commandLineArgs: ["--b", "/src/third", "--verbose", ...(additionalCommandLineArgs || [])], 123 baselineSourceMap: true, 124 modifyFs, 125 baselineReadFileCalls: !baselineOnly, 126 incrementalScenarios, 127 }; 128 return incrementalScenarios.length ? 129 verifyTscIncrementalEdits(input) : 130 verifyTsc(input); 131 } 132 133 // Verify initial + incremental edits 134 verifyOutFileScenario({ 135 subScenario: "baseline sectioned sourcemaps", 136 }); 137 138 verifyOutFileScenario({ 139 subScenario: "explainFiles", 140 additionalCommandLineArgs: ["--explainFiles"], 141 baselineOnly: true 142 }); 143 144 // Verify baseline with build info + dts unChanged 145 verifyOutFileScenario({ 146 subScenario: "when final project is not composite but uses project references", 147 modifyFs: fs => replaceText(fs, sources[Project.third][Source.config], `"composite": true,`, ""), 148 ignoreDtsChanged: true, 149 baselineOnly: true 150 }); 151 152 // Verify baseline with build info 153 verifyOutFileScenario({ 154 subScenario: "when final project is not composite but incremental", 155 modifyFs: fs => replaceText(fs, sources[Project.third][Source.config], `"composite": true,`, `"incremental": true,`), 156 ignoreDtsChanged: true, 157 ignoreDtsUnchanged: true, 158 baselineOnly: true 159 }); 160 161 // Verify baseline with build info 162 verifyOutFileScenario({ 163 subScenario: "when final project specifies tsBuildInfoFile", 164 modifyFs: fs => replaceText(fs, sources[Project.third][Source.config], `"composite": true,`, `"composite": true, 165 "tsBuildInfoFile": "./thirdjs/output/third.tsbuildinfo",`), 166 ignoreDtsChanged: true, 167 ignoreDtsUnchanged: true, 168 baselineOnly: true 169 }); 170 171 function getOutFileFsAfterBuild() { 172 const fs = outFileFs.shadow(); 173 const host = fakes.SolutionBuilderHost.create(fs); 174 const builder = createSolutionBuilder(host); 175 builder.build(); 176 fs.makeReadonly(); 177 return fs; 178 } 179 180 verifyTscSerializedIncrementalEdits({ 181 scenario: "outFile", 182 subScenario: "clean projects", 183 fs: getOutFileFsAfterBuild, 184 commandLineArgs: ["--b", "/src/third", "--clean"], 185 incrementalScenarios: noChangeOnlyRuns 186 }); 187 188 verifyTsc({ 189 scenario: "outFile", 190 subScenario: "verify buildInfo absence results in new build", 191 fs: getOutFileFsAfterBuild, 192 commandLineArgs: ["--b", "/src/third", "--verbose"], 193 modifyFs: fs => fs.unlinkSync(outputFiles[Project.first][Ext.buildinfo]), 194 }); 195 196 verifyTsc({ 197 scenario: "outFile", 198 subScenario: "tsbuildinfo is not generated when incremental is set to false", 199 fs: () => outFileFs, 200 commandLineArgs: ["--b", "/src/third", "--verbose"], 201 modifyFs: fs => replaceText(fs, sources[Project.third][Source.config], `"composite": true,`, ""), 202 }); 203 204 it("rebuilds completely when version in tsbuildinfo doesnt match ts version", () => { 205 const { fs, tick } = getFsWithTime(outFileFs); 206 const host = fakes.SolutionBuilderHost.create(fs); 207 let builder = createSolutionBuilder(host); 208 builder.build(); 209 host.assertDiagnosticMessages(...initialExpectedDiagnostics); 210 host.clearDiagnostics(); 211 tick(); 212 builder = createSolutionBuilder(host); 213 changeCompilerVersion(host); 214 tick(); 215 builder.build(); 216 host.assertDiagnosticMessages( 217 getExpectedDiagnosticForProjectsInBuild(relSources[Project.first][Source.config], relSources[Project.second][Source.config], relSources[Project.third][Source.config]), 218 [Diagnostics.Project_0_is_out_of_date_because_output_for_it_was_generated_with_version_1_that_differs_with_current_version_2, relSources[Project.first][Source.config], fakes.version, version], 219 [Diagnostics.Building_project_0, sources[Project.first][Source.config]], 220 [Diagnostics.Project_0_is_out_of_date_because_output_for_it_was_generated_with_version_1_that_differs_with_current_version_2, relSources[Project.second][Source.config], fakes.version, version], 221 [Diagnostics.Building_project_0, sources[Project.second][Source.config]], 222 [Diagnostics.Project_0_is_out_of_date_because_output_for_it_was_generated_with_version_1_that_differs_with_current_version_2, relSources[Project.third][Source.config], fakes.version, version], 223 [Diagnostics.Building_project_0, sources[Project.third][Source.config]], 224 ); 225 }); 226 227 it("rebuilds completely when command line incremental flag changes between non dts changes", () => { 228 const { fs, tick } = getFsWithTime(outFileFs); 229 // Make non composite third project 230 replaceText(fs, sources[Project.third][Source.config], `"composite": true,`, ""); 231 232 // Build with command line incremental 233 const host = fakes.SolutionBuilderHost.create(fs); 234 let builder = createSolutionBuilder(host, { incremental: true }); 235 builder.build(); 236 host.assertDiagnosticMessages(...initialExpectedDiagnostics); 237 host.clearDiagnostics(); 238 tick(); 239 240 // Make non incremental build with change in file that doesnt affect dts 241 appendText(fs, relSources[Project.first][Source.ts][Part.one], "console.log(s);"); 242 builder = createSolutionBuilder(host, { verbose: true }); 243 builder.build(); 244 host.assertDiagnosticMessages(getExpectedDiagnosticForProjectsInBuild(relSources[Project.first][Source.config], relSources[Project.second][Source.config], relSources[Project.third][Source.config]), 245 [Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, relSources[Project.first][Source.config], relOutputFiles[Project.first][Ext.js], relSources[Project.first][Source.ts][Part.one]], 246 [Diagnostics.Building_project_0, sources[Project.first][Source.config]], 247 [Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, relSources[Project.second][Source.config], relSources[Project.second][Source.ts][Part.one], relOutputFiles[Project.second][Ext.js]], 248 [Diagnostics.Project_0_is_out_of_date_because_output_of_its_dependency_1_has_changed, relSources[Project.third][Source.config], "src/first"], 249 [Diagnostics.Building_project_0, sources[Project.third][Source.config]] 250 ); 251 host.clearDiagnostics(); 252 tick(); 253 254 // Make incremental build with change in file that doesnt affect dts 255 appendText(fs, relSources[Project.first][Source.ts][Part.one], "console.log(s);"); 256 builder = createSolutionBuilder(host, { verbose: true, incremental: true }); 257 builder.build(); 258 // Builds completely because tsbuildinfo is old. 259 host.assertDiagnosticMessages( 260 getExpectedDiagnosticForProjectsInBuild(relSources[Project.first][Source.config], relSources[Project.second][Source.config], relSources[Project.third][Source.config]), 261 [Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, relSources[Project.first][Source.config], relOutputFiles[Project.first][Ext.js], relSources[Project.first][Source.ts][Part.one]], 262 [Diagnostics.Building_project_0, sources[Project.first][Source.config]], 263 [Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, relSources[Project.second][Source.config], relSources[Project.second][Source.ts][Part.one], relOutputFiles[Project.second][Ext.js]], 264 [Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, relSources[Project.third][Source.config], relOutputFiles[Project.third][Ext.buildinfo], "src/first"], 265 [Diagnostics.Building_project_0, sources[Project.third][Source.config]] 266 ); 267 host.clearDiagnostics(); 268 }); 269 270 it("builds till project specified", () => { 271 const fs = outFileFs.shadow(); 272 const host = fakes.SolutionBuilderHost.create(fs); 273 const builder = createSolutionBuilder(host, { verbose: false }); 274 const result = builder.build(sources[Project.second][Source.config]); 275 host.assertDiagnosticMessages(/*empty*/); 276 // First and Third is not built 277 verifyOutputsAbsent(fs, [...outputFiles[Project.first], ...outputFiles[Project.third]]); 278 // second is built 279 verifyOutputsPresent(fs, outputFiles[Project.second]); 280 assert.equal(result, ExitStatus.Success); 281 }); 282 283 it("cleans till project specified", () => { 284 const fs = outFileFs.shadow(); 285 const host = fakes.SolutionBuilderHost.create(fs); 286 const builder = createSolutionBuilder(host, { verbose: false }); 287 builder.build(); 288 const result = builder.clean(sources[Project.second][Source.config]); 289 host.assertDiagnosticMessages(/*empty*/); 290 // First and Third output for present 291 verifyOutputsPresent(fs, [...outputFiles[Project.first], ...outputFiles[Project.third]]); 292 // second is cleaned 293 verifyOutputsAbsent(fs, outputFiles[Project.second]); 294 assert.equal(result, ExitStatus.Success); 295 }); 296 297 describe("Prepend output with .tsbuildinfo", () => { 298 // Prologues 299 describe("Prologues", () => { 300 // Verify initial + incremental edits 301 verifyOutFileScenario({ 302 subScenario: "strict in all projects", 303 modifyFs: fs => { 304 enableStrict(fs, sources[Project.first][Source.config]); 305 enableStrict(fs, sources[Project.second][Source.config]); 306 enableStrict(fs, sources[Project.third][Source.config]); 307 }, 308 modifyAgainFs: fs => addTestPrologue(fs, relSources[Project.first][Source.ts][Part.one], `"myPrologue"`) 309 }); 310 311 // Verify ignore dtsChanged 312 verifyOutFileScenario({ 313 subScenario: "strict in one dependency", 314 modifyFs: fs => enableStrict(fs, sources[Project.second][Source.config]), 315 modifyAgainFs: fs => addTestPrologue(fs, "src/first/first_PART1.ts", `"myPrologue"`), 316 ignoreDtsChanged: true, 317 baselineOnly: true 318 }); 319 320 // Verify initial + incremental edits - sourcemap verification 321 verifyOutFileScenario({ 322 subScenario: "multiple prologues in all projects", 323 modifyFs: fs => { 324 enableStrict(fs, sources[Project.first][Source.config]); 325 addTestPrologue(fs, sources[Project.first][Source.ts][Part.one], `"myPrologue"`); 326 enableStrict(fs, sources[Project.second][Source.config]); 327 addTestPrologue(fs, sources[Project.second][Source.ts][Part.one], `"myPrologue"`); 328 addTestPrologue(fs, sources[Project.second][Source.ts][Part.two], `"myPrologue2";`); 329 enableStrict(fs, sources[Project.third][Source.config]); 330 addTestPrologue(fs, sources[Project.third][Source.ts][Part.one], `"myPrologue";`); 331 addTestPrologue(fs, sources[Project.third][Source.ts][Part.one], `"myPrologue3";`); 332 }, 333 modifyAgainFs: fs => addTestPrologue(fs, relSources[Project.first][Source.ts][Part.one], `"myPrologue5"`) 334 }); 335 336 // Verify ignore dtsChanged 337 verifyOutFileScenario({ 338 subScenario: "multiple prologues in different projects", 339 modifyFs: fs => { 340 enableStrict(fs, sources[Project.first][Source.config]); 341 addTestPrologue(fs, sources[Project.second][Source.ts][Part.one], `"myPrologue"`); 342 addTestPrologue(fs, sources[Project.second][Source.ts][Part.two], `"myPrologue2";`); 343 enableStrict(fs, sources[Project.third][Source.config]); 344 }, 345 modifyAgainFs: fs => addTestPrologue(fs, sources[Project.first][Source.ts][Part.one], `"myPrologue5"`), 346 ignoreDtsChanged: true, 347 baselineOnly: true 348 }); 349 }); 350 351 // Shebang 352 describe("Shebang", () => { 353 // changes declaration because its emitted in .d.ts file 354 // Verify initial + incremental edits 355 verifyOutFileScenario({ 356 subScenario: "shebang in all projects", 357 modifyFs: fs => { 358 addShebang(fs, "first", "first_PART1"); 359 addShebang(fs, "first", "first_part2"); 360 addShebang(fs, "second", "second_part1"); 361 addShebang(fs, "third", "third_part1"); 362 }, 363 }); 364 365 // Verify ignore dtsChanged 366 verifyOutFileScenario({ 367 subScenario: "shebang in only one dependency project", 368 modifyFs: fs => addShebang(fs, "second", "second_part1"), 369 ignoreDtsChanged: true, 370 baselineOnly: true 371 }); 372 }); 373 374 // emitHelpers 375 describe("emitHelpers", () => { 376 // Verify initial + incremental edits 377 verifyOutFileScenario({ 378 subScenario: "emitHelpers in all projects", 379 modifyFs: fs => { 380 addRest(fs, "first", "first_PART1"); 381 addRest(fs, "second", "second_part1"); 382 addRest(fs, "third", "third_part1"); 383 }, 384 modifyAgainFs: fs => removeRest(fs, "first", "first_PART1") 385 }); 386 387 // Verify ignore dtsChanged 388 verifyOutFileScenario({ 389 subScenario: "emitHelpers in only one dependency project", 390 modifyFs: fs => { 391 addStubFoo(fs, "first", "first_PART1"); 392 addRest(fs, "second", "second_part1"); 393 }, 394 modifyAgainFs: fs => changeStubToRest(fs, "first", "first_PART1"), 395 ignoreDtsChanged: true, 396 baselineOnly: true 397 }); 398 399 // Verify ignore dtsChanged 400 verifyOutFileScenario({ 401 subScenario: "multiple emitHelpers in all projects", 402 modifyFs: fs => { 403 addRest(fs, "first", "first_PART1"); 404 addSpread(fs, "first", "first_part3"); 405 addRest(fs, "second", "second_part1"); 406 addSpread(fs, "second", "second_part2"); 407 addRest(fs, "third", "third_part1"); 408 addSpread(fs, "third", "third_part1"); 409 }, 410 modifyAgainFs: fs => removeRest(fs, "first", "first_PART1"), 411 ignoreDtsChanged: true, 412 baselineOnly: true 413 }); 414 415 // Verify ignore dtsChanged 416 verifyOutFileScenario({ 417 subScenario: "multiple emitHelpers in different projects", 418 modifyFs: fs => { 419 addRest(fs, "first", "first_PART1"); 420 addSpread(fs, "second", "second_part1"); 421 addRest(fs, "third", "third_part1"); 422 }, 423 modifyAgainFs: fs => removeRest(fs, "first", "first_PART1"), 424 ignoreDtsChanged: true, 425 baselineOnly: true 426 }); 427 }); 428 429 // triple slash refs 430 describe("triple slash refs", () => { 431 // changes declaration because its emitted in .d.ts file 432 // Verify initial + incremental edits 433 verifyOutFileScenario({ 434 subScenario: "triple slash refs in all projects", 435 modifyFs: fs => { 436 addTripleSlashRef(fs, "first", "first_part2"); 437 addTripleSlashRef(fs, "second", "second_part1"); 438 addTripleSlashRef(fs, "third", "third_part1"); 439 } 440 }); 441 442 // Verify ignore dtsChanged 443 verifyOutFileScenario({ 444 subScenario: "triple slash refs in one project", 445 modifyFs: fs => addTripleSlashRef(fs, "second", "second_part1"), 446 ignoreDtsChanged: true, 447 baselineOnly: true 448 }); 449 }); 450 451 describe("stripInternal", () => { 452 function disableRemoveComments(fs: vfs.FileSystem, file: string) { 453 replaceText(fs, file, `"removeComments": true`, `"removeComments": false`); 454 } 455 456 function diableRemoveCommentsInAll(fs: vfs.FileSystem) { 457 disableRemoveComments(fs, sources[Project.first][Source.config]); 458 disableRemoveComments(fs, sources[Project.second][Source.config]); 459 disableRemoveComments(fs, sources[Project.third][Source.config]); 460 } 461 462 function stripInternalOfThird(fs: vfs.FileSystem) { 463 replaceText(fs, sources[Project.third][Source.config], `"declaration": true,`, `"declaration": true, 464 "stripInternal": true,`); 465 } 466 467 function stripInternalScenario(fs: vfs.FileSystem, removeCommentsDisabled?: boolean, jsDocStyle?: boolean) { 468 const internal: string = jsDocStyle ? `/**@internal*/` : `/*@internal*/`; 469 if (removeCommentsDisabled) { 470 diableRemoveCommentsInAll(fs); 471 } 472 stripInternalOfThird(fs); 473 replaceText(fs, sources[Project.first][Source.ts][Part.one], "interface", `${internal} interface`); 474 appendText(fs, sources[Project.second][Source.ts][Part.one], ` 475class normalC { 476 ${internal} constructor() { } 477 ${internal} prop: string; 478 ${internal} method() { } 479 ${internal} get c() { return 10; } 480 ${internal} set c(val: number) { } 481} 482namespace normalN { 483 ${internal} export class C { } 484 ${internal} export function foo() {} 485 ${internal} export namespace someNamespace { export class C {} } 486 ${internal} export namespace someOther.something { export class someClass {} } 487 ${internal} export import someImport = someNamespace.C; 488 ${internal} export type internalType = internalC; 489 ${internal} export const internalConst = 10; 490 ${internal} export enum internalEnum { a, b, c } 491} 492${internal} class internalC {} 493${internal} function internalfoo() {} 494${internal} namespace internalNamespace { export class someClass {} } 495${internal} namespace internalOther.something { export class someClass {} } 496${internal} import internalImport = internalNamespace.someClass; 497${internal} type internalType = internalC; 498${internal} const internalConst = 10; 499${internal} enum internalEnum { a, b, c }`); 500 } 501 502 // Verify initial + incremental edits 503 verifyOutFileScenario({ 504 subScenario: "stripInternal", 505 modifyFs: stripInternalScenario, 506 modifyAgainFs: fs => replaceText(fs, sources[Project.first][Source.ts][Part.one], `/*@internal*/ interface`, "interface"), 507 }); 508 509 // Verify ignore dtsChanged 510 verifyOutFileScenario({ 511 subScenario: "stripInternal with comments emit enabled", 512 modifyFs: fs => stripInternalScenario(fs, /*removeCommentsDisabled*/ true), 513 modifyAgainFs: fs => replaceText(fs, sources[Project.first][Source.ts][Part.one], `/*@internal*/ interface`, "interface"), 514 ignoreDtsChanged: true, 515 baselineOnly: true 516 }); 517 518 // Verify ignore dtsChanged 519 verifyOutFileScenario({ 520 subScenario: "stripInternal jsdoc style comment", 521 modifyFs: fs => stripInternalScenario(fs, /*removeCommentsDisabled*/ false, /*jsDocStyle*/ true), 522 modifyAgainFs: fs => replaceText(fs, sources[Project.first][Source.ts][Part.one], `/**@internal*/ interface`, "interface"), 523 ignoreDtsChanged: true, 524 baselineOnly: true 525 }); 526 527 // Verify ignore dtsChanged 528 verifyOutFileScenario({ 529 subScenario: "stripInternal jsdoc style with comments emit enabled", 530 modifyFs: fs => stripInternalScenario(fs, /*removeCommentsDisabled*/ true, /*jsDocStyle*/ true), 531 ignoreDtsChanged: true, 532 baselineOnly: true 533 }); 534 535 describe("with three levels of project dependency", () => { 536 function makeOneTwoThreeDependOrder(fs: vfs.FileSystem) { 537 replaceText(fs, sources[Project.second][Source.config], "[", `[ 538 { "path": "../first", "prepend": true }`); 539 replaceText(fs, sources[Project.third][Source.config], `{ "path": "../first", "prepend": true },`, ""); 540 } 541 542 function stripInternalWithDependentOrder(fs: vfs.FileSystem, removeCommentsDisabled?: boolean, jsDocStyle?: boolean) { 543 stripInternalScenario(fs, removeCommentsDisabled, jsDocStyle); 544 makeOneTwoThreeDependOrder(fs); 545 } 546 547 // Verify initial + incremental edits 548 verifyOutFileScenario({ 549 subScenario: "stripInternal when one-two-three are prepended in order", 550 modifyFs: stripInternalWithDependentOrder, 551 modifyAgainFs: fs => replaceText(fs, sources[Project.first][Source.ts][Part.one], `/*@internal*/ interface`, "interface"), 552 }); 553 554 // Verify ignore dtsChanged 555 verifyOutFileScenario({ 556 subScenario: "stripInternal with comments emit enabled when one-two-three are prepended in order", 557 modifyFs: fs => stripInternalWithDependentOrder(fs, /*removeCommentsDisabled*/ true), 558 modifyAgainFs: fs => replaceText(fs, sources[Project.first][Source.ts][Part.one], `/*@internal*/ interface`, "interface"), 559 ignoreDtsChanged: true, 560 baselineOnly: true 561 }); 562 563 // Verify ignore dtsChanged 564 verifyOutFileScenario({ 565 subScenario: "stripInternal jsdoc style comment when one-two-three are prepended in order", 566 modifyFs: fs => stripInternalWithDependentOrder(fs, /*removeCommentsDisabled*/ false, /*jsDocStyle*/ true), 567 modifyAgainFs: fs => replaceText(fs, sources[Project.first][Source.ts][Part.one], `/**@internal*/ interface`, "interface"), 568 ignoreDtsChanged: true, 569 baselineOnly: true 570 }); 571 572 // Verify ignore dtsChanged 573 verifyOutFileScenario({ 574 subScenario: "stripInternal jsdoc style with comments emit enabled when one-two-three are prepended in order", 575 modifyFs: fs => stripInternalWithDependentOrder(fs, /*removeCommentsDisabled*/ true, /*jsDocStyle*/ true), 576 ignoreDtsChanged: true, 577 baselineOnly: true 578 }); 579 }); 580 581 // only baseline 582 verifyOutFileScenario({ 583 subScenario: "stripInternal baseline when internal is inside another internal", 584 modifyFs: fs => { 585 stripInternalOfThird(fs); 586 prependText(fs, sources[Project.first][Source.ts][Part.one], `namespace ts { 587 /* @internal */ 588 /** 589 * Subset of properties from SourceFile that are used in multiple utility functions 590 */ 591 export interface SourceFileLike { 592 readonly text: string; 593 lineMap?: ReadonlyArray<number>; 594 /* @internal */ 595 getPositionOfLineAndCharacter?(line: number, character: number, allowEdits?: true): number; 596 } 597 598 /* @internal */ 599 export interface RedirectInfo { 600 /** Source file this redirects to. */ 601 readonly redirectTarget: SourceFile; 602 /** 603 * Source file for the duplicate package. This will not be used by the Program, 604 * but we need to keep this around so we can watch for changes in underlying. 605 */ 606 readonly unredirected: SourceFile; 607 } 608 609 // Source files are declarations when they are external modules. 610 export interface SourceFile { 611 someProp: string; 612 } 613}`); 614 }, 615 ignoreDtsChanged: true, 616 ignoreDtsUnchanged: true, 617 baselineOnly: true 618 }); 619 620 // only baseline 621 verifyOutFileScenario({ 622 subScenario: "stripInternal when few members of enum are internal", 623 modifyFs: fs => { 624 stripInternalOfThird(fs); 625 prependText(fs, sources[Project.first][Source.ts][Part.one], `enum TokenFlags { 626 None = 0, 627 /* @internal */ 628 PrecedingLineBreak = 1 << 0, 629 /* @internal */ 630 PrecedingJSDocComment = 1 << 1, 631 /* @internal */ 632 Unterminated = 1 << 2, 633 /* @internal */ 634 ExtendedUnicodeEscape = 1 << 3, 635 Scientific = 1 << 4, 636 Octal = 1 << 5, 637 HexSpecifier = 1 << 6, 638 BinarySpecifier = 1 << 7, 639 OctalSpecifier = 1 << 8, 640 /* @internal */ 641 ContainsSeparator = 1 << 9, 642 /* @internal */ 643 BinaryOrOctalSpecifier = BinarySpecifier | OctalSpecifier, 644 /* @internal */ 645 NumericLiteralFlags = Scientific | Octal | HexSpecifier | BinaryOrOctalSpecifier | ContainsSeparator 646} 647`); 648 }, 649 ignoreDtsChanged: true, 650 ignoreDtsUnchanged: true, 651 baselineOnly: true 652 }); 653 654 verifyOutFileScenario({ 655 subScenario: "stripInternal when prepend is completely internal", 656 baselineOnly: true, 657 ignoreDtsChanged: true, 658 ignoreDtsUnchanged: true, 659 modifyFs: fs => { 660 fs.writeFileSync(sources[Project.first][Source.ts][Part.one], "/* @internal */ const A = 1;"); 661 fs.writeFileSync(sources[Project.third][Source.ts][Part.one], "const B = 2;"); 662 fs.writeFileSync(sources[Project.first][Source.config], JSON.stringify({ 663 compilerOptions: { 664 composite: true, 665 declaration: true, 666 declarationMap: true, 667 skipDefaultLibCheck: true, 668 sourceMap: true, 669 outFile: "./bin/first-output.js" 670 }, 671 files: [sources[Project.first][Source.ts][Part.one]] 672 })); 673 fs.writeFileSync(sources[Project.third][Source.config], JSON.stringify({ 674 compilerOptions: { 675 composite: true, 676 declaration: true, 677 declarationMap: false, 678 stripInternal: true, 679 sourceMap: true, 680 outFile: "./thirdjs/output/third-output.js", 681 }, 682 references: [{ path: "../first", prepend: true }], 683 files: [sources[Project.third][Source.ts][Part.one]] 684 })); 685 } 686 }); 687 }); 688 689 describe("empty source files", () => { 690 function makeThirdEmptySourceFile(fs: vfs.FileSystem) { 691 fs.writeFileSync(sources[Project.third][Source.ts][Part.one], "", "utf8"); 692 } 693 694 // Verify ignore dtsChanged 695 verifyOutFileScenario({ 696 subScenario: "when source files are empty in the own file", 697 modifyFs: makeThirdEmptySourceFile, 698 ignoreDtsChanged: true, 699 baselineOnly: true 700 }); 701 702 // only baseline 703 verifyOutFileScenario({ 704 subScenario: "declarationMap and sourceMap disabled", 705 modifyFs: fs => { 706 makeThirdEmptySourceFile(fs); 707 replaceText(fs, sources[Project.third][Source.config], `"composite": true,`, ""); 708 replaceText(fs, sources[Project.third][Source.config], `"sourceMap": true,`, ""); 709 replaceText(fs, sources[Project.third][Source.config], `"declarationMap": true,`, ""); 710 }, 711 ignoreDtsChanged: true, 712 ignoreDtsUnchanged: true, 713 baselineOnly: true 714 }); 715 }); 716 }); 717 718 verifyTsc({ 719 scenario: "outFile", 720 subScenario: "non module projects without prepend", 721 fs: () => outFileFs, 722 commandLineArgs: ["--b", "/src/third", "--verbose"], 723 modifyFs: fs => { 724 // No prepend 725 replaceText(fs, sources[Project.third][Source.config], `{ "path": "../first", "prepend": true }`, `{ "path": "../first" }`); 726 replaceText(fs, sources[Project.third][Source.config], `{ "path": "../second", "prepend": true }`, `{ "path": "../second" }`); 727 728 // Non Modules 729 replaceText(fs, sources[Project.first][Source.config], `"composite": true,`, `"composite": true, "module": "none",`); 730 replaceText(fs, sources[Project.second][Source.config], `"composite": true,`, `"composite": true, "module": "none",`); 731 replaceText(fs, sources[Project.third][Source.config], `"composite": true,`, `"composite": true, "module": "none",`); 732 733 // Own file emit 734 replaceText(fs, sources[Project.first][Source.config], `"outFile": "./bin/first-output.js",`, ""); 735 replaceText(fs, sources[Project.second][Source.config], `"outFile": "../2/second-output.js",`, ""); 736 replaceText(fs, sources[Project.third][Source.config], `"outFile": "./thirdjs/output/third-output.js",`, ""); 737 }, 738 }); 739 }); 740} 741