plugins.ts 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. import path from 'path'
  2. import fs from 'fs'
  3. import MarkdownIt from 'markdown-it'
  4. import mdContainer from 'markdown-it-container'
  5. import { docRoot } from '@kankan-components/build-utils'
  6. import externalLinkIcon from '../plugins/external-link-icon'
  7. import tableWrapper from '../plugins/table-wrapper'
  8. import tooltip from '../plugins/tooltip'
  9. import { ApiTableContainer } from '../plugins/api-table'
  10. import { highlight } from '../utils/highlight'
  11. import type Token from 'markdown-it/lib/token'
  12. import type Renderer from 'markdown-it/lib/renderer'
  13. const localMd = MarkdownIt()
  14. interface ContainerOpts {
  15. marker?: string | undefined
  16. validate?(params: string): boolean
  17. render?(
  18. tokens: Token[],
  19. index: number,
  20. options: any,
  21. env: any,
  22. self: Renderer
  23. ): string
  24. }
  25. export const mdPlugin = (md: MarkdownIt) => {
  26. md.use(externalLinkIcon)
  27. md.use(tableWrapper)
  28. md.use(tooltip)
  29. // demo apply
  30. md.use(mdContainer, 'demo', {
  31. validate(params) {
  32. return !!params.trim().match(/^demo\s*(.*)$/)
  33. },
  34. render(tokens, idx) {
  35. const m = tokens[idx].info.trim().match(/^demo\s*(.*)$/)
  36. if (tokens[idx].nesting === 1 /* means the tag is opening */) {
  37. const description = m && m.length > 1 ? m[1] : ''
  38. const sourceFileToken = tokens[idx + 2]
  39. let source = ''
  40. const sourceFile = sourceFileToken.children?.[0].content ?? ''
  41. if (sourceFileToken.type === 'inline') {
  42. source = fs.readFileSync(
  43. path.resolve(docRoot, 'examples', `${sourceFile}.vue`),
  44. 'utf-8'
  45. )
  46. }
  47. if (!source) throw new Error(`Incorrect source file: ${sourceFile}`)
  48. return `<Demo :demos="demos" source="${encodeURIComponent(
  49. highlight(source, 'vue')
  50. )}" path="${sourceFile}" raw-source="${encodeURIComponent(
  51. source
  52. )}" description="${encodeURIComponent(localMd.render(description))}">`
  53. } else {
  54. return '</Demo>'
  55. }
  56. },
  57. } as ContainerOpts)
  58. // repl apply
  59. md.use(mdContainer, 'repl', {
  60. validate(params) {
  61. return !!params.trim().match(/^repl\s*(.*)$/)
  62. },
  63. render(tokens, idx) {
  64. const m = tokens[idx].info.trim().match(/^repl\s*(.*)$/)
  65. if (tokens[idx].nesting === 1 /* means the tag is opening */) {
  66. const description = m && m.length > 1 ? m[1] : ''
  67. const sourceFileToken = tokens[idx + 2]
  68. let source = ''
  69. const sourceFile = sourceFileToken.children?.[0].content ?? ''
  70. if (sourceFileToken.type === 'inline') {
  71. source = fs.readFileSync(
  72. path.resolve(docRoot, 'examples', `${sourceFile}.vue`),
  73. 'utf-8'
  74. )
  75. }
  76. if (!source) throw new Error(`Incorrect source file: ${sourceFile}`)
  77. return `<Demo isRepl :demos="demos" source="${encodeURIComponent(
  78. highlight(source, 'vue')
  79. )}" path="${sourceFile}" raw-source="${encodeURIComponent(
  80. source
  81. )}" description="${encodeURIComponent(localMd.render(description))}">`
  82. } else {
  83. return '</Demo>'
  84. }
  85. },
  86. } as ContainerOpts)
  87. md.use(ApiTableContainer)
  88. }