Skip to content

Commit f1949bb

Browse files
committed
Use emit builder to emit only changed files.
1 parent 47f5106 commit f1949bb

File tree

3 files changed

+78
-17
lines changed

3 files changed

+78
-17
lines changed

Diff for: src/compiler/builder.ts

+76-13
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,18 @@ namespace ts {
5454
* compilerOptions for the program
5555
*/
5656
compilerOptions: CompilerOptions;
57+
/**
58+
* Files pending to be emitted
59+
*/
60+
affectedFilesPendingEmit: ReadonlyArray<Path> | undefined;
61+
/**
62+
* Current index to retrieve pending affected file
63+
*/
64+
affectedFilesPendingEmitIndex: number | undefined;
65+
/**
66+
* Already seen affected files
67+
*/
68+
seenEmittedFiles: Map<true> | undefined;
5769
}
5870

5971
function hasSameKeys<T, U>(map1: ReadonlyMap<T> | undefined, map2: ReadonlyMap<U> | undefined): boolean {
@@ -89,6 +101,10 @@ namespace ts {
89101

90102
// Copy old state's changed files set
91103
copyEntries(oldState!.changedFilesSet, state.changedFilesSet);
104+
if (!compilerOptions.outFile && !compilerOptions.out && oldState!.affectedFilesPendingEmit) {
105+
state.affectedFilesPendingEmit = oldState!.affectedFilesPendingEmit;
106+
state.affectedFilesPendingEmitIndex = oldState!.affectedFilesPendingEmitIndex;
107+
}
92108
}
93109

94110
// Update changed files and copy semantic diagnostics if we can
@@ -203,6 +219,27 @@ namespace ts {
203219
}
204220
}
205221

222+
/**
223+
* Returns next file to be emitted from files that retrieved semantic diagnostics but did not emit yet
224+
*/
225+
function getNextAffectedFilePendingEmit(state: BuilderProgramState): SourceFile | undefined {
226+
const { affectedFilesPendingEmit } = state;
227+
if (affectedFilesPendingEmit) {
228+
const seenEmittedFiles = state.seenEmittedFiles || (state.seenEmittedFiles = createMap());
229+
for (let affectedFilesIndex = state.affectedFilesPendingEmitIndex!; affectedFilesIndex < affectedFilesPendingEmit.length; affectedFilesIndex++) {
230+
const affectedFile = Debug.assertDefined(state.program).getSourceFileByPath(affectedFilesPendingEmit[affectedFilesIndex]);
231+
if (affectedFile && !seenEmittedFiles.has(affectedFile.path)) {
232+
// emit this file
233+
state.affectedFilesPendingEmitIndex = affectedFilesIndex;
234+
return affectedFile;
235+
}
236+
}
237+
state.affectedFilesPendingEmit = undefined;
238+
state.affectedFilesPendingEmitIndex = undefined;
239+
}
240+
return undefined;
241+
}
242+
206243
/**
207244
* Remove the semantic diagnostics cached from old state for affected File and the files that are referencing modules that export entities from affected file
208245
*/
@@ -312,21 +349,26 @@ namespace ts {
312349
* This is called after completing operation on the next affected file.
313350
* The operations here are postponed to ensure that cancellation during the iteration is handled correctly
314351
*/
315-
function doneWithAffectedFile(state: BuilderProgramState, affected: SourceFile | Program) {
352+
function doneWithAffectedFile(state: BuilderProgramState, affected: SourceFile | Program, isPendingEmit?: boolean) {
316353
if (affected === state.program) {
317354
state.changedFilesSet.clear();
318355
}
319356
else {
320357
state.seenAffectedFiles!.set((affected as SourceFile).path, true);
321-
state.affectedFilesIndex!++;
358+
if (isPendingEmit) {
359+
state.affectedFilesPendingEmitIndex!++;
360+
}
361+
else {
362+
state.affectedFilesIndex!++;
363+
}
322364
}
323365
}
324366

325367
/**
326368
* Returns the result with affected file
327369
*/
328-
function toAffectedFileResult<T>(state: BuilderProgramState, result: T, affected: SourceFile | Program): AffectedFileResult<T> {
329-
doneWithAffectedFile(state, affected);
370+
function toAffectedFileResult<T>(state: BuilderProgramState, result: T, affected: SourceFile | Program, isPendingEmit?: boolean): AffectedFileResult<T> {
371+
doneWithAffectedFile(state, affected, isPendingEmit);
330372
return { result, affected };
331373
}
332374

@@ -442,18 +484,29 @@ namespace ts {
442484
* in that order would be used to write the files
443485
*/
444486
function emitNextAffectedFile(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): AffectedFileResult<EmitResult> {
445-
const affected = getNextAffectedFile(state, cancellationToken, computeHash);
487+
let affected = getNextAffectedFile(state, cancellationToken, computeHash);
488+
let isPendingEmitFile = false;
446489
if (!affected) {
490+
affected = getNextAffectedFilePendingEmit(state);
447491
// Done
448-
return undefined;
492+
if (!affected) {
493+
return undefined;
494+
}
495+
isPendingEmitFile = true;
496+
}
497+
498+
// Mark seen emitted files if there are pending files to be emitted
499+
if (state.affectedFilesPendingEmit && state.program !== affected) {
500+
(state.seenEmittedFiles || (state.seenEmittedFiles = createMap())).set((affected as SourceFile).path, true);
449501
}
450502

451503
return toAffectedFileResult(
452504
state,
453505
// When whole program is affected, do emit only once (eg when --out or --outFile is specified)
454506
// Otherwise just affected file
455507
Debug.assertDefined(state.program).emit(affected === state.program ? undefined : affected as SourceFile, writeFile || host.writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers),
456-
affected
508+
affected,
509+
isPendingEmitFile
457510
);
458511
}
459512

@@ -552,12 +605,22 @@ namespace ts {
552605
return getSemanticDiagnosticsOfFile(state, sourceFile, cancellationToken);
553606
}
554607

555-
if (kind === BuilderProgramKind.SemanticDiagnosticsBuilderProgram) {
556-
// When semantic builder asks for diagnostics of the whole program,
557-
// ensure that all the affected files are handled
558-
let affected: SourceFile | Program | undefined;
559-
while (affected = getNextAffectedFile(state, cancellationToken, computeHash)) {
560-
doneWithAffectedFile(state, affected);
608+
// When semantic builder asks for diagnostics of the whole program,
609+
// ensure that all the affected files are handled
610+
let affected: SourceFile | Program | undefined;
611+
let affectedFilesPendingEmit: Path[] | undefined;
612+
while (affected = getNextAffectedFile(state, cancellationToken, computeHash)) {
613+
if (affected !== state.program && kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) {
614+
(affectedFilesPendingEmit || (affectedFilesPendingEmit = [])).push((affected as SourceFile).path);
615+
}
616+
doneWithAffectedFile(state, affected);
617+
}
618+
619+
// In case of emit builder, cache the files to be emitted
620+
if (affectedFilesPendingEmit) {
621+
state.affectedFilesPendingEmit = concatenate(state.affectedFilesPendingEmit, affectedFilesPendingEmit);
622+
if (state.affectedFilesPendingEmitIndex === undefined) {
623+
state.affectedFilesPendingEmitIndex = 0;
561624
}
562625
}
563626

Diff for: src/compiler/tsbuild.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -389,9 +389,8 @@ namespace ts {
389389
return host;
390390
}
391391

392-
// TODO: we should use emit and semantic diagnostics builder but that needs to handle errors little differently so handle it later
393392
export function createSolutionBuilderWithWatchHost<T extends BuilderProgram = SemanticDiagnosticsBuilderProgram>(system = sys, createProgram?: CreateProgram<T>, reportDiagnostic?: DiagnosticReporter, reportSolutionBuilderStatus?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter) {
394-
const host = createSolutionBuilderHostBase(system, createProgram || createSemanticDiagnosticsBuilderProgram as any as CreateProgram<T>, reportDiagnostic, reportSolutionBuilderStatus) as SolutionBuilderWithWatchHost<T>;
393+
const host = createSolutionBuilderHostBase(system, createProgram || createEmitAndSemanticDiagnosticsBuilderProgram as any as CreateProgram<T>, reportDiagnostic, reportSolutionBuilderStatus) as SolutionBuilderWithWatchHost<T>;
395394
const watchHost = createWatchHost(system, reportWatchStatus);
396395
copyProperities(host, watchHost);
397396
return host;

Diff for: src/tsc/tsc.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -204,9 +204,8 @@ namespace ts {
204204
reportWatchModeWithoutSysSupport();
205205
}
206206

207-
// TODO: change this to host if watch => watchHost otherwiue without watch
208207
const buildHost = buildOptions.watch ?
209-
createSolutionBuilderWithWatchHost(sys, createSemanticDiagnosticsBuilderProgram, reportDiagnostic, createBuilderStatusReporter(sys, shouldBePretty()), createWatchStatusReporter()) :
208+
createSolutionBuilderWithWatchHost(sys, createEmitAndSemanticDiagnosticsBuilderProgram, reportDiagnostic, createBuilderStatusReporter(sys, shouldBePretty()), createWatchStatusReporter()) :
210209
createSolutionBuilderHost(sys, createAbstractBuilder, reportDiagnostic, createBuilderStatusReporter(sys, shouldBePretty()), createReportErrorSummary(buildOptions));
211210
updateCreateProgram(buildHost);
212211
buildHost.afterProgramEmitAndDiagnostics = (program: BuilderProgram) => reportStatistics(program.getProgram());

0 commit comments

Comments
 (0)