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