如何搭建自动生成markdown文档的工具函数网站
当涉及软件开发和团队协作时,文档是无可替代的。代码注释作为一种重要的文档形式,可以帮助开发人员理解和维护代码。然而,手动编写和更新文档是一项繁琐的任务,容易被忽视或忽略。
下面我将向你介绍,如何通过 JSDoc + vitepress
搭建可以通过注释自动生成 markdown,并通过 vitepress 托管文档。并使用 vitest 测试,提高工具函数的可维护性。
效果展示
注释代码
自动生成的markdown文件
自动生成的 vitepress 页面
如上图展示,该项目 demo 在本文最下方。有兴趣的请点个 ⭐️
1. 代码注释生成 Markdown 文件
1.1. 工具选择
1.1.1. JSDoc
JSDoc 是一个流行的代码文档生成工具,它基于特定的注释格式。通过使用 JSDoc,我们可以为函数、类和模块添加注释,并根据这些注释自动生成文档。JSDoc 提供了丰富的注释标签,用于描述参数、返回值、示例代码等信息。
它可以将如下注释转换成 Markdown 类型,但注意需要遵循 JSDoc 注释规范
优点:
- 可以根据 hbs 模板定制生成的 Markdown 文件内容
- 可以将对应的 js/ts 文件生成指定的 markdown
缺点:
- JSDoc 是根据注释生成的,没有注释的函数无法生成 Markdown 文档
1.1.2. TypeDoc
TypeDoc 是专门为 TypeScript 代码生成文档而设计的工具。与 JSDoc 类似,TypeDoc 可以从代码中提取类型信息,并生成相应的文档。与 JSDoc 不同的是,TypeDoc 不需要显式的注释来生成文档,它可以通过类型推断自动生成注释。
优点:
- 没有注释也可以生成文档,可以根据函数类型自动提取出类型信息
缺点:
- 无法定制生成的 Markdown 内容,没有 JSDoc 类似的模板文件
- 生成的 Markdown 不是一个简单的文件,而是将每个函数/类,解析成多个文件,如 classes/enums/interfaces Markdown 文件
基于以上原因,为了满足项目需求,故选择 JSDoc 作为注释转 Markdown 方案
1.2. 使用 JSDoc 转化注释
- 安装依赖
npm i jsdoc-to-markdown jsdoc-babel -D
- 遍历目标文件夹,通过 jsdoc2md.render 方法将目标文件生成 md 文件
const mdStr = await jsdoc2md.render({
template: template, // markdown 模板
'example-lang': 'javascript',
files: path.resolve(process.cwd(), sourcePath), // 目标文件所在路径
'name-format': 'backticks',
'heading-depth': 2,
'module-index-format': 'none',
configure: jsdocConfigPath // jsdoc.config.json 所在的路径
});
关于 jsdoc.render 的完整 Api 可以参见官方文档
- Markdown 模板
jsdoc2md 通过 render 方法的 template 字段指定 markdown 模板,该模板引擎由 jsdoc2md/dmd 项目提供,采用的是 handlerbars 模板语法。这里简单的看一下示例:
template不传的时候相当于如下模板:
{{>main-index~}}
{{>all-docs~}}
而 {{>main-index}}
相当于以下内容
{{#if (showMainIndex)~}}
{{>module-index~}}
{{>global-index~}}
{{/if~}}
{{>global-index~}}
相当于如下内容,具体见 dmd 项目,可以一步步拆开,修改模板内容
{{>global-index-kinds kind="class" title="Classes" ~}}
{{>global-index-kinds kind="mixin" title="Mixins" ~}}
{{>global-index-kinds kind="member" title="Members" ~}}
{{>global-index-kinds kind="namespace" title="Objects" ~}}
{{>global-index-kinds kind="constant" title="Constants" ~}}
{{>global-index-kinds kind="function" title="Functions" ~}}
{{>global-index-kinds kind="event" title="Events" ~}}
{{>global-index-kinds kind="typedef" title="Typedefs" ~}}
{{>global-index-kinds kind="external" title="External" ~}}
{{>global-index-kinds kind="interface" title="Interfaces" ~}}
2. Vitepress 网站搭建与配置
vitepress 是基于 vite 构建的 markdown 文档托管网站,可以根据 markdown 内容构建网站。
vitepress 的初始化与搭建请参照官方文档,这里我介绍一下如何通过 node 解析 markdown 目录,自动生成 vitepress 对应的 sidebar 结构
2.1. 将目录结构解析为 sidebars
当前目录结构为:
- doc
- strings
- is-number.md
- is-float.md
- numbers
- ...
需要解析为如下结构:
[ { "text": "strings", "items": [ { "text": "is-float", "link": "/doc/strings/is-float" }, { "text": "is-number", "link": "/doc/strings/is-number" } ]
},
{
"text": "numbers",
"items": [...]
}
]
具体实现逻辑请参见 getSideBar 方法,这里讲一下关键实现关键:
- 通过
path.join
获取目标目录路径
mdDirPath: path.join(__dirname, '../docs', 'docs),
- 通过 sep 内置方法解析父文件夹和子文件名称
import { sep } from 'path';
// 处理文件夹名
const getDirName = (path) => {
let name = path.split(sep).shift() || path;
name = name.replace(/^\d+[.-_ ]?/, '');
return name;
};
// 处理 markdown 文件名
const getName = (path) => {
let name = path.split(sep).pop() || path;
const argsIndex = name.lastIndexOf('--');
if (argsIndex > -1) {
name = name.substring(0, argsIndex);
}
return name;
};
- 通过 glob 库获取文件夹内所有的
.md
文件
const pattern = '/**/*.md';
// 查找指定目录下匹配的文件
const files = glob.sync(parentPath + pattern)
最后即可遍历拼接成 sidebar 所需结构。
2.2. config 配置
具体配置参见官网,这里列举一些特殊的自定义配置:
- 定制"在 github 上编辑此页",通过 pattern 的回调参数,可以解析路径参数,获取到对应的文档对应的代码位置,最终实现点击跳转到源代码,而不是 Markdown 文件位置。
themeConfig: {
editLink: {
pattern: ({ filePath }) => {
function extractPathSegment(url) {
const startIndex = url.indexOf('/doc/') + '/doc/'.length;
const endIndex = url.lastIndexOf('.md');
if (startIndex > -1 && endIndex > startIndex) {
return url.substring(startIndex, endIndex);
}
return null;
}
return `http://.../src/${extractPathSegment(filePath)}.ts`;
},
text: 'github 上编辑此页'
}
}
- 小 tips,markdown 里可以引入外层 markdown 文件
<!--@include: ../../README.md-->
3. 监听工作目录,自动化编译markdown
上文中介绍了如何转化注释为 markdown,现在我们给 src 目录内容 添加监听事件,实现内容修改自动触发编译:
- 监听 src 目录:
fs.watch(srcDirPath, { recursive: true }, (_eventType, filename) => {
console.log(`${filename} changed...`);
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
transformCommentToMarkdown();
}, delay);
});
- 触发命令执行:其中 npm run jsdoc2md 配置了 node 执行 markdown 转化脚本
const transformCommentToMarkdown = () => {
exec('npm run jsdoc2md', (error) => {
if (error) {
console.error(`Error occurred while running npm run jsdoc2md: ${error.message}`);
}
});
};
- 配置 scripts 执行命令
在运行 npm run dev
同时执行三个命令:编译、启动vitepress、监听
注意 vitepress dev docs
& node scripts/watch.js
中间只有一个 &
是防止前一条命令阻塞 watch.js
"scripts": {
"dev": "npm run jsdoc2md && vitepress dev docs & node scripts/watch.js",
},
4. 配置 vitest 测试用例
vitest 是一个基于 Vite 原生的单元测试框架,语法类似 jest,一行命令即可使用,具体语法见官方文档。
yarn add -D vitest
这里我们给工具函数添加 test 测试用例之后,在 build 命令之前添加 test 的执行,这样即可保证 测试不通过无法编译:
"scripts": {
"prebuild": "vitest run",
"build": "rimraf lib && npm run build:ts",
}
5. Github 地址
具体 demo 见 github,有兴趣的同学希望能点个⭐️。
转载自:https://juejin.cn/post/7237442920602009657