|
| 1 | +/** |
| 2 | + * Construct a quoted CSS String from a plain JavaScript value. |
| 3 | + * |
| 4 | + * A plain JavaScript string will be transformed to a quoted CSS string suitable for interpolation |
| 5 | + * in CSS text. |
| 6 | + * |
| 7 | + * The input is not trimmed. Trim should be performed before calling this method, if desired. |
| 8 | + * |
| 9 | + * @example |
| 10 | + * ```js |
| 11 | + * const fontName = 'CSS & a "<style>" tag\'s strings'; |
| 12 | + * const fontFaceRule = `@font-face { font-family: ${createCssString(fontName)}; }` |
| 13 | + * ``` |
| 14 | + * |
| 15 | + * `fontFaceRule` will have the value `'@font-face { font-family: "CSS \\26 a \\22 \\3C style\\3E \\22 tag\\27 s strings"; }'`. |
| 16 | + * The browser will parse this rule as equivalent to: `@font-face { font-family: "CSS & a \"<style>\" tag's strings"; }`. |
| 17 | + * |
| 18 | + * Some characters that are valid in CSS strings are escaped to avoid problems with subsequent |
| 19 | + * processing that may not correctly parse CSS: |
| 20 | + * |
| 21 | + * - "<", ">", and "&" are replaced to prevent issues with KSES and other sanitization that |
| 22 | + * is confused by HTML-like text. |
| 23 | + * - CSS syntax characters are replaced to prevent issues where CSS may be processed by simple |
| 24 | + * string splits or search and replaces: "',;{} |
| 25 | + * - Plain "\" backslashes are escaped. |
| 26 | + * |
| 27 | + * @param value The JavaScript string to serialize as a quoted CSS string. |
| 28 | + * @return A quoted CSS string suitable for interpolation in CSS text. |
| 29 | + */ |
| 30 | +export function createCssString( value: string ): string { |
| 31 | + return `"${ value |
| 32 | + /* |
| 33 | + * CSS Unicode escaping for problematic characters. |
| 34 | + * https://www.w3.org/TR/css-syntax-3/#escaping |
| 35 | + * |
| 36 | + * Note that the Unicode escape sequences are used rather than backslash-escaping so the |
| 37 | + * problematic characters are removed completely. CSS Unicode escapes are formed by a |
| 38 | + * "\" followed by the character code in hexadecimal. The escape may be terminated by |
| 39 | + * whitespace which is ignored. |
| 40 | + */ |
| 41 | +
|
| 42 | + // Escape existing backslashes to prevent unintentional escapes in result. |
| 43 | + .replaceAll( '\\', '\\5C ' ) |
| 44 | +
|
| 45 | + // Pre-processing replaces NULLs and some newlines. Replace and escape as necessary. |
| 46 | + .replaceAll( '\0', '\uFFFD' ) |
| 47 | +
|
| 48 | + // Normalize and replace newlines. https://www.w3.org/TR/css-syntax-3/#input-preprocessing |
| 49 | + .replaceAll( '\r\n', '\\A ' ) |
| 50 | + .replaceAll( '\r', '\\A ' ) |
| 51 | + .replaceAll( '\f', '\\A ' ) |
| 52 | +
|
| 53 | + // Newlines must be escaped in CSS strings. |
| 54 | + .replaceAll( '\n', '\\A ' ) |
| 55 | +
|
| 56 | + // Arbitrary characters for Unicode escaping: |
| 57 | +
|
| 58 | + // HTML syntax may be problematic. |
| 59 | + .replaceAll( '<', '\\3C ' ) |
| 60 | + .replaceAll( '>', '\\3E ' ) |
| 61 | + .replaceAll( '&', '\\26 ' ) |
| 62 | +
|
| 63 | + // CSS syntax may be problematic. |
| 64 | + .replaceAll( ',', '\\2C ' ) |
| 65 | + .replaceAll( ';', '\\3B ' ) |
| 66 | + .replaceAll( '{', '\\7B ' ) |
| 67 | + .replaceAll( '}', '\\7D ' ) |
| 68 | + .replaceAll( '"', '\\22 ' ) |
| 69 | + .replaceAll( "'", '\\27 ' ) }"`; |
| 70 | +} |
0 commit comments