UNPKG

@tuzki/cli

Version:

🐇 lowcode-cli is an efficient cli tool for Rabbitpre plugin component secondary development. ❤️

355 lines (354 loc) 13.1 kB
/* * 项目相关工具方法 * * @Author: xu.jin * @Date: 2023-08-15 19:21:16 * * Copyright © 2014-2023 Rabbitpre.com. All Rights Reserved. */ 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()); }); }; import Logger from '@tuzki/scaffold-logger'; import archiver from 'archiver'; import compressing from 'compressing'; import FormData from 'form-data'; import fse from 'fs-extra'; import inquirer from 'inquirer'; import { join, resolve } from 'path'; import { deployModule } from '../apis/deploy.js'; import { checkVersionOnline, downloadModuleCode, findDevVersionList, getModuleList, getModuleTypes, } from '../apis/init-project.js'; import { parse } from './comment-json.js'; import { DIST_PATH, EXTRA_MODULE, TipsText, ZIP_PATH } from './constants.js'; import { cdTargetDir } from './shell.js'; const logger = Logger.get('cli:utils:project'); const selectModule = (opts) => __awaiter(void 0, void 0, void 0, function* () { const { moduleType } = opts; try { const list = yield getModuleList(moduleType); const moduleList = list.filter(item => item.name && item.key); if (!moduleList.length) { logger.warn(`请先在低代码平台创建${TipsText[moduleType]}`); return; } // 询问选择模块 const answer = yield inquirer.prompt({ type: 'search-list', name: 'module', message: `Please select a ${moduleType} which created in lowcode manager`, default: moduleList[0], choices: moduleList.map(item => ({ name: item.name, value: item.key, })), }); const module = moduleList.find(m => m.key === answer.module); return module; } catch (err) { logger.error(`获取低代码平台${TipsText[moduleType]}列表失败:${err}`); process.exit(1); } }); const getModuleConfig = opt => { try { const { targetDir, schemaType, moduleType } = opt; const filePath = schemaType && moduleType ? join(targetDir, `${moduleType}-${schemaType}.jsonc`) : join(targetDir); if (!fse.pathExistsSync(filePath)) return {}; const schema = fse.readFileSync(filePath, 'utf-8'); return parse(schema); } catch (err) { logger.error(`获取模块配置数据 schema 失败,读取配置:${JSON.stringify(opt)}, ${err}`); process.exit(1); } }; const downloadCode = (opts) => __awaiter(void 0, void 0, void 0, function* () { const { moduleId, projectDir, moduleType, versionId } = opts; try { const templateRes = yield downloadModuleCode(Object.assign({ id: moduleId, type: moduleType }, (versionId ? { versionId } : {}))); // 解压到对应目录 yield compressing.zip.uncompress(templateRes, projectDir); logger.info(`${TipsText[moduleType]}源码下载成功:${projectDir}`); } catch (err) { logger.error(`${TipsText[moduleType]}源码下载失败:${err}`); process.exit(1); } }); const askProjectInfo = () => __awaiter(void 0, void 0, void 0, function* () { const questions = [ { type: 'input', name: 'author', message: '请输入项目开发者名称(author):', validate(author) { if (!author) return '开发者名称不能为空'; return true; }, }, { type: 'input', name: 'desc', message: '请输入项目描述(description):', validate(desc) { if (!desc) return '项目描述不能为空'; return true; }, }, ]; const answers = yield inquirer.prompt(questions); return answers; }); /** * 源码上传 */ const uploadSource = (dirPath, moduleType) => __awaiter(void 0, void 0, void 0, function* () { try { // cd 前一个目录 cdTargetDir('..'); // 获取当前项目路径 const zipPath = resolve(dirPath, `../${ZIP_PATH}`); /** 压缩 */ yield compress(dirPath, zipPath, moduleType); /** 发布 */ yield release(dirPath, zipPath, moduleType); } catch (err) { logger.error(`${TipsText[moduleType]}发布失败:${err}`); process.exit(1); } }); /** * 压缩方法 */ const compress = (projectPath, zipPath, moduleType) => { return new Promise((resolve, reject) => { const output = fse.createWriteStream(zipPath); const archive = archiver('zip', { zlib: { level: 9 }, }); // 扫描除 node_modules 和 dist 文件夹之外的文件进行压缩 archive.glob('**/*', { cwd: projectPath, ignore: ['node_modules/**', `${DIST_PATH}/**`, '.DS_Store'], dot: true, }); output.on('close', () => { logger.info(`${TipsText[moduleType]}压缩完成,压缩大小:${archive.pointer()}bytes`); resolve(archive.pointer()); }); // 监听结束 output.on('end', () => { logger.info(`${TipsText[moduleType]}压缩结束`); }); // 监听错误 archive.on('error', (err) => { reject(err); }); archive.pipe(output); archive.finalize(); }); }; const release = (dirPath, zipPath, moduleType) => __awaiter(void 0, void 0, void 0, function* () { if (!fse.existsSync(zipPath)) { logger.error('代码包[source.zip]不存在'); return; } try { // 模型数据获取 const model = getModuleConfig({ targetDir: join(dirPath, '.tuzki'), schemaType: "model" /* SchemaType.MODEL */, moduleType, }); // 实现数据获取 const implement = getModuleConfig({ targetDir: join(dirPath, '.tuzki'), schemaType: "implement" /* SchemaType.IMPLEMENT */, moduleType, }); // 读取 source.zip 存在 formData 中 const form = new FormData(); form.append('file', fse.createReadStream(zipPath), { filename: ZIP_PATH, contentType: 'application/zip', }); // 将模型数据塞到 formData 中 const modelDataStr = JSON.stringify(model); form.append('modelStr', modelDataStr); // 将实现数据塞到 formData 中 const implementDataStr = JSON.stringify(implement); form.append('implementStr', implementDataStr); form.append('type', moduleType); yield deployModule(form); logger.info(`部署${TipsText[moduleType]}代码包成功`); fse.removeSync(zipPath); } catch (error) { fse.removeSync(zipPath); logger.error(`部署${TipsText[moduleType]}代码包失败:${error}`); process.exit(1); } }); const askCoverProjectDir = (dirPath) => __awaiter(void 0, void 0, void 0, function* () { try { const answers = yield inquirer.prompt([ { type: 'confirm', name: 'isCover', message: `The project directory ${dirPath} already exists, do you want to cover it?`, }, ]); return answers.isCover; } catch (err) { logger.error('覆盖已存在项目目录出错:', err); process.exit(1); } }); const askProjectName = () => __awaiter(void 0, void 0, void 0, function* () { try { const answers = yield inquirer.prompt([ { type: 'input', name: 'name', message: 'Please input project name:', validate(name) { if (!name) return 'project name is required'; return true; }, }, ]); return answers.name; } catch (err) { logger.error('项目名称输入出错', err); process.exit(1); } }); const selectModuleType = () => __awaiter(void 0, void 0, void 0, function* () { try { const types = yield getModuleTypes(); const parsedTypes = [...types, ...EXTRA_MODULE].map((type, idx) => ({ name: `${idx + 1}. ${type} project${type === "component" /* ProjectType.COMPONENT */ ? ' (Coming soon)' : ''}`, value: type, })); const answer = yield inquirer.prompt([ { type: 'list', name: 'type', message: 'Please select a project type:', choices: parsedTypes, default: parsedTypes[0], pageSize: 15, }, ]); return answer.type; } catch (err) { logger.error('选择模块项目类型出错', err); process.exit(1); } }); const askSelectDevelopVersion = (versions, update) => __awaiter(void 0, void 0, void 0, function* () { try { // 解析 versions 为问询列表数据 const parsedVersions = versions.map(version => ({ name: version.version, value: version.versionId, })); const answer = yield inquirer.prompt([ { type: 'list', name: 'version', message: `Please select a developing version to ${update ? 'update' : 'deploy'}:`, choices: parsedVersions, default: parsedVersions[0], pageSize: 15, }, ]); const module = versions.find(m => m.versionId === answer.version); return module; } catch (err) { logger.error('问询选择开发版本失败'); process.exit(1); } }); const selectDevelopVersion = (moduleId, moduleType, update) => __awaiter(void 0, void 0, void 0, function* () { try { const versions = yield findDevVersionList(moduleId, moduleType); // 无开发版本 if (!versions.length) { logger.error('当前模块无开发中的版本,请先在低码平台创建开发版本'); process.exit(1); } return askSelectDevelopVersion(versions, update); } catch (err) { logger.error(`选择开发版本失败,原因:${err}`); process.exit(1); } }); const checkoutDevelopVersion = (localModel, moduleType) => __awaiter(void 0, void 0, void 0, function* () { try { let versionId; const params = { id: localModel.id, type: moduleType, }; (localModel === null || localModel === void 0 ? void 0 : localModel.versionId) && (params.versionId = localModel.versionId); const isRelease = yield checkVersionOnline(params); // 判断当前版本是否已上线 if (isRelease) { const versions = yield findDevVersionList(localModel.id, moduleType); if (!versions.length) { logger.warn('当前版本已上线,且无开发中的版本,请先在低码平台创建开发版本'); process.exit(1); } const isCheckout = yield askCheckoutVersion(); // 不切换版本号,退出 if (!isCheckout) { process.exit(1); } const version = yield askSelectDevelopVersion(versions); versionId = version.versionId; } return versionId; } catch (err) { logger.error('版本切换失败:', err); process.exit(1); } }); const askCheckoutVersion = () => __awaiter(void 0, void 0, void 0, function* () { try { const answer = yield inquirer.prompt([ { type: 'confirm', name: 'isCheckout', message: 'The current version has been released, do you want to switch to the developing version? If switch, will excute `update-project` to update project', default: true, }, ]); return answer.isCheckout; } catch (err) { logger.error('问询是否切换开发版本失败:', err); process.exit(1); } }); export { askCheckoutVersion, askCoverProjectDir, askProjectInfo, askProjectName, checkoutDevelopVersion, compress, downloadCode, getModuleConfig, release, selectDevelopVersion, selectModule, selectModuleType, uploadSource, };