diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 18987466352..078a158f8f8 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -104,6 +104,13 @@ namespace ts { category: Diagnostics.Advanced_Options, description: Diagnostics.Print_names_of_generated_files_part_of_the_compilation }, + { + name: "pretty", + type: "boolean", + showInSimplifiedHelpView: true, + category: Diagnostics.Command_line_Options, + description: Diagnostics.Stylize_errors_and_messages_using_color_and_context_experimental + }, { name: "traceResolution", @@ -157,13 +164,6 @@ namespace ts { category: Diagnostics.Command_line_Options, description: Diagnostics.Build_one_or_more_projects_and_their_dependencies_if_out_of_date }, - { - name: "pretty", - type: "boolean", - showInSimplifiedHelpView: true, - category: Diagnostics.Command_line_Options, - description: Diagnostics.Stylize_errors_and_messages_using_color_and_context_experimental - }, { name: "showConfig", type: "boolean", diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 01aa861cabb..e2dd0a1b7cd 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3771,6 +3771,14 @@ "category": "Message", "code": 6215 }, + "Found 1 error.": { + "category": "Message", + "code": 6216 + }, + "Found {0} errors.": { + "category": "Message", + "code": 6217 + }, "Projects to reference": { "category": "Message", diff --git a/src/compiler/tsbuild.ts b/src/compiler/tsbuild.ts index c2215b22ebf..4b9e75102b6 100644 --- a/src/compiler/tsbuild.ts +++ b/src/compiler/tsbuild.ts @@ -29,6 +29,9 @@ namespace ts { preserveWatchOutput?: boolean; listEmittedFiles?: boolean; listFiles?: boolean; + pretty?: boolean; + + traceResolution?: boolean; } enum BuildResultFlags { @@ -316,7 +319,7 @@ namespace ts { return fileExtensionIs(fileName, Extension.Dts); } - export interface SolutionBuilderHost extends CompilerHost { + export interface SolutionBuilderHostBase extends CompilerHost { getModifiedTime(fileName: string): Date | undefined; setModifiedTime(fileName: string, date: Date): void; deleteFile(fileName: string): void; @@ -325,13 +328,18 @@ namespace ts { reportSolutionBuilderStatus: DiagnosticReporter; } - export interface SolutionBuilderWithWatchHost extends SolutionBuilderHost, WatchHost { + export interface SolutionBuilderHost extends SolutionBuilderHostBase { + reportErrorSummary?: ReportEmitErrorSummary; + } + + export interface SolutionBuilderWithWatchHost extends SolutionBuilderHostBase, WatchHost { } export interface SolutionBuilder { buildAllProjects(): ExitStatus; cleanAllProjects(): ExitStatus; + // TODO:: All the below ones should technically only be in watch mode. but thats for later time /*@internal*/ resolveProjectName(name: string): ResolvedConfigFileName; /*@internal*/ getUpToDateStatusOfFile(configFileName: ResolvedConfigFileName): UpToDateStatus; /*@internal*/ getBuildGraph(configFileNames: ReadonlyArray): DependencyGraph; @@ -340,7 +348,9 @@ namespace ts { /*@internal*/ buildInvalidatedProject(): void; /*@internal*/ resetBuildContext(opts?: BuildOptions): void; + } + export interface SolutionBuilderWithWatch extends SolutionBuilder { /*@internal*/ startWatching(): void; } @@ -355,8 +365,8 @@ namespace ts { }; } - export function createSolutionBuilderHost(system = sys, reportDiagnostic?: DiagnosticReporter, reportSolutionBuilderStatus?: DiagnosticReporter) { - const host = createCompilerHostWorker({}, /*setParentNodes*/ undefined, system) as SolutionBuilderHost; + function createSolutionBuilderHostBase(system = sys, reportDiagnostic?: DiagnosticReporter, reportSolutionBuilderStatus?: DiagnosticReporter) { + const host = createCompilerHostWorker({}, /*setParentNodes*/ undefined, system) as SolutionBuilderHostBase; host.getModifiedTime = system.getModifiedTime ? path => system.getModifiedTime!(path) : () => undefined; host.setModifiedTime = system.setModifiedTime ? (path, date) => system.setModifiedTime!(path, date) : noop; host.deleteFile = system.deleteFile ? path => system.deleteFile!(path) : noop; @@ -365,8 +375,14 @@ namespace ts { return host; } - export function createSolutionBuilderWithWatchHost(system = sys, reportDiagnostic?: DiagnosticReporter, reportSolutionBuilderStatus?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter) { - const host = createSolutionBuilderHost(system, reportDiagnostic, reportSolutionBuilderStatus) as SolutionBuilderWithWatchHost; + export function createSolutionBuilderHost(system = sys, reportDiagnostic?: DiagnosticReporter, reportSolutionBuilderStatus?: DiagnosticReporter, reportErrorSummary?: ReportEmitErrorSummary) { + const host = createSolutionBuilderHostBase(system, reportDiagnostic, reportSolutionBuilderStatus) as SolutionBuilderHost; + host.reportErrorSummary = reportErrorSummary; + return host; + } + + export function createSolutionBuilderWithWatchHost(system?: System, reportDiagnostic?: DiagnosticReporter, reportSolutionBuilderStatus?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter) { + const host = createSolutionBuilderHostBase(system, reportDiagnostic, reportSolutionBuilderStatus) as SolutionBuilderWithWatchHost; const watchHost = createWatchHost(system, reportWatchStatus); host.onWatchStatusChange = watchHost.onWatchStatusChange; host.watchFile = watchHost.watchFile; @@ -390,7 +406,9 @@ namespace ts { * TODO: use SolutionBuilderWithWatchHost => watchedSolution * use SolutionBuilderHost => Solution */ - export function createSolutionBuilder(host: SolutionBuilderHost, rootNames: ReadonlyArray, defaultOptions: BuildOptions): SolutionBuilder { + export function createSolutionBuilder(host: SolutionBuilderHost, rootNames: ReadonlyArray, defaultOptions: BuildOptions): SolutionBuilder; + export function createSolutionBuilder(host: SolutionBuilderWithWatchHost, rootNames: ReadonlyArray, defaultOptions: BuildOptions): SolutionBuilderWithWatch; + export function createSolutionBuilder(host: SolutionBuilderHost | SolutionBuilderWithWatchHost, rootNames: ReadonlyArray, defaultOptions: BuildOptions): SolutionBuilderWithWatch { const hostWithWatch = host as SolutionBuilderWithWatchHost; const currentDirectory = host.getCurrentDirectory(); const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames()); @@ -803,9 +821,7 @@ namespace ts { globalDependencyGraph = undefined; } projectStatus.removeKey(resolved); - if (options.watch) { - diagnostics.removeKey(resolved); - } + diagnostics.removeKey(resolved); addProjToQueue(resolved, reloadLevel); } @@ -874,7 +890,7 @@ namespace ts { } function reportErrorSummary() { - if (options.watch) { + if (options.watch || (host as SolutionBuilderHost).reportErrorSummary) { // Report errors from the other projects getGlobalDependencyGraph().buildQueue.forEach(project => { if (!projectErrorsReported.hasKey(project)) { @@ -882,8 +898,13 @@ namespace ts { } }); let totalErrors = 0; - diagnostics.forEach(singleProjectErrors => totalErrors += singleProjectErrors.filter(diagnostic => diagnostic.category === DiagnosticCategory.Error).length); - reportWatchStatus(totalErrors === 1 ? Diagnostics.Found_1_error_Watching_for_file_changes : Diagnostics.Found_0_errors_Watching_for_file_changes, totalErrors); + diagnostics.forEach(singleProjectErrors => totalErrors += getErrorCountForSummary(singleProjectErrors)); + if (options.watch) { + reportWatchStatus(getWatchErrorSummaryDiagnosticMessage(totalErrors), totalErrors); + } + else { + (host as SolutionBuilderHost).reportErrorSummary!(totalErrors); + } } } @@ -1066,9 +1087,7 @@ namespace ts { type: UpToDateStatusType.UpToDate, newestDeclarationFileContentChangedTime: anyDtsChanged ? maximumDate : newestDeclarationFileContentChangedTime }; - if (options.watch) { - diagnostics.removeKey(proj); - } + diagnostics.removeKey(proj); projectStatus.setValue(proj, status); return resultFlags; @@ -1207,10 +1226,8 @@ namespace ts { function reportAndStoreErrors(proj: ResolvedConfigFileName, errors: ReadonlyArray) { reportErrors(errors); - if (options.watch) { - projectErrorsReported.setValue(proj, true); - diagnostics.setValue(proj, errors); - } + projectErrorsReported.setValue(proj, true); + diagnostics.setValue(proj, errors); } function reportErrors(errors: ReadonlyArray) { diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index f9ce3055f63..345991baefc 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -106,6 +106,21 @@ namespace ts { export type ReportEmitErrorSummary = (errorCount: number) => void; + export function getErrorCountForSummary(diagnostics: ReadonlyArray) { + return countWhere(diagnostics, diagnostic => diagnostic.category === DiagnosticCategory.Error); + } + + 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) { + const d = createCompilerDiagnostic(errorCount === 1 ? Diagnostics.Found_1_error : Diagnostics.Found_0_errors, errorCount); + return `${newLine}${flattenDiagnosticMessageText(d.messageText, newLine)}${newLine}${newLine}`; + } + /** * Helper that emit files, report diagnostics and lists emitted and/or source files depending on compiler options */ @@ -151,7 +166,7 @@ namespace ts { } if (reportSummary) { - reportSummary(diagnostics.filter(diagnostic => diagnostic.category === DiagnosticCategory.Error).length); + reportSummary(getErrorCountForSummary(diagnostics)); } if (emitSkipped && diagnostics.length > 0) { @@ -227,16 +242,16 @@ namespace ts { const compilerOptions = builderProgram.getCompilerOptions(); const newLine = getNewLineCharacter(compilerOptions, () => system.newLine); - const reportSummary = (errorCount: number) => { - if (errorCount === 1) { - onWatchStatusChange!(createCompilerDiagnostic(Diagnostics.Found_1_error_Watching_for_file_changes, errorCount), newLine, compilerOptions); - } - else { - onWatchStatusChange!(createCompilerDiagnostic(Diagnostics.Found_0_errors_Watching_for_file_changes, errorCount, errorCount), newLine, compilerOptions); - } - }; - - emitFilesAndReportErrors(builderProgram, reportDiagnostic, writeFileName, reportSummary); + emitFilesAndReportErrors( + builderProgram, + reportDiagnostic, + writeFileName, + errorCount => onWatchStatusChange!( + createCompilerDiagnostic(getWatchErrorSummaryDiagnosticMessage(errorCount), errorCount), + newLine, + compilerOptions + ) + ); } } diff --git a/src/harness/harness.ts b/src/harness/harness.ts index c19c1ab141e..d17eae4f80a 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -1325,6 +1325,9 @@ namespace Harness { const [, content] = value; outputLines += content; } + if (pretty) { + outputLines += ts.getErrorSummaryText(ts.getErrorCountForSummary(diagnostics), IO.newLine()); + } return outputLines; } diff --git a/src/tsc/tsc.ts b/src/tsc/tsc.ts index 549f79f9025..d3a60f58d05 100644 --- a/src/tsc/tsc.ts +++ b/src/tsc/tsc.ts @@ -200,19 +200,28 @@ namespace ts { } // TODO: change this to host if watch => watchHost otherwiue without wathc - const builder = createSolutionBuilder(createSolutionBuilderWithWatchHost(sys, reportDiagnostic, createBuilderStatusReporter(sys, shouldBePretty()), createWatchStatusReporter()), projects, buildOptions); + const builder = createSolutionBuilder(buildOptions.watch ? + createSolutionBuilderWithWatchHost(sys, reportDiagnostic, createBuilderStatusReporter(sys, shouldBePretty()), createWatchStatusReporter()) : + createSolutionBuilderHost(sys, reportDiagnostic, createBuilderStatusReporter(sys, shouldBePretty()), createReportErrorSummary(buildOptions)), + projects, buildOptions); if (buildOptions.clean) { return sys.exit(builder.cleanAllProjects()); } if (buildOptions.watch) { builder.buildAllProjects(); - return builder.startWatching(); + return (builder as SolutionBuilderWithWatch).startWatching(); } return sys.exit(builder.buildAllProjects()); } + function createReportErrorSummary(options: CompilerOptions | BuildOptions): ReportEmitErrorSummary | undefined { + return shouldBePretty(options) ? + errorCount => sys.write(getErrorSummaryText(errorCount, sys.newLine)) : + undefined; + } + function performCompilation(rootNames: string[], projectReferences: ReadonlyArray | undefined, options: CompilerOptions, configFileParsingDiagnostics?: ReadonlyArray) { const host = createCompilerHost(options); enableStatistics(options); @@ -225,7 +234,12 @@ namespace ts { configFileParsingDiagnostics }; const program = createProgram(programOptions); - const exitStatus = emitFilesAndReportErrors(program, reportDiagnostic, s => sys.write(s + sys.newLine)); + const exitStatus = emitFilesAndReportErrors( + program, + reportDiagnostic, + s => sys.write(s + sys.newLine), + createReportErrorSummary(options) + ); reportStatistics(program); return sys.exit(exitStatus); } diff --git a/tests/baselines/reference/deeplyNestedAssignabilityIssue.errors.txt b/tests/baselines/reference/deeplyNestedAssignabilityIssue.errors.txt index 772d1e4f5a3..4b81299983e 100644 --- a/tests/baselines/reference/deeplyNestedAssignabilityIssue.errors.txt +++ b/tests/baselines/reference/deeplyNestedAssignabilityIssue.errors.txt @@ -58,4 +58,6 @@ } } } - \ No newline at end of file + +Found 2 errors. + diff --git a/tests/baselines/reference/duplicateIdentifierRelatedSpans1.errors.txt b/tests/baselines/reference/duplicateIdentifierRelatedSpans1.errors.txt index 9a963614b61..ed4a93ad107 100644 --- a/tests/baselines/reference/duplicateIdentifierRelatedSpans1.errors.txt +++ b/tests/baselines/reference/duplicateIdentifierRelatedSpans1.errors.txt @@ -91,4 +91,6 @@ ~~~ !!! error TS2451: Cannot redeclare block-scoped variable 'Bar'. !!! related TS6203 tests/cases/compiler/file1.ts:2:7: 'Bar' was also declared here. - \ No newline at end of file + +Found 6 errors. + diff --git a/tests/baselines/reference/duplicateIdentifierRelatedSpans2.errors.txt b/tests/baselines/reference/duplicateIdentifierRelatedSpans2.errors.txt index c6d6291e66a..e149ce706f1 100644 --- a/tests/baselines/reference/duplicateIdentifierRelatedSpans2.errors.txt +++ b/tests/baselines/reference/duplicateIdentifierRelatedSpans2.errors.txt @@ -44,4 +44,6 @@ class G { } class H { } class I { } - \ No newline at end of file + +Found 2 errors. + diff --git a/tests/baselines/reference/duplicateIdentifierRelatedSpans3.errors.txt b/tests/baselines/reference/duplicateIdentifierRelatedSpans3.errors.txt index 2fb1879933c..81c8f5c0c00 100644 --- a/tests/baselines/reference/duplicateIdentifierRelatedSpans3.errors.txt +++ b/tests/baselines/reference/duplicateIdentifierRelatedSpans3.errors.txt @@ -84,4 +84,6 @@ !!! error TS2300: Duplicate identifier 'duplicate3'. !!! related TS6203 tests/cases/compiler/file1.ts:4:5: 'duplicate3' was also declared here. } - \ No newline at end of file + +Found 6 errors. + diff --git a/tests/baselines/reference/duplicateIdentifierRelatedSpans4.errors.txt b/tests/baselines/reference/duplicateIdentifierRelatedSpans4.errors.txt index 9512e55733e..b6cd9e2fe4e 100644 --- a/tests/baselines/reference/duplicateIdentifierRelatedSpans4.errors.txt +++ b/tests/baselines/reference/duplicateIdentifierRelatedSpans4.errors.txt @@ -46,4 +46,6 @@ duplicate7(): number; duplicate8(): number; } - \ No newline at end of file + +Found 2 errors. + diff --git a/tests/baselines/reference/duplicateIdentifierRelatedSpans5.errors.txt b/tests/baselines/reference/duplicateIdentifierRelatedSpans5.errors.txt index 497a0642296..ae55bedd138 100644 --- a/tests/baselines/reference/duplicateIdentifierRelatedSpans5.errors.txt +++ b/tests/baselines/reference/duplicateIdentifierRelatedSpans5.errors.txt @@ -91,4 +91,6 @@ } } export {} - \ No newline at end of file + +Found 6 errors. + diff --git a/tests/baselines/reference/duplicateIdentifierRelatedSpans6.errors.txt b/tests/baselines/reference/duplicateIdentifierRelatedSpans6.errors.txt index db980204718..c380c59c48a 100644 --- a/tests/baselines/reference/duplicateIdentifierRelatedSpans6.errors.txt +++ b/tests/baselines/reference/duplicateIdentifierRelatedSpans6.errors.txt @@ -91,4 +91,6 @@ !!! error TS2300: Duplicate identifier 'duplicate3'. !!! related TS6203 tests/cases/compiler/file2.ts:7:9: 'duplicate3' was also declared here. } - } \ No newline at end of file + } +Found 6 errors. + diff --git a/tests/baselines/reference/duplicateIdentifierRelatedSpans7.errors.txt b/tests/baselines/reference/duplicateIdentifierRelatedSpans7.errors.txt index 7b568736ff3..6e5c0ab902c 100644 --- a/tests/baselines/reference/duplicateIdentifierRelatedSpans7.errors.txt +++ b/tests/baselines/reference/duplicateIdentifierRelatedSpans7.errors.txt @@ -55,4 +55,6 @@ duplicate8: () => string; duplicate9: () => string; } - } \ No newline at end of file + } +Found 2 errors. + diff --git a/tests/baselines/reference/esModuleInteropPrettyErrorRelatedInformation.errors.txt b/tests/baselines/reference/esModuleInteropPrettyErrorRelatedInformation.errors.txt index 14c054c466c..8222133a9de 100644 --- a/tests/baselines/reference/esModuleInteropPrettyErrorRelatedInformation.errors.txt +++ b/tests/baselines/reference/esModuleInteropPrettyErrorRelatedInformation.errors.txt @@ -22,4 +22,6 @@ !!! error TS2345: Argument of type '{ default: () => void; }' is not assignable to parameter of type '() => void'. !!! error TS2345: Type '{ default: () => void; }' provides no match for the signature '(): void'. !!! related TS7038 tests/cases/compiler/index.ts:1:1: Type originates at this import. A namespace-style import cannot be called or constructed, and will cause a failure at runtime. Consider using a default import or import require here instead. - \ No newline at end of file + +Found 1 error. + diff --git a/tests/baselines/reference/multiLineContextDiagnosticWithPretty.errors.txt b/tests/baselines/reference/multiLineContextDiagnosticWithPretty.errors.txt index 525f30415d1..44b13d7ea8e 100644 --- a/tests/baselines/reference/multiLineContextDiagnosticWithPretty.errors.txt +++ b/tests/baselines/reference/multiLineContextDiagnosticWithPretty.errors.txt @@ -20,4 +20,6 @@ !!! error TS2322: Type '{ a: { b: string; }; }' is not assignable to type '{ c: string; }'. !!! error TS2322: Object literal may only specify known properties, and 'a' does not exist in type '{ c: string; }'. }; - \ No newline at end of file + +Found 1 error. + diff --git a/tests/baselines/reference/prettyContextNotDebugAssertion.errors.txt b/tests/baselines/reference/prettyContextNotDebugAssertion.errors.txt index d983f0d973e..008d3d3d817 100644 --- a/tests/baselines/reference/prettyContextNotDebugAssertion.errors.txt +++ b/tests/baselines/reference/prettyContextNotDebugAssertion.errors.txt @@ -8,4 +8,6 @@ if (true) { -!!! error TS1005: '}' expected. \ No newline at end of file +!!! error TS1005: '}' expected. +Found 1 error. + diff --git a/tests/baselines/reference/typedefCrossModule5.errors.txt b/tests/baselines/reference/typedefCrossModule5.errors.txt index 2784f0e1896..6ee46827441 100644 --- a/tests/baselines/reference/typedefCrossModule5.errors.txt +++ b/tests/baselines/reference/typedefCrossModule5.errors.txt @@ -55,4 +55,6 @@ ~~~ !!! error TS2451: Cannot redeclare block-scoped variable 'Bar'. !!! related TS6203 tests/cases/conformance/jsdoc/mod1.js:2:7: 'Bar' was also declared here. - \ No newline at end of file + +Found 4 errors. +