From 181c1dc96e1881181aa7b3e4e4de6ffa19c06600 Mon Sep 17 00:00:00 2001 From: swarnendusahu10-dotcom Date: Wed, 28 Oct 2026 09:33:39 +0530 Subject: [PATCH] fix(react): apply CommonMark flanking rules to prevent intra-word emphasis in composer Prevents intra-word underscores and whitespace-flanked asterisks from being parsed as emphasis in contentEditableComposer.js. CommonMark and GFM specify that underscore delimiters cannot open or close emphasis when adjacent to word characters, and emphasis markers cannot be flanked by whitespace. Previously, identifiers (e.g. my_file_name.js, foo_bar_baz) and arithmetic (e.g. 2 * 3 * 4) were corrupted into and tags when rendered in the composer. Fixes #1372 --- .../react/src/lib/contentEditableComposer.js | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/packages/react/src/lib/contentEditableComposer.js b/packages/react/src/lib/contentEditableComposer.js index ffa3f6f9c..29c8f2755 100644 --- a/packages/react/src/lib/contentEditableComposer.js +++ b/packages/react/src/lib/contentEditableComposer.js @@ -44,6 +44,38 @@ const findLink = (text, start) => { }; }; +const EMPHASIS_MARKERS = new Set(['*', '**', '_', '__']); + +const isWhitespace = (character) => + character === undefined || /\s/.test(character); + +const isWordCharacter = (character) => + character !== undefined && /[\p{L}\p{N}]/u.test(character); + +const canOpenEmphasis = (text, start, marker) => { + if (!EMPHASIS_MARKERS.has(marker)) return true; + if (isWhitespace(text[start + marker.length])) return false; + if (marker[0] === '_' && isWordCharacter(text[start - 1])) return false; + return true; +}; + +const canCloseEmphasis = (text, end, marker) => { + if (!EMPHASIS_MARKERS.has(marker)) return true; + if (isWhitespace(text[end - 1])) return false; + if (marker[0] === '_' && isWordCharacter(text[end + marker.length])) { + return false; + } + return true; +}; + +const findClosingMarker = (text, from, marker) => { + let end = text.indexOf(marker, from); + while (end !== -1 && !canCloseEmphasis(text, end, marker)) { + end = text.indexOf(marker, end + marker.length); + } + return end; +}; + const appendMarkdown = (parent, text) => { const tokens = [ ['**', 'strong'], @@ -89,9 +121,13 @@ const appendMarkdown = (parent, text) => { } } + if (token && !canOpenEmphasis(text, index, token[0])) { + token = undefined; + } + if (token) { const [marker, tagName] = token; - const end = text.indexOf(marker, index + marker.length); + const end = findClosingMarker(text, index + marker.length, marker); if (end > index + marker.length) { flush(); const formatted = document.createElement(tagName);