背景
在团队内做了一套通用UI组件库,虽然是内部使用,也总得有个组件文档,减低使用成本。当然,因为是内部使用,组件文档不需要花里胡哨,能用够用就行。分享一种利用 react-docgen-typescript 读取代码注释和类型定义生成组件 API 表格的方案。
react-docgen-typescript
react-docgen-typescript-loader 是一个解析器。它解析你的React组件,理解其类型信息和注释,并生成 API JSON,减少编写组件文档的时间成本。
方案
方案主要两步:
- 写一个 react-docgen-typescript 脚本读取组件的注释和类型定义生成一个 API JSON,并将 API JSON 写入 compDes.js 文件。
- 开发 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;