• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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