UNPKG

@erda-ui/cli

Version:

Command line interface for rapid Erda UI development

255 lines (254 loc) 10.9 kB
"use strict"; 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 }); const path_1 = __importDefault(require("path")); const fs_1 = __importDefault(require("fs")); const log_1 = require("./util/log"); const superagent_1 = __importDefault(require("superagent")); const file_walker_1 = require("./util/file-walker"); const lodash_1 = require("lodash"); const js_base64_1 = require("js-base64"); const os_1 = require("os"); const env_1 = require("./util/env"); const license_1 = __importDefault(require("../templates/license")); const NUMBER_TYPES = ['integer', 'float', 'double', 'number', 'long']; const REG_SEARCH = /(AUTO\s*GENERATED)/g; const REG_API = /([a-zA-Z]+):\s*{\n\s*api:\s*'(get|post|put|delete)@(\/api(\/:?[a-zA-Z-]+)+)'/g; const formatJson = (data) => { const jsonData = typeof data === 'string' ? data : JSON.stringify(data, null, 2); return jsonData.replace(/\\+n/g, '\n').replace(/"/g, ''); }; const formatApiPath = (apiPath) => { const pathParams = {}; const newApiPath = apiPath.replace(/(:[a-zA-Z]+)/g, (p) => { const pName = p.slice(1); pathParams[pName] = pName.toLocaleLowerCase().includes('id') ? 'number' : 'string'; return `<${pName}>`; }); return { url: newApiPath, pathParams, }; }; const getSteadyContent = (filePath, content) => { if (!fs_1.default.existsSync(filePath) || !content) { return ` ${license_1.default.GPLV3}`; } else if (!content.includes('GENERATED')) { return `${content} // > AUTO GENERATED `; } else { const lastIndex = content.indexOf('GENERATED'); const steadyContent = lastIndex ? content.slice(0, lastIndex + 9) : content; return `${steadyContent}${os_1.EOL}`; } }; const getBasicTypeData = (props, swaggerData) => { const { type, properties = {}, items, required = [] } = props || {}; let value; if (type === 'object') { const data = {}; for (const key in properties) { const pData = properties[key]; const __key = required.includes(key) ? key : `${key}?`; data[__key] = getBasicTypeData(Object.assign(Object.assign({}, pData), { propertyName: key }), swaggerData); } value = data; } else if (type === 'array' && items) { const itemsType = (0, lodash_1.get)(items, 'type'); if (itemsType === 'object' || itemsType === 'array') { value = `Array<${formatJson(getSchemaData(items, swaggerData))}>`; } else { value = `${itemsType}[]`; } } else if (type === 'integer' || type === 'number') { value = 'number'; } else { value = type; } return value; }; const getSchemaData = (schemaData, swaggerData) => { const { $ref, type } = schemaData || {}; let res; if ($ref) { const quoteList = $ref.split('/').slice(1); res = getSchemaData((0, lodash_1.get)(swaggerData, quoteList), swaggerData); } else if (type) { res = getBasicTypeData(schemaData, swaggerData); } else { res = schemaData; } return res || {}; }; const getResponseType = (schemaData, swaggerData) => { var _a, _b; const { $ref } = schemaData || {}; if ($ref) { const quoteList = $ref.split('/').slice(1); const _data = (0, lodash_1.get)(swaggerData, [...quoteList, 'properties', 'data']) || {}; if (_data.type === 'object' && ((_a = _data.properties) === null || _a === void 0 ? void 0 : _a.total) && ((_b = _data.properties) === null || _b === void 0 ? void 0 : _b.list)) { return 'pagingList'; } else { return _data.type; } } else { return schemaData === null || schemaData === void 0 ? void 0 : schemaData.type; } }; const autoGenerateService = (content, filePath, isEnd, swaggerData, resolve) => { var _a; if (!filePath.endsWith('-api.ts')) { return; } if (content.match(REG_SEARCH)) { const apiList = []; let serviceContent = getSteadyContent(filePath, content); const typeFilePath = filePath.replace(/\/services\//, '/types/').replace(/-api\.ts/, '.d.ts'); let typeContent = getSteadyContent(typeFilePath, fs_1.default.existsSync(typeFilePath) ? fs_1.default.readFileSync(typeFilePath, 'utf8') : ''); let regRes = REG_API.exec(content); while (regRes) { const [, apiName, method, _apiPath] = regRes; const { url: apiPath, pathParams } = formatApiPath(_apiPath); let parameters = Object.assign({}, pathParams); (0, lodash_1.forEach)((0, lodash_1.get)(swaggerData, ['paths', apiPath, method, 'parameters']), ({ name, type, in: paramsIn, schema, required, }) => { if (paramsIn === 'query') { parameters[`${name}${required ? '' : '?'}`] = NUMBER_TYPES.includes(type) ? 'number' : type; } else if (paramsIn === 'path') { parameters[name] = NUMBER_TYPES.includes(type) ? 'number' : type; } else { parameters = Object.assign(Object.assign({}, parameters), (getSchemaData(schema) || {})); } }); const _schemaData = (0, lodash_1.get)(swaggerData, ['paths', apiPath, method, 'responses', '200', 'schema']); const resType = getResponseType(_schemaData, swaggerData); const fullData = getSchemaData(_schemaData) || {}; const responseData = (resType === 'pagingList' ? (_a = fullData.data) === null || _a === void 0 ? void 0 : _a.list : fullData.data || fullData['data?']) || {}; const _resData = JSON.stringify(responseData, null, 2); let resData = formatJson(_resData); if (_resData.startsWith('"Array<')) { resData = formatJson(_resData.slice(7, _resData.length - 2)); } apiList.push({ apiName, method, parameters: formatJson(parameters), resData, resType, }); regRes = REG_API.exec(content); } (0, lodash_1.forEach)(apiList, ({ apiName, parameters, resData, resType }) => { let pType = '{}'; let bType = 'RAW_RESPONSE'; if (resData !== '{}') { if (resType === 'pagingList') { bType = `IPagingResp<T_${apiName}_item>`; } else if (resType === 'array') { bType = `T_${apiName}_item[]`; } else { bType = `T_${apiName}_data`; } typeContent += ` interface T_${apiName}_${['array', 'pagingList'].includes(resType) ? 'item' : 'data'} ${resData}\n`; } if (parameters !== '{}') { typeContent += ` interface T_${apiName}_params ${parameters}\n`; pType = `T_${apiName}_params`; } serviceContent += ` export const ${apiName} = apiCreator<(p: ${pType}) => ${bType}>(apis.${apiName}); `; }); fs_1.default.writeFileSync(typeFilePath, typeContent, 'utf8'); fs_1.default.writeFileSync(filePath, serviceContent, 'utf8'); } if (isEnd) { (0, log_1.logSuccess)('service data is updated, bye 👋'); resolve(); } }; const getSwaggerData = (url) => { return superagent_1.default .get(url) .set('content-type', 'application-json') .set('User-Agent', '') .then((response) => { var _a; (0, log_1.logInfo)('get swagger data successfully!'); const content = (0, js_base64_1.decode)((_a = response === null || response === void 0 ? void 0 : response.body) === null || _a === void 0 ? void 0 : _a.content); return content ? JSON.parse(content) : ''; }) .catch((err) => { (0, log_1.logError)('fail to get swagger data: ', err); return false; }); }; const getLocalSwaggerConfig = (configPath) => { if (!configPath.includes('erda-ui')) { return null; } let curPath = configPath; while (!(0, env_1.isCwdInRoot)({ currentPath: curPath })) { curPath = !curPath.endsWith('erda-ui-enterprise') ? path_1.default.resolve(curPath, '..') : path_1.default.resolve(curPath, '../erda-ui'); } const filePath = path_1.default.join(curPath, 'swagger-config.json'); return fs_1.default.existsSync(filePath) ? fs_1.default.readFileSync(filePath, 'utf8') : null; }; exports.default = ({ workDir }) => __awaiter(void 0, void 0, void 0, function* () { var _a; try { const config = getLocalSwaggerConfig(workDir); (0, log_1.logInfo)(`local swagger config: ${config}`); const repository = (config === null || config === void 0 ? void 0 : config.repository) || 'erda'; const username = (config === null || config === void 0 ? void 0 : config.username) || 'erda-project'; const swaggerFilePath = (config === null || config === void 0 ? void 0 : config.swaggerFilePath) || 'pkg/swagger/oas3/testdata/swagger_all.json'; const swagger = yield getSwaggerData(`https://api.github.com/repos/${username}/${repository}/contents/${swaggerFilePath}`); if ((_a = (0, lodash_1.keys)(swagger === null || swagger === void 0 ? void 0 : swagger.paths)) === null || _a === void 0 ? void 0 : _a.length) { yield new Promise((resolve) => { (0, file_walker_1.walker)({ root: workDir, dealFile: (...args) => { autoGenerateService.apply(null, [...args, swagger, resolve]); }, }); }); } else { (0, log_1.logError)('It is an empty swagger!'); process.exit(1); } } catch (error) { (0, log_1.logError)(error); } });