mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-22 22:55:36 -05:00
fix(45713) Improve error report summaries (#45742)
* Improve error report summaries (#45713) * fixup! Improve error report summaries (#45713) * fixup! fixup! Improve error report summaries (#45713) * Adds support for handling localization renaming the 'files' header due to localization * fixup! Adds support for handling localization renaming the 'files' header due to localization - Fixed baseline error - Fixed linter error Co-authored-by: Orta <git@orta.io> Co-authored-by: Orta Therox <ortam@microsoft.com>
This commit is contained in:
@@ -4156,6 +4156,12 @@
|
||||
"category": "Message",
|
||||
"code": 6040
|
||||
},
|
||||
"Errors Files": {
|
||||
"_locale_notes": "There is a double space, and the order cannot be changed (they're table headings) ^",
|
||||
"category": "Message",
|
||||
"code": 6041
|
||||
},
|
||||
|
||||
"Generates corresponding '.map' file.": {
|
||||
"category": "Message",
|
||||
"code": 6043
|
||||
@@ -4935,6 +4941,18 @@
|
||||
"category": "Error",
|
||||
"code": 6258
|
||||
},
|
||||
"Found 1 error in {1}": {
|
||||
"category": "Message",
|
||||
"code": 6259
|
||||
},
|
||||
"Found {0} errors in 1 file.": {
|
||||
"category": "Message",
|
||||
"code": 6260
|
||||
},
|
||||
"Found {0} errors in {1} files.": {
|
||||
"category": "Message",
|
||||
"code": 6261
|
||||
},
|
||||
|
||||
"Directory '{0}' has no containing package.json scope. Imports will not resolve.": {
|
||||
"category": "Message",
|
||||
|
||||
@@ -76,7 +76,12 @@ namespace ts {
|
||||
return fileExtensionIs(fileName, Extension.Dts);
|
||||
}
|
||||
|
||||
export type ReportEmitErrorSummary = (errorCount: number) => void;
|
||||
export type ReportEmitErrorSummary = (errorCount: number, filesInError: (ReportFileInError | undefined)[]) => void;
|
||||
|
||||
export interface ReportFileInError {
|
||||
fileName: string;
|
||||
line: number;
|
||||
}
|
||||
|
||||
export interface SolutionBuilderHostBase<T extends BuilderProgram> extends ProgramHost<T> {
|
||||
createDirectory?(path: string): void;
|
||||
@@ -2003,10 +2008,12 @@ namespace ts {
|
||||
const canReportSummary = state.watch || !!state.host.reportErrorSummary;
|
||||
const { diagnostics } = state;
|
||||
let totalErrors = 0;
|
||||
let filesInError: (ReportFileInError | undefined)[] = [];
|
||||
if (isCircularBuildOrder(buildOrder)) {
|
||||
reportBuildQueue(state, buildOrder.buildOrder);
|
||||
reportErrors(state, buildOrder.circularDiagnostics);
|
||||
if (canReportSummary) totalErrors += getErrorCountForSummary(buildOrder.circularDiagnostics);
|
||||
if (canReportSummary) filesInError = [...filesInError, ...getFilesInErrorForSummary(buildOrder.circularDiagnostics)];
|
||||
}
|
||||
else {
|
||||
// Report errors from the other projects
|
||||
@@ -2017,13 +2024,14 @@ namespace ts {
|
||||
}
|
||||
});
|
||||
if (canReportSummary) diagnostics.forEach(singleProjectErrors => totalErrors += getErrorCountForSummary(singleProjectErrors));
|
||||
if (canReportSummary) diagnostics.forEach(singleProjectErrors => [...filesInError, ...getFilesInErrorForSummary(singleProjectErrors)]);
|
||||
}
|
||||
|
||||
if (state.watch) {
|
||||
reportWatchStatus(state, getWatchErrorSummaryDiagnosticMessage(totalErrors), totalErrors);
|
||||
}
|
||||
else if (state.host.reportErrorSummary) {
|
||||
state.host.reportErrorSummary(totalErrors);
|
||||
state.host.reportErrorSummary(totalErrors, filesInError);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace ts {
|
||||
} : undefined;
|
||||
|
||||
/**
|
||||
* Create a function that reports error by writing to the system and handles the formating of the diagnostic
|
||||
* Create a function that reports error by writing to the system and handles the formatting of the diagnostic
|
||||
*/
|
||||
export function createDiagnosticReporter(system: System, pretty?: boolean): DiagnosticReporter {
|
||||
const host: FormatDiagnosticsHost = system === sys && sysFormatDiagnosticsHost ? sysFormatDiagnosticsHost : {
|
||||
@@ -101,16 +101,87 @@ namespace ts {
|
||||
return countWhere(diagnostics, diagnostic => diagnostic.category === DiagnosticCategory.Error);
|
||||
}
|
||||
|
||||
export function getFilesInErrorForSummary(diagnostics: readonly Diagnostic[]): (ReportFileInError | undefined)[] {
|
||||
const filesInError =
|
||||
filter(diagnostics, diagnostic => diagnostic.category === DiagnosticCategory.Error)
|
||||
.map(
|
||||
errorDiagnostic => {
|
||||
if(errorDiagnostic.file === undefined) return;
|
||||
return `${errorDiagnostic.file.fileName}`;
|
||||
});
|
||||
return filesInError.map((fileName: string) => {
|
||||
const diagnosticForFileName = find(diagnostics, diagnostic =>
|
||||
diagnostic.file !== undefined && diagnostic.file.fileName === fileName
|
||||
);
|
||||
|
||||
if(diagnosticForFileName !== undefined) {
|
||||
const { line } = getLineAndCharacterOfPosition(diagnosticForFileName.file!, diagnosticForFileName.start!);
|
||||
return {
|
||||
fileName,
|
||||
line: line + 1,
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function getWatchErrorSummaryDiagnosticMessage(errorCount: number) {
|
||||
return errorCount === 1 ?
|
||||
Diagnostics.Found_1_error_Watching_for_file_changes :
|
||||
Diagnostics.Found_0_errors_Watching_for_file_changes;
|
||||
}
|
||||
|
||||
export function getErrorSummaryText(errorCount: number, newLine: string) {
|
||||
export function getErrorSummaryText(
|
||||
errorCount: number,
|
||||
filesInError: readonly (ReportFileInError | undefined)[],
|
||||
newLine: string
|
||||
) {
|
||||
if (errorCount === 0) return "";
|
||||
const d = createCompilerDiagnostic(errorCount === 1 ? Diagnostics.Found_1_error : Diagnostics.Found_0_errors, errorCount);
|
||||
return `${newLine}${flattenDiagnosticMessageText(d.messageText, newLine)}${newLine}${newLine}`;
|
||||
const nonNilFiles = filesInError.filter(fileInError => fileInError !== undefined);
|
||||
const distinctFileNamesWithLines = nonNilFiles.map(fileInError => `${fileInError!.fileName}:${fileInError!.line}`)
|
||||
.filter((value, index, self) => self.indexOf(value) === index);
|
||||
const d = errorCount === 1 ?
|
||||
createCompilerDiagnostic(
|
||||
filesInError[0] !== undefined ?
|
||||
Diagnostics.Found_1_error_in_1 :
|
||||
Diagnostics.Found_1_error,
|
||||
errorCount,
|
||||
distinctFileNamesWithLines[0]) :
|
||||
createCompilerDiagnostic(
|
||||
distinctFileNamesWithLines.length === 0 ?
|
||||
Diagnostics.Found_0_errors :
|
||||
distinctFileNamesWithLines.length === 1 ?
|
||||
Diagnostics.Found_0_errors_in_1_file :
|
||||
Diagnostics.Found_0_errors_in_1_files,
|
||||
errorCount,
|
||||
distinctFileNamesWithLines.length);
|
||||
return `${newLine}${flattenDiagnosticMessageText(d.messageText, newLine)}${newLine}${newLine}${errorCount > 1 ? createTabularErrorsDisplay(nonNilFiles) : ""}`;
|
||||
}
|
||||
|
||||
function createTabularErrorsDisplay(filesInError: (ReportFileInError | undefined)[]) {
|
||||
const distinctFiles = filesInError.filter((value, index, self) => index === self.findIndex(file => file?.fileName === value?.fileName));
|
||||
if (distinctFiles.length === 0) return "";
|
||||
|
||||
const numberLength = (num: number) => Math.log(num) * Math.LOG10E + 1;
|
||||
const fileToErrorCount = distinctFiles.map(file => ([file, countWhere(filesInError, fileInError => fileInError!.fileName === file!.fileName)] as const));
|
||||
const maxErrors = fileToErrorCount.reduce((acc, value) => Math.max(acc, value[1] || 0), 0);
|
||||
|
||||
const headerRow = Diagnostics.Errors_Files.message;
|
||||
const leftColumnHeadingLength = headerRow.split(" ")[0].length;
|
||||
const leftPaddingGoal = Math.max(leftColumnHeadingLength, numberLength(maxErrors));
|
||||
const headerPadding = Math.max(numberLength(maxErrors) - leftColumnHeadingLength, 0);
|
||||
|
||||
let tabularData = "";
|
||||
tabularData += " ".repeat(headerPadding) + headerRow + "\n";
|
||||
fileToErrorCount.forEach((row) => {
|
||||
const [file, errorCount] = row;
|
||||
const errorCountDigitsLength = Math.log(errorCount) * Math.LOG10E + 1 | 0;
|
||||
const leftPadding = errorCountDigitsLength < leftPaddingGoal ?
|
||||
" ".repeat(leftPaddingGoal - errorCountDigitsLength)
|
||||
: "";
|
||||
tabularData += `${leftPadding}${errorCount} ${file!.fileName}:${file!.line}\n`;
|
||||
});
|
||||
|
||||
return tabularData;
|
||||
}
|
||||
|
||||
export function isBuilderProgram(program: Program | BuilderProgram): program is BuilderProgram {
|
||||
@@ -350,7 +421,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
if (reportSummary) {
|
||||
reportSummary(getErrorCountForSummary(diagnostics));
|
||||
reportSummary(getErrorCountForSummary(diagnostics), getFilesInErrorForSummary(diagnostics));
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -656,7 +727,7 @@ namespace ts {
|
||||
builderProgram,
|
||||
input.reportDiagnostic || createDiagnosticReporter(system),
|
||||
s => host.trace && host.trace(s),
|
||||
input.reportErrorSummary || input.options.pretty ? errorCount => system.write(getErrorSummaryText(errorCount, system.newLine)) : undefined
|
||||
input.reportErrorSummary || input.options.pretty ? (errorCount, filesInError) => system.write(getErrorSummaryText(errorCount, filesInError, system.newLine)) : undefined
|
||||
);
|
||||
if (input.afterProgramEmitAndDiagnostics) input.afterProgramEmitAndDiagnostics(builderProgram);
|
||||
return exitStatus;
|
||||
|
||||
Reference in New Issue
Block a user