From 35ec807808c91e759cfb2819b2e545f85b203506 Mon Sep 17 00:00:00 2001 From: Kendell R Date: Tue, 26 Aug 2025 14:24:33 -0700 Subject: [PATCH 1/4] fix(formatters): don't escape * in links --- packages/formatters/src/escapers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/formatters/src/escapers.ts b/packages/formatters/src/escapers.ts index bceaf484f1d9..63d093a2142f 100644 --- a/packages/formatters/src/escapers.ts +++ b/packages/formatters/src/escapers.ts @@ -212,7 +212,7 @@ export function escapeInlineCode(text: string): string { */ export function escapeItalic(text: string): string { let idx = 0; - const newText = text.replaceAll(/(?<=^|[^*])\*([^*]|\*\*|$)/g, (_, match) => { + const newText = text.replaceAll(/(?<=^|[^*])(? { if (match === '**') return ++idx % 2 ? `\\*${match}` : `${match}\\*`; return `\\*${match}`; }); From 30ef6f6383a117333cb9a46050faccadd19e1edd Mon Sep 17 00:00:00 2001 From: KTibow Date: Tue, 26 Aug 2025 14:36:12 -0700 Subject: [PATCH 2/4] add test for * -> * in url --- packages/formatters/__tests__/escapers.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/formatters/__tests__/escapers.test.ts b/packages/formatters/__tests__/escapers.test.ts index 280dc4d1a392..5fef0fb61ab1 100644 --- a/packages/formatters/__tests__/escapers.test.ts +++ b/packages/formatters/__tests__/escapers.test.ts @@ -23,6 +23,7 @@ const testURLs = [ 'https://example.com/name_with_underscores', 'https://example.com/name__with__underscores', 'https://example.com/name_with_underscores_and__double__underscores', + 'https://*.example.com/globbed/*', ]; describe('Markdown escapers', () => { From 92c8e72a6ac833e339c4819420c55bb3f90763ef Mon Sep 17 00:00:00 2001 From: KTibow Date: Tue, 26 Aug 2025 14:45:51 -0700 Subject: [PATCH 3/4] `\S+` -> `\S*` (tested locally) --- packages/formatters/src/escapers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/formatters/src/escapers.ts b/packages/formatters/src/escapers.ts index 63d093a2142f..8c9e1a836559 100644 --- a/packages/formatters/src/escapers.ts +++ b/packages/formatters/src/escapers.ts @@ -212,7 +212,7 @@ export function escapeInlineCode(text: string): string { */ export function escapeItalic(text: string): string { let idx = 0; - const newText = text.replaceAll(/(?<=^|[^*])(? { + const newText = text.replaceAll(/(?<=^|[^*])(? { if (match === '**') return ++idx % 2 ? `\\*${match}` : `${match}\\*`; return `\\*${match}`; }); From 1a1eb111146317c32f6e400cf1b6f38b5c500c6f Mon Sep 17 00:00:00 2001 From: KTibow Date: Fri, 29 Aug 2025 12:17:22 -0700 Subject: [PATCH 4/4] @Qjuh: handle italics both inside+outside s --- .../formatters/__tests__/escapers.test.ts | 11 ++++++++++ packages/formatters/src/escapers.ts | 22 ++++++++++++------- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/packages/formatters/__tests__/escapers.test.ts b/packages/formatters/__tests__/escapers.test.ts index 5fef0fb61ab1..a5d69763ced3 100644 --- a/packages/formatters/__tests__/escapers.test.ts +++ b/packages/formatters/__tests__/escapers.test.ts @@ -24,6 +24,7 @@ const testURLs = [ 'https://example.com/name__with__underscores', 'https://example.com/name_with_underscores_and__double__underscores', 'https://*.example.com/globbed/*', + 'https://example.com/*before*/>/*after*', ]; describe('Markdown escapers', () => { @@ -88,6 +89,16 @@ describe('Markdown escapers', () => { test('url', () => { for (const url of testURLs) expect(escapeItalic(url)).toBe(url); }); + + test('after-url', () => { + const url = ''; + for (const [input, output] of [ + ['*hi*', '\\*hi\\*'], + ['_hi_', '\\_hi\\_'], + ]) { + expect(escapeItalic(url + input)).toBe(url + output); + } + }); }); describe('escapeUnderline', () => { diff --git a/packages/formatters/src/escapers.ts b/packages/formatters/src/escapers.ts index 8c9e1a836559..b1f0b028a226 100644 --- a/packages/formatters/src/escapers.ts +++ b/packages/formatters/src/escapers.ts @@ -212,15 +212,21 @@ export function escapeInlineCode(text: string): string { */ export function escapeItalic(text: string): string { let idx = 0; - const newText = text.replaceAll(/(?<=^|[^*])(? { - if (match === '**') return ++idx % 2 ? `\\*${match}` : `${match}\\*`; - return `\\*${match}`; - }); + const newText = text.replaceAll( + /(?<=^|[^*])(?]*)\*([^*]|\*\*|$)/g, + (_, match) => { + if (match === '**') return ++idx % 2 ? `\\*${match}` : `${match}\\*`; + return `\\*${match}`; + }, + ); idx = 0; - return newText.replaceAll(/(?<=^|[^_])(?)([^_]|__|$)/g, (_, match) => { - if (match === '__') return ++idx % 2 ? `\\_${match}` : `${match}\\_`; - return `\\_${match}`; - }); + return newText.replaceAll( + /(?<=^|[^_])(?]*)_(?!:\d+>)([^_]|__|$)/g, + (_, match) => { + if (match === '__') return ++idx % 2 ? `\\_${match}` : `${match}\\_`; + return `\\_${match}`; + }, + ); } /**