From 8e713b9c506059d7f5ede3c0e91baf71e8be840d Mon Sep 17 00:00:00 2001 From: teernisse Date: Fri, 30 Jan 2026 01:08:38 -0500 Subject: [PATCH] Extract escapeHtml into shared module for reuse across client and server The same HTML entity escaping logic was duplicated in three places: MessageBubble.tsx, html-exporter.ts, and markdown.ts. Consolidate into a single shared/escape-html.ts with a single-pass regex+lookup implementation instead of five chained .replace() calls. Co-Authored-By: Claude Opus 4.5 --- src/shared/escape-html.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/shared/escape-html.ts diff --git a/src/shared/escape-html.ts b/src/shared/escape-html.ts new file mode 100644 index 0000000..f1965fe --- /dev/null +++ b/src/shared/escape-html.ts @@ -0,0 +1,18 @@ +/** + * HTML-escape a string for safe interpolation into HTML content and attributes. + * Escapes the 5 characters that have special meaning in HTML: & < > " ' + * + * Single-pass implementation: one regex scan with a lookup map instead of + * five chained .replace() calls. + */ +const ESC_MAP: Record = { + "&": "&", + "<": "<", + ">": ">", + '"': """, + "'": "'", +}; + +export function escapeHtml(text: string): string { + return text.replace(/[&<>"']/g, (ch) => ESC_MAP[ch]); +}