• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1namespace ts {
2    describe("unittests:: builder", () => {
3        it("emits dependent files", () => {
4            const files: NamedSourceText[] = [
5                { name: "/a.ts", text: SourceText.New("", 'import { b } from "./b";', "") },
6                { name: "/b.ts", text: SourceText.New("", ' import { c } from "./c";', "export const b = c;") },
7                { name: "/c.ts", text: SourceText.New("", "", "export const c = 0;") },
8            ];
9
10            let program = newProgram(files, ["/a.ts"], {});
11            const assertChanges = makeAssertChanges(() => program);
12
13            assertChanges(["/c.js", "/b.js", "/a.js"]);
14
15            program = updateProgramFile(program, "/a.ts", "//comment");
16            assertChanges(["/a.js"]);
17
18            program = updateProgramFile(program, "/b.ts", "export const b = c + 1;");
19            assertChanges(["/b.js", "/a.js"]);
20
21            program = updateProgramFile(program, "/c.ts", "export const c = 1;");
22            assertChanges(["/c.js", "/b.js"]);
23        });
24
25        it("if emitting all files, emits the changed file first", () => {
26            const files: NamedSourceText[] = [
27                { name: "/a.ts", text: SourceText.New("", "", "namespace A { export const x = 0; }") },
28                { name: "/b.ts", text: SourceText.New("", "", "namespace B { export const x = 0; }") },
29            ];
30
31            let program = newProgram(files, ["/a.ts", "/b.ts"], {});
32            const assertChanges = makeAssertChanges(() => program);
33
34            assertChanges(["/a.js", "/b.js"]);
35
36            program = updateProgramFile(program, "/a.ts", "namespace A { export const x = 1; }");
37            assertChanges(["/a.js", "/b.js"]);
38
39            program = updateProgramFile(program, "/b.ts", "namespace B { export const x = 1; }");
40            assertChanges(["/b.js", "/a.js"]);
41        });
42
43        it("keeps the file in affected files if cancellation token throws during the operation", () => {
44            const files: NamedSourceText[] = [
45                { name: "/a.ts", text: SourceText.New("", 'import { b } from "./b";', "") },
46                { name: "/b.ts", text: SourceText.New("", ' import { c } from "./c";', "export const b = c;") },
47                { name: "/c.ts", text: SourceText.New("", "", "export const c = 0;") },
48                { name: "/d.ts", text: SourceText.New("", "", "export const dd = 0;") },
49                { name: "/e.ts", text: SourceText.New("", "", "export const ee = 0;") },
50            ];
51
52            let program = newProgram(files, ["/d.ts", "/e.ts", "/a.ts"], {});
53            const assertChanges = makeAssertChangesWithCancellationToken(() => program);
54            // No cancellation
55            assertChanges(["/d.js", "/e.js", "/c.js", "/b.js", "/a.js"]);
56
57            // cancel when emitting a.ts
58            program = updateProgramFile(program, "/a.ts", "export function foo() { }");
59            assertChanges(["/a.js"], 0);
60            // Change d.ts and verify previously pending a.ts is emitted as well
61            program = updateProgramFile(program, "/d.ts", "export function bar() { }");
62            assertChanges(["/a.js", "/d.js"]);
63
64            // Cancel when emitting b.js
65            program = updateProgramFile(program, "/b.ts", "export class b { foo() { c + 1; } }");
66            program = updateProgramFile(program, "/d.ts", "export function bar2() { }");
67            assertChanges(["/d.js", "/b.js", "/a.js"], 1);
68            // Change e.ts and verify previously b.js as well as a.js get emitted again since previous change was consumed completely but not d.ts
69            program = updateProgramFile(program, "/e.ts", "export function bar3() { }");
70            assertChanges(["/b.js", "/a.js", "/e.js"]);
71        });
72    });
73
74    function makeAssertChanges(getProgram: () => Program): (fileNames: readonly string[]) => void {
75        const host: BuilderProgramHost = { useCaseSensitiveFileNames: returnTrue };
76        let builderProgram: EmitAndSemanticDiagnosticsBuilderProgram | undefined;
77        return fileNames => {
78            const program = getProgram();
79            builderProgram = createEmitAndSemanticDiagnosticsBuilderProgram(program, host, builderProgram);
80            const outputFileNames: string[] = [];
81            // eslint-disable-next-line no-empty
82            while (builderProgram.emitNextAffectedFile(fileName => outputFileNames.push(fileName))) {
83            }
84            assert.deepEqual(outputFileNames, fileNames);
85        };
86    }
87
88    function makeAssertChangesWithCancellationToken(getProgram: () => Program): (fileNames: readonly string[], cancelAfterEmitLength?: number) => void {
89        const host: BuilderProgramHost = { useCaseSensitiveFileNames: returnTrue };
90        let builderProgram: EmitAndSemanticDiagnosticsBuilderProgram | undefined;
91        let cancel = false;
92        const cancellationToken: CancellationToken = {
93            isCancellationRequested: () => cancel,
94            throwIfCancellationRequested: () => {
95                if (cancel) {
96                    throw new OperationCanceledException();
97                }
98            },
99        };
100        return (fileNames, cancelAfterEmitLength?: number) => {
101            cancel = false;
102            let operationWasCancelled = false;
103            const program = getProgram();
104            builderProgram = createEmitAndSemanticDiagnosticsBuilderProgram(program, host, builderProgram);
105            const outputFileNames: string[] = [];
106            try {
107                do {
108                    assert.isFalse(cancel);
109                    if (outputFileNames.length === cancelAfterEmitLength) {
110                        cancel = true;
111                    }
112                } while (builderProgram.emitNextAffectedFile(fileName => outputFileNames.push(fileName), cancellationToken));
113            }
114            catch (e) {
115                assert.isFalse(operationWasCancelled);
116                assert(e instanceof OperationCanceledException, e.toString());
117                operationWasCancelled = true;
118            }
119            assert.equal(cancel, operationWasCancelled);
120            assert.equal(operationWasCancelled, fileNames.length > cancelAfterEmitLength!);
121            assert.deepEqual(outputFileNames, fileNames.slice(0, cancelAfterEmitLength));
122        };
123    }
124
125    function updateProgramFile(program: ProgramWithSourceTexts, fileName: string, fileContent: string): ProgramWithSourceTexts {
126        return updateProgram(program, program.getRootFileNames(), program.getCompilerOptions(), files => {
127            updateProgramText(files, fileName, fileContent);
128        });
129    }
130}
131