UNPKG

@ywfe/cli

Version:

遥望前端开发命令行工具

425 lines (423 loc) 17.3 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.apiGen = void 0; const path_1 = require("path"); const fs_extra_1 = __importDefault(require("fs-extra")); const sync_request_1 = __importDefault(require("sync-request")); const chalk_1 = __importDefault(require("chalk")); const ejs_1 = __importDefault(require("ejs")); const R = __importStar(require("ramda")); const PATH = __importStar(require("path")); const constants_1 = require("../../util/constants"); const utils_1 = require("./utils"); const API_TEMPLATE_FILE = (0, path_1.resolve)(__dirname, './files/apiTemplate.js'); const cleanPathFiles = [ '.DS_Store', 'generated/.DS_Store', ]; const typeMap = { 'integer': 'number', 'Integer': 'number', 'int': 'number', 'BigDecimal': 'number', 'Long': 'number', 'long': 'number', 'number': 'number', 'array': 'any[]', 'Array': 'any[]', 'String': 'string', 'string': 'string', 'Date': 'string', 'date': 'string', 'text': 'string', 'boolean': 'boolean', 'Boolean': 'boolean', 'enum': 'enum', 'file': 'any', }; function apiGen(options) { return __awaiter(this, void 0, void 0, function* () { const { source_path } = options; const list = yield getApiInfoList(Object.assign(Object.assign({}, options), { yapi_apis: options.yapi_apis ? JSON.parse(decodeURIComponent((options.yapi_apis))) : [] })); const path = PATH.basename(process.cwd() + source_path); const filePath = (0, path_1.join)(path); const optionsList = []; list.map((item) => { if (item) { const option = transform(options, item, path); generateApiFile(option, filePath); optionsList.push(option); } }); cleanPath(filePath); generateApiExportFile(optionsList, filePath); updateConfigFile(options); }); } exports.apiGen = apiGen; const fetchYAPiData = (url, yapiCookie) => { const res = (0, sync_request_1.default)("GET", url, { headers: { 'Cookie': yapiCookie } }); if (res) { try { const result = JSON.parse(res.getBody('utf8')); return result; } catch (error) { (0, utils_1.logger)(chalk_1.default.red('»» api.factory.ts schematics fetchYAPiData Error=>\r\n'), error); return null; } } else { return null; } }; const fetchApiInfoFromId = ({ id, projectId, isFunction, isMethod, isV2, isMock, yapi_domain, yapiCookie }) => __awaiter(void 0, void 0, void 0, function* () { const result = fetchYAPiData(`${yapi_domain}api/interface/get?id=${id}`, JSON.parse(decodeURIComponent(yapiCookie))); if (result) { if (result.errcode > 0 || !result.data) { (0, utils_1.logger)(chalk_1.default.red('✖️ api.factory 从YAPI获取API定义发生错误:'), `API: ${id} => ${result.errmsg}`); } else { return { api: result.data, projectId, projectInfo: result.data.projectInfo, isFunction, isMethod, isMock, isV2, }; } } }); const getApiInfoList = (options) => __awaiter(void 0, void 0, void 0, function* () { const { yapiCookie, yapi_apis } = options; let apiResult = []; if (yapi_apis) { apiResult = yield Promise.all(yapi_apis === null || yapi_apis === void 0 ? void 0 : yapi_apis.map((item) => __awaiter(void 0, void 0, void 0, function* () { return yield fetchApiInfoFromId(Object.assign(Object.assign({}, item), { yapi_domain: constants_1.YAPI_DOMAIN, yapiCookie })); }))); } return apiResult; }); const transform = (source, data, path) => { const target = R.clone(source); const { api, projectId, isFunction, isMethod, projectInfo, isV2, isMock } = data; const finalPath = projectInfo.basepath !== '' ? `${projectInfo.basepath}/${api.path}` : api.path; api.path = finalPath.replace(new RegExp("\/\/", "gm"), "/"); const isOld = target.old; let name = ''; if (isOld) { name = (0, utils_1.transformToName)(api.path, isOld); } else { name = (0, utils_1.transformToName)(api._id, isOld); } let inputs = []; let requestRes = { responses: [], interfaces: undefined }; if (api.req_query && api.req_query.length) { inputs = (0, utils_1.reduceQuery)(api.req_query, typeMap); } else if (api.req_params && api.req_params.length) { inputs = (0, utils_1.reduceRequestParams)(api.req_params); } else if (api.req_body_form && api.req_body_form.length || api.req_body_other) { inputs = (0, utils_1.reduceRequestBodyForm)(api.req_body_form, typeMap); if (api.req_body_other) { requestRes = reduceResponse(api.req_body_other, `${name}Req`, api.method, isMethod); inputs = [ ...inputs, ...(0, utils_1.reduceRequestBodyOther)(requestRes.responses) ]; } else { } } const responsesRes = api.res_body ? reduceResponse(api.res_body, `${name}Res`, api.method, isMethod) : {}; if (requestRes.interfaces) { responsesRes.interfaces = Object.assign(Object.assign({}, requestRes.interfaces), responsesRes.interfaces); } const interfaces = responsesRes.interfaces ? R.pipe(R.keys, R.map(item => R.mergeAll([{ data: responsesRes.interfaces[item] }, { name: item }])), R.flatten)(responsesRes.interfaces) : []; target.name = name; target.title = api.title; target.path = (0, utils_1.transformToPath)(api.path); target.id = api._id; target.method = api.method; target.projectId = projectId.indexOf('_V2') > -1 ? projectId.replace('_V2', '') : projectId; target.project_id = api.project_id; target.inputs = inputs; target.requestType = requestRes.responsesType || '{}'; target.responses = responsesRes.responses; target.responsesType = responsesRes.responsesType || '{}'; target.interfaces = interfaces; target.isRestful = api.path.indexOf('{') >= 0 && api.path.indexOf('}') >= 0; target.filePath = (0, path_1.join)(path); target.isFunction = isFunction; target.isMethod = isMethod; target.isV2 = isV2; target.isMock = isMock; target.file_name = isOld ? api.method + name : name; return target; }; const generateApiFile = (apiFileParams, srcPath) => { apiFileParams.classify = utils_1.classify; apiFileParams.dasherize = utils_1.dasherize; const templateFile = API_TEMPLATE_FILE; const templateContent = fs_extra_1.default.readFileSync(templateFile, { encoding: 'utf-8' }).toString(); const content = ejs_1.default.render(templateContent, apiFileParams); const outApiFile = `${constants_1.API_GEN_DIR}/${apiFileParams.file_name}.ts`; let action = '更新'; if (!fs_extra_1.default.existsSync(outApiFile)) { action = '新增'; fs_extra_1.default.createFileSync(outApiFile); } fs_extra_1.default.writeFileSync(outApiFile, content); if (action === '新增') { (0, utils_1.logger)(chalk_1.default.green(`➕ ${action} ${constants_1.GENERATE_DIR_NAME}/${apiFileParams.file_name}.ts`)); } else { (0, utils_1.logger)(chalk_1.default.yellow(`✔️ ${action} ${constants_1.GENERATE_DIR_NAME}/${apiFileParams.file_name}.ts`)); } }; const reduceResponse = (str, name, method, isMethod) => { var _a; if (str.indexOf('{') < 0) { return { responses: [], interfaces: {} }; } const res = JSON.parse(str); const interfaceName = `I${isMethod ? method : ''}${name}`; if (res.properties && res.properties.data && res.properties.data.type === 'object') { const { properties, required = {} } = ((_a = res.properties) === null || _a === void 0 ? void 0 : _a.data) || {}; return transformResponse(properties, required, interfaceName); } else if (res.properties && res.properties.data && res.properties.data.type !== 'object') { const { properties, required = {} } = res || {}; const result = transformResponse({ data: properties === null || properties === void 0 ? void 0 : properties.data }, required, interfaceName); return { responses: [], interfaces: result.interfaces, responsesType: result.responses[0].type, }; } else if (res.properties && !res.properties.data) { const { properties, required = {} } = res; return transformResponse(properties, required, interfaceName); } else { const result = transformResponse({ data: res }, [], interfaceName); return { responses: [], interfaces: result.interfaces, responsesType: result.responses[0].type, }; } }; const transformResponse = (data, required, name, interfaces = {}) => { const responses = []; const keys = R.keys(data); keys.map((key) => { let interfaceName = `${name}${(0, utils_1.upperCaseParam)(key)}`; let type; if (data[key].type === 'array') { interfaceName = `${interfaceName}Item`; type = (0, utils_1.reduceArray)(data[key], interfaceName, typeMap); } else if (data[key].type === 'object') { interfaceName = `${interfaceName}Obj`; type = interfaceName; } else { type = (0, utils_1.reduceType)(data[key], typeMap); } responses.push({ name: key, type, required: (0, utils_1.checkRequired)(required, key), description: data[key].description, }); if (data[key].items || data[key].type === 'object') { const { items, type } = data[key]; const { properties, required } = type === 'object' ? data[key] : (0, utils_1.reduceProperties)(items); if (!interfaces[interfaceName] && properties) { interfaces[interfaceName] = transformResponse(properties, required, interfaceName, interfaces).responses; } } }); return { responses, interfaces }; }; const generateApiExportFile = (optionsList, filePath) => { try { const apiEntryFile = `${filePath}/generated/api.ts`; let newContent = ''; const hasFile = fs_extra_1.default.existsSync(apiEntryFile); if (hasFile) { newContent = fs_extra_1.default.readFileSync(apiEntryFile, { encoding: 'utf-8' }).toString(); } optionsList.map(option => { if (newContent.indexOf(option.file_name) < 0 || newContent.indexOf(option.id) < 0) { newContent += ` // id=${option.id} title=${option.title} path=${option.path} method=${option.method} export * from './${option.file_name}';`; } }); if (hasFile) { fs_extra_1.default.writeFileSync(apiEntryFile, `${newContent}`); } else { fs_extra_1.default.createFileSync(apiEntryFile); fs_extra_1.default.writeFileSync(apiEntryFile, `${newContent}`); } } catch (error) { (0, utils_1.logger)(chalk_1.default.red('generateApiExportFile Error:'), error); } }; const createUseApis = (optionsList, filePath, isOld) => { const importMap = { 'POST': 'Post', 'GET': 'Get' }; const methodMap = { 'POST': isOld ? 'post' : 'Post', 'GET': isOld ? 'get' : 'Get' }; return (tree) => { let imports = ''; let methods = ''; let results = ''; const apiPath = filePath + '/generated/useApis.ts'; let hasFile; let initContent = ''; if (tree.exists(apiPath)) { const data = tree.read(apiPath).toString(); if (data.indexOf("import {") < 0) { tree.delete(apiPath); hasFile = false; } else { initContent = data; hasFile = true; } } optionsList.map(option => { if (!option.isFunction) { const useName = `use${importMap[option.method]}${option.name}`; if (initContent.indexOf(useName) < 0) { let methodName = ''; if (isOld) { methodName = `${methodMap[option.method]}${option.name}Data`; } else { methodName = `fetch${option.name}Data`; } imports += useName + ', \n'; methods += `const { ${methodName} } = ${useName}();\n`; results += `id${option.id}: ${methodName},\n`; } } }); if (hasFile) { const endImport = initContent.indexOf("} from './api';"); initContent = (0, utils_1.insertStr)(initContent, endImport, imports); const endHook = initContent.indexOf("return {"); initContent = (0, utils_1.insertStr)(initContent, endHook, methods); const endReturn = initContent.indexOf("}}"); initContent = (0, utils_1.insertStr)(initContent, endReturn, results); tree.overwrite(apiPath, initContent); } else { const content = ` import { ${imports} } from './api'; export const useApis = () => { ${methods} return { ${results} }} `; tree.create(apiPath, content); } return tree; }; }; const cleanPath = (filePath) => { try { cleanPathFiles.map((item) => { if (fs_extra_1.default.existsSync(`${filePath}/${item}`)) { fs_extra_1.default.rmSync(`${filePath}/${item}`); } }); } catch (error) { (0, utils_1.logger)(chalk_1.default.red('cleanPath Error:'), error); } }; const updateConfigFile = (options) => { const { yapi_apiId, yapi_apiId_function, yapi_apiId_method, new_apiId, new_apiId_method, new_apiId_function } = options; const configFile = constants_1.YW_CONFIG_FILE; const concatValues = (k, l, r) => { return R.union(l, r); }; try { if (fs_extra_1.default.existsSync(configFile)) { const data = fs_extra_1.default.readFileSync(configFile) || '{}'; const result = JSON.parse(data.toString()); const apiId = JSON.parse(decodeURIComponent(yapi_apiId || new_apiId || '{}')); const apiId_function = JSON.parse(decodeURIComponent(yapi_apiId_function || new_apiId_function || '{}')); const apiId_method = JSON.parse(decodeURIComponent(yapi_apiId_method || new_apiId_method || '{}')); result.yapi_apiId = R.mergeWithKey(concatValues, result.yapi_apiId, apiId); result.yapi_apiId_function = R.mergeWithKey(concatValues, result.yapi_apiId_function, apiId_function); result.yapi_apiId_method = R.mergeWithKey(concatValues, result.yapi_apiId_method, apiId_method); fs_extra_1.default.writeJSONSync(configFile, result, { spaces: 2 }); } } catch (error) { (0, utils_1.logger)(chalk_1.default.red('updateConfigFile Error=>'), error); } };