const ESCAPED_OPENING_CHEVRON = '<'; const ESCAPED_CLOSING_CHEVRON = '>'; type Tags = { transformed: { opening: string; closing: string; }; escapedRegex: { opening: RegExp; closing: RegExp; }; }; class EmphasisHtmlTransformer { tags: Tags[]; constructor(whitelistedTags?: string[]) { this.tags = (whitelistedTags || []).map((tag) => { return { transformed: { opening: ``, closing: '', }, escapedRegex: { opening: new RegExp(`${ESCAPED_OPENING_CHEVRON}${tag}${ESCAPED_CLOSING_CHEVRON}`, 'g'), closing: new RegExp(`${ESCAPED_OPENING_CHEVRON}/${tag}${ESCAPED_CLOSING_CHEVRON}`, 'g'), }, }; }); } // Algorithm: // 1) Escape all dangerous characters (<,>,&) // 2) Replace all escaped, whitelisted tags with styled spans. // 3) Transform new line characters to `
`s // // Note: for simplicity this doesn't support tags with non-standard whitespaces, e.g. transform(unsafe: string): string | null { if (!unsafe) { return null; } const safe = unsafe.replace(/&/g, '&').replace(//g, '>'); return this.tags .reduce((accumulator, tag) => { return accumulator .replace(tag.escapedRegex.opening, tag.transformed.opening) .replace(tag.escapedRegex.closing, tag.transformed.closing); }, safe) .replace(/\\n|\n/g, '
'); } } export default EmphasisHtmlTransformer;