diff options
author | Chocobozzz <me@florianbigard.com> | 2022-02-04 10:31:54 +0100 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2022-02-04 10:38:32 +0100 |
commit | c68e2b2d223c57836e04e18105255cf0e10ae75b (patch) | |
tree | a40348363efc90464ff44306435d45079b0b7fca /server/helpers/markdown.ts | |
parent | 457c83486ed2037a8cf0e55b06b1ae9370ed4d93 (diff) | |
download | PeerTube-c68e2b2d223c57836e04e18105255cf0e10ae75b.tar.gz PeerTube-c68e2b2d223c57836e04e18105255cf0e10ae75b.tar.zst PeerTube-c68e2b2d223c57836e04e18105255cf0e10ae75b.zip |
Fix plaintext markdown converter
Diffstat (limited to 'server/helpers/markdown.ts')
-rw-r--r-- | server/helpers/markdown.ts | 68 |
1 files changed, 52 insertions, 16 deletions
diff --git a/server/helpers/markdown.ts b/server/helpers/markdown.ts index 0b8c2fabc..25685ec6d 100644 --- a/server/helpers/markdown.ts +++ b/server/helpers/markdown.ts | |||
@@ -1,14 +1,14 @@ | |||
1 | import { getSanitizeOptions, TEXT_WITH_HTML_RULES } from '@shared/core-utils' | 1 | import { getDefaultSanitizeOptions, getTextOnlySanitizeOptions, TEXT_WITH_HTML_RULES } from '@shared/core-utils' |
2 | 2 | ||
3 | const sanitizeOptions = getSanitizeOptions() | 3 | const defaultSanitizeOptions = getDefaultSanitizeOptions() |
4 | const textOnlySanitizeOptions = getTextOnlySanitizeOptions() | ||
4 | 5 | ||
5 | const sanitizeHtml = require('sanitize-html') | 6 | const sanitizeHtml = require('sanitize-html') |
6 | const markdownItEmoji = require('markdown-it-emoji/light') | 7 | const markdownItEmoji = require('markdown-it-emoji/light') |
7 | const MarkdownItClass = require('markdown-it') | 8 | const MarkdownItClass = require('markdown-it') |
8 | const markdownIt = new MarkdownItClass('default', { linkify: true, breaks: true, html: true }) | ||
9 | 9 | ||
10 | markdownIt.enable(TEXT_WITH_HTML_RULES) | 10 | const markdownItWithHTML = new MarkdownItClass('default', { linkify: true, breaks: true, html: true }) |
11 | markdownIt.use(markdownItEmoji) | 11 | const markdownItWithoutHTML = new MarkdownItClass('default', { linkify: true, breaks: true, html: false }) |
12 | 12 | ||
13 | const toSafeHtml = (text: string) => { | 13 | const toSafeHtml = (text: string) => { |
14 | if (!text) return '' | 14 | if (!text) return '' |
@@ -17,29 +17,65 @@ const toSafeHtml = (text: string) => { | |||
17 | const textWithLineFeed = text.replace(/<br.?\/?>/g, '\r\n') | 17 | const textWithLineFeed = text.replace(/<br.?\/?>/g, '\r\n') |
18 | 18 | ||
19 | // Convert possible markdown (emojis, emphasis and lists) to html | 19 | // Convert possible markdown (emojis, emphasis and lists) to html |
20 | const html = markdownIt.render(textWithLineFeed) | 20 | const html = markdownItWithHTML.enable(TEXT_WITH_HTML_RULES) |
21 | .use(markdownItEmoji) | ||
22 | .render(textWithLineFeed) | ||
21 | 23 | ||
22 | // Convert to safe Html | 24 | // Convert to safe Html |
23 | return sanitizeHtml(html, sanitizeOptions) | 25 | return sanitizeHtml(html, defaultSanitizeOptions) |
24 | } | 26 | } |
25 | 27 | ||
26 | const mdToPlainText = (text: string) => { | 28 | const mdToOneLinePlainText = (text: string) => { |
27 | if (!text) return '' | 29 | if (!text) return '' |
28 | 30 | ||
29 | // Convert possible markdown (emojis, emphasis and lists) to html | 31 | markdownItWithoutHTML.use(markdownItEmoji) |
30 | const html = markdownIt.render(text) | 32 | .use(plainTextPlugin) |
33 | .render(text) | ||
31 | 34 | ||
32 | // Convert to safe Html | 35 | // Convert to safe Html |
33 | const safeHtml = sanitizeHtml(html, sanitizeOptions) | 36 | return sanitizeHtml(markdownItWithoutHTML.plainText, textOnlySanitizeOptions) |
34 | |||
35 | return safeHtml.replace(/<[^>]+>/g, '') | ||
36 | .replace(/\n$/, '') | ||
37 | .replace(/\n/g, ', ') | ||
38 | } | 37 | } |
39 | 38 | ||
40 | // --------------------------------------------------------------------------- | 39 | // --------------------------------------------------------------------------- |
41 | 40 | ||
42 | export { | 41 | export { |
43 | toSafeHtml, | 42 | toSafeHtml, |
44 | mdToPlainText | 43 | mdToOneLinePlainText |
44 | } | ||
45 | |||
46 | // --------------------------------------------------------------------------- | ||
47 | |||
48 | // Thanks: https://github.com/wavesheep/markdown-it-plain-text | ||
49 | function plainTextPlugin (markdownIt: any) { | ||
50 | let lastSeparator = '' | ||
51 | |||
52 | function plainTextRule (state: any) { | ||
53 | const text = scan(state.tokens) | ||
54 | |||
55 | markdownIt.plainText = text.replace(/\s+/g, ' ') | ||
56 | } | ||
57 | |||
58 | function scan (tokens: any[]) { | ||
59 | let text = '' | ||
60 | |||
61 | for (const token of tokens) { | ||
62 | if (token.children !== null) { | ||
63 | text += scan(token.children) | ||
64 | continue | ||
65 | } | ||
66 | |||
67 | if (token.type === 'list_item_close') { | ||
68 | lastSeparator = ', ' | ||
69 | } else if (/[a-zA-Z]+_close/.test(token.type)) { | ||
70 | lastSeparator = ' ' | ||
71 | } else if (token.content) { | ||
72 | text += lastSeparator | ||
73 | text += token.content | ||
74 | } | ||
75 | } | ||
76 | |||
77 | return text | ||
78 | } | ||
79 | |||
80 | markdownIt.core.ruler.push('plainText', plainTextRule) | ||
45 | } | 81 | } |