diff options
Diffstat (limited to 'ui/lib')
| -rw-r--r-- | ui/lib/markdown.js | 22 | ||||
| -rw-r--r-- | ui/lib/markdown.test.js | 55 |
2 files changed, 76 insertions, 1 deletions
diff --git a/ui/lib/markdown.js b/ui/lib/markdown.js index 2e73309..c4f2803 100644 --- a/ui/lib/markdown.js +++ b/ui/lib/markdown.js @@ -1,6 +1,26 @@ import { marked } from 'marked'; import DOMPurify from 'dompurify'; +const extension = { + useNewRenderer: true, + renderer: { + link({ title, href, tokens }) { + const titleAttr = title ? ` title="${title}"` : ``; + const text = this.parser.parseInline(tokens); + return `<a + target="_blank" + rel="noreferrer" + ${titleAttr} + href="${href}">${text}</a>`; + } + } +}; + +marked.use(extension); + export function render(body) { - return DOMPurify.sanitize(marked.parse(body, { breaks: true })); + const rendered = marked.parse(body, { breaks: true }); + return DOMPurify.sanitize(rendered, { + ADD_ATTR: ['target'] + }); } diff --git a/ui/lib/markdown.test.js b/ui/lib/markdown.test.js new file mode 100644 index 0000000..126eacd --- /dev/null +++ b/ui/lib/markdown.test.js @@ -0,0 +1,55 @@ +import * as md from './markdown.js'; +import { expect, describe, it } from 'vitest'; + +describe('render', async () => { + it('renders inline links', async () => { + const markdown = `[a link](https://example.com?foo=bar)`; + const html = md.render(markdown); + expect(html).toStrictEqual( + `<p><a href="https://example.com?foo=bar" rel="noreferrer" target="_blank">a link</a></p> +` + ); + }); + + it('renders inline links with titles', async () => { + const markdown = `[a link](https://example.com?foo=bar "what title")`; + const html = md.render(markdown); + expect(html).toStrictEqual( + `<p><a href="https://example.com?foo=bar" title="what title" rel="noreferrer" target="_blank">a link</a></p> +` + ); + }); + + it('renders footnote links', async () => { + const markdown = ` +[a link] + +[a link]: https://example.com?foo=bar`; + const html = md.render(markdown); + expect(html).toStrictEqual( + `<p><a href="https://example.com?foo=bar" rel="noreferrer" target="_blank">a link</a></p> +` + ); + }); + + it('renders footnote links with titles', async () => { + const markdown = ` +[a link] + +[a link]: https://example.com?foo=bar "what title"`; + const html = md.render(markdown); + expect(html).toStrictEqual( + `<p><a href="https://example.com?foo=bar" title="what title" rel="noreferrer" target="_blank">a link</a></p> +` + ); + }); + + it('renders links with embedded markup', async () => { + const markdown = `[a _link_](https://example.com?foo=bar)`; + const html = md.render(markdown); + expect(html).toStrictEqual( + `<p><a href="https://example.com?foo=bar" rel="noreferrer" target="_blank">a <em>link</em></a></p> +` + ); + }); +}); |
