UNPKG

yapi-ts-builder

Version:

基于 yapi-to-typescript 实现的 YApi 接口定义生成工具

651 lines (650 loc) 19.1 kB
/// <reference types="node" /> import { AsyncOrSync, LiteralUnion, OmitStrict, OneOrMore } from 'vtils/types'; import { JSONSchema4, JSONSchema4TypeName } from 'json-schema'; import { ParsedPath } from 'path'; export interface ChangeCase { /** * @example * changeCase.camelCase('test string') // => 'testString' */ camelCase: (value: string) => string; /** * @example * changeCase.constantCase('test string') // => 'TEST_STRING' */ constantCase: (value: string) => string; /** * @example * changeCase.dotCase('test string') // => 'test.string' */ dotCase: (value: string) => string; /** * @example * changeCase.headerCase('test string') // => 'Test-String' */ headerCase: (value: string) => string; /** * @example * changeCase.lowerCase('TEST STRING') // => 'test string' */ lowerCase: (value: string) => string; /** * @example * changeCase.lowerCaseFirst('TEST') // => 'tEST' */ lowerCaseFirst: (value: string) => string; /** * @example * changeCase.paramCase('test string') // => 'test-string' */ paramCase: (value: string) => string; /** * @example * changeCase.pascalCase('test string') // => 'TestString' */ pascalCase: (value: string) => string; /** * @example * changeCase.pathCase('test string') // => 'test/string' */ pathCase: (value: string) => string; /** * @example * changeCase.sentenceCase('testString') // => 'Test string' */ sentenceCase: (value: string) => string; /** * @example * changeCase.snakeCase('test string') // => 'test_string' */ snakeCase: (value: string) => string; /** * @example * changeCase.swapCase('Test String') // => 'tEST sTRING' */ swapCase: (value: string) => string; /** * @example * changeCase.titleCase('a simple test') // => 'A Simple Test' */ titleCase: (value: string) => string; /** * @example * changeCase.upperCase('test string') // => 'TEST STRING' */ upperCase: (value: string) => string; /** * @example * changeCase.upperCaseFirst('test') // => 'Test' */ upperCaseFirst: (value: string) => string; } /** 请求方式 */ export declare enum Method { GET = "GET", POST = "POST", PUT = "PUT", DELETE = "DELETE", HEAD = "HEAD", OPTIONS = "OPTIONS", PATCH = "PATCH" } /** 是否必需 */ export declare enum Required { /** 不必需 */ false = "0", /** 必需 */ true = "1" } /** 请求数据类型 */ export declare enum RequestBodyType { /** 查询字符串 */ query = "query", /** 表单 */ form = "form", /** JSON */ json = "json", /** 纯文本 */ text = "text", /** 文件 */ file = "file", /** 原始数据 */ raw = "raw", /** 无请求数据 */ none = "none" } /** 请求路径参数类型 */ export declare enum RequestParamType { /** 字符串 */ string = "string", /** 数字 */ number = "number" } /** 请求查询参数类型 */ export declare enum RequestQueryType { /** 字符串 */ string = "string", /** 数字 */ number = "number" } /** 请求表单条目类型 */ export declare enum RequestFormItemType { /** 纯文本 */ text = "text", /** 文件 */ file = "file" } /** 返回数据类型 */ export declare enum ResponseBodyType { /** JSON */ json = "json", /** 纯文本 */ text = "text", /** XML */ xml = "xml", /** 原始数据 */ raw = "raw" } /** 查询字符串数组格式化方式 */ export declare enum QueryStringArrayFormat { /** 示例: `a[]=b&a[]=c` */ 'brackets' = "brackets", /** 示例: `a[0]=b&a[1]=c` */ 'indices' = "indices", /** 示例: `a=b&a=c` */ 'repeat' = "repeat", /** 示例: `a=b,c` */ 'comma' = "comma", /** 示例: `a=["b","c"]` */ 'json' = "json" } /** 接口定义 */ export interface Interface { /** 接口 ID */ _id: number; /** 所属分类信息(由 YTT 自行实现) */ _category: OmitStrict<Category, 'list'>; /** 所属项目信息(由 YTT 自行实现) */ _project: Project; /** 接口在 YApi 上的地址(由 YTT 自行实现) */ _url: string; /** 接口名称 */ title: string; /** 状态 */ status: LiteralUnion<'done' | 'undone', string>; /** 接口备注 */ markdown: string; /** 请求路径 */ path: string; /** 请求方式,HEAD、OPTIONS 处理与 GET 相似,其余处理与 POST 相似 */ method: Method; /** 所属项目 id */ project_id: number; /** 所属分类 id */ catid: number; /** 标签列表 */ tag: string[]; /** 请求头 */ req_headers: Array<{ /** 名称 */ name: string; /** 值 */ value: string; /** 备注 */ desc: string; /** 示例 */ example: string; /** 是否必需 */ required: Required; }>; /** 路径参数 */ req_params: Array<{ /** 名称 */ name: string; /** 备注 */ desc: string; /** 示例 */ example: string; /** 类型(YApi-X) */ type?: RequestParamType; }>; /** 仅 GET:请求串 */ req_query: Array<{ /** 名称 */ name: string; /** 备注 */ desc: string; /** 示例 */ example: string; /** 是否必需 */ required: Required; /** 类型(YApi-X) */ type?: RequestQueryType; }>; /** 仅 POST:请求内容类型。为 text, file, raw 时不必特殊处理。 */ req_body_type: RequestBodyType; /** `req_body_type = json` 时是否为 json schema */ req_body_is_json_schema: boolean; /** `req_body_type = form` 时的请求内容 */ req_body_form: Array<{ /** 名称 */ name: string; /** 类型 */ type: RequestFormItemType; /** 备注 */ desc: string; /** 示例 */ example: string; /** 是否必需 */ required: Required; }>; /** `req_body_type = json` 时的请求内容 */ req_body_other: string; /** 返回数据类型 */ res_body_type: ResponseBodyType; /** `res_body_type = json` 时是否为 json schema */ res_body_is_json_schema: boolean; /** 返回数据 */ res_body: string; /** 创建时间(unix时间戳) */ add_time: number; /** 更新时间(unix时间戳) */ up_time: number; /** 创建人 ID */ uid: number; [key: string]: any; } /** 扩展接口定义 */ export interface ExtendedInterface extends Interface { parsedPath: ParsedPath; } /** 接口列表 */ export type InterfaceList = Interface[]; /** 分类信息 */ export interface Category { /** ID */ _id: number; /** 分类在 YApi 上的地址(由 YTT 自行实现) */ _url: string; /** 分类名称 */ name: string; /** 分类备注 */ desc: string; /** 分类接口列表 */ list: InterfaceList; /** 创建时间(unix时间戳) */ add_time: number; /** 更新时间(unix时间戳) */ up_time: number; } /** 分类列表,对应数据导出的 json 内容 */ export type CategoryList = Category[]; /** 项目信息 */ export interface Project { /** ID */ _id: number; /** 项目在 YApi 上的地址(由 YTT 自行实现) */ _url: string; /** 名称 */ name: string; /** 描述 */ desc: string; /** 基本路径 */ basepath: string; /** 标签 */ tag: string[]; /** 环境配置 */ env: Array<{ /** 环境名称 */ name: string; /** 环境域名 */ domain: string; }>; } /** 支持生成 JSON Schema 的相关配置 */ export interface JsonSchemaConfig { /** * 是否开启该项功能。 */ enabled: boolean; /** * 是否生成请求数据的 JSON Schema。 * * @default true */ requestData?: boolean; /** * 是否生成返回数据的 JSON Schema。 * * @default true */ responseData?: boolean; } /** 支持生成注释的相关配置 */ export interface CommentConfig { /** * 是否开启该项功能。 * * @default true */ enabled?: boolean; /** * 是否有标题。 * * @default true */ title?: boolean; /** * 是否有分类名称。 * * @default true */ category?: boolean; /** * 是否有标签。 * * @default true */ tag?: boolean; /** * 是否有请求头。 * * @default true */ requestHeader?: boolean; /** * 是否有更新时间。 * * @default true */ updateTime?: boolean; /** * 是否为标题、分类名称添加链接。 * * @default true */ link?: boolean; /** * 额外的注释标签。生成的内容形如:`@{name} {value}`。 */ extraTags?: (interfaceInfo: ExtendedInterface) => Array<{ /** * 标签名。 */ name: string; /** * 标签值。 */ value: string; /** * 标签位置,即将新标签插在标签列表的开头还是末尾。 * * @default 'end' */ position?: 'start' | 'end'; }>; } /** * 共享的配置。 */ export interface SharedConfig { /** * 要生成的目标代码类型。 * 默认为 `typescript`,若设为 `javascript`,会将生成的 `.ts` 文件转换为 `.js` + `.d.ts` 文件并删除原 `.ts` 文件。 * * @default 'typescript' */ target?: 'typescript'; /** * 是否只生成接口请求内容和返回内容的 TypeSript 类型,是则请求文件和请求函数都不会生成。 * * @default false */ typesOnly?: boolean; /** * 测试环境名称。 * * **用于获取测试环境域名。** * * 获取方式:打开项目 --> `设置` --> `环境配置` --> 点开或新增测试环境 --> 复制测试环境名称。 * * @example 'dev' */ devEnvName?: string; /** * 生产环境名称。 * * **用于获取生产环境域名。** * * 获取方式:打开项目 --> `设置` --> `环境配置` --> 点开或新增生产环境 --> 复制生产环境名称。 * * @example 'prod' */ prodEnvName?: string; /** * 输出文件路径。 * * 可以是 `相对路径` 或 `绝对路径`。 * * @example 'src/api/index.ts' */ outputFilePath?: string | ((interfaceInfo: Interface, changeCase: ChangeCase) => string); /** * 如果接口响应的结果是 `JSON` 对象, * 且我们想要的数据在该对象下, * 那我们就可将 `dataKey` 设为我们想要的数据对应的键。 * * 比如该对象为 `{ code: 0, msg: '成功', data: 100 }`, * 我们想要的数据为 `100`, * 则我们可将 `dataKey` 设为 `data`。 * * @example 'data' */ dataKey?: OneOrMore<string>; /** * 支持生成 JSON Schema 的相关配置。 */ jsonSchema?: JsonSchemaConfig; /** * 支持生成注释的相关配置。 */ comment?: CommentConfig; /** * 将自定义类型转为 JSONSchema 类型的映射表,自定义类型名称大小写不敏感。 */ customTypeMapping?: Record<string, JSONSchema4TypeName>; /** * 如何格式化查询字符串中的数组值。 * * @default QueryStringArrayFormat.brackets */ queryStringArrayFormat?: QueryStringArrayFormat; /** * 设置传给请求函数的参数中的 extraInfo 的值。 * * @param interfaceInfo 接口信息 * @param changeCase 常用的大小写转换函数集合对象 * @returns 返回要赋给 extraInfo 的值 */ setRequestFunctionExtraInfo?(interfaceInfo: Interface, changeCase: ChangeCase): Record<string, any>; /** * 预处理接口信息,返回新的接口信息。可返回 false 排除当前接口。 * * 譬如你想对接口的 `path` 进行某些处理或者想排除某些接口,就可使用该方法。 * * @param interfaceInfo 接口信息 * @param changeCase 常用的大小写转换函数集合对象 * @param syntheticalConfig 作用到当前接口的最终配置 * @example * * ```js * interfaceInfo => { * interfaceInfo.path = interfaceInfo.path.replace('v1', 'v2') * return interfaceInfo * } * ``` */ preproccessInterface?(interfaceInfo: Interface, changeCase: ChangeCase, syntheticalConfig: SyntheticalConfig): Interface | false; /** * 获取请求数据类型的名称。 * * @default changeCase.pascalCase(`${requestFunctionName}Request`) * @param interfaceInfo 接口信息 * @param changeCase 常用的大小写转换函数集合对象 * @returns 请求数据类型的名称 */ getRequestDataTypeName?(interfaceInfo: ExtendedInterface, changeCase: ChangeCase): string; /** * 获取响应数据类型的名称。 * * @default changeCase.pascalCase(`${requestFunctionName}Response`) * @param interfaceInfo 接口信息 * @param changeCase 常用的大小写转换函数集合对象 * @returns 响应数据类型的名称 */ getResponseDataTypeName?(interfaceInfo: ExtendedInterface, changeCase: ChangeCase): string; } /** * 分类的配置。 */ export interface CategoryConfig extends SharedConfig { /** * 分类 ID,可以设置多个。设为 `0` 时表示全部分类。 * * 如果需要获取全部分类,同时排除指定分类,可以这样:`[0, -20, -21]`,分类 ID 前面的负号表示排除。 * * 获取方式:打开项目 --> 点开分类 --> 复制浏览器地址栏 `/api/cat_` 后面的数字。 * * @example 20 */ id: number | number[]; } /** * 项目的配置。 */ export interface ProjectConfig extends SharedConfig { /** * 项目的唯一标识。支持多个项目。 * * 获取方式:打开项目 --> `设置` --> `token配置` --> 复制 token。 * * @example 'e02a47122259d0c1973a9ff81cabb30685d64abc72f39edaa1ac6b6a792a647d' */ token: string | string[]; /** * 分类列表。 */ categories: CategoryConfig[]; } /** * 服务器的配置。 */ export interface ServerConfig extends SharedConfig { /** * 服务地址。若服务类型为 `yapi`,此处填其首页地址 * * @example 'http://yapi.foo.bar' */ serverUrl: string; /** * 服务类型。 * * @default 'yapi' */ serverType?: 'yapi'; /** * 项目列表。 */ projects: ProjectConfig[]; } /** 混合的配置。 */ export type SyntheticalConfig = Partial<ServerConfig & ServerConfig['projects'][0] & ServerConfig['projects'][0]['categories'][0] & { mockUrl: string; devUrl: string; prodUrl: string; }>; /** 配置。 */ export type Config = ServerConfig | ServerConfig[]; /** * 请求配置。 */ export interface RequestConfig<MockUrl extends string = string, DevUrl extends string = string, ProdUrl extends string = string, Path extends string = string, DataKey extends OneOrMore<string> | undefined = OneOrMore<string> | undefined, ParamName extends string = string, QueryName extends string = string, RequestDataOptional extends boolean = boolean> { /** 接口 Mock 地址,结尾无 `/` */ mockUrl: MockUrl; /** 接口测试环境地址,结尾无 `/` */ devUrl: DevUrl; /** 接口生产环境地址,结尾无 `/` */ prodUrl: ProdUrl; /** 接口路径,以 `/` 开头 */ path: Path; /** 请求方法 */ method: Method; /** 请求头,除了 Content-Type 的所有头 */ requestHeaders: Record<string, string>; /** 请求数据类型 */ requestBodyType: RequestBodyType; /** 返回数据类型 */ responseBodyType: ResponseBodyType; /** 数据所在键 */ dataKey: DataKey; /** 路径参数的名称列表 */ paramNames: ParamName[]; /** 查询参数的名称列表 */ queryNames: QueryName[]; /** 请求数据是否可选 */ requestDataOptional: RequestDataOptional; /** 请求数据的 JSON Schema (仅开启了 JSON Schema 生成时生效) */ requestDataJsonSchema: JSONSchema4; /** 返回数据的 JSON Schema (仅开启了 JSON Schema 生成时生效) */ responseDataJsonSchema: JSONSchema4; /** 请求函数名称 */ requestFunctionName: string; /** 如何格式化查询字符串中的数组值 */ queryStringArrayFormat: QueryStringArrayFormat; /** 额外信息 */ extraInfo: Record<string, any>; } /** * 请求参数。 */ export interface RequestFunctionParams extends RequestConfig { /** 原始数据 */ rawData: Record<string, any>; /** 请求数据,不含文件数据 */ data: Record<string, any>; /** 是否有文件数据 */ hasFileData: boolean; /** 请求文件数据 */ fileData: Record<string, any>; /** 所有请求数据,包括 data、fileData */ allData: Record<string, any>; /** 获取全部请求数据(包含文件)的 FormData 实例 */ getFormData: () => FormData; } /** 请求函数的额外参数 */ export type RequestFunctionRestArgs<T extends Function> = T extends (payload: any, ...args: infer R) => any ? R : never; /** 属性定义 */ export interface PropDefinition { /** 属性名称 */ name: string; /** 是否必需 */ required: boolean; /** 类型 */ type: JSONSchema4['type']; /** 注释 */ comment: string; } /** 属性定义列表 */ export type PropDefinitions = PropDefinition[]; /** 命令行钩子 */ export interface CliHooks { /** 生成成功时触发 */ success?: () => AsyncOrSync<void>; /** 生成失败时触发 */ fail?: () => AsyncOrSync<void>; /** 生成完毕时触发(无论成功、失败) */ complete?: () => AsyncOrSync<void>; } export type ConfigWithHooks = Config & { hooks?: CliHooks; };