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
411 lines (410 loc) • 17.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseDescriptionEnum = void 0;
exports.stripDot = stripDot;
exports.resolveTypeName = resolveTypeName;
exports.getRefName = getRefName;
exports.getLastRefName = getLastRefName;
exports.getDefaultType = getDefaultType;
exports.getDefaultFileTag = getDefaultFileTag;
exports.handleDuplicateTypeNames = handleDuplicateTypeNames;
exports.getBasePrefix = getBasePrefix;
exports.genDefaultFunctionName = genDefaultFunctionName;
exports.getFinalFileName = getFinalFileName;
exports.replaceDot = replaceDot;
exports.resolveFunctionName = resolveFunctionName;
exports.markAllowedSchema = markAllowedSchema;
exports.isReferenceObject = isReferenceObject;
exports.isSchemaObject = isSchemaObject;
exports.isNonArraySchemaObject = isNonArraySchemaObject;
exports.isArraySchemaObject = isArraySchemaObject;
exports.isBinaryArraySchemaObject = isBinaryArraySchemaObject;
exports.resolveRefs = resolveRefs;
exports.isAllNumeric = isAllNumeric;
exports.isAllNumber = isAllNumber;
exports.capitalizeFirstLetter = capitalizeFirstLetter;
const tslib_1 = require("tslib");
const lodash_1 = require("lodash");
const reserved_words_1 = tslib_1.__importDefault(require("reserved-words"));
const tiny_pinyin_1 = tslib_1.__importDefault(require("tiny-pinyin"));
const config_1 = require("../config");
const log_1 = tslib_1.__importDefault(require("../log"));
const config_2 = require("./config");
function stripDot(str) {
return str.replace(/[-_ .](\w)/g, (_all, letter) => letter.toUpperCase());
}
// 兼容C#泛型的typeLastName取法
function getTypeLastName(typeName) {
var _a, _b, _c, _d, _e;
const tempTypeName = typeName || '';
const childrenTypeName = (_a = tempTypeName === null || tempTypeName === void 0 ? void 0 : tempTypeName.match(/\[\[.+\]\]/g)) === null || _a === void 0 ? void 0 : _a[0];
if (!childrenTypeName) {
const publicKeyToken = ((_c = (_b = tempTypeName.split('PublicKeyToken=')) === null || _b === void 0 ? void 0 : _b[1]) !== null && _c !== void 0 ? _c : '').replace('null', '');
const firstTempTypeName = (_e = (_d = tempTypeName.split(',')) === null || _d === void 0 ? void 0 : _d[0]) !== null && _e !== void 0 ? _e : tempTypeName;
let typeLastName = firstTempTypeName.split('/').pop().split('.').pop();
if (typeLastName.endsWith('[]')) {
typeLastName =
typeLastName.substring(0, typeLastName.length - 2) + 'Array';
}
// 特殊处理C#默认系统类型,不追加publicKeyToken
const isCsharpSystemType = firstTempTypeName.startsWith('System.');
if (!publicKeyToken || isCsharpSystemType) {
return typeLastName;
}
return `${typeLastName}_${publicKeyToken}`;
}
const currentTypeName = getTypeLastName(tempTypeName.replace(childrenTypeName, ''));
const childrenTypeNameLastName = getTypeLastName(childrenTypeName.substring(2, childrenTypeName.length - 2));
return `${currentTypeName}_${childrenTypeNameLastName}`;
}
// 类型声明过滤关键字
function resolveTypeName(typeName) {
if (reserved_words_1.default.check(typeName)) {
return `__openAPI__${typeName}`;
}
const typeLastName = getTypeLastName(typeName);
const name = typeLastName
.replace(/[-_ ](\w)/g, (_all, letter) => letter.toUpperCase())
.replace(/[^\w^\s^\u4e00-\u9fa5]/gi, '');
// 当model名称是number开头的时候,ts会报错。这种场景一般发生在后端定义的名称是中文
if (name === '_' || /^\d+$/.test(name)) {
(0, log_1.default)('⚠️ models不能以number开头,原因可能是Model定义名称为中文, 建议联系后台修改');
return `Pinyin_${name}`;
}
if (!/[\u3220-\uFA29]/.test(name) && !/^\d$/.test(name)) {
return (0, lodash_1.upperFirst)(name);
}
const noBlankName = name.replace(/ +/g, '');
return (0, lodash_1.upperFirst)(tiny_pinyin_1.default.convertToPinyin(noBlankName, '', true));
}
function getRefName(refObject) {
if (!isReferenceObject(refObject)) {
return refObject;
}
return resolveTypeName(getLastRefName(refObject.$ref));
}
function getLastRefName(refPath = '') {
const refPaths = refPath.split('/');
return refPaths.length > 0
? decodeURIComponent(refPaths[refPaths.length - 1])
: '';
}
function getDefaultType(schemaObject, namespace = '', schemas) {
var _a, _b;
if ((0, lodash_1.isUndefined)(schemaObject) || (0, lodash_1.isNull)(schemaObject)) {
return 'unknown';
}
if (!(0, lodash_1.isObject)(schemaObject)) {
return schemaObject;
}
if (isReferenceObject(schemaObject)) {
// return getRefName(schemaObject);
return [namespace, getRefName(schemaObject)].filter((s) => s).join('.');
}
let type = schemaObject === null || schemaObject === void 0 ? void 0 : schemaObject.type;
const dateEnum = ['Date', 'date', 'dateTime', 'date-time', 'datetime'];
const stringEnum = ['string', 'email', 'password', 'url', 'byte', 'binary'];
if (type === 'null') {
return 'null';
}
if (config_2.numberEnum.includes(schemaObject.format)) {
type = 'number';
}
if (schemaObject.enum) {
type = 'enum';
}
if (config_2.numberEnum.includes(type)) {
return 'number';
}
if (dateEnum.includes(type)) {
return 'Date';
}
if (stringEnum.includes(type)) {
return 'string';
}
if (type === 'boolean') {
return 'boolean';
}
if (type === 'array') {
let items = schemaObject.items;
if ('schema' in schemaObject) {
items = schemaObject.schema.items;
}
if (Array.isArray(items)) {
const arrayItemType = items
.map((subType) => getDefaultType((subType.schema || subType), namespace))
.toString();
return `[${arrayItemType}]`;
}
const arrayType = getDefaultType(items, namespace);
return arrayType.includes(' | ') ? `(${arrayType})[]` : `${arrayType}[]`;
}
if (type === 'enum') {
return Array.isArray(schemaObject.enum)
? Array.from(new Set(schemaObject.enum.map((v) => typeof v === 'string'
? `"${v.replace(/"/g, '"')}"`
: getDefaultType(v)))).join(' | ')
: 'string';
}
if (schemaObject.oneOf && schemaObject.oneOf.length) {
return schemaObject.oneOf
.map((item) => getDefaultType(item, namespace))
.join(' | ');
}
if ((_a = schemaObject.anyOf) === null || _a === void 0 ? void 0 : _a.length) {
return schemaObject.anyOf
.map((item) => getDefaultType(item, namespace))
.join(' | ');
}
if ((_b = schemaObject.allOf) === null || _b === void 0 ? void 0 : _b.length) {
const allofList = schemaObject.allOf.map((item) => {
var _a;
if (isReferenceObject(item)) {
// 不使用 getRefName 函数处理,无法通过 schemas[schemaKey] 获取到schema
const schemaKey = getLastRefName(item.$ref);
if ((_a = schemas === null || schemas === void 0 ? void 0 : schemas[schemaKey]) === null || _a === void 0 ? void 0 : _a.enum) {
// return `I${getDefaultType(item, namespace)}`;
return getDefaultType(item, namespace);
}
}
return getDefaultType(item, namespace);
});
return `(${allofList.join(' & ')})`;
}
if (schemaObject.type === 'object' || schemaObject.properties) {
if ((0, lodash_1.isObject)(schemaObject.additionalProperties)) {
const type = getDefaultType(schemaObject.additionalProperties, namespace, schemas);
return `Record<string, ${type}>`;
}
if (!(0, lodash_1.keys)(schemaObject.properties).length) {
return 'Record<string, unknown>';
}
return `{ ${(0, lodash_1.keys)(schemaObject.properties)
.map((key) => {
var _a;
let required = false;
const property = (((_a = schemaObject.properties) === null || _a === void 0 ? void 0 : _a[key]) || {});
if ((0, lodash_1.isBoolean)(schemaObject.required) && schemaObject.required) {
required = true;
}
if ((0, lodash_1.isArray)(schemaObject.required) &&
schemaObject.required.includes(key)) {
required = true;
}
if (property.required) {
required = true;
}
/**
* 将类型属性变为字符串,兼容错误格式如:
* 3d_tile(数字开头)等错误命名,
* 在后面进行格式化的时候会将正确的字符串转换为正常形式,
* 错误的继续保留字符串。
* */
return `
${property.description ? `/** ${property.description} */\n` : ''}'${key}'${required ? '' : '?'}: ${getDefaultType(property, namespace)}; `;
})
.join('')}}`;
}
return 'unknown';
}
function getDefaultFileTag(operationObject, apiPath) {
let lastTags = [];
if (operationObject['x-swagger-router-controller']) {
lastTags = [operationObject['x-swagger-router-controller']];
}
else if (!(0, lodash_1.isEmpty)(operationObject.tags)) {
lastTags = operationObject.tags;
}
else if (operationObject.operationId) {
lastTags = [operationObject.operationId];
}
else {
lastTags = [apiPath.replace('/', '').split('/')[1]];
}
return lastTags;
}
function findDuplicateTypeNames(arr) {
const counts = (0, lodash_1.countBy)(arr);
const duplicates = (0, lodash_1.filter)((0, lodash_1.keys)(counts), (key) => counts[key] > 1);
return duplicates;
}
function handleDuplicateTypeNames(interfaceTPConfigs) {
const duplicateTypeNames = findDuplicateTypeNames((0, lodash_1.map)(interfaceTPConfigs, (item) => item.typeName));
if (!(0, lodash_1.isEmpty)(duplicateTypeNames)) {
(0, lodash_1.forEach)(duplicateTypeNames, (typeName) => {
const selectInterfaceTPConfigs = (0, lodash_1.filter)(interfaceTPConfigs, (interfaceTP) => interfaceTP.typeName === typeName);
(0, lodash_1.forEach)(selectInterfaceTPConfigs, (interfaceTP, index) => {
if (index >= 1) {
interfaceTP.typeName = `${interfaceTP.typeName}${index + 1}`;
if (interfaceTP.displayLabelFuncName) {
interfaceTP.displayLabelFuncName = `${interfaceTP.displayLabelFuncName}${index + 1}`;
}
}
});
});
}
}
// 检测所有path重复区域(prefix)
function getBasePrefix(paths) {
const arr = [];
paths
.map((item) => item.split('/'))
.forEach((pathItem) => {
pathItem.forEach((item, key) => {
if (arr.length <= key) {
arr[key] = [];
}
arr[key].push(item);
});
});
const res = [];
arr
.map((item) => Array.from(new Set(item)))
.every((item) => {
const b = item.length === 1;
if (b) {
res.push(item);
}
return b;
});
return `${res.join('/')}/`;
}
// 将地址path路径转为大驼峰
function genDefaultFunctionName(path, pathBasePrefix) {
// 首字母转大写
function toUpperFirstLetter(text) {
return text.charAt(0).toUpperCase() + text.slice(1);
}
return path === null || path === void 0 ? void 0 : path.replace(pathBasePrefix, '').split('/').map((str) => {
/**
* 兼容错误命名如 /user/:id/:name
* 因为是typeName,所以直接进行转换
* */
let s = resolveTypeName(str);
if (s.includes('-')) {
s = s.replace(/(-\w)+/g, (_match, p1) => p1 === null || p1 === void 0 ? void 0 : p1.slice(1).toUpperCase());
}
if (s.match(/^{.+}$/gim)) {
return `By${toUpperFirstLetter(s.slice(1, s.length - 1))}`;
}
return toUpperFirstLetter(s);
}).join('');
}
function getFinalFileName(s) {
// 支持下划线、中划线和空格分隔符,注意分隔符枚举值的顺序不能改变,否则正则匹配会报错
return s.replace(/[-_ ](\w)/g, (_all, letter) => letter.toUpperCase());
}
function replaceDot(s) {
return s
.replace(/\./g, '_')
.replace(/[-_ ](\w)/g, (_all, letter) => letter.toUpperCase());
}
function resolveFunctionName(functionName, methodName) {
// 类型声明过滤关键字
if (reserved_words_1.default.check(functionName)) {
return `${functionName}Using${methodName.toUpperCase()}`;
}
return functionName;
}
// 标记引用的 $ref 对应的schema
function markAllowedSchema(schemaStr, openAPIData) {
const refs = (0, lodash_1.map)(schemaStr === null || schemaStr === void 0 ? void 0 : schemaStr.match(/"#\/components\/[^"]+"/g), (item) => item.slice(1, -1));
(0, lodash_1.forEach)(refs, (ref) => {
// const schema = schemas?.[getLastRefName(ref)] as ICustomSchemaObject;
const refPaths = ref.split('/');
const schema = resolveRefs(openAPIData, refPaths.slice(1));
if (schema && !schema.isAllowed) {
schema.isAllowed = true;
markAllowedSchema(JSON.stringify(schema), openAPIData);
}
});
}
function isReferenceObject(schema) {
return (schema === null || schema === void 0 ? void 0 : schema.$ref) !== undefined;
}
function isSchemaObject(schema) {
return (schema === null || schema === void 0 ? void 0 : schema.properties) !== undefined;
}
function isNonArraySchemaObject(schema) {
return ((schema === null || schema === void 0 ? void 0 : schema.type) === 'object' &&
(schema === null || schema === void 0 ? void 0 : schema.properties) !== undefined);
}
function isArraySchemaObject(schema) {
return (((schema === null || schema === void 0 ? void 0 : schema.type) === config_1.SchemaObjectType.array ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
(schema === null || schema === void 0 ? void 0 : schema.type) === config_1.SchemaObjectType.stringArray) &&
(schema === null || schema === void 0 ? void 0 : schema.items) !== undefined);
}
function isBinaryArraySchemaObject(schema) {
var _a, _b;
return (isArraySchemaObject(schema) &&
(((_a = schema.items) === null || _a === void 0 ? void 0 : _a.format) === 'binary' ||
((_b = schema.items) === null || _b === void 0 ? void 0 : _b.format) === 'base64'));
}
function resolveRefs(obj, fields) {
return fields.reduce((acc, field) => {
if (!acc)
return;
const s = acc[decodeURIComponent(field)];
if (!s)
return;
return s;
}, obj);
}
function isAllNumeric(arr) {
return (0, lodash_1.every)(arr, (item) => (0, lodash_1.isString)(item) && /^-?[0-9]+$/.test(item));
}
// 检查数组每一项是否都是数字
function isAllNumber(arr) {
return (0, lodash_1.every)(arr, (item) => (0, lodash_1.isNumber)(item));
}
function capitalizeFirstLetter(str) {
return str.replace(/^[a-z]/, (match) => match.toUpperCase());
}
// 解析 description 中的枚举翻译
const parseDescriptionEnum = (description) => {
const enumMap = new Map();
if (!description)
return enumMap;
// 首先处理可能的总体描述,例如 "系统用户角色:User=0,..."
let descToProcess = description;
const mainDescriptionMatch = description.match(/^([^:]+):(.*)/);
if (mainDescriptionMatch) {
// 如果有总体描述(如 "系统用户角色:"),只处理冒号后面的部分
descToProcess = mainDescriptionMatch[2];
}
// 匹配形如 "User(普通用户)=0" 或 "User=0" 的模式
const enumPattern = /([^():,=]+)(?:\(([^)]+)\))?=(\d+)/g;
let match;
while ((match = enumPattern.exec(descToProcess)) !== null) {
const name = match[1] ? match[1].trim() : '';
const valueStr = match[3] ? match[3].trim() : '';
if (valueStr && !isNaN(Number(valueStr))) {
// 统一使用英文key(如User)
enumMap.set(Number(valueStr), name);
}
}
// 如果没有匹配到任何枚举,尝试使用简单的分割方法作为后备
if (enumMap.size === 0) {
const pairs = descToProcess.split(',');
pairs.forEach((pair) => {
const parts = pair.split('=');
if (parts.length === 2) {
let label = parts[0].trim();
const value = parts[1].trim();
// 处理可能带有括号的情况
const bracketMatch = label.match(/([^(]+)\(([^)]+)\)/);
if (bracketMatch) {
// 只使用括号前的英文key
label = bracketMatch[1].trim();
}
if (label && value && !isNaN(Number(value))) {
enumMap.set(Number(value), label);
}
}
});
}
return enumMap;
};
exports.parseDescriptionEnum = parseDescriptionEnum;