From 0fc9229383e23cd535d8fa0a5867e6e51618f00a Mon Sep 17 00:00:00 2001 From: Rico Hermans Date: Tue, 28 Feb 2023 12:40:01 +0100 Subject: [PATCH] fix(rosetta): prints colors to log file (#3953) Rosetta error messages contain colors codes in the errors, which makes the errors produced by Rosetta look like garbage and very hard to parse. Strip the colors if we detect a non-TTY input. (Copied the regex from an MIT-licensed package to avoid adding extra dependencies) --- By submitting this pull request, I confirm that my contribution is made under the terms of the [Apache 2.0 license]. [Apache 2.0 license]: https://www.apache.org/licenses/LICENSE-2.0 --- packages/jsii-rosetta/bin/jsii-rosetta.ts | 6 +++--- .../jsii-rosetta/lib/commands/transliterate.ts | 2 +- packages/jsii-rosetta/lib/rosetta-reader.ts | 4 ++-- packages/jsii-rosetta/lib/util.ts | 17 +++++++++++++++-- 4 files changed, 21 insertions(+), 8 deletions(-) diff --git a/packages/jsii-rosetta/bin/jsii-rosetta.ts b/packages/jsii-rosetta/bin/jsii-rosetta.ts index 732a77c284..cfc403fbb6 100644 --- a/packages/jsii-rosetta/bin/jsii-rosetta.ts +++ b/packages/jsii-rosetta/bin/jsii-rosetta.ts @@ -514,7 +514,7 @@ function handleDiagnostics(diagnostics: readonly RosettaDiagnostic[], fail: bool if (fail !== false) { // Fail on any diagnostic if (diagnostics.length > 0) { - printDiagnostics(diagnostics, process.stderr); + printDiagnostics(diagnostics, process.stderr, process.stderr.isTTY); logging.error( [ `${diagnostics.length} diagnostics encountered in ${snippetCount} snippets`, @@ -531,7 +531,7 @@ function handleDiagnostics(diagnostics: readonly RosettaDiagnostic[], fail: bool // (so it's very clear what is failing the build), otherwise print everything. const strictDiagnostics = diagnostics.filter((diag) => diag.isFromStrictAssembly); if (strictDiagnostics.length > 0) { - printDiagnostics(strictDiagnostics, process.stderr); + printDiagnostics(strictDiagnostics, process.stderr, process.stderr.isTTY); const remaining = diagnostics.length - strictDiagnostics.length; logging.warn( [ @@ -544,7 +544,7 @@ function handleDiagnostics(diagnostics: readonly RosettaDiagnostic[], fail: bool } if (diagnostics.length > 0) { - printDiagnostics(diagnostics, process.stderr); + printDiagnostics(diagnostics, process.stderr, process.stderr.isTTY); logging.warn(`${diagnostics.length} diagnostics encountered in ${snippetCount} snippets`); } } diff --git a/packages/jsii-rosetta/lib/commands/transliterate.ts b/packages/jsii-rosetta/lib/commands/transliterate.ts index cba52a96e8..853c851f15 100644 --- a/packages/jsii-rosetta/lib/commands/transliterate.ts +++ b/packages/jsii-rosetta/lib/commands/transliterate.ts @@ -126,7 +126,7 @@ export async function transliterateAssembly( } } - rosetta.printDiagnostics(process.stderr); + rosetta.printDiagnostics(process.stderr, process.stderr.isTTY); if (rosetta.hasErrors && options.strict) { throw new Error('Strict mode is enabled and some examples failed compilation!'); } diff --git a/packages/jsii-rosetta/lib/rosetta-reader.ts b/packages/jsii-rosetta/lib/rosetta-reader.ts index 1331bddda8..ba70145d1c 100644 --- a/packages/jsii-rosetta/lib/rosetta-reader.ts +++ b/packages/jsii-rosetta/lib/rosetta-reader.ts @@ -293,8 +293,8 @@ export class RosettaTabletReader { ); } - public printDiagnostics(stream: NodeJS.WritableStream) { - printDiagnostics(this.diagnostics, stream); + public printDiagnostics(stream: NodeJS.WritableStream, colors = true) { + printDiagnostics(this.diagnostics, stream, colors); } public get hasErrors() { diff --git a/packages/jsii-rosetta/lib/util.ts b/packages/jsii-rosetta/lib/util.ts index 2d526b19e2..55df4cf25d 100644 --- a/packages/jsii-rosetta/lib/util.ts +++ b/packages/jsii-rosetta/lib/util.ts @@ -12,12 +12,12 @@ export interface File { readonly fileName: string; } -export function printDiagnostics(diags: readonly RosettaDiagnostic[], stream: NodeJS.WritableStream) { +export function printDiagnostics(diags: readonly RosettaDiagnostic[], stream: NodeJS.WritableStream, colors: boolean) { // Don't print too much, at some point it just clogs up the log const maxDiags = 50; for (const diag of diags.slice(0, maxDiags)) { - stream.write(diag.formattedMessage); + stream.write(colors ? diag.formattedMessage : stripColorCodes(diag.formattedMessage)); } if (diags.length > maxDiags) { @@ -243,3 +243,16 @@ export async function pathExists(path: string): Promise { throw err; } } + +// Copy/pasted from the 'ansi-regex' package to avoid taking a dependency for this one line that will never change +const ANSI_PATTERN = new RegExp( + [ + '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)', + '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))', + ].join('|'), + 'g', +); + +function stripColorCodes(x: string) { + return x.replace(ANSI_PATTERN, ''); +}