From f8ebc0e99a305cb51a811f8272c40369d8e31810 Mon Sep 17 00:00:00 2001 From: Robert van Hoesel Date: Tue, 20 Dec 2022 01:23:06 +0100 Subject: [PATCH] fix: don't decode HTML entities (`&foo;`) until rendering (#465) --- composables/content-parse.ts | 5 ++--- composables/content-render.ts | 5 +++-- tests/__snapshots__/html-parse.test.ts.snap | 7 +++++++ tests/html-parse.test.ts | 6 ++++++ 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/composables/content-parse.ts b/composables/content-parse.ts index ad58d8db..d96e2269 100644 --- a/composables/content-parse.ts +++ b/composables/content-parse.ts @@ -3,7 +3,7 @@ import type { Node } from 'ultrahtml' import { TEXT_NODE, parse, render, walkSync } from 'ultrahtml' const decoder = process.client ? document.createElement('textarea') : null as any as HTMLTextAreaElement -function decode(text: string) { +export function decodeHtml(text: string) { decoder.innerHTML = text return decoder.value } @@ -40,7 +40,6 @@ export function parseMastodonHTML(html: string, customEmojis: Record$1'], [/~~(.*?)~~/g, '$1'], [/`([^`]+?)`/g, '$1'], - [/&[^;]+;/g, (val: string) => decode(val)], ] as any for (const [re, replacement] of replacements) { @@ -77,7 +76,7 @@ export function treeToText(input: Node): string { let post = '' if (input.type === TEXT_NODE) - return input.value + return decodeHtml(input.value) if (input.name === 'br') return '\n' diff --git a/composables/content-render.ts b/composables/content-render.ts index b0a6ef61..2affedf2 100644 --- a/composables/content-render.ts +++ b/composables/content-render.ts @@ -4,7 +4,7 @@ import type { Node } from 'ultrahtml' import { Fragment, h, isVNode } from 'vue' import type { VNode } from 'vue' import { RouterLink } from 'vue-router' -import { parseMastodonHTML } from './content-parse' +import { decodeHtml, parseMastodonHTML } from './content-parse' import ContentCode from '~/components/content/ContentCode.vue' import AccountHoverWrapper from '~/components/account/AccountHoverWrapper.vue' @@ -45,11 +45,12 @@ export function nodeToVNode(node: Node): VNode | string | null { } return null } + function treeToVNode( input: Node, ): VNode | string | null { if (input.type === TEXT_NODE) - return input.value as string + return decodeHtml(input.value) if ('children' in input) { const node = handleNode(input) diff --git a/tests/__snapshots__/html-parse.test.ts.snap b/tests/__snapshots__/html-parse.test.ts.snap index 55208cf5..0fd2ff3a 100644 --- a/tests/__snapshots__/html-parse.test.ts.snap +++ b/tests/__snapshots__/html-parse.test.ts.snap @@ -58,6 +58,13 @@ exports[`html-parse > empty > html 1`] = `""`; exports[`html-parse > empty > text 1`] = `""`; +exports[`html-parse > html entities > html 1`] = ` +"

Hello <World />.

+" +`; + +exports[`html-parse > html entities > text 1`] = `"Hello ."`; + exports[`html-parse > inline markdown > html 1`] = `"

text code bold italic del

code block

"`; exports[`html-parse > inline markdown > text 1`] = ` diff --git a/tests/html-parse.test.ts b/tests/html-parse.test.ts index 354cbacd..cd70365a 100644 --- a/tests/html-parse.test.ts +++ b/tests/html-parse.test.ts @@ -52,6 +52,12 @@ describe('html-parse', () => { expect(formatted).toMatchSnapshot('html') expect(serializedText).toMatchSnapshot('text') }) + + it('html entities', async () => { + const { formatted, serializedText } = await render('

Hello <World />.

') + expect(formatted).toMatchSnapshot('html') + expect(serializedText).toMatchSnapshot('text') + }) }) async function render(input: string, emojis?: Record) {