Skip to content

Commit dedb2ae

Browse files
committed
Combine buildHost methods into SolutionBuilderHost's reportDiagnostic and reportStatus
1 parent 26b4b6c commit dedb2ae

File tree

5 files changed

+132
-138
lines changed

5 files changed

+132
-138
lines changed

src/compiler/tsbuild.ts

Lines changed: 41 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -392,21 +392,37 @@ namespace ts {
392392
getModifiedTime(fileName: string): Date | undefined;
393393
setModifiedTime(fileName: string, date: Date): void;
394394
deleteFile(fileName: string): void;
395+
396+
reportDiagnostic: DiagnosticReporter; // Technically we want to move it out and allow steps of actions on Solution, but for now just merge stuff in build host here
397+
reportSolutionBuilderStatus: DiagnosticReporter;
395398
}
396399

397400
export interface SolutionBuilderWithWatchHost extends SolutionBuilderHost, WatchHost {
398401
}
399402

400-
export function createSolutionBuilderHost(system = sys) {
403+
/**
404+
* Create a function that reports watch status by writing to the system and handles the formating of the diagnostic
405+
*/
406+
export function createBuilderStatusReporter(system: System, pretty?: boolean): DiagnosticReporter {
407+
return diagnostic => {
408+
let output = pretty ? `[${formatColorAndReset(new Date().toLocaleTimeString(), ForegroundColorEscapeSequences.Grey)}] ` : `${new Date().toLocaleTimeString()} - `;
409+
output += `${flattenDiagnosticMessageText(diagnostic.messageText, system.newLine)}${system.newLine + system.newLine}`;
410+
system.write(output);
411+
};
412+
}
413+
414+
export function createSolutionBuilderHost(system = sys, reportDiagnostic?: DiagnosticReporter, reportSolutionBuilderStatus?: DiagnosticReporter) {
401415
const host = createCompilerHost({}, /*setParentNodes*/ undefined, system) as SolutionBuilderHost;
402416
host.getModifiedTime = system.getModifiedTime ? path => system.getModifiedTime!(path) : () => undefined;
403417
host.setModifiedTime = system.setModifiedTime ? (path, date) => system.setModifiedTime!(path, date) : noop;
404418
host.deleteFile = system.deleteFile ? path => system.deleteFile!(path) : noop;
419+
host.reportDiagnostic = reportDiagnostic || createDiagnosticReporter(system);
420+
host.reportSolutionBuilderStatus = reportSolutionBuilderStatus || createBuilderStatusReporter(system);
405421
return host;
406422
}
407423

408-
export function createSolutionBuilderWithWatchHost(system = sys, reportWatchStatus?: WatchStatusReporter) {
409-
const host = createSolutionBuilderHost(system) as SolutionBuilderWithWatchHost;
424+
export function createSolutionBuilderWithWatchHost(system = sys, reportDiagnostic?: DiagnosticReporter, reportSolutionBuilderStatus?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter) {
425+
const host = createSolutionBuilderHost(system, reportDiagnostic, reportSolutionBuilderStatus) as SolutionBuilderWithWatchHost;
410426
const watchHost = createWatchHost(system, reportWatchStatus);
411427
host.onWatchStatusChange = watchHost.onWatchStatusChange;
412428
host.watchFile = watchHost.watchFile;
@@ -422,7 +438,7 @@ namespace ts {
422438
* TODO: use SolutionBuilderWithWatchHost => watchedSolution
423439
* use SolutionBuilderHost => Solution
424440
*/
425-
export function createSolutionBuilder(host: SolutionBuilderHost, buildHost: BuildHost, rootNames: ReadonlyArray<string>, defaultOptions: BuildOptions) {
441+
export function createSolutionBuilder(host: SolutionBuilderHost, rootNames: ReadonlyArray<string>, defaultOptions: BuildOptions) {
426442
const hostWithWatch = host as SolutionBuilderWithWatchHost;
427443
const configFileCache = createConfigFileCache(host);
428444
let context = createBuildContext(defaultOptions);
@@ -445,6 +461,10 @@ namespace ts {
445461
startWatching
446462
};
447463

464+
function reportStatus(message: DiagnosticMessage, ...args: string[]) {
465+
host.reportSolutionBuilderStatus(createCompilerDiagnostic(message, ...args));
466+
}
467+
448468
function startWatching() {
449469
const graph = getGlobalDependencyGraph()!;
450470
if (!graph.buildQueue) {
@@ -743,7 +763,7 @@ namespace ts {
743763
verboseReportProjectStatus(next, status);
744764

745765
if (status.type === UpToDateStatusType.UpstreamBlocked) {
746-
if (context.options.verbose) buildHost.verbose(Diagnostics.Skipping_build_of_project_0_because_its_dependency_1_has_errors, resolved, status.upstreamProjectName);
766+
if (context.options.verbose) reportStatus(Diagnostics.Skipping_build_of_project_0_because_its_dependency_1_has_errors, resolved, status.upstreamProjectName);
747767
continue;
748768
}
749769

@@ -780,7 +800,7 @@ namespace ts {
780800
if (temporaryMarks[projPath]) {
781801
if (!inCircularContext) {
782802
hadError = true;
783-
buildHost.error(Diagnostics.Project_references_may_not_form_a_circular_graph_Cycle_detected_Colon_0, circularityReportStack.join("\r\n"));
803+
reportStatus(Diagnostics.Project_references_may_not_form_a_circular_graph_Cycle_detected_Colon_0, circularityReportStack.join("\r\n"));
784804
return;
785805
}
786806
}
@@ -812,11 +832,11 @@ namespace ts {
812832

813833
function buildSingleProject(proj: ResolvedConfigFileName): BuildResultFlags {
814834
if (context.options.dry) {
815-
buildHost.message(Diagnostics.A_non_dry_build_would_build_project_0, proj);
835+
reportStatus(Diagnostics.A_non_dry_build_would_build_project_0, proj);
816836
return BuildResultFlags.Success;
817837
}
818838

819-
if (context.options.verbose) buildHost.verbose(Diagnostics.Building_project_0, proj);
839+
if (context.options.verbose) reportStatus(Diagnostics.Building_project_0, proj);
820840

821841
let resultFlags = BuildResultFlags.None;
822842
resultFlags |= BuildResultFlags.DeclarationOutputUnchanged;
@@ -850,7 +870,7 @@ namespace ts {
850870
if (syntaxDiagnostics.length) {
851871
resultFlags |= BuildResultFlags.SyntaxErrors;
852872
for (const diag of syntaxDiagnostics) {
853-
buildHost.errorDiagnostic(diag);
873+
host.reportDiagnostic(diag);
854874
}
855875
context.projectStatus.setValue(proj, { type: UpToDateStatusType.Unbuildable, reason: "Syntactic errors" });
856876
return resultFlags;
@@ -862,7 +882,7 @@ namespace ts {
862882
if (declDiagnostics.length) {
863883
resultFlags |= BuildResultFlags.DeclarationEmitErrors;
864884
for (const diag of declDiagnostics) {
865-
buildHost.errorDiagnostic(diag);
885+
host.reportDiagnostic(diag);
866886
}
867887
context.projectStatus.setValue(proj, { type: UpToDateStatusType.Unbuildable, reason: "Declaration file errors" });
868888
return resultFlags;
@@ -874,7 +894,7 @@ namespace ts {
874894
if (semanticDiagnostics.length) {
875895
resultFlags |= BuildResultFlags.TypeErrors;
876896
for (const diag of semanticDiagnostics) {
877-
buildHost.errorDiagnostic(diag);
897+
host.reportDiagnostic(diag);
878898
}
879899
context.projectStatus.setValue(proj, { type: UpToDateStatusType.Unbuildable, reason: "Semantic errors" });
880900
return resultFlags;
@@ -913,11 +933,11 @@ namespace ts {
913933

914934
function updateOutputTimestamps(proj: ParsedCommandLine) {
915935
if (context.options.dry) {
916-
return buildHost.message(Diagnostics.A_non_dry_build_would_build_project_0, proj.options.configFilePath!);
936+
return reportStatus(Diagnostics.A_non_dry_build_would_build_project_0, proj.options.configFilePath!);
917937
}
918938

919939
if (context.options.verbose) {
920-
buildHost.verbose(Diagnostics.Updating_output_timestamps_of_project_0, proj.options.configFilePath!);
940+
reportStatus(Diagnostics.Updating_output_timestamps_of_project_0, proj.options.configFilePath!);
921941
}
922942

923943
const now = new Date();
@@ -970,18 +990,18 @@ namespace ts {
970990
function cleanAllProjects() {
971991
const resolvedNames: ReadonlyArray<ResolvedConfigFileName> | undefined = getAllProjectsInScope();
972992
if (resolvedNames === undefined) {
973-
buildHost.message(Diagnostics.Skipping_clean_because_not_all_projects_could_be_located);
993+
reportStatus(Diagnostics.Skipping_clean_because_not_all_projects_could_be_located);
974994
return ExitStatus.DiagnosticsPresent_OutputsSkipped;
975995
}
976996

977997
const filesToDelete = getFilesToClean(resolvedNames);
978998
if (filesToDelete === undefined) {
979-
buildHost.message(Diagnostics.Skipping_clean_because_not_all_projects_could_be_located);
999+
reportStatus(Diagnostics.Skipping_clean_because_not_all_projects_could_be_located);
9801000
return ExitStatus.DiagnosticsPresent_OutputsSkipped;
9811001
}
9821002

9831003
if (context.options.dry) {
984-
buildHost.message(Diagnostics.A_non_dry_build_would_delete_the_following_files_Colon_0, filesToDelete.map(f => `\r\n * ${f}`).join(""));
1004+
reportStatus(Diagnostics.A_non_dry_build_would_delete_the_following_files_Colon_0, filesToDelete.map(f => `\r\n * ${f}`).join(""));
9851005
return ExitStatus.Success;
9861006
}
9871007

@@ -1001,7 +1021,7 @@ namespace ts {
10011021
if (host.fileExists(fullPathWithTsconfig)) {
10021022
return fullPathWithTsconfig as ResolvedConfigFileName;
10031023
}
1004-
buildHost.error(Diagnostics.File_0_not_found, relName(fullPath));
1024+
host.reportDiagnostic(createCompilerDiagnostic(Diagnostics.File_0_not_found, relName(fullPath)));
10051025
return undefined;
10061026
}
10071027

@@ -1039,7 +1059,7 @@ namespace ts {
10391059
// Up to date, skip
10401060
if (defaultOptions.dry) {
10411061
// In a dry build, inform the user of this fact
1042-
buildHost.message(Diagnostics.Project_0_is_up_to_date, projName);
1062+
reportStatus(Diagnostics.Project_0_is_up_to_date, projName);
10431063
}
10441064
continue;
10451065
}
@@ -1051,7 +1071,7 @@ namespace ts {
10511071
}
10521072

10531073
if (status.type === UpToDateStatusType.UpstreamBlocked) {
1054-
if (context.options.verbose) buildHost.verbose(Diagnostics.Skipping_build_of_project_0_because_its_dependency_1_has_errors, projName, status.upstreamProjectName);
1074+
if (context.options.verbose) reportStatus(Diagnostics.Skipping_build_of_project_0_because_its_dependency_1_has_errors, projName, status.upstreamProjectName);
10551075
continue;
10561076
}
10571077

@@ -1076,23 +1096,19 @@ namespace ts {
10761096
for (const name of graph.buildQueue) {
10771097
names.push(name);
10781098
}
1079-
if (context.options.verbose) buildHost.verbose(Diagnostics.Projects_in_this_build_Colon_0, names.map(s => "\r\n * " + relName(s)).join(""));
1099+
if (context.options.verbose) reportStatus(Diagnostics.Projects_in_this_build_Colon_0, names.map(s => "\r\n * " + relName(s)).join(""));
10801100
}
10811101

10821102
function relName(path: string): string {
10831103
return convertToRelativePath(path, host.getCurrentDirectory(), f => host.getCanonicalFileName(f));
10841104
}
10851105

1086-
function reportVerbose(message: DiagnosticMessage, ...args: string[]) {
1087-
buildHost.verbose(message, ...args);
1088-
}
1089-
10901106
/**
10911107
* Report the up-to-date status of a project if we're in verbose mode
10921108
*/
10931109
function verboseReportProjectStatus(configFileName: string, status: UpToDateStatus) {
10941110
if (!context.options.verbose) return;
1095-
return formatUpToDateStatus(configFileName, status, relName, reportVerbose);
1111+
return formatUpToDateStatus(configFileName, status, relName, reportStatus);
10961112
}
10971113
}
10981114

src/harness/fakes.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ namespace fakes {
205205
/**
206206
* A fake `ts.CompilerHost` that leverages a virtual file system.
207207
*/
208-
export class CompilerHost implements ts.CompilerHost, ts.SolutionBuilderHost {
208+
export class CompilerHost implements ts.CompilerHost {
209209
public readonly sys: System;
210210
public readonly defaultLibLocation: string;
211211
public readonly outputs: documents.TextDocument[] = [];
@@ -374,5 +374,41 @@ namespace fakes {
374374
return parsed;
375375
}
376376
}
377+
378+
export class SolutionBuilderHost extends CompilerHost implements ts.SolutionBuilderHost {
379+
diagnostics: ts.Diagnostic[] = [];
380+
381+
reportDiagnostic(diagnostic: ts.Diagnostic) {
382+
this.diagnostics.push(diagnostic);
383+
}
384+
385+
reportSolutionBuilderStatus(diagnostic: ts.Diagnostic) {
386+
this.diagnostics.push(diagnostic);
387+
}
388+
389+
clearDiagnostics() {
390+
this.diagnostics.length = 0;
391+
}
392+
393+
assertDiagnosticMessages(...expected: ts.DiagnosticMessage[]) {
394+
const actual = this.diagnostics.slice();
395+
if (actual.length !== expected.length) {
396+
assert.fail<any>(actual, expected, `Diagnostic arrays did not match - got\r\n${actual.map(a => " " + a.messageText).join("\r\n")}\r\nexpected\r\n${expected.map(e => " " + e.message).join("\r\n")}`);
397+
}
398+
for (let i = 0; i < actual.length; i++) {
399+
if (actual[i].code !== expected[i].code) {
400+
assert.fail(actual[i].messageText, expected[i].message, `Mismatched error code - expected diagnostic ${i} "${actual[i].messageText}" to match ${expected[i].message}`);
401+
}
402+
}
403+
}
404+
405+
printDiagnostics(header = "== Diagnostics ==") {
406+
const out = ts.createDiagnosticReporter(ts.sys);
407+
ts.sys.write(header + "\r\n");
408+
for (const d of this.diagnostics) {
409+
out(d);
410+
}
411+
}
412+
}
377413
}
378414

0 commit comments

Comments
 (0)