From 66393cd83822650f1ffb5d781112bd26b9492f5d Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Wed, 30 Nov 2022 14:50:47 +0800 Subject: [PATCH] feat(content): serialize custom emoji --- composables/content.ts | 13 +++-- tests/__snapshots__/content-rich.test.ts.snap | 1 + tests/__snapshots__/html-parse.test.ts.snap | 50 ++++++++++++++++--- tests/html-parse.test.ts | 42 ++++++++++------ 4 files changed, 80 insertions(+), 26 deletions(-) diff --git a/composables/content.ts b/composables/content.ts index 3aa9d502..c1a32418 100644 --- a/composables/content.ts +++ b/composables/content.ts @@ -54,7 +54,7 @@ export function parseMastodonHTML(html: string, customEmojis: Record { const emoji = customEmojis[name] if (emoji) - return `:${name}:` + return `:${name}:` return `:${name}:` }) // handle code blocks @@ -75,10 +75,10 @@ export function parseMastodonHTML(html: string, customEmojis: Record$1') .replace(/\*\*(.*?)\*\*/g, '$1') .replace(/\*(.*?)\*/g, '$1') .replace(/~~(.*?)~~/g, '$1') - .replace(/__(.*?)__/g, '$1') .replace(/`([^`]+?)`/g, '$1') if (converted !== text) @@ -149,7 +149,7 @@ export function htmlToText(html: string) { return tree.childNodes.map(n => treeToText(n)).join('').trim() } -function treeToText(input: Node): string { +export function treeToText(input: Node): string { let pre = '' let body = '' let post = '' @@ -185,9 +185,16 @@ function treeToText(input: Node): string { pre = '*' post = '*' } + else if (input.nodeName === 'del') { + pre = '~~' + post = '~~' + } if ('childNodes' in input) body = input.childNodes.map(n => treeToText(n)).join('') + if (input.nodeName === 'img' && input.attrs.some(attr => attr.name === 'class' && attr.value.includes('custom-emoji'))) + return `:${input.attrs.find(attr => attr.name === 'data-emoji-id')?.value}:` + return pre + body + post } diff --git a/tests/__snapshots__/content-rich.test.ts.snap b/tests/__snapshots__/content-rich.test.ts.snap index 15932808..fafa68b8 100644 --- a/tests/__snapshots__/content-rich.test.ts.snap +++ b/tests/__snapshots__/content-rich.test.ts.snap @@ -31,6 +31,7 @@ exports[`content-rich > custom emoji 1`] = ` src=\\"https://media.mas.to/masto-public/cache/custom_emojis/images/000/288/667/original/c96ba3cb0e0e1eac.png\\" alt=\\":nuxt:\\" class=\\"custom-emoji\\" + data-emoji-id=\\"nuxt\\" /> " `; diff --git a/tests/__snapshots__/html-parse.test.ts.snap b/tests/__snapshots__/html-parse.test.ts.snap index 9955b476..da4a8a19 100644 --- a/tests/__snapshots__/html-parse.test.ts.snap +++ b/tests/__snapshots__/html-parse.test.ts.snap @@ -1,6 +1,6 @@ // Vitest Snapshot v1 -exports[`html-parse > code frame 1`] = ` +exports[`html-parse > code frame > html 1`] = ` "

Testing code block

import { useMouse, usePreferredDark } from '@vueuse/core'
@@ -13,7 +13,20 @@ const isDark = usePreferredDark()
" `; -exports[`html-parse > code frame 2 1`] = ` +exports[`html-parse > code frame > text 1`] = ` +"Testing code block + +\`\`\`ts +import { useMouse, usePreferredDark } from '@vueuse/core' + +// tracks mouse position +const { x, y } = useMouse() +// is the user prefers dark theme +const isDark = usePreferredDark() +\`\`\`" +`; + +exports[`html-parse > code frame 2 > html 1`] = ` "

code frame 2 1`] = ` " `; -exports[`html-parse > custom emoji 1`] = ` +exports[`html-parse > code frame 2 > text 1`] = ` +"@antfu Testing + +\`\`\`ts +const a = hello +\`\`\`" +`; + +exports[`html-parse > custom emoji > html 1`] = ` "Daniel Roe \\":nuxt:\\" " `; -exports[`html-parse > empty 1`] = `""`; +exports[`html-parse > custom emoji > text 1`] = `"Daniel Roe :nuxt:"`; -exports[`html-parse > inline markdown 1`] = ` -"

text code bold italic

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

text code bold italic del

code block

" `; -exports[`html-parse > link + mention 1`] = ` +exports[`html-parse > inline markdown > text 1`] = ` +"text \`code\` **bold** *italic* ~~del~~ + +\`\`\`js +code block +\`\`\`" +`; + +exports[`html-parse > link + mention > html 1`] = ` "

Happy 🤗 we’re now using link + mention 1`] = `

" `; + +exports[`html-parse > link + mention > text 1`] = `"Happy 🤗 we’re now using @vitest (migrated from chai+mocha) https://github.com/ayoayco/astro-reactive-library/pull/203"`; diff --git a/tests/html-parse.test.ts b/tests/html-parse.test.ts index 306fbc86..e4703919 100644 --- a/tests/html-parse.test.ts +++ b/tests/html-parse.test.ts @@ -2,22 +2,24 @@ import type { Emoji } from 'masto' import { describe, expect, it } from 'vitest' import { format } from 'prettier' import { serialize } from 'parse5' -import { parseMastodonHTML } from '~/composables/content' +import { parseMastodonHTML, treeToText } from '~/composables/content' describe('html-parse', () => { it('empty', async () => { - const { formatted } = await render('') - expect(formatted).toMatchSnapshot() + const { formatted, serializedText } = await render('') + expect(formatted).toMatchSnapshot('html') + expect(serializedText).toMatchSnapshot('text') }) it('link + mention', async () => { // https://fosstodon.org/@ayo/109383002937620723 - const { formatted } = await render('

Happy 🤗 we’re now using @vitest (migrated from chai+mocha) github.com/ayoayco/astro-react

') - expect(formatted).toMatchSnapshot() + const { formatted, serializedText } = await render('

Happy 🤗 we’re now using @vitest (migrated from chai+mocha) github.com/ayoayco/astro-react

') + expect(formatted).toMatchSnapshot('html') + expect(serializedText).toMatchSnapshot('text') }) it('custom emoji', async () => { - const { formatted } = await render('Daniel Roe :nuxt:', { + const { formatted, serializedText } = await render('Daniel Roe :nuxt:', { nuxt: { shortcode: 'nuxt', url: 'https://media.mas.to/masto-public/cache/custom_emojis/images/000/288/667/original/c96ba3cb0e0e1eac.png', @@ -25,30 +27,35 @@ describe('html-parse', () => { visibleInPicker: true, }, }) - expect(formatted).toMatchSnapshot() + expect(formatted).toMatchSnapshot('html') + expect(serializedText).toMatchSnapshot('text') }) it('code frame', async () => { // https://mas.to/@antfu/109396489827394721 - const { formatted } = await render('

Testing code block

```ts
import { useMouse, usePreferredDark } from '@vueuse/core'

// tracks mouse position
const { x, y } = useMouse()

// is the user prefers dark theme
const isDark = usePreferredDark()
```

') - expect(formatted).toMatchSnapshot() + const { formatted, serializedText } = await render('

Testing code block

```ts
import { useMouse, usePreferredDark } from '@vueuse/core'

// tracks mouse position
const { x, y } = useMouse()

// is the user prefers dark theme
const isDark = usePreferredDark()
```

') + expect(formatted).toMatchSnapshot('html') + expect(serializedText).toMatchSnapshot('text') }) it('code frame 2', async () => { - const { formatted } = await render('

@antfu Testing
```ts
const a = hello
```

') - expect(formatted).toMatchSnapshot() + const { formatted, serializedText } = await render('

@antfu Testing
```ts
const a = hello
```

') + expect(formatted).toMatchSnapshot('html') + expect(serializedText).toMatchSnapshot('text') }) it('inline markdown', async () => { - const { formatted } = await render('

text `code` **bold** *italic*

```js
code block
```

') - expect(formatted).toMatchSnapshot() + const { formatted, serializedText } = await render('

text `code` **bold** *italic* ~~del~~

```js
code block
```

') + expect(formatted).toMatchSnapshot('html') + expect(serializedText).toMatchSnapshot('text') }) }) -async function render(content: string, emojis?: Record) { - const node = parseMastodonHTML(content, emojis) - const html = serialize(node) +async function render(input: string, emojis?: Record) { + const tree = parseMastodonHTML(input, emojis) + const html = serialize(tree) let formatted = '' + const serializedText = tree.childNodes.map(n => treeToText(n)).join('').trim() try { formatted = format(html, { @@ -60,6 +67,9 @@ async function render(content: string, emojis?: Record) { } return { + serializedText, + input, + tree, html, formatted, }