import { s } from './misc.js'; /** * When inside a double-quoted attribute value, only `&` and `"` hold special meaning. * @see https://html.spec.whatwg.org/multipage/parsing.html#attribute-value-(double-quoted)-state * @type {Record} */ const escape_html_attr_dict = { '&': '&', '"': '"' // Svelte also escapes < because the escape function could be called inside a `noscript` there // https://github.com/sveltejs/svelte/security/advisories/GHSA-8266-84wp-wv5c // However, that doesn't apply in SvelteKit }; /** * @type {Record} */ const escape_html_dict = { '&': '&', '<': '<' }; const surrogates = // high surrogate without paired low surrogate '[\\ud800-\\udbff](?![\\udc00-\\udfff])|' + // a valid surrogate pair, the only match with 2 code units // we match it so that we can match unpaired low surrogates in the same pass // TODO: use lookbehind assertions once they are widely supported: (?...`; */ export function escape_html(str, is_attr) { const dict = is_attr ? escape_html_attr_dict : escape_html_dict; const escaped_str = str.replace(is_attr ? escape_html_attr_regex : escape_html_regex, (match) => { if (match.length === 2) { // valid surrogate pair return match; } return dict[match] ?? `&#${match.charCodeAt(0)};`; }); return escaped_str; } /** @typedef {{ placeholder: string, replacement: string }} Replacement */ /** * Escapes backslashes, backticks, and dollar signs so that the string can be * safely used as part of a template literal. * @param {string} str * @param {Replacement[]} replacements Placeholders to replace after escaping. * This is necessary when the string contains * placeholders that we want to preserve, such as `${assets}` * @returns {string} escaped string */ export function escape_for_interpolation(str, replacements) { let escaped = s(str).slice(1, -1).replaceAll('`', '\\`').replaceAll('$', '\\$'); for (const { placeholder, replacement } of replacements) { escaped = escaped.replaceAll(placeholder, replacement); } return escaped; }