openapi-ts-request
Version:
Swagger2/OpenAPI3/Apifox to TypeScript/JavaScript, request client(support any client), request mock service, enum and enum translation, react-query/vue-query, type field label, JSON Schemas
578 lines (577 loc) • 24.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveArray = resolveArray;
exports.resolveProperties = resolveProperties;
exports.resolveEnumObject = resolveEnumObject;
exports.resolveAllOfObject = resolveAllOfObject;
exports.getProps = getProps;
exports.resolveParameterRef = resolveParameterRef;
exports.resolveRefObject = resolveRefObject;
exports.getResponsesType = getResponsesType;
exports.parseResponseEntries = parseResponseEntries;
exports.getResponseTypeFromContent = getResponseTypeFromContent;
exports.getParamsTP = getParamsTP;
exports.analyzeTypeReferences = analyzeTypeReferences;
exports.getModuleImports = getModuleImports;
exports.moveCommonTypeDependenciesToCommon = moveCommonTypeDependenciesToCommon;
exports.resolveFileTP = resolveFileTP;
const lodash_1 = require("lodash");
const config_1 = require("../config");
const config_2 = require("./config");
const util_1 = require("./util");
/**
* 解析数组类型
*/
function resolveArray(params) {
var _a;
const { schemaObject, namespace, getType } = params;
if ((0, util_1.isReferenceObject)(schemaObject.items)) {
const refName = (0, util_1.getRefName)(schemaObject.items);
return {
type: `${refName}[]`,
};
}
else if ((_a = schemaObject.items) === null || _a === void 0 ? void 0 : _a.enum) {
return {
type: getType(schemaObject, namespace),
};
}
return { type: 'unknown[]' };
}
/**
* 解析属性
*/
function resolveProperties(params) {
const { schemaObject, getProps } = params;
return {
props: [getProps(schemaObject)],
};
}
/**
* 解析枚举对象
*/
function resolveEnumObject(params) {
var _a;
const { schemaObject, config } = params;
const enumArray = schemaObject.enum;
let enumStr = '';
let enumLabelTypeStr = '';
// 获取实际的类型(处理 OpenAPI 3.1 的 type 数组情况)
const getActualType = (type) => {
if (Array.isArray(type)) {
// 如果是数组,返回第一个非 null 类型
return type.find((t) => t !== 'null') || 'string';
}
return type;
};
const actualType = getActualType(schemaObject.type);
if (config_2.numberEnum.includes(actualType) || (0, util_1.isAllNumber)(enumArray)) {
if (config.isSupportParseEnumDesc && schemaObject.description) {
const enumMap = (0, util_1.parseDescriptionEnum)(schemaObject.description);
enumStr = `{${(0, lodash_1.map)(enumArray, (value) => {
const enumLabel = enumMap.get(Number(value));
return `${enumLabel}=${Number(value)}`;
}).join(',')}}`;
}
else {
enumStr = `{${(0, lodash_1.map)(enumArray, (value) => `"NUMBER_${value}"=${Number(value)}`).join(',')}}`;
}
}
else if ((0, util_1.isAllNumeric)(enumArray)) {
enumStr = `{${(0, lodash_1.map)(enumArray, (value) => `"STRING_NUMBER_${value}"="${value}"`).join(',')}}`;
}
else {
enumStr = `{${(0, lodash_1.map)(enumArray, (value) => `"${value}"="${value}"`).join(',')}}`;
}
// 翻译枚举
if (schemaObject['x-enum-varnames'] && schemaObject['x-enum-comments']) {
enumLabelTypeStr = `{${(0, lodash_1.map)(enumArray, (value, index) => {
const enumKey = schemaObject['x-enum-varnames'][index];
return `${value}:"${schemaObject['x-enum-comments'][enumKey]}"`;
}).join(',')}}`;
}
else if ((_a = schemaObject === null || schemaObject === void 0 ? void 0 : schemaObject['x-apifox']) === null || _a === void 0 ? void 0 : _a['enumDescriptions']) {
enumLabelTypeStr = `{${(0, lodash_1.map)(enumArray, (value) => {
const enumLabel = schemaObject['x-apifox']['enumDescriptions'][value];
return `${value}:"${enumLabel}"`;
}).join(',')}}`;
}
else if (schemaObject === null || schemaObject === void 0 ? void 0 : schemaObject['x-apifox-enum']) {
enumLabelTypeStr = `{${(0, lodash_1.map)(enumArray, (value) => {
var _a;
const enumLabel = (_a = (0, lodash_1.find)(schemaObject['x-apifox-enum'], (item) => item.value === value)) === null || _a === void 0 ? void 0 : _a.description;
return `${value}:"${enumLabel}"`;
}).join(',')}}`;
}
else {
if (config_2.numberEnum.includes(actualType) || (0, util_1.isAllNumber)(enumArray)) {
if ((config.isSupportParseEnumDesc || config.supportParseEnumDescByReg) &&
schemaObject.description) {
const enumMap = config.isSupportParseEnumDesc
? (0, util_1.parseDescriptionEnum)(schemaObject.description)
: (0, util_1.parseDescriptionEnumByReg)(schemaObject.description, config.supportParseEnumDescByReg);
enumLabelTypeStr = `{${(0, lodash_1.map)(enumArray, (value) => {
const enumLabel = enumMap.get(Number(value));
return `${Number(value)}:"${enumLabel}"`;
}).join(',')}}`;
}
else {
enumLabelTypeStr = `{${(0, lodash_1.map)(enumArray, (value) => `${Number(value) >= 0 ? Number(value) : `"${value}"`}:"NUMBER_${value}"`).join(',')}}`;
}
}
else if ((0, util_1.isAllNumeric)(enumArray)) {
enumLabelTypeStr = `{${(0, lodash_1.map)(enumArray, (value) => `"${value}":"STRING_NUMBER_${value}"`).join(',')}}`;
}
else {
enumLabelTypeStr = `{${(0, lodash_1.map)(enumArray, (value) => `"${value}":"${value}"`).join(',')}}`;
}
}
// 格式化描述文本,让描述支持换行
const formattedDescription = schemaObject.description && config_2.lineBreakReg.test(schemaObject.description)
? '\n * ' + schemaObject.description.split('\n').join('\n * ') + '\n'
: schemaObject.description;
return {
isEnum: true,
type: Array.isArray(enumArray) ? enumStr : 'string',
enumLabelType: enumLabelTypeStr,
description: formattedDescription,
};
}
/**
* 解析 allOf 对象
*/
function resolveAllOfObject(params) {
const { schemaObject, getType, getProps } = params;
const props = (0, lodash_1.map)(schemaObject.allOf, (item) => {
return (0, util_1.isReferenceObject)(item)
? [Object.assign(Object.assign({}, item), { type: getType(item) })]
: getProps(item);
});
if (schemaObject.properties) {
const extProps = getProps(schemaObject);
return { props: [...props, extProps] };
}
return { props };
}
/**
* 获取 TS 类型的属性列表
*/
function getProps(params) {
var _a;
const { schemaObject, getType, openAPIData } = params;
const requiredPropKeys = (_a = schemaObject === null || schemaObject === void 0 ? void 0 : schemaObject.required) !== null && _a !== void 0 ? _a : false;
const properties = schemaObject.properties;
return (0, lodash_1.keys)(properties).map((propKey) => {
var _a, _b;
const schema = ((properties === null || properties === void 0 ? void 0 : properties[propKey]) || config_2.DEFAULT_SCHEMA);
// 剔除属性键值中的特殊符号,因为函数入参变量存在特殊符号会导致解析文件失败
// eslint-disable-next-line no-useless-escape
propKey = propKey.replace(/[\[|\]]/g, '');
// 获取描述信息,如果是 $ref 引用,尝试获取引用对象的描述
let desc = [schema.title, schema.description]
.filter((item) => item)
.join(' ');
// 格式化描述文本,让描述支持换行
desc = config_2.lineBreakReg.test(desc)
? '\n * ' + desc.split('\n').join('\n * ') + '\n'
: desc;
// 如果是 $ref 引用,尝试获取引用对象的描述
if ((0, util_1.isReferenceObject)(schema) && openAPIData) {
const refName = (0, util_1.getLastRefName)(schema.$ref);
const refSchema = (_b = (_a = openAPIData.components) === null || _a === void 0 ? void 0 : _a.schemas) === null || _b === void 0 ? void 0 : _b[refName];
if (refSchema) {
let refDesc = [refSchema.title, refSchema.description]
.filter((item) => item)
.join(' ');
// 格式化描述文本,让描述支持换行
refDesc = config_2.lineBreakReg.test(refDesc)
? '\n * ' + refDesc.split('\n').join('\n * ') + '\n'
: refDesc;
if (refDesc) {
desc = desc + refDesc;
}
}
}
// 复用 schema 部分字段
return Object.assign(Object.assign({}, schema), { name: propKey, type: getType(schema), desc: desc,
// 如果没有 required 信息,默认全部是非必填
required: requiredPropKeys
? requiredPropKeys.some((key) => key === propKey)
: false });
});
}
/**
* 解析参数引用
*/
function resolveParameterRef(params) {
var _a, _b;
const { param, openAPIData } = params;
if (!(0, util_1.isReferenceObject)(param)) {
return param;
}
// 解析 $ref 引用,从 components.parameters 中获取实际定义
const refName = (0, util_1.getLastRefName)(param.$ref);
const parameter = ((_b = (_a = openAPIData.components) === null || _a === void 0 ? void 0 : _a.parameters) === null || _b === void 0 ? void 0 : _b[refName]) || null;
return parameter;
}
/**
* 解析引用对象
*/
function resolveRefObject(params) {
const { refObject, openAPIData, resolveRefObjectFunc } = params;
if (!(0, util_1.isReferenceObject)(refObject)) {
return refObject;
}
const refPaths = refObject.$ref.split('/');
if (refPaths[0] === '#') {
const schema = (0, util_1.resolveRefs)(openAPIData, refPaths.slice(1));
if (!schema) {
throw new Error(`[GenSDK] Data Error! Notfoud: ${refObject.$ref}`);
}
const resolvedSchema = resolveRefObjectFunc({
refObject: schema,
openAPIData,
resolveRefObjectFunc,
});
let resolvedType;
if ((0, util_1.isReferenceObject)(schema)) {
const refResolved = resolveRefObjectFunc({
refObject: schema,
openAPIData,
resolveRefObjectFunc,
});
resolvedType = refResolved === null || refResolved === void 0 ? void 0 : refResolved.type;
}
else {
const schemaObj = schema;
// 处理 OpenAPI 3.1 的 type 数组情况
if (Array.isArray(schemaObj.type)) {
// 如果是数组,使用第一个非 null 类型
resolvedType =
schemaObj.type.find((t) => t !== 'null') ||
'string';
}
else {
resolvedType = schemaObj.type;
}
}
const finalSchema = schema;
return Object.assign(Object.assign({}, (resolvedSchema || {})), { type: resolvedType || finalSchema.type });
}
return refObject;
}
/**
* 生成多状态码响应类型定义
*/
function getResponsesType(params) {
const { responses, functionName, interfaceTPConfigs, components, getResponseTypeFromContentFunc, resolveRefObjectFunc, } = params;
if (Object.keys(responses || {}).length === 0 ||
interfaceTPConfigs.find((item) => item.typeName === (0, lodash_1.upperFirst)(`${functionName}Responses`))) {
return null;
}
// 生成主响应类型名称
const mainResponseTypeName = (0, lodash_1.upperFirst)(`${functionName}Response`);
const responseEntries = parseResponseEntries({
responses,
components,
getResponseTypeFromContentFunc,
resolveRefObjectFunc,
});
const responseTypes = responseEntries.map(({ statusCode, type, description = '' }) => {
var _a;
// 检查是否已存在对应的主响应类型,如果存在则复用,避免重复定义
const existType = interfaceTPConfigs.find((item) => item.typeName === mainResponseTypeName);
const lastType = existType ? mainResponseTypeName : type;
// 格式化描述文本,让描述支持换行
const formattedDescription = config_2.lineBreakReg.test(description)
? (_a = description.split('\n')) === null || _a === void 0 ? void 0 : _a.join('\n * ')
: description;
// 生成带注释的类型定义
return formattedDescription
? ` /**\n * ${formattedDescription}\n */\n ${statusCode}: ${lastType};`
: ` ${statusCode}: ${lastType};`;
});
// 返回完整的对象类型定义
return `{\n${responseTypes.join('\n')}\n}`;
}
/**
* 解析响应条目
*/
function parseResponseEntries(params) {
const { responses, components, getResponseTypeFromContentFunc, resolveRefObjectFunc, } = params;
return (0, lodash_1.keys)(responses).map((statusCode) => {
const response = resolveRefObjectFunc(responses[statusCode]);
if (!response) {
return { statusCode, type: 'unknown', description: '' };
}
const responseType = getResponseTypeFromContentFunc({
response,
components,
});
const description = response.description || '';
return { statusCode, type: responseType, description };
});
}
/**
* 从响应内容中提取 TypeScript 类型
*/
function getResponseTypeFromContent(params) {
var _a;
const { response, components, config, getType } = params;
if (!response.content) {
return 'unknown';
}
const resContent = response.content;
const resContentMediaTypes = (0, lodash_1.keys)(resContent);
// 检测二进制流媒体类型
const binaryMediaTypes = (0, util_1.getBinaryMediaTypes)(config.binaryMediaTypes);
const binaryMediaType = resContentMediaTypes.find((mediaType) => (0, util_1.isBinaryMediaType)(mediaType, binaryMediaTypes));
const mediaType = resContentMediaTypes.includes('application/json')
? 'application/json'
: binaryMediaType || resContentMediaTypes[0];
if (!(0, lodash_1.isObject)(resContent) || !mediaType) {
return 'unknown';
}
// 如果是二进制媒体类型,直接返回二进制类型
if ((0, util_1.isBinaryMediaType)(mediaType, binaryMediaTypes)) {
return (0, util_1.getBinaryResponseType)();
}
let schema = (resContent[mediaType].schema || config_2.DEFAULT_SCHEMA);
if ((0, util_1.isReferenceObject)(schema)) {
const refName = (0, util_1.getLastRefName)(schema.$ref);
const childrenSchema = components.schemas[refName];
// 如果配置了 dataFields,尝试从指定字段提取类型
if ((0, util_1.isNonArraySchemaObject)(childrenSchema) && config.dataFields) {
schema = (((_a = config.dataFields
.map((field) => childrenSchema.properties[field])
.filter(Boolean)) === null || _a === void 0 ? void 0 : _a[0]) ||
resContent[mediaType].schema ||
config_2.DEFAULT_SCHEMA);
}
return getType(schema);
}
else if ((0, util_1.isSchemaObject)(schema)) {
// 设置属性的 required 状态
(0, lodash_1.keys)(schema.properties).map((fieldName) => {
var _a, _b;
schema.properties[fieldName]['required'] =
(_b = (_a = schema.required) === null || _a === void 0 ? void 0 : _a.includes(fieldName)) !== null && _b !== void 0 ? _b : false;
});
return getType(schema);
}
else {
return getType(schema);
}
}
/**
* 获取参数模板
*/
function getParamsTP(params) {
const { parameters = [], path = null, namespace, openAPIData, getType, resolveParameterRefFunc, } = params;
const templateParams = {};
if (parameters === null || parameters === void 0 ? void 0 : parameters.length) {
(0, lodash_1.forEach)(config_2.parametersIn, (source) => {
const paramsList = parameters
.map((p) => resolveParameterRefFunc({ param: p, openAPIData }))
.filter((p) => p !== null && p.in === source)
.map((p) => {
var _a, _b, _c;
const isDirectObject = (((_a = p.schema) === null || _a === void 0 ? void 0 : _a.type) === 'object' ||
p.type) === 'object';
const refName = (0, util_1.getLastRefName)(((_b = p.schema) === null || _b === void 0 ? void 0 : _b.$ref) ||
p.$ref);
const schemas = ((_c = openAPIData.components) === null || _c === void 0 ? void 0 : _c.schemas) || {};
const deRefObjEntry = Object.entries(schemas).find(([k]) => k === refName);
const deRefSchema = deRefObjEntry === null || deRefObjEntry === void 0 ? void 0 : deRefObjEntry[1];
const isRefObject = deRefSchema !== undefined &&
(deRefSchema === null || deRefSchema === void 0 ? void 0 : deRefSchema.type) === 'object' &&
Object.keys((deRefSchema === null || deRefSchema === void 0 ? void 0 : deRefSchema.properties) || {}).length > 0;
return Object.assign(Object.assign({}, p), { isObject: isDirectObject || isRefObject, type: getType(p.schema || config_2.DEFAULT_SCHEMA, namespace) });
});
if (paramsList.length) {
templateParams[source] = paramsList;
}
});
}
if (path && path.length > 0) {
const regex = /\{(\w+)\}/g;
templateParams.path = templateParams.path || [];
let match = null;
while ((match = regex.exec(path))) {
if (!templateParams.path.some((p) => p.name === match[1])) {
templateParams.path.push(Object.assign(Object.assign({}, config_2.DEFAULT_PATH_PARAM), { name: match[1] }));
}
}
// 如果 path 没有内容,则将删除 path 参数,避免影响后续的 hasParams 判断
if (!templateParams.path.length)
delete templateParams.path;
}
return templateParams;
}
/**
* 分析类型定义中使用的类型名称
*/
function analyzeTypeReferences(types) {
const references = new Set();
types.forEach((typeItem) => {
// 分析 type 字段
if (typeof typeItem.type === 'string') {
// 匹配类型引用,例如: Category, Tag[], _CInputDto, Category | null
// 支持以下划线开头的类型名
const typeMatches = typeItem.type.match(/\b_*[A-Z][a-zA-Z0-9_]*\b/g);
if (typeMatches) {
typeMatches.forEach((match) => references.add(match));
}
}
// 分析 props
if (typeItem.props && typeItem.props.length > 0) {
typeItem.props.forEach((propGroup) => {
propGroup.forEach((prop) => {
if (prop.type) {
// 匹配类型引用,支持以下划线开头的类型名
const propTypeMatches = prop.type.match(/\b_*[A-Z][a-zA-Z0-9_]*\b/g);
if (propTypeMatches) {
propTypeMatches.forEach((match) => references.add(match));
}
}
});
});
}
});
return references;
}
/**
* 获取模块需要导入的类型
*/
function getModuleImports(params) {
const { moduleTypes, commonTypes, enumTypes } = params;
const references = analyzeTypeReferences(moduleTypes);
// 获取公共类型名称集合
const commonTypeNames = new Set(commonTypes.map((t) => t.typeName));
// 获取枚举类型名称集合(包括 IEnumName)
const enumTypeNames = new Set();
enumTypes.forEach((t) => {
enumTypeNames.add(t.typeName);
if (t.isEnum) {
enumTypeNames.add(`I${t.typeName}`);
}
});
// 筛选出实际需要导入的类型
const commonImports = Array.from(references).filter((ref) => commonTypeNames.has(ref));
const enumImports = Array.from(references).filter((ref) => enumTypeNames.has(ref));
return { commonImports, enumImports };
}
/**
* 将公共类型依赖的类型从模块类型移到公共类型
* 同时检测跨模块引用,将被多个模块使用的类型移到公共类型
* @param moduleTypes 模块类型
* @param commonTypes 公共类型
*/
function moveCommonTypeDependenciesToCommon(params) {
const { moduleTypes, commonTypes } = params;
let moved = true;
while (moved) {
moved = false;
// 1. 处理公共类型的依赖
const commonTypeRefs = analyzeTypeReferences(commonTypes);
moduleTypes.forEach((types) => {
const toMove = [];
types.forEach((typeItem) => {
if (commonTypeRefs.has(typeItem.typeName)) {
toMove.push(typeItem);
moved = true;
}
});
toMove.forEach((typeItem) => {
const index = types.indexOf(typeItem);
if (index > -1) {
types.splice(index, 1);
commonTypes.push(typeItem);
}
});
});
// 2. 检测跨模块引用:如果一个类型被其他模块引用,移到 common
// 首先建立类型名称到定义模块的映射
const typeDefModule = new Map(); // typeName -> moduleName (where it's defined)
moduleTypes.forEach((types, moduleName) => {
types.forEach((typeItem) => {
typeDefModule.set(typeItem.typeName, moduleName);
});
});
// 收集每个模块引用了哪些定义在其他模块的类型
const crossModuleRefs = new Map(); // typeName -> Set<moduleName that references it>
moduleTypes.forEach((types, moduleName) => {
const referencedTypes = analyzeTypeReferences(types);
referencedTypes.forEach((typeName) => {
const defModule = typeDefModule.get(typeName);
// 如果这个类型定义在其他模块,记录跨模块引用
if (defModule && defModule !== moduleName) {
if (!crossModuleRefs.has(typeName)) {
crossModuleRefs.set(typeName, new Set());
}
crossModuleRefs.get(typeName).add(moduleName);
}
});
});
// 找出被其他模块引用的类型(包括被一个或多个其他模块引用)
const typesToMove = new Set();
crossModuleRefs.forEach((referencingModules, typeName) => {
if (referencingModules.size > 0) {
typesToMove.add(typeName);
}
});
// 将这些类型移到 common
if (typesToMove.size > 0) {
moduleTypes.forEach((types) => {
const toMove = [];
types.forEach((typeItem) => {
if (typesToMove.has(typeItem.typeName)) {
toMove.push(typeItem);
moved = true;
}
});
toMove.forEach((typeItem) => {
const index = types.indexOf(typeItem);
if (index > -1) {
types.splice(index, 1);
commonTypes.push(typeItem);
}
});
});
}
}
}
/**
* 解析文件模板
*/
function resolveFileTP(params) {
var _a;
const { obj, resolveObjectFunc } = params;
let ret = [];
const resolved = resolveObjectFunc(obj);
const props = (((_a = resolved.props) === null || _a === void 0 ? void 0 : _a.length) > 0 &&
resolved.props[0].filter((p) => p.format === 'binary' ||
p.format === 'base64' ||
(0, util_1.isBinaryArraySchemaObject)(p))) ||
[];
if (props.length > 0) {
ret = props.map((p) => {
// 这里 p.type 是自定义type, 注意别混淆
return {
title: p.name,
multiple: p.type === `${config_1.SchemaObjectType.array}` ||
p.type === `${config_1.SchemaObjectType.stringArray}`,
};
});
}
if (resolved.type) {
ret = [
...ret,
...resolveFileTP({
obj: resolved.type,
resolveObjectFunc,
}),
];
}
return ret;
}