markdown.ts 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. import MarkdownItClass from 'markdown-it'
  2. import markdownItEmoji from 'markdown-it-emoji/light.js'
  3. import sanitizeHtml from 'sanitize-html'
  4. import { getDefaultSanitizeOptions, getTextOnlySanitizeOptions, TEXT_WITH_HTML_RULES } from '@peertube/peertube-core-utils'
  5. const defaultSanitizeOptions = getDefaultSanitizeOptions()
  6. const textOnlySanitizeOptions = getTextOnlySanitizeOptions()
  7. const markdownItForSafeHtml = new MarkdownItClass('default', { linkify: true, breaks: true, html: true })
  8. .enable(TEXT_WITH_HTML_RULES)
  9. .use(markdownItEmoji)
  10. const markdownItForPlainText = new MarkdownItClass('default', { linkify: false, breaks: true, html: false })
  11. .use(markdownItEmoji)
  12. .use(plainTextPlugin)
  13. const toSafeHtml = (text: string) => {
  14. if (!text) return ''
  15. // Restore line feed
  16. const textWithLineFeed = text.replace(/<br.?\/?>/g, '\r\n')
  17. // Convert possible markdown (emojis, emphasis and lists) to html
  18. const html = markdownItForSafeHtml.render(textWithLineFeed)
  19. // Convert to safe Html
  20. return sanitizeHtml(html, defaultSanitizeOptions)
  21. }
  22. const mdToOneLinePlainText = (text: string) => {
  23. if (!text) return ''
  24. markdownItForPlainText.render(text)
  25. // Convert to safe Html
  26. return sanitizeHtml(markdownItForPlainText.plainText, textOnlySanitizeOptions)
  27. }
  28. // ---------------------------------------------------------------------------
  29. export {
  30. toSafeHtml,
  31. mdToOneLinePlainText
  32. }
  33. // ---------------------------------------------------------------------------
  34. // Thanks: https://github.com/wavesheep/markdown-it-plain-text
  35. function plainTextPlugin (markdownIt: any) {
  36. function plainTextRule (state: any) {
  37. const text = scan(state.tokens)
  38. markdownIt.plainText = text
  39. }
  40. function scan (tokens: any[]) {
  41. let lastSeparator = ''
  42. let text = ''
  43. function buildSeparator (token: any) {
  44. if (token.type === 'list_item_close') {
  45. lastSeparator = ', '
  46. }
  47. if (token.tag === 'br' || token.type === 'paragraph_close') {
  48. lastSeparator = ' '
  49. }
  50. }
  51. for (const token of tokens) {
  52. buildSeparator(token)
  53. if (token.type !== 'inline') continue
  54. for (const child of token.children) {
  55. buildSeparator(child)
  56. if (!child.content) continue
  57. text += lastSeparator + child.content
  58. lastSeparator = ''
  59. }
  60. }
  61. return text
  62. }
  63. markdownIt.core.ruler.push('plainText', plainTextRule)
  64. }