Skip to content

Commit 0cfeda9

Browse files
committed
fix(formatters): prevent backticks in exception values and stack frames from breaking code spans
- markdown.ts safeCodeSpan: also replace ` with ˋ (U+02CB MODIFIER LETTER GRAVE ACCENT) — visually identical in monospace but never a code-span delimiter. Prevents JS exception messages like 'Unexpected token `' from prematurely closing the backtick-delimited code span. - human.ts formatStackFrameMarkdown: use safeCodeSpan() for the full 'at fn (file:line:col)' string instead of raw template-literal backticks - human.ts formatExceptionValueMarkdown: use safeCodeSpan() for the 'type: value' header instead of raw backticks
1 parent cb5ac36 commit 0cfeda9

File tree

2 files changed

+16
-12
lines changed

2 files changed

+16
-12
lines changed

src/lib/formatters/human.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -718,7 +718,7 @@ function formatStackFrameMarkdown(frame: StackFrame): string {
718718
const col = frame.colNo ?? "?";
719719
const inAppTag = frame.inApp ? " `[in-app]`" : "";
720720

721-
lines.push(`\`at ${fn} (${file}:${line}:${col})\`${inAppTag}`);
721+
lines.push(`${safeCodeSpan(`at ${fn} (${file}:${line}:${col})`)}${inAppTag}`);
722722

723723
if (frame.context && frame.context.length > 0) {
724724
lines.push("");
@@ -743,7 +743,7 @@ function formatExceptionValueMarkdown(exception: ExceptionValue): string {
743743

744744
const type = exception.type || "Error";
745745
const value = exception.value || "";
746-
lines.push(`**\`${type}: ${value}\`**`);
746+
lines.push(`**${safeCodeSpan(`${type}: ${value}`)}**`);
747747

748748
if (exception.mechanism) {
749749
const handled = exception.mechanism.handled ? "handled" : "unhandled";

src/lib/formatters/markdown.ts

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -203,22 +203,26 @@ export function mdRow(cells: readonly string[]): string {
203203
}
204204

205205
/**
206-
* Wrap a user-supplied string in a backtick code span for use inside a
207-
* markdown table cell.
206+
* Wrap a user-supplied string in a backtick code span.
208207
*
209208
* Inside a CommonMark code span, backslashes are **literal** (not escape
210-
* characters), so `\|` would render as `\|` rather than protecting the
211-
* table delimiter. Instead this helper:
212-
*
213-
* - Replaces `|` with `│` (U+2502, BOX DRAWINGS LIGHT VERTICAL) — visually
214-
* identical but not a markdown table delimiter.
215-
* - Replaces newlines with a space — code spans cannot span lines.
209+
* characters), so backslash-based escaping of special characters does not
210+
* work. This helper sanitises the three characters that would otherwise break
211+
* the span or the surrounding table structure:
212+
*
213+
* - Replaces `` ` `` with `ˋ` (U+02CB MODIFIER LETTER GRAVE ACCENT) —
214+
* visually identical in monospace fonts but never treated as a code-span
215+
* delimiter. Prevents exception messages like `Unexpected token \`` from
216+
* prematurely closing the span.
217+
* - Replaces `|` with `│` (U+2502 BOX DRAWINGS LIGHT VERTICAL) — prevents
218+
* table column splitting when used inside a markdown table cell.
219+
* - Replaces newlines with a space — code spans cannot span multiple lines.
216220
*
217221
* @param value - Raw string to format as a code span
218-
* @returns `` `value` `` with pipe and newline characters sanitised
222+
* @returns `` `value` `` with backtick, pipe, and newline characters sanitised
219223
*/
220224
export function safeCodeSpan(value: string): string {
221-
return `\`${value.replace(/\|/g, "│").replace(/\n/g, " ")}\``;
225+
return `\`${value.replace(/`/g, "ˋ").replace(/\|/g, "│").replace(/\n/g, " ")}\``;
222226
}
223227

224228
/**

0 commit comments

Comments
 (0)