fix: don't decode HTML entities (`&foo;`) until rendering (#465)
parent
9a7c37db24
commit
f8ebc0e99a
|
@ -3,7 +3,7 @@ import type { Node } from 'ultrahtml'
|
||||||
import { TEXT_NODE, parse, render, walkSync } from 'ultrahtml'
|
import { TEXT_NODE, parse, render, walkSync } from 'ultrahtml'
|
||||||
|
|
||||||
const decoder = process.client ? document.createElement('textarea') : null as any as HTMLTextAreaElement
|
const decoder = process.client ? document.createElement('textarea') : null as any as HTMLTextAreaElement
|
||||||
function decode(text: string) {
|
export function decodeHtml(text: string) {
|
||||||
decoder.innerHTML = text
|
decoder.innerHTML = text
|
||||||
return decoder.value
|
return decoder.value
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,6 @@ export function parseMastodonHTML(html: string, customEmojis: Record<string, Emo
|
||||||
[/\*(.*?)\*/g, '<em>$1</em>'],
|
[/\*(.*?)\*/g, '<em>$1</em>'],
|
||||||
[/~~(.*?)~~/g, '<del>$1</del>'],
|
[/~~(.*?)~~/g, '<del>$1</del>'],
|
||||||
[/`([^`]+?)`/g, '<code>$1</code>'],
|
[/`([^`]+?)`/g, '<code>$1</code>'],
|
||||||
[/&[^;]+;/g, (val: string) => decode(val)],
|
|
||||||
] as any
|
] as any
|
||||||
|
|
||||||
for (const [re, replacement] of replacements) {
|
for (const [re, replacement] of replacements) {
|
||||||
|
@ -77,7 +76,7 @@ export function treeToText(input: Node): string {
|
||||||
let post = ''
|
let post = ''
|
||||||
|
|
||||||
if (input.type === TEXT_NODE)
|
if (input.type === TEXT_NODE)
|
||||||
return input.value
|
return decodeHtml(input.value)
|
||||||
|
|
||||||
if (input.name === 'br')
|
if (input.name === 'br')
|
||||||
return '\n'
|
return '\n'
|
||||||
|
|
|
@ -4,7 +4,7 @@ import type { Node } from 'ultrahtml'
|
||||||
import { Fragment, h, isVNode } from 'vue'
|
import { Fragment, h, isVNode } from 'vue'
|
||||||
import type { VNode } from 'vue'
|
import type { VNode } from 'vue'
|
||||||
import { RouterLink } from 'vue-router'
|
import { RouterLink } from 'vue-router'
|
||||||
import { parseMastodonHTML } from './content-parse'
|
import { decodeHtml, parseMastodonHTML } from './content-parse'
|
||||||
import ContentCode from '~/components/content/ContentCode.vue'
|
import ContentCode from '~/components/content/ContentCode.vue'
|
||||||
import AccountHoverWrapper from '~/components/account/AccountHoverWrapper.vue'
|
import AccountHoverWrapper from '~/components/account/AccountHoverWrapper.vue'
|
||||||
|
|
||||||
|
@ -45,11 +45,12 @@ export function nodeToVNode(node: Node): VNode | string | null {
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
function treeToVNode(
|
function treeToVNode(
|
||||||
input: Node,
|
input: Node,
|
||||||
): VNode | string | null {
|
): VNode | string | null {
|
||||||
if (input.type === TEXT_NODE)
|
if (input.type === TEXT_NODE)
|
||||||
return input.value as string
|
return decodeHtml(input.value)
|
||||||
|
|
||||||
if ('children' in input) {
|
if ('children' in input) {
|
||||||
const node = handleNode(input)
|
const node = handleNode(input)
|
||||||
|
|
|
@ -58,6 +58,13 @@ exports[`html-parse > empty > html 1`] = `""`;
|
||||||
|
|
||||||
exports[`html-parse > empty > text 1`] = `""`;
|
exports[`html-parse > empty > text 1`] = `""`;
|
||||||
|
|
||||||
|
exports[`html-parse > html entities > html 1`] = `
|
||||||
|
"<p>Hello <World />.</p>
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`html-parse > html entities > text 1`] = `"Hello <World />."`;
|
||||||
|
|
||||||
exports[`html-parse > inline markdown > html 1`] = `"<p>text <code>code</code> <b>bold</b> <em>italic</em> <del>del</del></p><p><pre><code class=\\"language-js\\">code block</code></pre></p>"`;
|
exports[`html-parse > inline markdown > html 1`] = `"<p>text <code>code</code> <b>bold</b> <em>italic</em> <del>del</del></p><p><pre><code class=\\"language-js\\">code block</code></pre></p>"`;
|
||||||
|
|
||||||
exports[`html-parse > inline markdown > text 1`] = `
|
exports[`html-parse > inline markdown > text 1`] = `
|
||||||
|
|
|
@ -52,6 +52,12 @@ describe('html-parse', () => {
|
||||||
expect(formatted).toMatchSnapshot('html')
|
expect(formatted).toMatchSnapshot('html')
|
||||||
expect(serializedText).toMatchSnapshot('text')
|
expect(serializedText).toMatchSnapshot('text')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('html entities', async () => {
|
||||||
|
const { formatted, serializedText } = await render('<p>Hello <World />.</p>')
|
||||||
|
expect(formatted).toMatchSnapshot('html')
|
||||||
|
expect(serializedText).toMatchSnapshot('text')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
async function render(input: string, emojis?: Record<string, Emoji>) {
|
async function render(input: string, emojis?: Record<string, Emoji>) {
|
||||||
|
|
Loading…
Reference in New Issue