react-docgen-typescript 自动生成组件文档(API 表格)

191 阅读2分钟

背景

在团队内做了一套通用UI组件库,虽然是内部使用,也总得有个组件文档,减低使用成本。当然,因为是内部使用,组件文档不需要花里胡哨,能用够用就行。分享一种利用 react-docgen-typescript 读取代码注释和类型定义生成组件 API 表格的方案。

react-docgen-typescript

react-docgen-typescript-loader 是一个解析器。它解析你的React组件,理解其类型信息和注释,并生成 API JSON,减少编写组件文档的时间成本。

方案

方案主要两步:

  1. 写一个 react-docgen-typescript 脚本读取组件的注释和类型定义生成一个 API JSON,并将 API JSON 写入 compDes.js 文件。
  2. 开发 RenderTypeTable 组件,将 compDes.js 的 JSON 传入展示为 API 表格。

react-docgen-typescript 脚本

  • package.json 配置命令:执行 yarn docgen Badge 会将 Badge 组件的注释和类型定义生成一个 API json。
"scripts": {
    "docgen": "./doc.js"
},
  • react-docgen-typescript 脚本 doc.js
/* eslint-disable no-console */
const fs = require('fs');
const path = require('path');
const docgen = require('react-docgen-typescript');

const options = {
  savePropValueAsString: false,
  shouldExtractLiteralValuesFromEnum: true,
  propFilter: prop => {
    if (prop.parent.fileName.indexOf('node_modules') !== -1) {
      // 过滤第三方插件的 props
      return false;
    }
    return true;
  },
};
const createCompDes = compFileName => {
  // ./Badge/index.tsx
  const filePath = path.resolve(__dirname, `./${compFileName}/index.tsx`);

  // 读取文件内容
  const docs = docgen.parse(filePath, options);
  const jsonContent = JSON.stringify(docs);
  const docjs = `const compDes = ${jsonContent}; export default compDes;`;
  const compDesPath = path.resolve(__dirname, `./${compFileName}/compDes.js`);

  // 写入文件
  fs.writeFile(
    compDesPath,
    docjs,
    {
      encoding: 'utf8',
      flag: 'w',
    },
    err => {
      if (err) {
        console.log(`${compFileName},An error occured while writing JSON Object to File.`);
        console.log(err);
      } else {
        console.log('JSON file has been saved.');
      }
    }
  );
};

// yarn docgen Badge Button Table 获取命令行参数[Badge, Button, Table],然后遍历参数,生成对应的 JSON 文件
const args = process.argv.slice(2);

args.forEach(name => {
  createCompDes(name);
});

RenderTypeTable 组件

import React, { useMemo } from 'react';
import { Props as docgenOutProps } from 'react-docgen-typescript';
import { Table } from 'antd';

export interface RenderTypeTableProps {
  compDesProps: docgenOutProps;
}

const columns = [
  {
    title: '属性',
    dataIndex: 'name',
    key: 'name',
  },
  {
    title: '说明',
    dataIndex: 'description',
    key: 'description',
  },
  {
    title: '类型',
    dataIndex: 'type',
    key: 'type',
  },
  {
    title: '默认值',
    dataIndex: 'defaultValue',
    key: 'defaultValue',
    width: 75,
  },
  {
    title: '是否必传',
    dataIndex: 'required',
    key: 'required',
    width: 90,
  },
];

export const RenderTypeTable: React.FC<RenderTypeTableProps> = props => {
  const { compDesProps } = props;

  const compDesPropsArr = useMemo(() => {
    let res = [];
    if (compDesProps) {
      const props = Object.keys(compDesProps);
      for (let i = 0; i < props.length; i++) {
        res.push({
          ...compDesProps[props[i]],
          key: compDesProps[props[i]].name,
          type: compDesProps[props[i]].type.name === 'enum' ? compDesProps[props[i]].type.value.map((i: any) => i.value).join(' | ') : compDesProps[props[i]].type.name,
          defaultValue: compDesProps[props[i]].defaultValue ? compDesProps[props[i]].defaultValue.value.toString() : '_',
          required: compDesProps[props[i]].required ? '是' : '否',
        });
      }
    }
    return res;
  }, [compDesProps]);

  return (
    <div style={{ padding: '15px', marginBottom: '15px' }}>
      <p style={{ height: '34px', fontSize: '22px', lineHeight: '34px', fontWeight: 500 }}>API</p>
      <Table dataSource={compDesPropsArr} columns={columns} pagination={false} bordered />
    </div>
  );
};

export default RenderTypeTable;

效果

组件文档api表格.png