UNPKG

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
"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;