@erda-ui/cli
Version:
Command line interface for rapid Erda UI development
255 lines (254 loc) • 10.9 kB
JavaScript
;
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);
}
});