likes
comments
collection
share

如何搭建自动生成markdown文档的工具函数网站

作者站长头像
站长
· 阅读数 32

当涉及软件开发和团队协作时,文档是无可替代的。代码注释作为一种重要的文档形式,可以帮助开发人员理解和维护代码。然而,手动编写和更新文档是一项繁琐的任务,容易被忽视或忽略。

下面我将向你介绍,如何通过 JSDoc + vitepress搭建可以通过注释自动生成 markdown,并通过 vitepress 托管文档。并使用 vitest 测试,提高工具函数的可维护性。

效果展示

注释代码

如何搭建自动生成markdown文档的工具函数网站

自动生成的markdown文件

如何搭建自动生成markdown文档的工具函数网站

自动生成的 vitepress 页面

如何搭建自动生成markdown文档的工具函数网站

如上图展示,该项目 demo 在本文最下方。有兴趣的请点个 ⭐️

1. 代码注释生成 Markdown 文件

1.1. 工具选择

1.1.1. JSDoc

JSDoc 是一个流行的代码文档生成工具,它基于特定的注释格式。通过使用 JSDoc,我们可以为函数、类和模块添加注释,并根据这些注释自动生成文档。JSDoc 提供了丰富的注释标签,用于描述参数、返回值、示例代码等信息。

它可以将如下注释转换成 Markdown 类型,但注意需要遵循 JSDoc 注释规范

优点

  1. 可以根据 hbs 模板定制生成的 Markdown 文件内容
  2. 可以将对应的 js/ts 文件生成指定的 markdown

缺点

  1. JSDoc 是根据注释生成的,没有注释的函数无法生成 Markdown 文档
1.1.2. TypeDoc

TypeDoc 是专门为 TypeScript 代码生成文档而设计的工具。与 JSDoc 类似,TypeDoc 可以从代码中提取类型信息,并生成相应的文档。与 JSDoc 不同的是,TypeDoc 不需要显式的注释来生成文档,它可以通过类型推断自动生成注释。

优点

  1. 没有注释也可以生成文档,可以根据函数类型自动提取出类型信息

缺点

  1. 无法定制生成的 Markdown 内容,没有 JSDoc 类似的模板文件
  2. 生成的 Markdown 不是一个简单的文件,而是将每个函数/类,解析成多个文件,如 classes/enums/interfaces Markdown 文件

如何搭建自动生成markdown文档的工具函数网站

基于以上原因,为了满足项目需求,故选择 JSDoc 作为注释转 Markdown 方案

1.2. 使用 JSDoc 转化注释

  1. 安装依赖
npm i jsdoc-to-markdown jsdoc-babel -D
  1. 遍历目标文件夹,通过 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 可以参见官方文档

  1. 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 方法,这里讲一下关键实现关键:

  1. 通过 path.join获取目标目录路径
mdDirPath: path.join(__dirname, '../docs', 'docs),
  1. 通过 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;
};
  1. 通过 glob 库获取文件夹内所有的 .md 文件
const pattern = '/**/*.md';
// 查找指定目录下匹配的文件
const files = glob.sync(parentPath + pattern)

最后即可遍历拼接成 sidebar 所需结构。

2.2. config 配置

具体配置参见官网,这里列举一些特殊的自定义配置:

  1. 定制"在 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 上编辑此页'
  }
}
  1. 小 tips,markdown 里可以引入外层 markdown 文件
<!--@include: ../../README.md-->

3. 监听工作目录,自动化编译markdown

上文中介绍了如何转化注释为 markdown,现在我们给 src 目录内容 添加监听事件,实现内容修改自动触发编译:

  1. 监听 src 目录:
fs.watch(srcDirPath, { recursive: true }, (_eventType, filename) => {
  console.log(`${filename} changed...`);

  if (timer) {
    clearTimeout(timer);
  }

  timer = setTimeout(() => {
    transformCommentToMarkdown();
  }, delay);
});
  1. 触发命令执行:其中 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}`);
    }
  });
};
  1. 配置 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
评论
请登录