plugins.ts 3.1 KB

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