diff --git a/packages/labs/ssr/src/lib/util/escape-html.ts b/packages/labs/ssr/src/lib/util/escape-html.ts index d0d66edd62..d4a4e56ab6 100644 --- a/packages/labs/ssr/src/lib/util/escape-html.ts +++ b/packages/labs/ssr/src/lib/util/escape-html.ts @@ -4,22 +4,57 @@ * SPDX-License-Identifier: BSD-3-Clause */ -const replacements = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - // Note ' was not defined in the HTML4 spec, and is not supported by very - // old browsers like IE8, so a codepoint entity is used instead. - "'": ''', -}; +const escapePattern = /[&<>"']/g; /** * Replaces characters which have special meaning in HTML (&<>"') with escaped * HTML entities ("&", "<", etc.). */ -export const escapeHtml = (str: string) => - str.replace( - /[&<>"']/g, - (char) => replacements[char as keyof typeof replacements] - ); +export const escapeHtml = (str: string) => { + let match = escapePattern.exec(str); + + if (!match) { + return str; + } + + let escapeStr; + let html = ''; + let lastIndex = 0; + + while (match) { + switch (str.charCodeAt(match.index)) { + // Character: " + case 34: + escapeStr = '"'; + break; + // Character: & + case 38: + escapeStr = '&'; + break; + // Character: ' + // Note ' was not defined in the HTML4 spec, and is not supported by + // very old browsers like IE8, so a codepoint entity is used instead. + case 39: + escapeStr = '''; + break; + // Character: < + case 60: + escapeStr = '<'; + break; + // Character: > + case 62: + escapeStr = '>'; + break; + } + + html += str.substring(lastIndex, match.index) + escapeStr; + lastIndex = match.index + 1; + match = escapePattern.exec(str); + } + + escapePattern.lastIndex = 0; + + return lastIndex !== str.length - 1 + ? html + str.substring(lastIndex, str.length) + : html; +};