@esydoc/resolver-demo
v2.1.3
Published
> TODO: description
Downloads
201
Readme
resolver-demo
一个解析 Api 源码生成 Demo 代码的解析器
Installation
npm i @esydoc/resolver-demo -D
Usage
在 esydoc.config.js
文件中的resolves
字段添加@esydoc/resolver-demo
对应的配置就 ok 拉。
// for example in esydoc.config.js
{
resolves: {
'@esydoc/resolver-demo': {
excludes: [], // glob Exp
includes: [], // glob Exp
output: {
template: 'hyext-demo-miniapp',
dist: path.join(__dirname, 'demo-miniapp'),
hostContext: {
projectName: 'esydoc-demo'
}
}
}
}
}
Esydoc Extra Config
无。
API 配置文件对应配置
我们知道,esydoc 会为每个标记了@eapi 标签的接口源码,生成一份以接口命名的配置文件,这里的对应配置
是指resolver-demo
输出的相应配置。
For example:
某 api 文件配置
const demo: DemoConfig = {
modifed: {} // 可以修改参数描述节点的一个对象
}
const apiConfig = {
demo: demo // 属于 resolver-demo 配置
}
export type Modified = { [path: string]: ModifiedNodeMember } // 配置需要修改的 描述节点, path 是访问路径
export type ModifiedArgs = Modified
export type ModifiedNodeMember =
| ValueDescriptionNode
| ((node: ValueDescriptionNode) => ValueDescriptionNode)
export type Interceptors = { onCall?: (...args: any[]) => any }
export type DemoConfig = {
modified: Modified
validate?: Validate
} & Interceptors
接下来,我们来说说这几个参数的作用
Modifed 对象
Modifed 对象是一个可以间接修改 ValueDescriptionNode
的一个配置对象,key 为访问节点的路径,value 为节点配置的子集
。
For example:
SDK 源码:
/**
* test api
* @eapi
* @param {string} name 输入参数
* @param {number} id 输入参数
* @returns {Promise<void>} 调用结果
*/
testApi(name, id) {
}
如果我想封装一下 id 这个参数,原本的 input 组件改成 switch, 我们可以这样操作:
// testApi接口配置
module.exports = {
demo: {
modified: {
id: {
// rewrite node propertys
formItemType: 'switch', // 原来是string
defaultValue: false, // 原来是 ''
onCall(helper, val) {
// 增加一个 intercept hook 拦截输入控制输出
if (val === false) {
return 10086
} else {
return 10010
}
}
}
}
}
}
通过上面简单的配置,我们关于 id 字段的 input 组件,就会变为 switch 组件,那我们就不需要手动输入 id 了。
节点访问范围
由于 resolver 将 AST 转换为 ValueDescriptionNode 的过程中,对其进行了最大深度为 3 的树压缩。
何为深度为 3 的压缩?
// 1 -> 2 -> 3
// args -> args[0] -> args[0].obj
第一层是 args,代表调用的参数
第二层是 args[0],代表其中一个参数
第三层是 args[0].obj,代表其中一个参数的子参数(如果有)
第四层是 args[0].obj.obj 如果节点再有可嵌套的子节点
,此时节点的 value 值是可嵌套的子节点
渲染的值
举例子:
/**
* @edata
* @typedef {Object} NestObj
* @property {NestObj} nestObj
*/
/**
* test api
* @eapi
* @param {NestObj} params 输入参数
* @returns {Promise<void>} 调用结果
*/
testApi(params) {
}
看上面的代码 这个params
是一个无尽嵌套的对象(举例而已,也不是无尽嵌套,也可能是个很复杂的对象),如果我们不做压缩,那表单UI会一直渲染下去,会无比复杂,这并不是我们想要的结果,我们可以通过 压缩 + 节点封装
对其进行优化处理:
module.exports = {
demo: {
modified: {
// 最大访问深度
'params.nestObj.nestObj': {
formItemType: 'select',
options: [
{
value: Mode.One,
label: '模式1',
isDefault: true
},
{
value: Mode.Two,
label: '模式2',
isDefault: false
}
],
onCall(helper, mode) {
return getObjByMode(mode) // 节点封装
}
}
}
}
}
通过上述简单的封装,我们就可以直接通过选择 mode 去生成对应复杂的对象,而不需要每个字段都去配置。
特殊节点
array
对于 array 数据类型不支持元组,只支持数组,例如:Array<Member>
, 数组里面的成员数据结构都是唯一的,它的访问方式跟问对象节点相似:
source code
/**
* @edata
* @typedef {Object} MOCK
* @property {string} md5 资源的md5
* @property {string} fileName 文件名
* @property {Object} [param] 额外参数
*/
/**
* test api
* @eapi
* @param {Array<MOCK>} params 输入参数
* @returns {Promise<void>} 调用结果
*/
testApi(params) {
}
配置:
module.exports = {
demo: {
modified: {
'params.MOCK': {
// rewrite node propertys
formItemType: 'input',
onCall() {
// do someting
}
}
}
}
}
我们看到可以通过 params.MOCK
路径就能访问 MOCK 节点的数据,而不是使用 params[0] ...params[n]
,
上面已经说的很清楚,不支持元组风格的独立性修改,只能改所有数组成员的数据结构。
合并策略
目前比较low:Object.assign(ValueDescriptionNode, ModifedNode)
,
你也自定义:
module.exports = {
demo: {
modified: {
'params': (node) => {
// custom modifed
return node
}
}
}
}
ValueDescriptionNode
ValueDescriptionNode 是对 UI 表单的一种抽象,直接控制 UI 该渲染什么组件,由 AST Node 转换而来
export type FormItemOption = {
value: any
label: string
isDefault: boolean
}
export type FormItemOptions = FormItemOption[]
export type ValueDescriptionNode = {
id: string
name: string
valueType: string
description: string
value: any
defaultValue: any
formItemType: 'input' | 'select' | 'switch' | 'checkbox' | 'slef-impliment'
options?: FormItemOptions // formItemType = select | checkbox存在
parentNodeType?: string
} & Interceptors
下面挑一些比较有内涵的参数介绍一下
description
description 是一个有约定的参数注释,具体请看下面图片解析:
formItemType
resolver 的模板会根据 formItemType 的不同生成不一样的表单控件
- 'input' - 输入框
- 'select' - 选择框
- 'switch' - 开关
- 'slef-impliment' - 自我实现
slef-impliment
为什么会有这个选项?
因为参数的类型多种多样,有一些特殊的情况,你就无法用到那些表单控件,所以这里用 slef-impliment 去跳过控件的渲染
我目前总结的特殊情况有如下:
- 当参数是一个函数,你不能直接用 ui 控件去展示
- 当参数是一个很复杂的对象,我不建议你直接去展示,你可以设置 slef-impliment,直接或间接修改 value
- 当参数需要的数据不适合从表单控件中获取的时候(例如:我需要一个原型对象),直接或间接修改 value
Interceptors
目前可以 Interceptors 存在于 ValueDescriptionNode 或 demo config 中
intercept hooks
- onCall(helper, ...args) - 当接口调用时触发。
一共有 2 个粒度,第一个是针对每个参数的 onCall(helper, value)(ValueDescriptionNode.onCall), 另一个是针对调用参数的 onCall(helper, ...args)(config.onCall)
// testApi接口配置
module.exports = {
demo: {
modified: {
id: {
onCall(helper, val) {
// 粒度 1
if (val === false) {
return 10086
} else {
return 10010
}
}
}
},
onCall(helper, name, id) {
// 粒度 2
return [name, id]
}
}
}
helper
从上文得知,interceptor 的回调函数会传入一个 helper 对象,这个 helper 封装了一些接口,方面用户获取运行时的一些资源。
helper.getJceInfo()
- 返回一个 jce 信息对象,用于 jce 相关接口调用,数据结构具体如下:
{
uri: '8856',
class: HUYA // 结构体wrapper
}
helper.tip(msg:string)
- 弹出 toast
validate
validate 配置提供高定制表单验证功能
所有字段验证
- 当
validate:true
时,所有字段都会被验证,验证规则为'required' - 当
validate:${rules}
时,所有字段都会使用被验证,验证规则为 rules, rules 可以是单个规则也可以是多个,例如:'required'或者'required|date|some rule'
指定某些字段验证
输出默认错误模式,每个规则都有默认的错误语句
{
validate:{
[name]: true | 'date' | 'required|date' // true - 默认使用’required‘, {rule} - 动态指定规则
}
}
自定义错误模式
{
validate:{
[name]: {
rules: 'required',
errors: ['这个xx字段你必须要填哦~']
}
}
}
内置规则
required
- 判断字段是否空白date
- 检查日期 格式:YYYY-MM-DD hh:mm:ssphone
- 检查手机号码格式
、
模板
hyext-demo-miniapp
- Demo 虎牙小程序模板