markdown-transform.ts 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. import fs from 'fs'
  2. import path from 'path'
  3. import glob from 'fast-glob'
  4. import { docRoot, docsDirName, projRoot } from '@element-plus/build-utils'
  5. import { REPO_BRANCH, REPO_PATH } from '@element-plus/build-constants'
  6. import { getLang, languages } from '../utils/lang'
  7. import footerLocale from '../i18n/component/footer.json'
  8. import type { Plugin } from 'vite'
  9. type Append = Record<'headers' | 'footers' | 'scriptSetups', string[]>
  10. export function MarkdownTransform(): Plugin {
  11. return {
  12. name: 'element-plus-md-transform',
  13. enforce: 'pre',
  14. async transform(code, id) {
  15. if (!id.endsWith('.md')) return
  16. const componentId = path.basename(id, '.md')
  17. const append: Append = {
  18. headers: [],
  19. footers: [],
  20. scriptSetups: [
  21. `const demos = import.meta.globEager('../../examples/${componentId}/*.vue')`,
  22. ],
  23. }
  24. code = transformVpScriptSetup(code, append)
  25. const pattern = `{${[...languages, languages[0]].join(',')}}/component`
  26. const compPaths = await glob(pattern, {
  27. cwd: docRoot,
  28. absolute: true,
  29. onlyDirectories: true,
  30. })
  31. if (compPaths.some((compPath) => id.startsWith(compPath))) {
  32. code = transformComponentMarkdown(id, componentId, code, append)
  33. }
  34. return combineMarkdown(
  35. code,
  36. [combineScriptSetup(append.scriptSetups), ...append.headers],
  37. append.footers
  38. )
  39. },
  40. }
  41. }
  42. const combineScriptSetup = (codes: string[]) =>
  43. `\n<script setup>
  44. ${codes.join('\n')}
  45. </script>
  46. `
  47. const combineMarkdown = (
  48. code: string,
  49. headers: string[],
  50. footers: string[]
  51. ) => {
  52. const frontmatterEnds = code.indexOf('---\n\n') + 4
  53. const firstSubheader = code.search(/\n## \w/)
  54. const sliceIndex = firstSubheader < 0 ? frontmatterEnds : firstSubheader
  55. if (headers.length > 0)
  56. code =
  57. code.slice(0, sliceIndex) + headers.join('\n') + code.slice(sliceIndex)
  58. code += footers.join('\n')
  59. return `${code}\n`
  60. }
  61. const vpScriptSetupRE = /<vp-script\s(.*\s)?setup(\s.*)?>([\s\S]*)<\/vp-script>/
  62. const transformVpScriptSetup = (code: string, append: Append) => {
  63. const matches = code.match(vpScriptSetupRE)
  64. if (matches) code = code.replace(matches[0], '')
  65. const scriptSetup = matches?.[3] ?? ''
  66. if (scriptSetup) append.scriptSetups.push(scriptSetup)
  67. return code
  68. }
  69. const GITHUB_BLOB_URL = `https://github.com/${REPO_PATH}/blob/${REPO_BRANCH}`
  70. const GITHUB_TREE_URL = `https://github.com/${REPO_PATH}/tree/${REPO_BRANCH}`
  71. const transformComponentMarkdown = (
  72. id: string,
  73. componentId: string,
  74. code: string,
  75. append: Append
  76. ) => {
  77. const lang = getLang(id)
  78. const docUrl = `${GITHUB_BLOB_URL}/${docsDirName}/en-US/component/${componentId}.md`
  79. const componentUrl = `${GITHUB_TREE_URL}/packages/components/${componentId}`
  80. const componentPath = path.resolve(
  81. projRoot,
  82. `packages/components/${componentId}`
  83. )
  84. const isComponent = fs.existsSync(componentPath)
  85. const links = [[footerLocale[lang].docs, docUrl]]
  86. if (isComponent) links.unshift([footerLocale[lang].component, componentUrl])
  87. const linksText = links
  88. .filter((i) => i)
  89. .map(([text, link]) => `[${text}](${link})`)
  90. .join(' • ')
  91. const sourceSection = `
  92. ## ${footerLocale[lang].source}
  93. ${linksText}
  94. `
  95. const contributorsSection = `
  96. ## ${footerLocale[lang].contributors}
  97. <Contributors id="${componentId}" />
  98. `
  99. append.footers.push(sourceSection, isComponent ? contributorsSection : '')
  100. return code
  101. }