96 lines
2.7 KiB
JavaScript
96 lines
2.7 KiB
JavaScript
#!/usr/bin/env node
|
||
/**
|
||
* 从组件源码生成 API 数据。
|
||
*
|
||
* 用 vue-docgen-api 解析 packages/ui/src 下的 .vue 文件,
|
||
* 提取 props / events / slots 及其 JSDoc 注释,输出到 docs/data/api/<组件名>.json,
|
||
* 由文档站的 <ApiTable> 组件读取渲染。
|
||
*
|
||
* 这样 API 表格永远与源码一致,禁止手写。
|
||
* 源码改动后执行:pnpm gen:api
|
||
*/
|
||
import { mkdir, readdir, writeFile } from 'node:fs/promises'
|
||
import { basename, dirname, join } from 'node:path'
|
||
import { fileURLToPath } from 'node:url'
|
||
import { parse } from 'vue-docgen-api'
|
||
|
||
const root = fileURLToPath(new URL('..', import.meta.url))
|
||
const uiSrc = join(root, 'packages/ui/src')
|
||
const outDir = join(root, 'docs/data/api')
|
||
|
||
/** 递归收集所有 .vue 组件文件 */
|
||
async function findComponents(dir) {
|
||
const found = []
|
||
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
||
const full = join(dir, entry.name)
|
||
if (entry.isDirectory()) {
|
||
found.push(...(await findComponents(full)))
|
||
} else if (entry.name.endsWith('.vue')) {
|
||
found.push(full)
|
||
}
|
||
}
|
||
return found
|
||
}
|
||
|
||
/** packages/ui/src/button/Button.vue -> button */
|
||
function toKey(file) {
|
||
return basename(dirname(file))
|
||
}
|
||
|
||
/** docgen 给出的值可能带引号,统一剥掉 */
|
||
function clean(text = '') {
|
||
return String(text).replace(/^['"]|['"]$/g, '')
|
||
}
|
||
|
||
/** 把说明文字压成单行,避免表格里出现换行 */
|
||
function oneLine(text = '') {
|
||
return String(text).replace(/\s+/g, ' ').trim()
|
||
}
|
||
|
||
function mapMember(item) {
|
||
return {
|
||
name: item.name,
|
||
description: oneLine(item.description),
|
||
type: item.type?.name ?? '',
|
||
defaultValue: item.defaultValue ? clean(item.defaultValue.value) : '',
|
||
}
|
||
}
|
||
|
||
async function main() {
|
||
const files = await findComponents(uiSrc)
|
||
const results = []
|
||
|
||
for (const file of files) {
|
||
const info = await parse(file)
|
||
const key = toKey(file)
|
||
|
||
results.push([
|
||
key,
|
||
{
|
||
displayName: info.displayName || key,
|
||
description: oneLine(info.description),
|
||
props: (info.props ?? []).map(mapMember),
|
||
events: (info.events ?? []).map(mapMember),
|
||
slots: (info.slots ?? []).map((slot) => ({
|
||
name: slot.name,
|
||
description: oneLine(slot.description),
|
||
})),
|
||
},
|
||
])
|
||
}
|
||
|
||
await mkdir(outDir, { recursive: true })
|
||
|
||
for (const [key, doc] of results) {
|
||
await writeFile(join(outDir, `${key}.json`), `${JSON.stringify(doc, null, 2)}\n`, 'utf-8')
|
||
console.log(
|
||
`✓ ${key.padEnd(12)} props=${doc.props.length} events=${doc.events.length} slots=${doc.slots.length}`,
|
||
)
|
||
}
|
||
}
|
||
|
||
main().catch((error) => {
|
||
console.error('[gen-api] 失败:', error)
|
||
process.exit(1)
|
||
})
|