@esydoc/doclet-parser
Take the doclet root to parse be a whole AST.
Doclet
What is the doclet?
可以理解它为一个对注释的一个抽象
,通常 babel 解析 JS AST 的时候,是不会处理 comment 节点(它只是个字符串),
而jsdoc
则利用 babel 提供的 hooks, 拿到 comment 再进行二次解析,转成 doclet 对象。
Doclet vs AST
Doclet
与 AST
最大区别在于,Doclet
节点都是属于离散的节点,它只保留子树的 key,而不是子树,这就意味着它并不是一颗完整的 AST,
而我们渲染数据的时候却需要, 这时候需要 doclet-parser
为我们处理这一难题。
举例子:
localControlEntrance(params) {
return extsdk.core.callEvent(MODULE_NAME, 'localControlEntrance', params);
},
转换后的 api doclet 是这样的:
const apiDoclet = {
name: 'localControlEntrance',
params: [
{
type: {
names: ['EntranceReq']
},
name: 'params'
}
],
....
}
const EntranceReqDoclect = {
name: EntranceReq,
type: {
names: ['Object']
}
properties: [
{
name: 'extType',
types: {
{
names: ['string']
}
}
},
...
]
}
经过 doclet-parser 处理之后:
const root = {
name: 'root',
type: 'Array',
properties: [
{
name: 'params',
type: 'Object',
properties: [
{
name: 'extType',
type: 'string'
},
...
]
}
]
}
看到转换后的节点我们发现离散的 doclet 节点都被适当地抽象融合到了一个颗 root 树上,这样我们就可以使用 root 树去渲染各种数据啦~。
API
Constructor(fileDataMap: FileDataMap)
import DocletParser from '@esydoc/doclet-parser'
export type DataMap = Map<string, RawDataTreeNode>
export type FileDataMap = Map<string | symbol, DataMap>
const fileDataMap = new Map()
const parser = new DocletParser(fileDataMap)
Instance API
parser.parse(apiDoclet, reflectConfig): ParsedResult
- 将 apiDoclet 节点转换为一颗完整 AST,其中 apiDoclet 是通过@eapi标签
标记的注释节点,而reflectConfig 配置是一个格式化 Doclet 字段的一个映射配置。
reflectConfig
介绍
export const ApiReflectConfig = Object.freeze({
args: {
key: 'args',
reflectKey: 'params',
format: (data: any) => {
return [data]
},
nodeType: 'Array'
},
ret: {
key: 'ret',
reflectKey: 'returns',
format: (data: any) => {
return data
},
nodeType: 'Array'
}
})
如何获取上述默认配置?
import {
ApiReflectConfig,
} from '@esydoc/doclet-parser'
ParsedResult数据结构:
export type TransformedDataTreeNode = {
name: string
properties?: TransformedDataTreeNode[]
parent: TransformedDataTreeNode | null
description?: string
optional?: boolean
type: string
id: string
rawType?: string
}
type ParsedResult = {
[key: string]: TransformedDataTreeNode;
} | null
Helpers
节点相关操作
- traverseNode - 深度优先遍历节点(递归)
function traverseNode(
node: TransformedDataTreeNode,
onPush: (node: TransformedDataTreeNode) => void,
onPop: (node: TransformedDataTreeNode) => void
): void
- widthFirstTraverseNode - 广度优先遍历节点(循环)
虽然用递归的方式计算深度比较简单,但因为栈的原因,不能提前结束,所以采用循环比较合理
const widthFirstTraverseNode = (
node: DataTreeNode,
visit: (node: DataTreeNode, depth: number) => boolean | void
): void
- transofrmDataTreeNode2Value - 转换节点为数据值
function transofrmDataTreeNode2Value(node: DataTreeNode): any
以上文提到的 root 为例子:
const root = {
name: 'root',
type: 'Array',
properties: [
{
name: 'params',
type: 'Object',
properties: [
{
name: 'extType',
type: 'string'
},
...
]
}
]
}
const val = transofrmDataTreeNode2Value(root)
val = [
{
extType: ''
}
]
-
isDateTreeNode(node: DataTreeNode): boolean - 是否是 DataTreeNode
-
getFirstChild(node: DataTreeNode): DataTreeNode | null - 获取第一个子节点
-
isMutiValueNode(node: DataTreeNode): boolean - 该节点是否是一个多分支节点,例如:enum节点,polytype节点 (a|b|c)
-
unwrap(node: DataTreeNode): DataTreeNode - 提取 doclet root 实际有效的节点
doclet root 有两种形态:args, ret
localControlEntrance(params) {
return extsdk.core.callEvent(MODULE_NAME, 'localControlEntrance', params);
}
从上述代码我们可以看出,@param
和 @returns
分别 转换为 doclet root 的 args 和 ret 两种形态。
此时,我们使用unwarp进行解包可以速度拿到关键数据:
const unwarpedArgs = unwrap(argsDocletRoot)
const unwarpedRet = unwrap(retDocletRoot)
- clean - 删除树多余字段,优化序列化的速度,减少生成文件的代码量
type CleanRuleObj = {
key: keyof TransformedDataTreeNode,
assert: string | ((val: any) => boolean)
}
type CleanRule = keyof TransformedDataTreeNode | CleanRuleObj
const cleanDefaultRules: CleanRule[] = [
'description',
'parent',
'id',
{
key: 'properties',
assert: 'undefined'
},
{
key: 'optional',
assert: 'undefined'
},
{
key: 'rawType',
assert: 'undefined'
}
]
function clean(tree: TransformedDataTreeNode, rules: CleanRule[] = cleanDefaultRules): TransformedDataTreeNode
通用相关操作
- genUniId(prefix: string) - prefix 拼接一个 hash, 生成一个唯一id
引用方式
import {
genUniId,
clean,
unwrap,
getFirstChild,
isDateTreeNode,
transofrmDataTreeNode2Value,
widthFirstTraverseNode,
traverseNode
} from '@esydoc/doclet-parser'