naive-ui/build/loaders/convert-md-to-doc.js

215 lines
6.1 KiB
JavaScript
Raw Normal View History

2021-04-05 14:38:39 +08:00
const path = require('path')
const fse = require('fs-extra')
const marked = require('marked')
2019-09-25 12:19:57 +08:00
const camelCase = require('lodash/camelCase')
const createRenderer = require('./md-renderer')
2021-04-05 14:38:39 +08:00
const projectPath = require('./project-path')
2019-12-22 23:19:08 +08:00
const mdRenderer = createRenderer()
2021-04-05 14:38:39 +08:00
async function resolveDemoTitle (fileName, demoEntryPath) {
const demoStr = await fse.readFile(
path.resolve(projectPath, demoEntryPath, '..', fileName),
'utf-8'
)
return demoStr.match(/# ([^\n]+)/)[1]
}
2021-04-05 14:38:39 +08:00
async function resolveDemoInfos (literal, url, env) {
const ids = literal
.split('\n')
2021-04-05 14:38:39 +08:00
.map((line) => line.trim())
.filter((id) => id.length)
const infos = []
for (const id of ids) {
const debug = id.includes('debug') || id.includes('Debug')
if (env === 'production' && debug) {
2021-04-05 14:38:39 +08:00
continue
}
const fileName = `${id}.demo.md`
const variable = `${camelCase(id)}Demo`
infos.push({
id,
variable,
fileName,
title: await resolveDemoTitle(fileName, url),
tag: `<${variable} />`,
debug
})
2021-04-05 14:38:39 +08:00
}
return infos
}
2021-04-05 14:38:39 +08:00
function genDemosTemplate (demoInfos, colSpan) {
return `<component-demos :span="${colSpan}">${demoInfos
.map(({ tag }) => tag)
.join('\n')}</component-demos>`
}
function genAnchorTemplate (children, options = {}) {
return `
<n-anchor
:bound="16"
style="width: 144px; position: sticky; top: 32px;"
offset-target="#doc-layout"
:ignore-gap="${options.ignoreGap}"
>
${children}
</n-anchor>
`
2021-04-05 14:38:39 +08:00
}
function genDemosAnchorTemplate (demoInfos) {
const links = demoInfos.map(
({ id, title, debug }) => `<n-anchor-link
v-if="(displayMode === 'debug') || ${!debug}"
2021-04-05 14:38:39 +08:00
title="${title}"
href="#${id}"
/>`
2020-12-16 02:24:35 +08:00
)
2021-04-05 14:38:39 +08:00
return genAnchorTemplate(links.join('\n'))
2019-10-14 17:49:23 +08:00
}
2021-04-05 14:38:39 +08:00
function genPageAnchorTemplate (tokens) {
const titles = tokens
.filter((token) => token.type === 'heading' && token.depth === 2)
.map((token) => token.text)
const links = titles.map((title) => {
const href = title.replace(/ /g, '-')
return `<n-anchor-link title="${title}" href="#${href}"/>`
})
return genAnchorTemplate(links.join('\n'), { ignoreGap: true })
}
function genScript (demoInfos, components = [], url, forceShowAnchor) {
const showAnchor = !!(demoInfos.length || forceShowAnchor)
const importStmts = demoInfos
.map(({ variable, fileName }) => `import ${variable} from './${fileName}'`)
.concat(components.map(({ importStmt }) => importStmt))
2019-10-25 15:43:06 +08:00
.join('\n')
2021-04-05 14:38:39 +08:00
const componentStmts = demoInfos
.map(({ variable }) => variable)
.concat(components.map(({ ids }) => ids).flat())
2021-04-05 14:38:39 +08:00
.join(',\n')
const script = `<script>
2021-04-05 14:38:39 +08:00
${importStmts}
import { computed } from 'vue'
import { useMemo } from 'vooks'
import { useDisplayMode } from '/demo/store'
import { useIsMobile } from '/demo/utils/composables'
export default {
components: {
2021-04-05 14:38:39 +08:00
${componentStmts}
2019-10-14 17:49:23 +08:00
},
2021-04-05 14:38:39 +08:00
setup () {
const isMobileRef = useIsMobile()
2021-04-06 01:31:09 +08:00
const showAnchorRef = useMemo(() => {
if (isMobileRef.value) return false
2021-04-05 14:38:39 +08:00
return ${showAnchor}
})
const useSmallPaddingRef = isMobileRef
2019-10-14 17:49:23 +08:00
return {
2021-04-05 14:38:39 +08:00
showAnchor: showAnchorRef,
displayMode: useDisplayMode(),
2021-04-05 14:38:39 +08:00
wrapperStyle: computed(() => {
2021-04-06 01:31:09 +08:00
return !useSmallPaddingRef.value
2021-05-04 02:27:16 +08:00
? 'display: flex; flex-wrap: nowrap; padding: 32px 24px 56px 56px;'
2021-05-26 14:39:33 +08:00
: 'padding: 16px 16px 24px 16px;'
2021-04-05 14:38:39 +08:00
}),
contentStyle: computed(() => {
2021-04-06 01:31:09 +08:00
return showAnchorRef.value
2021-04-05 14:38:39 +08:00
? 'width: calc(100% - 180px); margin-right: 36px;'
2021-04-07 01:44:02 +08:00
: 'width: 100%; padding-right: 12px;';
2021-04-05 14:38:39 +08:00
}),
2020-03-19 13:26:06 +08:00
url: ${JSON.stringify(url)}
2019-10-14 17:49:23 +08:00
}
}
}
</script>`
return script
}
2021-04-05 14:38:39 +08:00
async function convertMd2ComponentDocumentation (
text,
url,
env = 'development'
) {
const forceShowAnchor = !!~text.search('<!--anchor:on-->')
const colSpan = ~text.search('<!--single-column-->') ? 1 : 2
const tokens = marked.lexer(text)
2021-04-05 14:38:39 +08:00
// resolve external components
2020-12-16 02:24:35 +08:00
const componentsIndex = tokens.findIndex(
(token) => token.type === 'code' && token.lang === 'component'
)
2019-10-25 15:43:06 +08:00
let components = []
if (~componentsIndex) {
components = tokens[componentsIndex].text
2020-12-16 02:24:35 +08:00
components = components
.split('\n')
.map((component) => {
const [ids, importStmt] = component.split(':')
if (!ids.trim()) throw new Error('No component id')
if (!importStmt.trim()) throw new Error('No component source url')
return {
ids: ids.split(',').map((id) => id.trim()),
importStmt: importStmt.trim()
}
})
.filter(({ ids, importStmt }) => ids && importStmt)
2019-10-25 15:43:06 +08:00
tokens.splice(componentsIndex, 1)
}
2021-04-05 14:38:39 +08:00
// add edit on github button on title
const titleIndex = tokens.findIndex(
(token) => token.type === 'heading' && token.depth === 1
)
if (titleIndex > -1) {
const titleText = JSON.stringify(tokens[titleIndex].text)
const btnTemplate = `<edit-on-github-header relative-url="${url}" text=${titleText}></edit-on-github-header>`
tokens.splice(titleIndex, 1, {
type: 'html',
pre: false,
text: btnTemplate
})
}
// resolve demos, debug demos are removed from production build
2020-12-16 02:24:35 +08:00
const demosIndex = tokens.findIndex(
(token) => token.type === 'code' && token.lang === 'demo'
)
2021-04-05 14:38:39 +08:00
let demoInfos = []
if (~demosIndex) {
2021-04-05 14:38:39 +08:00
demoInfos = await resolveDemoInfos(tokens[demosIndex].text, url, env)
tokens.splice(demosIndex, 1, {
type: 'html',
pre: false,
2021-04-05 14:38:39 +08:00
text: genDemosTemplate(demoInfos, colSpan)
})
}
2021-04-05 14:38:39 +08:00
const docMainTemplate = marked.parser(tokens, {
gfm: true,
renderer: mdRenderer
})
// generate page
const docTemplate = `
2019-10-14 17:49:23 +08:00
<template>
2021-04-05 14:38:39 +08:00
<div
class="doc"
2021-04-05 14:38:39 +08:00
:style="wrapperStyle"
>
<div :style="contentStyle">
${docMainTemplate}
</div>
<div style="width: 144px;" v-if="showAnchor">
${
demoInfos.length
? genDemosAnchorTemplate(demoInfos)
: genPageAnchorTemplate(tokens)
}
2019-10-14 17:49:23 +08:00
</div>
2021-04-05 14:38:39 +08:00
</div>
2019-10-14 17:49:23 +08:00
</template>`
2021-04-05 14:38:39 +08:00
const docScript = await genScript(demoInfos, components, url, forceShowAnchor)
return `${docTemplate}\n\n${docScript}`
}
module.exports = convertMd2ComponentDocumentation