UNPKG

@frostup/swr-request-generator

Version:

A tool for generating TypeScript code and interface from swagger by using SWR and axios as client.

673 lines (551 loc) 24 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var tslib = require('tslib'); var lodash = require('lodash'); var prettier = require('prettier'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var prettier__default = /*#__PURE__*/_interopDefaultLegacy(prettier); var isArray = function (data) { return Object.prototype.toString.call(data) === "[object Array]"; }; var isObject = function (data) { return Object.prototype.toString.call(data) === "[object Object]"; }; var isNumber = function (n) { return !isNaN( /*#__PURE__*/parseFloat(n)) && isFinite(n); }; var isSchema = function (schema) { return !lodash.has(schema, "$ref"); }; var isRequestBody = function (requestBody) { return !lodash.has(requestBody, "$ref"); }; var ERROR_MESSAGES = { INVALID_JSON_FILE_ERROR: "Your json file is invalid, please check it!", FETCH_CLIENT_FAILED_ERROR: "Fetch client failed! Please check your network or ts-codegen.config.ts file." }; var HTTP_METHODS = ["get", "post", "put", "delete", "patch", "options", "head"]; var SLASH = "/"; var ENUM_SUFFIX = "#EnumTypeSuffix"; var toCapitalCase = function (str) { if (!str) { return ""; } var camelStr = /*#__PURE__*/lodash.camelCase(str); return "" + camelStr.charAt(0).toUpperCase() + camelStr.slice(1); }; var addPrefix = function (prefix) { return function (str) { if (str === void 0) { str = ""; } return "" + prefix + str; }; }; var addPrefixForInterface = /*#__PURE__*/addPrefix("I"); var arrayToObject = function (arr) { if (arr === void 0) { arr = []; } var obj = {}; lodash.forEach(arr, function (item) { obj[item] = item; }); return obj; }; var prettifyCode = function (code) { return prettier__default['default'].format(code, { printWidth: 120, trailingComma: "all", arrowParens: "always", parser: "typescript" }); }; var toTypes = function (definitions) { if (lodash.isEmpty(definitions)) { return; } var fieldDefinitionList = /*#__PURE__*/lodash.map(definitions, function (value, key) { if (isObject(value) && Object.keys(value).length === 1 && !lodash.isEmpty( /*#__PURE__*/lodash.pickBy(value, function (type) { return type === "FormData"; }))) { return convertKeyToCamelCaseAndAddQuote(key) + ": FormData"; } return isObject(value) ? convertKeyToCamelCaseAndAddQuote(key) + ": " + JSON.stringify(value).replace(/"/g, "") + ";" : convertKeyToCamelCaseAndAddQuote(key) + ": " + lodash.replace(value, ENUM_SUFFIX, "") + ";"; }); return definitions && "{\n " + fieldDefinitionList.sort().join("\n") + "\n }"; }; var convertKeyToCamelCaseAndAddQuote = function (key) { var isOptional = lodash.indexOf(key, "?") > -1; key = /*#__PURE__*/lodash.trimEnd(key, "?"); var isValidVariableName = /*#__PURE__*/ /^([a-zA-Z_$][a-zA-Z\d_$]*)$/gm.test(key); return "'" + (isValidVariableName ? key : lodash.camelCase( /*#__PURE__*/toCapitalCase(key))) + "'" + (isOptional ? "?" : ""); }; var convertJsonToString = function (str, errorMsg, output) { if (errorMsg === void 0) { errorMsg = ERROR_MESSAGES.INVALID_JSON_FILE_ERROR; } if (output === void 0) { output = console.error; } if (typeof str !== "string") { return; } try { return JSON.parse(str); } catch (e) { output(errorMsg); return; } }; var SchemaResolver = /** @class */ function () { function SchemaResolver(inputs) { var _this = this; this.inputs = inputs; this.schemaType = {}; this.getSchemaType = function () { return _this.schemaType; }; this.resolve = function (type) { var _a = _this.inputs, _b = _a.schema, schema = _b === void 0 ? {} : _b, results = _a.results, parentKey = _a.parentKey, key = _a.key; var advancedType = /*#__PURE__*/_this.resolveRef(schema.$ref, type || schema.type); if (schema.$ref) { _this.schemaType = advancedType; return _this; } if (schema.allOf || schema.oneOf || schema.anyOf) { var schemas = schema.oneOf || schema.allOf || schema.anyOf; _this.schemaType = /*#__PURE__*/_this.resolveRef((schemas !== null && schemas !== void 0 ? schemas : [])[0].$ref, type || schema.type); return _this; } if (schema.items) { _this.schemaType = /*#__PURE__*/_this.resolveItems(schema.items, schema.type, key, parentKey); return _this; } if (schema.enum) { var enumKey = /*#__PURE__*/_this.getEnumName(key, parentKey); // Implicit operation!: Assign enum array definition to results results[enumKey] = schema.enum; _this.schemaType = enumKey; return _this; } if (schema.type === "object") { if (schema.properties) { _this.schemaType = /*#__PURE__*/_this.resolveProperties(schema.properties, schema.required, parentKey); return _this; } if (schema.title) { _this.schemaType = schema.type; return _this; } _this.schemaType = "{[key:string]:any}"; return _this; } if (schema.type === "string" && schema.format === "binary") { _this.schemaType = "FormData"; return _this; } _this.schemaType = /*#__PURE__*/_this.getBasicType(schema.type, advancedType); return _this; }; this.resolveNullable = function () { var _a; if ((_a = _this.inputs.schema) === null || _a === void 0 ? void 0 : _a.nullable) { _this.schemaType = isObject(_this.schemaType) ? JSON.stringify(_this.schemaType) + " | null" : _this.schemaType + " | null"; } return _this; }; this.getEnumName = function (propertyName, parentKey) { if (parentKey === void 0) { parentKey = ""; } return "" + toCapitalCase(parentKey) + toCapitalCase(propertyName) + ENUM_SUFFIX; }; this.resolveRef = function ($ref, type) { if (!$ref) { return ""; } var refType = /*#__PURE__*/addPrefixForInterface( /*#__PURE__*/toCapitalCase( /*#__PURE__*/_this.pickTypeByRef($ref))); return type === "array" ? refType + "[]" : refType; }; this.getBasicType = function (basicType, advancedType) { if (basicType === void 0) { basicType = ""; } switch (basicType) { case "integer": return "number"; case "array": return _this.getTypeForArray(advancedType); case "": return advancedType || ""; default: return basicType; } }; this.getTypeForArray = function (advancedType) { return advancedType ? advancedType + "[]" : "Array<any>"; }; this.pickTypeByRef = function (str) { if (!str) { return; } var list = /*#__PURE__*/str.split("/"); return list[list.length - 1]; }; this.resolveItems = function (items, type, key, parentKey) { if (!items) { return {}; } var child = /*#__PURE__*/lodash.get(items, "items"); if (type === "array") { if (child) { return _this.resolveItems(child, items.type, key, parentKey) + "[]"; } if (!lodash.get(items, "$ref")) { return lodash.get(items, "type") + "[]"; } } if (isArray(items)) { return lodash.map(items, function (item) { return SchemaResolver.of({ results: _this.inputs.results, schema: item, key: key, parentKey: parentKey }).resolve().resolveNullable().getSchemaType(); }); } return SchemaResolver.of({ results: _this.inputs.results, schema: items, key: key, parentKey: parentKey }).resolve(type).resolveNullable().getSchemaType(); }; this.resolveProperties = function (properties, required, parentKey) { if (properties === void 0) { properties = {}; } if (required === void 0) { required = []; } return lodash.reduce(properties, function (o, v, k) { var _a; return tslib.__assign( /*#__PURE__*/tslib.__assign({}, o), (_a = {}, _a["" + k + (lodash.indexOf(required, k) > -1 ? "" : "?")] = /*#__PURE__*/SchemaResolver.of({ results: _this.inputs.results, schema: v, key: k, parentKey: parentKey }).resolve().resolveNullable().getSchemaType(), _a)); }, {}); }; } SchemaResolver.of = function (inputs) { return new SchemaResolver(inputs); }; return SchemaResolver; }(); var generateEnums = function (definitions, key) { if (lodash.isEmpty(definitions)) { return ""; } var enums = definitions[key]; var hasNumber = /*#__PURE__*/lodash.some(enums, function (enumValue) { return isNumber(enumValue); }); var enumName = /*#__PURE__*/lodash.replace(key, ENUM_SUFFIX, ""); return hasNumber ? "export type " + enumName + " = " + enums.map(function (item) { return JSON.stringify(item); }).join("|") : "export enum " + enumName + " " + JSON.stringify( /*#__PURE__*/arrayToObject(enums)).replace(/:/gi, "="); }; var generateFunctionName = function (method, operationId) { return method === "get" ? "use" + toCapitalCase( /*#__PURE__*/lodash.camelCase(operationId)) + "Request" : lodash.camelCase(operationId) + "Request"; }; var generateClientName = function (method, responseType) { return method === "get" ? "useRequest<" + (responseType || undefined) + ", IResponseError>" : "client.request<" + (responseType || undefined) + ", AxiosResponse<" + (responseType || undefined) + ">>"; }; // TODO: 1.refactor THeader logic to align with resolvedPath.xxxParams // TODO: 2.add response type for download file var generateRequestArguments = function (resolvedPath) { var argumentTypes = !lodash.isEmpty( /*#__PURE__*/tslib.__assign( /*#__PURE__*/tslib.__assign({}, resolvedPath.TReq), resolvedPath.THeader)) ? toTypes( /*#__PURE__*/tslib.__assign( /*#__PURE__*/tslib.__assign({}, resolvedPath.TReq), resolvedPath.THeader)) : undefined; var requestParamList = /*#__PURE__*/lodash.compact( /*#__PURE__*/tslib.__spreadArrays(resolvedPath.pathParams, resolvedPath.queryParams, resolvedPath.bodyParams, resolvedPath.formDataParams, /*#__PURE__*/Object.keys(resolvedPath.THeader), [resolvedPath.requestBody])).map(function (param) { return lodash.camelCase(param); }); var requestParams = requestParamList.length === 0 ? "" : "{" + requestParamList.join(",") + "}:" + argumentTypes; return resolvedPath.method === "get" ? (requestParams ? requestParams + ", " : "") + "SWRConfig?: ISWRConfig<" + (resolvedPath.TResp || undefined) + ", IResponseError>, axiosConfig?: AxiosRequestConfig" : (requestParams ? requestParams + ", " : "") + "axiosConfig?: AxiosRequestConfig"; }; var generateHeader = function (hasBody, contentTypes, operationId, header) { var result = lodash.reduce(header, function (result, _, key) { return result + ("\"" + key + "\": ") + lodash.camelCase(key) + ", "; }, ""); var contentType = hasBody ? "\"Content-Type\": \"" + lodash.get(contentTypes, operationId !== null && operationId !== void 0 ? operationId : "", "application/json") + "\"" : ""; return "headers: { " + result + contentType + "},"; }; var generateResponseType = function (axiosHeaderConfig) { return axiosHeaderConfig.includes('"Accept":') ? 'responseType: "blob",' : ""; }; var PathResolver = /** @class */ function () { function PathResolver(paths) { var _this = this; this.paths = paths; this.resolvedPaths = []; this.extraDefinitions = {}; this.contentType = {}; this.resolve = function () { _this.resolvedPaths = /*#__PURE__*/lodash.reduce(_this.paths, function (results, path, pathName) { return tslib.__spreadArrays(results, /*#__PURE__*/_this.resolvePath(path, pathName)); }, []); return _this; }; this.toRequest = function () { var data = /*#__PURE__*/lodash.sortBy(_this.resolvedPaths, function (o) { return o.operationId; }); var requests = /*#__PURE__*/data.map(function (resolvedPath) { var bodyData = /*#__PURE__*/lodash.get(resolvedPath.bodyParams, "[0]"); var headerType = /*#__PURE__*/lodash.get(resolvedPath, "THeader"); var cookie = /*#__PURE__*/lodash.get(resolvedPath.formDataParams, "[0]"); var requestBody = /*#__PURE__*/lodash.get(resolvedPath, "requestBody"); var body = /*#__PURE__*/lodash.camelCase( /*#__PURE__*/toCapitalCase(requestBody || bodyData || cookie)); var params = /*#__PURE__*/_this.toRequestParams( /*#__PURE__*/lodash.get(resolvedPath, "queryParams")); var axiosHeaderConfig = /*#__PURE__*/generateHeader(!lodash.isEmpty(body), _this.contentType, resolvedPath.operationId, headerType); return "export const " + generateFunctionName(resolvedPath.method, resolvedPath.operationId) + " = (" + generateRequestArguments(resolvedPath) + ") => \n " + generateClientName(resolvedPath.method, resolvedPath.TResp) + "({\n url: `" + resolvedPath.url + "`,\n method: \"" + resolvedPath.method + "\"," + axiosHeaderConfig + generateResponseType(axiosHeaderConfig) + "\n " + (body ? "data: " + body + "," : "") + (params ? "params: " + params + "," : "") + "...axiosConfig}" + (resolvedPath.method === "get" ? ", SWRConfig" : "") + ");"; }); var enums = /*#__PURE__*/Object.keys(_this.extraDefinitions).map(function (k) { return generateEnums(_this.extraDefinitions, k); }); return tslib.__spreadArrays(requests, enums); }; this.toRequestParams = function (data) { if (data === void 0) { data = []; } return !lodash.isEmpty(data) ? "{\n " + data.join(",\n") + "\n }" : undefined; }; this.getRequestURL = function (pathName) { return lodash.chain(pathName).split(SLASH).map(function (p) { return _this.isPathParam(p) ? "$" + p : p; }).join(SLASH).value(); }; this.isPathParam = function (str) { return str.startsWith("{"); }; // TODO: handle the case when v.parameters = Reference this.resolveOperation = function (operation) { var pickParamsByType = /*#__PURE__*/_this.pickParams(operation.parameters); var headerParams = /*#__PURE__*/pickParamsByType("header"); var params = { pathParams: pickParamsByType("path"), queryParams: pickParamsByType("query"), bodyParams: pickParamsByType("body"), formDataParams: pickParamsByType("cookie") }; return tslib.__assign( /*#__PURE__*/tslib.__assign({ operationId: operation.operationId, TResp: _this.getResponseTypes(operation.responses), TReq: _this.getRequestTypes(params, operation.operationId, /*#__PURE__*/lodash.get(operation, "requestBody")), THeader: _this.getPathParamsTypes(headerParams) }, /*#__PURE__*/_this.getParamsNames(params)), /*#__PURE__*/_this.getRequestBodyName( /*#__PURE__*/lodash.get(operation, "requestBody"), operation.operationId)); }; this.getParamsNames = function (params) { var getNames = function (list) { return lodash.isEmpty(list) ? [] : lodash.map(list, function (item) { return item.name; }); }; return { pathParams: getNames(params.pathParams), queryParams: getNames(params.queryParams), bodyParams: getNames(params.bodyParams), formDataParams: getNames(params.formDataParams) }; }; this.getRequestTypes = function (params, operationId, requestBody) { return tslib.__assign( /*#__PURE__*/tslib.__assign( /*#__PURE__*/tslib.__assign( /*#__PURE__*/tslib.__assign( /*#__PURE__*/tslib.__assign({}, /*#__PURE__*/_this.getPathParamsTypes(params.pathParams)), /*#__PURE__*/_this.getBodyAndQueryParamsTypes(params.bodyParams)), /*#__PURE__*/_this.getBodyAndQueryParamsTypes(params.queryParams)), /*#__PURE__*/_this.getFormDataParamsTypes(params.formDataParams)), /*#__PURE__*/_this.getRequestBodyTypes(operationId, requestBody)); }; this.getPathParamsTypes = function (pathParams) { return pathParams.reduce(function (results, param) { var _a; var schema = /*#__PURE__*/lodash.get(param, "schema"); if (isSchema(schema)) { return tslib.__assign( /*#__PURE__*/tslib.__assign({}, results), (_a = {}, _a["" + param.name + (param.required ? "" : "?")] = schema.type === "integer" ? "number" : schema.type, _a)); } return tslib.__assign({}, results); }, {}); }; this.getBodyAndQueryParamsTypes = function (bodyParams) { return bodyParams.reduce(function (results, param) { var _a; return tslib.__assign( /*#__PURE__*/tslib.__assign({}, results), (_a = {}, _a["" + param.name + (param.required ? "" : "?")] = /*#__PURE__*/SchemaResolver.of({ results: _this.extraDefinitions, schema: param.schema, key: param.name, parentKey: param.name }).resolve().getSchemaType(), _a)); }, {}); }; // TODO: handle other params here? this.getFormDataParamsTypes = function (formDataParams) { return formDataParams.reduce(function (results, param) { var _a, _b; if (param.schema) { return tslib.__assign( /*#__PURE__*/tslib.__assign({}, results), (_a = {}, _a["" + param.name + (param.required ? "" : "?")] = /*#__PURE__*/SchemaResolver.of({ results: _this.extraDefinitions, schema: param.schema, key: param.name, parentKey: param.name }).resolve().getSchemaType(), _a)); } return tslib.__assign( /*#__PURE__*/tslib.__assign({}, results), (_b = {}, _b["" + param.name + (param.required ? "" : "?")] = param.type === "file" ? "File" : param.type, _b)); }, {}); }; // TODO: handle Response or Reference this.getResponseTypes = function (responses) { return SchemaResolver.of({ results: _this.extraDefinitions, // TODO: handle other content type here schema: lodash.get(responses, "200.content.application/json.schema") || lodash.get(responses, "200.content.*/*.schema") || lodash.get(responses, "201.content.application/json.schema") || lodash.get(responses, "201.content.*/*.schema") }).resolve().getSchemaType(); }; // TODO: when parameters has enum this.pickParams = function (parameters) { return function (type) { return lodash.filter(parameters, function (param) { return param.in === type; }); }; }; } PathResolver.of = function (paths) { return new PathResolver(paths); }; PathResolver.prototype.resolvePath = function (path, pathName) { var _this = this; var operations = /*#__PURE__*/lodash.pick(path, HTTP_METHODS); return Object.keys(operations).map(function (httpMethod) { return tslib.__assign({ url: _this.getRequestURL(pathName), method: httpMethod }, /*#__PURE__*/_this.resolveOperation(operations[httpMethod])); }); }; PathResolver.prototype.getContentType = function (operationId, key) { var _a; // in openAPI spec, the key of content in requestBody field is content type lodash.assign(this.contentType, (_a = {}, _a[operationId] = key, _a)); }; PathResolver.prototype.getRequestBodyTypes = function (operationId, requestBody) { var _a; var _this = this; if (isRequestBody(requestBody)) { return lodash.reduce( /*#__PURE__*/lodash.get(requestBody, "content"), function (results, content, key) { var _a; _this.getContentType(operationId, key); return tslib.__assign( /*#__PURE__*/tslib.__assign({}, results), (_a = {}, _a[operationId + "Request"] = /*#__PURE__*/SchemaResolver.of({ results: _this.extraDefinitions, schema: content.schema, key: operationId + "Request", parentKey: operationId + "Request" }).resolve().getSchemaType(), _a)); }, {}); } return _a = {}, _a[operationId + "Request"] = /*#__PURE__*/SchemaResolver.of({ results: this.extraDefinitions, schema: requestBody, key: operationId + "Request", parentKey: operationId + "Request" }).resolve().getSchemaType(), _a; }; PathResolver.prototype.getRequestBodyName = function (requestBody, operationId) { if (requestBody) { return { requestBody: operationId + "Request" }; } }; return PathResolver; }(); // TODO: handle `in: fromData` // TODO: handle `in schema` // TODO: 确认不同 endpoint 是否都会生成 openAPI var DefinitionsResolver = /** @class */ function () { function DefinitionsResolver(components) { var _this = this; this.components = components; this.scanDefinitions = function () { var results = {}; var requestBodies = /*#__PURE__*/lodash.get(_this.components, "requestBodies"); var schemas = /*#__PURE__*/lodash.get(_this.components, "schemas"); lodash.forEach(requestBodies, function (requestBody, requestBodyName) { if (isRequestBody(requestBody)) { return results[requestBodyName] = /*#__PURE__*/SchemaResolver.of({ results: results, schema: lodash.get(requestBody, "content.application/json.schema"), key: requestBodyName, parentKey: requestBodyName }).resolve().getSchemaType(); } return results[requestBodyName] = /*#__PURE__*/SchemaResolver.of({ results: results, schema: requestBody, key: requestBodyName, parentKey: requestBodyName }).resolve().getSchemaType(); }); lodash.forEach(schemas, function (schema, schemaName) { return results[schemaName] = /*#__PURE__*/SchemaResolver.of({ results: results, schema: schema, key: schemaName, parentKey: schemaName }).resolve().getSchemaType(); }); _this.resolvedDefinitions = results; return _this; }; this.toDeclarations = function () { var arr = /*#__PURE__*/Object.keys(_this.resolvedDefinitions).sort().map(function (key) { if (lodash.includes(key, ENUM_SUFFIX)) { return generateEnums(_this.resolvedDefinitions, key); } if (_this.resolvedDefinitions[key] === "object" || lodash.isEmpty(_this.resolvedDefinitions[key])) { return "export interface " + addPrefixForInterface( /*#__PURE__*/toCapitalCase(key)) + " {[key:string]:any}"; } var val = /*#__PURE__*/toTypes(_this.resolvedDefinitions[key]); if (val) { return "export interface " + addPrefixForInterface( /*#__PURE__*/toCapitalCase(key)) + " " + val; } }); return lodash.compact(arr); }; } DefinitionsResolver.of = function (components) { return new DefinitionsResolver(components); }; return DefinitionsResolver; }(); exports.DefinitionsResolver = DefinitionsResolver; exports.PathResolver = PathResolver; exports.SchemaResolver = SchemaResolver; exports.addPrefixForInterface = addPrefixForInterface; exports.arrayToObject = arrayToObject; exports.convertJsonToString = convertJsonToString; exports.generateClientName = generateClientName; exports.generateEnums = generateEnums; exports.generateFunctionName = generateFunctionName; exports.generateHeader = generateHeader; exports.generateRequestArguments = generateRequestArguments; exports.generateResponseType = generateResponseType; exports.isArray = isArray; exports.isNumber = isNumber; exports.isObject = isObject; exports.isRequestBody = isRequestBody; exports.isSchema = isSchema; exports.prettifyCode = prettifyCode; exports.toCapitalCase = toCapitalCase; exports.toTypes = toTypes;