Skip to content

Commit 489f54c

Browse files
committed
Call Program#emit only once, fix performance issue (#495)
1 parent 7c91b82 commit 489f54c

File tree

1 file changed

+65
-32
lines changed

1 file changed

+65
-32
lines changed

lib/compiler.ts

Lines changed: 65 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,16 @@ export interface ICompiler {
1313
inputDone(): void;
1414
}
1515

16+
interface OutputFile {
17+
file: File | undefined;
18+
19+
jsFileName?: string;
20+
dtsFileName?: string;
21+
jsContent?: string;
22+
jsMapContent?: string;
23+
dtsContent?: string;
24+
}
25+
1626
/**
1727
* Compiles a whole project, with full type checking
1828
*/
@@ -72,51 +82,74 @@ export class ProjectCompiler implements ICompiler {
7282
}
7383

7484
if (this.project.singleOutput) {
75-
this.emitFile(result, currentDirectory);
85+
const output: OutputFile = {
86+
file: undefined
87+
};
88+
89+
this.emit(result, (fileName, content) => {
90+
this.attachContentToFile(output, fileName, content);
91+
});
92+
93+
this.emitFile(output, currentDirectory);
7694
} else {
77-
// Emit files one by one
78-
for (const fileName of this.host.input.getFileNames(true)) {
95+
const output: utils.Map<OutputFile> = {};
96+
97+
const input = this.host.input.getFileNames(true);
98+
99+
for (let i = 0; i < input.length; i++) {
100+
const fileName = utils.normalizePath(input[i]);
79101
const file = this.project.input.getFile(fileName);
80102

81-
this.emitFile(result, currentDirectory, file);
103+
output[fileName] = { file };
104+
}
105+
106+
this.emit(result, (fileName, content, writeByteOrderMark, onError, sourceFiles) => {
107+
if (sourceFiles.length !== 1) {
108+
throw new Error("Failure: sourceFiles in WriteFileCallback should have length 1, got " + sourceFiles.length);
109+
}
110+
111+
const fileNameOriginal = utils.normalizePath(sourceFiles[0].fileName);
112+
const file = output[fileNameOriginal];
113+
if (!file) return;
114+
115+
this.attachContentToFile(file, fileName, content);
116+
});
117+
118+
for (let i = 0; i < input.length; i++) {
119+
const fileName = utils.normalizePath(input[i]);
120+
this.emitFile(output[fileName], currentDirectory);
82121
}
83122
}
84123

85124
this.project.output.finish(result);
86125
}
87126

88-
private emitFile(result: CompilationResult, currentDirectory: string, file?: File) {
89-
let jsFileName: string;
90-
let dtsFileName: string;
91-
let jsContent: string;
92-
let dtsContent: string;
93-
let jsMapContent: string;
94-
95-
const emitOutput = this.program.emit(file && file.ts, (fileName: string, content: string) => {
96-
const [, extension] = utils.splitExtension(fileName, ['d.ts']);
97-
switch (extension) {
98-
case 'js':
99-
case 'jsx':
100-
jsFileName = fileName;
101-
jsContent = content;
102-
break;
103-
case 'd.ts':
104-
dtsFileName = fileName;
105-
dtsContent = content;
106-
break;
107-
case 'map':
108-
jsMapContent = content;
109-
break;
110-
}
111-
});
127+
private attachContentToFile(file: OutputFile, fileName: string, content: string) {
128+
const [, extension] = utils.splitExtension(fileName, ['d.ts']);
129+
switch (extension) {
130+
case 'js':
131+
case 'jsx':
132+
file.jsFileName = fileName;
133+
file.jsContent = content;
134+
break;
135+
case 'd.ts':
136+
file.dtsFileName = fileName;
137+
file.dtsContent = content;
138+
break;
139+
case 'map':
140+
file.jsMapContent = content;
141+
break;
142+
}
143+
}
144+
private emit(result: CompilationResult, callback: ts.WriteFileCallback) {
145+
const emitOutput = this.program.emit(undefined, callback);
112146

113147
result.emitErrors += emitOutput.diagnostics.length;
114148
this.reportDiagnostics(emitOutput.diagnostics);
115149

116-
if (emitOutput.emitSkipped) {
117-
result.emitSkipped = true;
118-
}
119-
150+
result.emitSkipped = emitOutput.emitSkipped;
151+
}
152+
private emitFile({ file, jsFileName, dtsFileName, jsContent, dtsContent, jsMapContent }: OutputFile, currentDirectory: string) {
120153
if (!jsFileName) return;
121154

122155
let base: string;

0 commit comments

Comments
 (0)