这是一个将swagger接口文档自动生成TypeScript的api接口以及interface定义。
说明
- 该示例的url是apifox接口文档导出的url
命名:接口分组的目录需要是对应的tag
参数说明:在接口请求参数中,需要按照实际规定来写,不管是在
params
还是body
中,如果是必填项,请设置必填,这样会得到required:true
, 那么在interface
中就没有可选,否则会得到required:false
,那么在interface
中会有可选操作符。- 结果示例:
使用
- 直接node这个文件,即可生成api文件
代码
/**
* 说明:此文件是基于apifox创建,如果是swagger也应该是适用的,api获取是从项目设置的中的打开url获取,我这里是选择的是swagger2.0
* fun api函数名称
* funArr api定义的约束数组
* oldTag 上一组tag
* srcFolder 生产的api文件夹
* url 需要生产api的链接
*/
const fs = require("fs");
const path = require("path");
const http = require("http");
let fun = "";
let funArr = [];
let oldTag = "";
const srcFolder = "./src/server";
const typeFolder = "./types/server";
const url = "http://127.0.0.1:4523/export/openapi?projectId=603885&version=2.0";
const mkdirsSync = (dirname) => {
if (fs.existsSync(dirname)) {
return true;
} else {
if (mkdirsSync(path.dirname(dirname))) {
fs.mkdirSync(dirname);
return true;
}
}
};
const getPath = (pathUrl) => {
return path.resolve(__dirname, pathUrl);
};
const generateTemplate = (tagName) => {
let str = "";
funArr.forEach((v) => {
str += `${v}Type,`;
});
return `import request from '../../request'
import {${str} } from '../../types/server/${tagName}.types'
`;
};
const generateFunc = (url, summary, type = "post") => {
const arr = url.slice(1).split("/");
fun = arr[arr.length - 1].replace(/^\S/, (s) => s.toUpperCase());
funArr.push(fun);
return `
// ${summary || ""}
export const ${fun} = (${
type === "get" ? `params:${fun}Type` : `data:${fun}Type`
}):Promise<any> =>{
return request({
url:'${url}',
method: '${type}',
${type === "get" ? "params" : "data"},
})
}
`;
};
const generateTypeFunc = (typeName, str) => {
return `
export interface ${typeName}Type {
${str}
}
`;
};
const httpgetJson = (url) => {
return new Promise((resolve, reject) => {
http
.get(url, (res) => {
const { statusCode } = res;
const contentType = res.headers["content-type"];
let error;
if (statusCode !== 200) {
error = new Error("请求失败。" + `状态码: ${statusCode}`);
} else if (!contentType.includes("application/json")) {
error = new Error(
"无效的 content-type." +
`期望 application/json 但获取的是 ${contentType}`
);
}
if (error) {
console.error(error.message);
// 消耗响应数据以释放内存
res.resume();
return;
}
res.setEncoding("utf8");
let rawData = "";
res.on("data", (chunk) => {
rawData += chunk;
});
res.on("end", () => {
try {
const parsedData = JSON.parse(rawData);
resolve(parsedData);
} catch (e) {
reject(`错误: ${e.message}`);
}
});
})
.on("error", (e) => {
reject(`错误: ${e.message}`);
});
});
};
const main = async () => {
console.log("正在获取apifox文件...");
const { paths } = await httpgetJson(url);
console.log("获取成功,正在生成api文件...");
const obj = {};
const typeObj = {};
for (const name in paths) {
const path = paths[name];
let folder = "";
if (path.post) {
const tag = path.post.tags[0];
if (!tag) continue;
const urlArray = name.slice(1).split("/");
const typeName = urlArray[1] ? urlArray[1] : urlArray[0];
if (name.slice(1).split("/").length === 4) {
folder = urlArray[1];
} else {
if (name.slice(1).split("/")[0] !== tag) continue;
}
if (obj[path.post.tags[0]]) {
obj[path.post.tags[0]].push({
summary: path.post.summary,
tag,
name,
type: "post",
folder,
});
} else {
obj[path.post.tags[0]] = [
{ summary: path.post.summary, tag, name, type: "post", folder },
];
}
if (typeObj[path.post.tags[0]]) {
path.post.parameters.forEach((v) => {
typeObj[path.post.tags[0]].push({
...v,
tag: tag.replace(/^\S/, (s) => s.toUpperCase()),
folder: urlArray[0],
typeName: urlArray[1] ? urlArray[1] : urlArray[0],
});
});
} else {
typeObj[path.post.tags[0]] = [];
path.post.parameters.forEach((v) => {
typeObj[path.post.tags[0]].push({
...v,
tag: tag.replace(/^\S/, (s) => s.toUpperCase()),
folder: urlArray[0],
typeName: urlArray[1] ? urlArray[1] : urlArray[0],
});
});
}
} else if (path.get) {
const tag = path.get.tags[0];
if (!tag) continue;
const urlArray = name.slice(1).split("/");
const typeName = urlArray[1] ? urlArray[1] : urlArray[0];
if (name.slice(1).split("/").length === 4) {
folder = urlArray[1];
} else {
if (name.slice(1).split("/")[0] !== tag) continue;
}
if (obj[path.get.tags[0]]) {
obj[path.get.tags[0]].push({
summary: path.get.summary,
tag,
name,
type: "get",
folder,
});
} else {
obj[path.get.tags[0]] = [
{ summary: path.get.summary, tag, name, type: "get", folder },
];
}
if (typeObj[path.get.tags[0]]) {
path.get.parameters.forEach((v) => {
typeObj[path.get.tags[0]].push({
...v,
tag: tag.replace(/^\S/, (s) => s.toUpperCase()),
folder: urlArray[0],
typeName: urlArray[1] ? urlArray[1] : urlArray[0],
});
});
} else {
typeObj[path.get.tags[0]] = [];
path.get.parameters.forEach((v) => {
typeObj[path.get.tags[0]].push({
...v,
tag: tag.replace(/^\S/, (s) => s.toUpperCase()),
folder: urlArray[0],
typeName: urlArray[1] ? urlArray[1] : urlArray[0],
});
});
}
}
}
// 创建api文件
for (const tagName in obj) {
let jsString = "";
const requestTypes = [];
let folder = "";
for (const item of obj[tagName]) {
if (item.tag !== oldTag) {
funArr = [];
}
oldTag = item.tag;
const requestType = requestTypes.filter((o) => o === item.type);
if (requestType.length === 0) requestTypes.push(item.type);
jsString += generateFunc(item.name, item.summary, item.type);
folder = item.folder;
}
jsString = generateTemplate(tagName) + jsString;
mkdirsSync(getPath(`${srcFolder}/${folder}`));
fs.writeFileSync(getPath(`${srcFolder}/${folder}/${tagName}.ts`), jsString);
}
// 创建types文件
mkdirsSync(getPath(`${typeFolder}`));
for (const typeName in typeObj) {
let typeStr = "";
let typeNameStr = "";
let index = 0;
let beginIndex = 0;
let endIndex = 0;
const currentTypeItemNameList = typeObj[typeName].map((v) => {
return v.typeName;
});
for (const item of typeObj[typeName]) {
console.log("item", item.name);
typeStr += `${item.name}${item.required ? "" : "?"}:${item.type},`;
index++;
if (index === typeObj[typeName].length) {
const newStrList = typeStr.split(",");
currentTypeItemNameList.forEach((v, i) => {
if (v != currentTypeItemNameList[i + 1]) {
endIndex = i + 1;
typeNameStr += generateTypeFunc(
v.replace(/^\S/, (s) => s.toUpperCase()),
newStrList.slice(beginIndex, endIndex).join(",\n ")
);
beginIndex = i + 1;
}
});
}
}
fs.writeFileSync(
getPath(`${typeFolder}/${typeName}.types.ts`),
typeNameStr
);
}
console.log("api文件生成完毕!");
};
main();
注:思路参考网络,加上自己思考以及功能需求,最终形成这样的版本代码!