从JSON到接口文档,教你如何优雅生成组件API!

技术博客 (311) 2023-11-16 09:01:01

背景

最近在做一个组件库的项目,对第三方库进行二次封装并暴露自定义属性,每个组件都有一份默认数据的 JSON格式。如何根据默认数据生成组件 API 文档?本文将教你如何使用 json-to-ts、prettier、ShellJS、TypeDoc、fs-extra、ora、path 等 JS 库优雅生成接口文档。

方案

方案非常直接,只需要两步:

  1. JSON 转 TypeScript
  2. 通过 TypeScript 生成组件 API 文档

原本以为这个需求已经有现成的轮子,但找了一下午都没有找到,只能自己实现一个

准备工作

首先,我们需要安装以下依赖:

npm install json-to-ts prettier shelljs typedoc fs-extra ora path --save-dev

我们所有的脚本文件都用 ts 来写,这样可以避免很多 类型 和 引用 问题

生成 TypeScript 接口

我们可以使用 json-to-ts 库 将 JSON 数据转换为 TypeScript 接口。首先,创建一个名为 json-to-interface.ts 的文件,并编写以下代码:

import * as jsonToTs from "json-to-ts";
import * as fs from "fs-extra";
import * as path from "path";
import prettier from "prettier";

// 获取目标文件夹下的定义的 默认数据文件
export const getTargetFiles = (targetPath) => {
  let components = [];

  const files = fs.readdirSync(targetPath);
  files.forEach((item, index) => {
    let stat = fs.statSync(`${targetPath}/` + item);

    let isBool = true;
    try {
      fs.statSync(`${targetPath}/` + item + "/defaultProps.ts");
    } catch (error) {
      isBool = false;
    }

    if (stat.isDirectory() === true && isBool) {
      components.push(item);
    }
  });

  return components;
};

const transform = () => {
   // 获取待处理的组件名. targetPath 为存放默认数据的文件夹路径
  let components = getTargetFiles(targetPath);

  // 开始处理组件
  components.forEach((name) => {
    console.log(`当前处理组件的 - ${name}`);

    const jsonFilePath = path.resolve(
      __dirname,
      `${targetPath}/${name}/defaultProps.ts`
    );
   
    // 获取数据
    const { defaultProps: json } = require(jsonFilePath);

    // 可以先对默认数据做一下前置处理
    // ...
    
    // 获取 interface 数据
    const interfaceData: string[] = JsonToTS(json, { rootName: "DefaultProps" });
    
    // 可以对生成的 interfaceData 进行自定义配置处理
    // ...

    // interface 文件路径
    let filePath = `${targetPath}/${name}/interface.ts`;
    filePath = path.resolve(__dirname, filePath);

    // 使用 Prettier 格式化代码
    const formatData = prettier.format(interfaceData, {
      singleQuote: true,
      parser: "typescript",
    });

    // 写 interface 文件
    fs.outputFileSync(filePath, formatData);
 });
}

这段代码首先从我们的目标文件夹中读取名为 defaultProps.ts默认数据 文件(json 格式),然后使用 json-to-ts 库将 JSON 数据转换为 TypeScript 接口,并将结果写入名为 interfaces.ts 的文件中。

由于 json-to-ts 提供的 api 较少,如果我们需要给生成的接口上添加 自定义的数据,可以通过遍历 interfaceData 来动态添加

interfaceData.map(item => {
    if (item.includes("interface DefaultProps {")) {
      const linkName = item.split(" ")[1];
      return ` /** * @module ${title} * @see 点击此处查看区块详细 props -> {@link ${linkName}} */ ${item} `;
    }
    return item;
  })

生成的 interfaces.ts 内容如下

从JSON到接口文档,教你如何优雅生成组件API! (https://mushiming.com/) 技术博客 第1张

生成接口文档

我们可以使用 TypeDoc 库 将生成的 TypeScript 接口转换为 HTML 文档。创建一个名为 generate-doc.ts 的文件,并编写以下代码:

// 从 json-to-interface.ts 中导出相关的方法和属性
...

const generateDoc = () => {
  // 获取 typeDoc 配置文件
  const typeDocConfigPath = path.resolve(__dirname, "../../typedoc.json");
  let configJson = require(typeDocConfigPath);

  /** 将待生成文档的组件地址,注入到 typeDoc 配置文件中 * basePath、name、components:是在上个阶段【生成 TypeScript 接口】获取的 */
  configJson["entryPoints"] = components.map(
    (name) => `.${basePath}/${name}/interface.ts`
  );

  // 格式化 json 代码
  configJson = prettier.format(JSON.stringify(configJson), { parser: "json" });

  // 更新 typeDoc 配置文件
  fs.writeFileSync(typeDocConfigPath, configJson);

  // 生成文档
  shell.exec("typedoc --tsconfig ./tsconfig.json");
}

最终生成的 typedoc.json 大概如下图:

{
  "entryPoints": [
    "./src/components/AboutUs/interface.ts",
    "./src/components/Contact/interface.ts",
    ...
  ],
  "out": "demo-docs",
  "name": "demo 组件 api",
  "exclude": [
    "**/__tests__/**/*",
    "**/node_modules/**/*"
  ],
  "theme": "default",
  "excludeExternals": true,
  "excludePrivate": true,
  "excludeProtected": true,
  "skipErrorChecking": true,
  "disableSources": true,
  "readme": "./scripts/generate/readme.md"
}

修改 package.json 文件,添加以下脚本:

"scripts": {
  "docs": "npx tsx ./generate-doc.ts",
}

然后,执行以下命令生成接口文档:

npm run docs

这将在项目根目录下生成一个名为 demo-docs 的文件夹,其中包含接口文档的 HTML 文件。

从JSON到接口文档,教你如何优雅生成组件API! (https://mushiming.com/) 技术博客 第2张

可以定制 文档首页 的显示内容,通过一份 readme.md 来定制

最后

通过 json-to-tsTypeDoc 两个组件库的配合,以及多项交互优化,让你轻松生成组件 API

THE END

发表回复