@ywfe/cli
Version:
遥望前端开发命令行工具
425 lines (423 loc) • 17.3 kB
JavaScript
;
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);
}
};