From 9cfbe0a035169288370e76200960be0b287dc606 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Chalk?= Date: Wed, 3 Dec 2025 15:12:16 +0100 Subject: [PATCH] fix(utils): fix long word wrapping with ansis styles in tables --- .../utils/src/lib/text-formats/ascii/link.ts | 3 +- .../utils/src/lib/text-formats/ascii/table.ts | 3 + .../lib/text-formats/ascii/table.unit.test.ts | 63 +++++++++++++++++++ 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/packages/utils/src/lib/text-formats/ascii/link.ts b/packages/utils/src/lib/text-formats/ascii/link.ts index 1525fa5e2..93e727f22 100644 --- a/packages/utils/src/lib/text-formats/ascii/link.ts +++ b/packages/utils/src/lib/text-formats/ascii/link.ts @@ -1,5 +1,6 @@ import ansis from 'ansis'; export function formatAsciiLink(url: string): string { - return ansis.underline.blueBright(url); + // no underline because terminals recognize URLs, and nested ansis styles aren't handled by wrap-ansi + return ansis.blueBright(url); } diff --git a/packages/utils/src/lib/text-formats/ascii/table.ts b/packages/utils/src/lib/text-formats/ascii/table.ts index f8c888e7b..0efa9ee7e 100644 --- a/packages/utils/src/lib/text-formats/ascii/table.ts +++ b/packages/utils/src/lib/text-formats/ascii/table.ts @@ -158,6 +158,9 @@ function wrapText(text: string, width: number | undefined): string { if (!width || getTextWidth(text) <= width) { return text; } + if (text !== ansis.strip(text)) { + return wrapAnsi(text, width, { hard: true }); + } const words = extractWords(text); const longWords = words.filter(word => word.length > width); const replacements = longWords.map(original => { diff --git a/packages/utils/src/lib/text-formats/ascii/table.unit.test.ts b/packages/utils/src/lib/text-formats/ascii/table.unit.test.ts index 52bd6cc64..c330f5fec 100644 --- a/packages/utils/src/lib/text-formats/ascii/table.unit.test.ts +++ b/packages/utils/src/lib/text-formats/ascii/table.unit.test.ts @@ -453,6 +453,69 @@ Code coverage: ├──────────┼────────────┼─────────┼────────────┼──────────┼─────────┼──────────┤ │ 81 │ 64 │ 92 │ 100 │ 95 │ 62 │ 6 │ └──────────┴────────────┴─────────┴────────────┴──────────┴─────────┴──────────┘ +`.trim(), + ); + }); + + it('should wrap ansi styles correctly', () => { + const output = formatAsciiTable( + { + rows: [ + [''], + [ansis.bold('💡 Integrate the Portal')], + [''], + [`${ansis.gray('❯')} Configure upload in code-pushup.config.ts`], + [ + ansis.underline( + 'https://github.com/code-pushup/cli/blob/main/packages/cli/README.md#portal-integration', + ), + ], + [ + `${ansis.gray('❯')} npx code-pushup upload${ansis.gray(' - Upload previously collected report to the Portal')}`, + ], + [ + ansis.underline( + 'https://github.com/code-pushup/cli/tree/main/packages/cli#upload-command', + ), + ], + [''], + ], + }, + { padding: 4 }, + ); + + expect(ansis.strip(output)).toBe( + ` +┌──────────────────────────────────────────────────────────────────────────────┐ +│ │ +│ 💡 Integrate the Portal │ +│ │ +│ ❯ Configure upload in code-pushup.config.ts │ +│ https://github.com/code-pushup/cli/blob/main/packages/cli/README.md#po │ +│ rtal-integration │ +│ ❯ npx code-pushup upload - Upload previously collected report to the │ +│ Portal │ +│ https://github.com/code-pushup/cli/tree/main/packages/cli#upload-comma │ +│ nd │ +│ │ +└──────────────────────────────────────────────────────────────────────────────┘ +`.trim(), + ); + expect(output).toBe( + ` +${ansis.dim('┌──────────────────────────────────────────────────────────────────────────────┐')} +${ansis.dim('│')} ${ansis.dim('│')} +${ansis.dim('│')} ${ansis.bold('💡 Integrate the Portal')} ${ansis.dim('│')} +${ansis.dim('│')} ${ansis.dim('│')} +${ansis.dim('│')} ${ansis.gray('❯')} Configure upload in code-pushup.config.ts ${ansis.dim('│')} +${ansis.dim('│')} ${ansis.underline('https://github.com/code-pushup/cli/blob/main/packages/cli/README.md#po')} ${ansis.dim('│')} +${ansis.dim('│')} ${ansis.underline('rtal-integration')} ${ansis.dim('│')} +${ansis.dim('│')} ${ansis.gray('❯')} npx code-pushup upload${ansis.gray(' - Upload previously collected report to the')} ${ansis.dim('│')} +${ansis.dim('│')} ${ansis.gray('Portal')} ${ansis.dim('│')} +${ansis.dim('│')} ${ansis.underline('https://github.com/code-pushup/cli/tree/main/packages/cli#upload-comma')} ${ansis.dim('│')} +${ansis.dim('│')} ${ansis.underline('nd')} ${ansis.dim('│')} +${ansis.dim('│')} ${ansis.dim('│')} +${ansis.dim('└──────────────────────────────────────────────────────────────────────────────┘')} `.trim(), ); });