Skip to content

Commit 9fbcb99

Browse files
authored
Emit build info even on noEmitOnError or tsc --build (#38853)
* Emit buildinfo when there are errors with noEmitOnError? TODO: --build mode * Always emit tsbuild info even if there are non syntax errors in tsc --build mode * Sort affectedFilesPendingEmit for consistent build info text
1 parent f0da6d1 commit 9fbcb99

File tree

25 files changed

+2548
-409
lines changed

25 files changed

+2548
-409
lines changed

src/compiler/builder.ts

+36-7
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ namespace ts {
148148
/**
149149
* true if build info is emitted
150150
*/
151-
emittedBuildInfo?: boolean;
151+
buildInfoEmitPending: boolean;
152152
/**
153153
* Already seen emitted files
154154
*/
@@ -250,14 +250,14 @@ namespace ts {
250250
BuilderState.getAllFilesExcludingDefaultLibraryFile(state, newProgram, /*firstSourceFile*/ undefined)
251251
.forEach(file => state.changedFilesSet.set(file.resolvedPath, true));
252252
}
253-
else if (oldCompilerOptions && compilerOptionsAffectEmit(compilerOptions, oldCompilerOptions)) {
253+
else if (oldCompilerOptions && !outFile(compilerOptions) && compilerOptionsAffectEmit(compilerOptions, oldCompilerOptions)) {
254254
// Add all files to affectedFilesPendingEmit since emit changed
255255
newProgram.getSourceFiles().forEach(f => addToAffectedFilesPendingEmit(state, f.resolvedPath, BuilderFileEmit.Full));
256256
Debug.assert(!state.seenAffectedFiles || !state.seenAffectedFiles.size);
257257
state.seenAffectedFiles = state.seenAffectedFiles || createMap<true>();
258258
}
259259

260-
state.emittedBuildInfo = !state.changedFilesSet.size && !state.affectedFilesPendingEmit;
260+
state.buildInfoEmitPending = !!state.changedFilesSet.size;
261261
return state;
262262
}
263263

@@ -611,7 +611,7 @@ namespace ts {
611611
isBuildInfoEmit?: boolean
612612
) {
613613
if (isBuildInfoEmit) {
614-
state.emittedBuildInfo = true;
614+
state.buildInfoEmitPending = false;
615615
}
616616
else if (affected === state.program) {
617617
state.changedFilesSet.clear();
@@ -624,6 +624,7 @@ namespace ts {
624624
}
625625
if (isPendingEmit) {
626626
state.affectedFilesPendingEmitIndex!++;
627+
state.buildInfoEmitPending = true;
627628
}
628629
else {
629630
state.affectedFilesIndex!++;
@@ -688,12 +689,14 @@ namespace ts {
688689
}
689690

690691
export type ProgramBuildInfoDiagnostic = string | [string, readonly ReusableDiagnostic[]];
692+
export type ProgramBuilderInfoFilePendingEmit = [string, BuilderFileEmit];
691693
export interface ProgramBuildInfo {
692694
fileInfos: MapLike<BuilderState.FileInfo>;
693695
options: CompilerOptions;
694696
referencedMap?: MapLike<string[]>;
695697
exportedModulesMap?: MapLike<string[]>;
696698
semanticDiagnosticsPerFile?: ProgramBuildInfoDiagnostic[];
699+
affectedFilesPendingEmit?: ProgramBuilderInfoFilePendingEmit[];
697700
}
698701

699702
/**
@@ -751,6 +754,17 @@ namespace ts {
751754
result.semanticDiagnosticsPerFile = semanticDiagnosticsPerFile;
752755
}
753756

757+
if (state.affectedFilesPendingEmit) {
758+
const affectedFilesPendingEmit: ProgramBuilderInfoFilePendingEmit[] = [];
759+
const seenFiles = createMap<true>();
760+
for (const path of state.affectedFilesPendingEmit.slice(state.affectedFilesPendingEmitIndex).sort(compareStringsCaseSensitive)) {
761+
if (addToSeen(seenFiles, path)) {
762+
affectedFilesPendingEmit.push([relativeToBuildInfo(path), state.affectedFilesPendingEmitKind!.get(path)!]);
763+
}
764+
}
765+
result.affectedFilesPendingEmit = affectedFilesPendingEmit;
766+
}
767+
754768
return result;
755769

756770
function relativeToBuildInfoEnsuringAbsolutePath(path: string) {
@@ -916,13 +930,23 @@ namespace ts {
916930
else if (kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) {
917931
(builderProgram as EmitAndSemanticDiagnosticsBuilderProgram).getSemanticDiagnosticsOfNextAffectedFile = getSemanticDiagnosticsOfNextAffectedFile;
918932
(builderProgram as EmitAndSemanticDiagnosticsBuilderProgram).emitNextAffectedFile = emitNextAffectedFile;
933+
builderProgram.emitBuildInfo = emitBuildInfo;
919934
}
920935
else {
921936
notImplemented();
922937
}
923938

924939
return builderProgram;
925940

941+
function emitBuildInfo(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken): EmitResult {
942+
if (state.buildInfoEmitPending) {
943+
const result = Debug.checkDefined(state.program).emitBuildInfo(writeFile || maybeBind(host, host.writeFile), cancellationToken);
944+
state.buildInfoEmitPending = false;
945+
return result;
946+
}
947+
return emitSkippedWithNoDiagnostics;
948+
}
949+
926950
/**
927951
* Emits the next affected file's emit result (EmitResult and sourceFiles emitted) or returns undefined if iteration is complete
928952
* The first of writeFile if provided, writeFile of BuilderProgramHost if provided, writeFile of compiler host
@@ -936,7 +960,7 @@ namespace ts {
936960
if (!outFile(state.compilerOptions)) {
937961
const pendingAffectedFile = getNextAffectedFilePendingEmit(state);
938962
if (!pendingAffectedFile) {
939-
if (state.emittedBuildInfo) {
963+
if (!state.buildInfoEmitPending) {
940964
return undefined;
941965
}
942966

@@ -993,7 +1017,7 @@ namespace ts {
9931017
function emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult {
9941018
if (kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) {
9951019
assertSourceFileOkWithoutNextAffectedCall(state, targetSourceFile);
996-
const result = handleNoEmitOptions(builderProgram, targetSourceFile, cancellationToken);
1020+
const result = handleNoEmitOptions(builderProgram, targetSourceFile, writeFile, cancellationToken);
9971021
if (result) return result;
9981022
if (!targetSourceFile) {
9991023
// Emit and report any errors we ran into.
@@ -1142,7 +1166,10 @@ namespace ts {
11421166
referencedMap: getMapOfReferencedSet(program.referencedMap, toPath),
11431167
exportedModulesMap: getMapOfReferencedSet(program.exportedModulesMap, toPath),
11441168
semanticDiagnosticsPerFile: program.semanticDiagnosticsPerFile && arrayToMap(program.semanticDiagnosticsPerFile, value => toPath(isString(value) ? value : value[0]), value => isString(value) ? emptyArray : value[1]),
1145-
hasReusableDiagnostic: true
1169+
hasReusableDiagnostic: true,
1170+
affectedFilesPendingEmit: map(program.affectedFilesPendingEmit, value => toPath(value[0])),
1171+
affectedFilesPendingEmitKind: program.affectedFilesPendingEmit && arrayToMap(program.affectedFilesPendingEmit, value => toPath(value[0]), value => value[1]),
1172+
affectedFilesPendingEmitIndex: program.affectedFilesPendingEmit && 0,
11461173
};
11471174
return {
11481175
getState: () => state,
@@ -1165,6 +1192,7 @@ namespace ts {
11651192
getCurrentDirectory: notImplemented,
11661193
emitNextAffectedFile: notImplemented,
11671194
getSemanticDiagnosticsOfNextAffectedFile: notImplemented,
1195+
emitBuildInfo: notImplemented,
11681196
close: noop,
11691197
};
11701198

@@ -1195,6 +1223,7 @@ namespace ts {
11951223
getDeclarationDiagnostics: (sourceFile, cancellationToken) => getProgram().getDeclarationDiagnostics(sourceFile, cancellationToken),
11961224
getSemanticDiagnostics: (sourceFile, cancellationToken) => getProgram().getSemanticDiagnostics(sourceFile, cancellationToken),
11971225
emit: (sourceFile, writeFile, cancellationToken, emitOnlyDts, customTransformers) => getProgram().emit(sourceFile, writeFile, cancellationToken, emitOnlyDts, customTransformers),
1226+
emitBuildInfo: (writeFile, cancellationToken) => getProgram().emitBuildInfo(writeFile, cancellationToken),
11981227
getAllDependencies: notImplemented,
11991228
getCurrentDirectory: () => getProgram().getCurrentDirectory(),
12001229
close: noop,

src/compiler/builderPublic.ts

+2
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ namespace ts {
9999
* in that order would be used to write the files
100100
*/
101101
emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult;
102+
/*@internal*/
103+
emitBuildInfo(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken): EmitResult;
102104
/**
103105
* Get the current directory of the program
104106
*/

src/compiler/program.ts

+19-8
Original file line numberDiff line numberDiff line change
@@ -1585,7 +1585,7 @@ namespace ts {
15851585

15861586
function emitWorker(program: Program, sourceFile: SourceFile | undefined, writeFileCallback: WriteFileCallback | undefined, cancellationToken: CancellationToken | undefined, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers, forceDtsEmit?: boolean): EmitResult {
15871587
if (!forceDtsEmit) {
1588-
const result = handleNoEmitOptions(program, sourceFile, cancellationToken);
1588+
const result = handleNoEmitOptions(program, sourceFile, writeFileCallback, cancellationToken);
15891589
if (result) return result;
15901590
}
15911591

@@ -3638,11 +3638,17 @@ namespace ts {
36383638
}
36393639

36403640
/*@internal*/
3641-
export function handleNoEmitOptions(program: ProgramToEmitFilesAndReportErrors, sourceFile: SourceFile | undefined, cancellationToken: CancellationToken | undefined): EmitResult | undefined {
3641+
export const emitSkippedWithNoDiagnostics: EmitResult = { diagnostics: emptyArray, sourceMaps: undefined, emittedFiles: undefined, emitSkipped: true };
3642+
3643+
/*@internal*/
3644+
export function handleNoEmitOptions(
3645+
program: ProgramToEmitFilesAndReportErrors,
3646+
sourceFile: SourceFile | undefined,
3647+
writeFile: WriteFileCallback | undefined,
3648+
cancellationToken: CancellationToken | undefined
3649+
): EmitResult | undefined {
36423650
const options = program.getCompilerOptions();
3643-
if (options.noEmit) {
3644-
return { diagnostics: emptyArray, sourceMaps: undefined, emittedFiles: undefined, emitSkipped: true };
3645-
}
3651+
if (options.noEmit) return emitSkippedWithNoDiagnostics;
36463652

36473653
// If the noEmitOnError flag is set, then check if we have any errors so far. If so,
36483654
// immediately bail out. Note that we pass 'undefined' for 'sourceFile' so that we
@@ -3659,9 +3665,14 @@ namespace ts {
36593665
diagnostics = program.getDeclarationDiagnostics(/*sourceFile*/ undefined, cancellationToken);
36603666
}
36613667

3662-
return diagnostics.length > 0 ?
3663-
{ diagnostics, sourceMaps: undefined, emittedFiles: undefined, emitSkipped: true } :
3664-
undefined;
3668+
if (!diagnostics.length) return undefined;
3669+
let emittedFiles: string[] | undefined;
3670+
if (!sourceFile && !outFile(options)) {
3671+
const emitResult = program.emitBuildInfo(writeFile, cancellationToken);
3672+
if (emitResult.diagnostics) diagnostics = [...diagnostics, ...emitResult.diagnostics];
3673+
emittedFiles = emitResult.emittedFiles;
3674+
}
3675+
return { diagnostics, sourceMaps: undefined, emittedFiles, emitSkipped: true };
36653676
}
36663677

36673678
/*@internal*/

0 commit comments

Comments
 (0)