@esydoc/resolver-demo
TypeScript icon, indicating that this package has built-in type declarations

2.1.3 • Public • Published

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 是一个有约定的参数注释,具体请看下面图片解析:

img img

formItemType

resolver 的模板会根据 formItemType 的不同生成不一样的表单控件

  • 'input' - 输入框
  • 'select' - 选择框
  • 'switch' - 开关
  • 'slef-impliment' - 自我实现

slef-impliment

为什么会有这个选项?

因为参数的类型多种多样,有一些特殊的情况,你就无法用到那些表单控件,所以这里用 slef-impliment 去跳过控件的渲染

我目前总结的特殊情况有如下:

  1. 当参数是一个函数,你不能直接用 ui 控件去展示
  2. 当参数是一个很复杂的对象,我不建议你直接去展示,你可以设置 slef-impliment,直接或间接修改 value
  3. 当参数需要的数据不适合从表单控件中获取的时候(例如:我需要一个原型对象),直接或间接修改 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:ss
  • phone - 检查手机号码格式

模板

  • hyext-demo-miniapp - Demo 虎牙小程序模板

Readme

Keywords

none

Package Sidebar

Install

npm i @esydoc/resolver-demo

Weekly Downloads

0

Version

2.1.3

License

ISC

Unpacked Size

866 kB

Total Files

35

Last publish

Collaborators

  • limingyi_100
  • hy-ext