UNPKG

@szzbmy/lowcode-cli

Version:

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

305 lines (304 loc) 12.1 kB
"use strict"; /** * @describe 组件调试 * @author fanyihuo * @since 20210816 */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (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 __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const react_1 = __importStar(require("react")); const ink_1 = require("ink"); const open_1 = __importDefault(require("open")); const config_1 = __importStar(require("../../config")); const fs_1 = require("../../config/fs"); const components_1 = require("../../components"); const request_1 = require("../../utils/request"); const logger_1 = __importDefault(require("../../utils/logger")); const shell_1 = require("../../utils/shell"); const server_helper_1 = require("../../utils/server-helper"); const json_1 = require("../../utils/json"); /** 检查端口是否可用 true-可用 */ const isAvailablePort = async () => { const { debugConfig: { debugPluginCDN }, } = config_1.default; try { await (0, request_1.get)(`${debugPluginCDN}/config.jsonc`, null, { customOptions: { blob: true }, }); /** 调试服务已启动,端口不可用 */ return false; } catch (err) { /** 请求出错,调试服务未启动,端口可用 */ return true; } }; /** * 生成 debugSession * @returns */ const generateDebugSession = () => { try { return Buffer.from(encodeURIComponent(JSON.stringify(config_1.default.debugConfig))).toString('base64'); } catch (err) { throw Error(err.message || '未获取调试配置'); } }; /** 生成编辑器调试链接 */ const getDebugUrl = () => { try { const debugSession = generateDebugSession(); const { debugConfig: { appid, shortUrl }, } = config_1.default; if (appid && shortUrl) { return `${config_1.default.editorHost}/?debugSession=${debugSession}&debug=1&appid=${appid}`; } return `${config_1.default.editorHost}/?debugSession=${debugSession}&debug=1`; } catch (err) { throw Error(err.message || '未获取 debugSession'); } }; /** 生成渲染引擎调试链接 */ const getDebugShortUrl = () => { try { const debugSession = generateDebugSession(); const { debugConfig: { appid, shortUrl }, } = config_1.default; if (appid && shortUrl) { return `${config_1.default.rendererHost}/m2/${shortUrl}?debugSession=${debugSession}&debug=1&appid=${appid}`; } return `${config_1.default.rendererHost}/m2/${shortUrl}?debugSession=${debugSession}&debug=1`; } catch (err) { throw Error(err.message || '未获取 debugSession'); } }; /** 生成调试链接并输出提示 */ const generateDebugUrl = () => { const { debugConfig: { appid }, } = config_1.default; const debugUrl = getDebugUrl(); const debugShortUrl = getDebugShortUrl(); return { debugUrl, debugShortUrl, appid, }; }; const createLowcodeDebugServer = async (randomPort) => { const { debugUrl, debugShortUrl, appid } = generateDebugUrl(); const server = (0, server_helper_1.createLowCodeServer)({ randomPort, routers: [ { /** 创建打开调试链接的服务与拼接请求链接 */ path: '/openDebugUrl', func: async function openDebugUrl(_req, res) { res.writeHead(200, { 'Content-Type': 'application/json; charset=utf8', }); await (0, open_1.default)(debugUrl); if (appid) { logDebugUrlIfNeed({ debugUrl, debugShortUrl }); } res.end(JSON.stringify({ code: 200, msg: '打开调试链接成功!' })); }, }, { /** 创建保存调试作品信息(appid、shortUrl)的服务 */ path: '/saveDebugAppInfo', func: function saveDebugAppInfo(req, res) { let debugAppInfo = ''; req.on('data', chunk => { debugAppInfo += chunk; }); req.on('end', () => { const { appid, shortUrl } = (0, json_1.parse)(debugAppInfo); // appid、shortUrl保存在config (0, config_1.updateConfig)({ debugConfig: { ...config_1.default.debugConfig, appid, shortUrl, }, }); // 将appid、shortUrl保存在本地jsonc文件 (0, fs_1.updateJsoncDebugConfig)(); res.writeHead(200, { 'Content-Type': 'application/json; charset=utf8', }); // 为了确保拿到的 debugUrl 包含绑定的作品信息,这里在保存了调试作品后再输出调试链接 // 逻辑流程图可以见wiki: http://wiki.tuzhanai.com/pages/viewpage.action?pageId=59507174 const { debugUrl, debugShortUrl } = generateDebugUrl(); logDebugUrlIfNeed({ debugUrl, debugShortUrl }); res.end(JSON.stringify({ code: 200, msg: '绑定作品成功!' })); }); }, }, ], }); return server; }; let hasLoggedDebugUrl = false; function logDebugUrlIfNeed({ debugUrl, debugShortUrl, }) { if (!hasLoggedDebugUrl) { logger_1.default.info(`调试链接为:${debugUrl} \n`); logger_1.default.info(`生成 H5 调试链接为:${debugShortUrl} \n`); hasLoggedDebugUrl = true; } } const selectPureItems = [ { label: 'Yes', value: true, }, { label: 'No', value: false, }, ]; // 当前代码执行状态 var DEBUG_STEP; (function (DEBUG_STEP) { DEBUG_STEP[DEBUG_STEP["INIT"] = 0] = "INIT"; DEBUG_STEP[DEBUG_STEP["BUILDING"] = 1] = "BUILDING"; DEBUG_STEP[DEBUG_STEP["INTERACTION"] = 2] = "INTERACTION"; DEBUG_STEP[DEBUG_STEP["FINISHED"] = 3] = "FINISHED"; /** 纯净模式,只要打开浏览器调试链接即可 */ DEBUG_STEP[DEBUG_STEP["PURE_OPEN"] = 4] = "PURE_OPEN"; DEBUG_STEP[DEBUG_STEP["ERROR"] = 5] = "ERROR"; })(DEBUG_STEP || (DEBUG_STEP = {})); var INTERACTION; (function (INTERACTION) { INTERACTION[INTERACTION["CONFIRM_USE_PURE_DEBUG"] = 0] = "CONFIRM_USE_PURE_DEBUG"; })(INTERACTION || (INTERACTION = {})); function Debug(props) { const [debugStep, setDebugStepOrigional] = (0, react_1.useState)({ type: DEBUG_STEP.INIT, options: {}, }); const [interactionValues, setInteractionValues] = (0, react_1.useState)({ usePure: undefined, }); const setDebugStep = (0, react_1.useCallback)((type, options) => { setDebugStepOrigional({ type, options, }); }, []); // 根据命令行参数或用户输入参数执行初始化逻辑 const init = (0, react_1.useCallback)(async () => { const isPortAvailable = await isAvailablePort(); const isPure = interactionValues.usePure ? true : props.pure; if (isPure) { if (isPortAvailable) { setDebugStep(DEBUG_STEP.ERROR); } else { /** 纯净模式 端口被占用(已起调试服务),直接打开生成的调试链接即可 */ setDebugStep(DEBUG_STEP.PURE_OPEN); } } else if (!isPortAvailable) { // 端口已占用, 等待用户输入 setDebugStep(DEBUG_STEP.INTERACTION, { interactionType: INTERACTION.CONFIRM_USE_PURE_DEBUG, }); } else { // 未占用的话, 直接构建 setDebugStep(DEBUG_STEP.BUILDING); } }, [interactionValues, props.pure, setDebugStep]); // 构建处理中 const building = (0, react_1.useCallback)(async () => { try { const randomPort = await (0, server_helper_1.getRandomPort)(); /** 编译需要的环境变量 */ const lowcodeCompileEnv = { hostname: '127.0.0.1', port: randomPort, }; await createLowcodeDebugServer(randomPort); logger_1.default.info('开始编译代码'); // 3. 传入环境变量并开始构建 const code = await (0, shell_1.execCommandPromise)({ cmd: 'npm start', silent: false, env: { LOWCODE_COMPILE_ENV: JSON.stringify(lowcodeCompileEnv), }, }); if (code !== 0) logger_1.default.error('组件模板构建配置错误'); } catch (_) { logger_1.default.error('编译代码失败: ', _); } }, []); /** 纯净模式,只要打开浏览器调试链接即可 */ const pureOpen = (0, react_1.useCallback)(async () => { const { debugUrl, debugShortUrl } = generateDebugUrl(); logger_1.default.info(`调试链接为:${debugUrl} \n`); if (debugShortUrl) { logger_1.default.info(`生成 H5 短链为:${debugShortUrl} \n`); } await (0, open_1.default)(debugUrl); }, []); // 执行出错逻辑 const error = (0, react_1.useCallback)(async () => { const isPure = interactionValues.usePure ? true : props.pure; if (isPure) { logger_1.default.warn('调试服务未启动,请使用非纯净调试模式'); } else { logger_1.default.error('调试出错'); } }, [interactionValues.usePure, props.pure]); (0, react_1.useEffect)(() => { const func = { [DEBUG_STEP.INIT]: init, [DEBUG_STEP.BUILDING]: building, [DEBUG_STEP.PURE_OPEN]: pureOpen, [DEBUG_STEP.ERROR]: error, }[debugStep.type]; func && func(); }, [debugStep, init, building, error, pureOpen]); const handleSelectPure = (0, react_1.useCallback)(async (currentItem) => { setInteractionValues({ ...interactionValues, usePure: currentItem.value, }); if (currentItem.value) setDebugStep(DEBUG_STEP.PURE_OPEN); else setDebugStep(DEBUG_STEP.ERROR); }, [interactionValues, setDebugStep]); if (debugStep.type === DEBUG_STEP.INTERACTION && debugStep.options?.interactionType === INTERACTION.CONFIRM_USE_PURE_DEBUG) { return (react_1.default.createElement(react_1.default.Fragment, null, react_1.default.createElement(ink_1.Text, { color: "yellow" }, "\u7AEF\u53E3\u5DF2\u88AB\u5360\u7528\uFF0C\u662F\u5426\u4F7F\u7528\u7EAF\u51C0\u8C03\u8BD5\u6A21\u5F0F\uFF1F"), react_1.default.createElement(components_1.SelectInput, { items: selectPureItems, onSelect: handleSelectPure }))); } return null; } exports.default = Debug;