@szzbmy/lowcode-cli
Version:
🐇 lowcode-cli is an efficient cli tool for Rabbitpre plugin component secondary development. ❤️
305 lines (304 loc) • 12.1 kB
JavaScript
;
/**
* @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;