useMarkdown.ts 1.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
  1. import type { Ref } from 'vue'
  2. import { marked } from 'marked'
  3. import { computed } from 'vue'
  4. import dompurify from 'dompurify'
  5. export const useMarkdown = (text: Ref<string|undefined|null>, minHeadingLevel: Ref<number|undefined>) => {
  6. const minHeading = computed(() => Math.min(Math.max(minHeadingLevel.value ?? 1, 1), 6))
  7. const renderer = new marked.Renderer()
  8. renderer.link = function(href, title, text) {
  9. let out = `<a href="${href}" rel="noreferrer noopener" target="_blank"`
  10. if (title) {
  11. out += ' title="' + title + '"'
  12. }
  13. out += '>' + text + '</a>'
  14. return out
  15. }
  16. renderer.image = function(href, title, text) {
  17. if (text) {
  18. return text
  19. }
  20. return title ?? ''
  21. }
  22. renderer.heading = (text: string, level: number) => {
  23. const headingLevel = Math.max(minHeading.value, level)
  24. return `<h${headingLevel}>${text}</h${headingLevel}>`
  25. }
  26. const html = computed(() => dompurify.sanitize(
  27. marked((text.value ?? '').trim(), {
  28. renderer,
  29. gfm: false,
  30. breaks: false,
  31. pedantic: false,
  32. }),
  33. {
  34. SAFE_FOR_JQUERY: true,
  35. ALLOWED_TAGS: [
  36. 'h1',
  37. 'h2',
  38. 'h3',
  39. 'h4',
  40. 'h5',
  41. 'h6',
  42. 'strong',
  43. 'p',
  44. 'a',
  45. 'ul',
  46. 'ol',
  47. 'li',
  48. 'em',
  49. 'del',
  50. 'blockquote',
  51. ],
  52. },
  53. ))
  54. return { html }
  55. }