💢 背景或痛点
和服务端联调接口,一般都是先给 swagger 文件,前端粘贴到 editor.swagger.io/ 然后从入参到出参逐个字段复制粘贴其描述当做注释、人工推断类型生成 TS,最后手写成 Service 层代码共 view 调用。
整个过程不仅耗时而且易出错,要是能有个 cli 工具自动生成 Service 层代码就好了。
生成效果大概如下:
ts
/** Comment about this service from schema description */
export const JobService = {
// 🔥 Grouped by path or tag, nicely organized. Give it a try by passing the `grouped` flag
prefix: '/api/model/v1/evaluate/job',
/**
* Comment about getJobDetail
*/
async getJobDetail(params: IGetJobDetailParams) {
return request<Data<IGetJobDetailRespData>>(`${this.prefix}/${params.jobId}`, {
method: 'GET',
});
},
/**
* Comment about createJob
*/
async createJob(data: ICreateJobReqData) {
return request<Data<ICreateJobRespData>>(this.prefix, {
method: 'POST',
data: ICreateJobReqData
});
},
/**
* Comment about stopJob
*/
async stopJob(params: IStopJobParams) {
return request<Data<IStopJobRespData>>(`${this.prefix}/${params.jobId}`, {
method: 'POST',
});
},
};
// 🔥 Generate **Generic Response Type** from parsing your response structure intelligently!
type Data<T> = {
code: number;
data: T;
message: string;
}
interface IGetJobDetailParams { ... }
interface IGetJobDetailRespData { ... }
// ... other types omitted
市面上的我都试过没法生成这么满足个人诉求的,故开发了一个"从 Swagger JSON Schema 生成 TypeScript 客户端请求代码并带有严格的 TS 类型 和丰富的注释 "的 CLI 工具 swaggered。
✨ 特点
- 严格的 TS 类型:生成带有严格 TS 类型的客户端代码,字段描述通过 tsdoc 方式保留。
- 可过滤:可通过按 API 路径和 HTTP 方法进行过滤,只生成你需要的部分。
- 灵活使用:可以通过 CLI 或编程方式使用。
- 灵活的格式:
- 可仅生成类型或函数
- 按路径或标签自动分组。让代码组织结构更优。🔥
- 通过智能解析你的响应结构,生成通用响应类型!🔥
- 美化输出 💅:使用 shikijs 高亮输出内容。
- 经实战测试:支持所有 Swagger OpenAPI 版本以及从 v16 到 v22 的 Node.js。
- 单元测试 :使用 Node.js 内置单测和覆盖率统计
node --test --experimental-test-coverage
📀 使用
详见 www.npmjs.com/package/swa...
图来自 https://apifox.com/apiskills/introduction-to-swagger-ui-3/
🔬 实现原理
关键依赖库:
- 将 json schema 的 ref "展开"成 schema:@apidevtools/json-schema-ref-parser
- 不符合规范的 schema 做预处理:json-schema-traverse
- 将 schema 转成 TS 类型:json-schema-to-typescript
- 将 TS 类型组装成 Service 代码(自行实现)
- 控制台高亮输出:@shikijs/cli
👨💻 代码流程图
关键伪代码
graph TD
A[generateTSFromFile] --> B[readJSONFile]
B --> C[generateTSFromSchema]
C --> D{filterSchema api/reg/method}
D -->|Yes| E[generateTS]
D -->|No| F[End]
E --> E1[dereference: await RefParser.dereference jsonSchema]
E1 --> E2[removePropertyTitle json-schema-traverse]
E2 --> E3[compileSwaggerJSON]
E3 --> E4[for each of item]
E4 --> E5[preProcess]
E5 --> E5a[extract summary]
E5 --> E5b[extract parameters and normalize to schema]
E5 --> E5c[extract request data and normalize to schema]
E5 --> E5d[extract response body and normalize to schema]
%% E4 --> E6[compile]
E5a --> E6[compile]
E5b --> E6[compile]
E5c --> E6[compile]
E5d --> E6[compile]
E6 --> E6a[gen parameters interface]
E6 --> E6b[gen requestData interface]
E6 --> E6c[gen responseBody interface]
E6 --> E6d[gen genericResp interface]
E6 --> E6e[extract group label from tags]
E6a --> E6f[gen code]
E6b --> E6f[gen code]
E6c --> E6f[gen code]
E6d --> E6f[gen code]
E6e --> E6f[gen code]
E6f --> G[postProcess]
G --> G1[addPrefixToInterface]
G --> G2[removeExport]
G1 --> H[prettyPrint]
G2 --> H[prettyPrint]
H --> H1[printCodeByGroup]
H1 --> H1a[groupBy groupLabel]
H1 --> H1b[toServiceName]
H1 --> H1c[extract common api prefix]
H --> H2[printTypes]
H2 --> H3[print highlighted code using shiki]
H1a --> H3[print highlighted code using shiki]
H1b --> H3[print highlighted code using shiki]
H1c --> H3[print highlighted code using shiki]