qdmp-cli 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,17 @@
1
+ # QDMP CLI
2
+
3
+ 千岛小程序开发平台命令行工具
4
+
5
+
6
+ ## ✨ 功能特性
7
+ - 项目初始化与模板管理
8
+ - 用户认证与权限管理
9
+ - 小程序版本上传与发布
10
+ - 开发工作流自动化
11
+
12
+ ## 🚀 快速开始
13
+
14
+ ### 安装
15
+ ```bash
16
+ npm install -g qdmp-cli
17
+ ```
package/actions.js ADDED
@@ -0,0 +1,140 @@
1
+ import shell from "shelljs";
2
+ import { error, info, success } from "./utils/logHandler.js";
3
+ import { TEMPLATES } from "./constants.js";
4
+ import { download } from "./utils/gitClone.js";
5
+ import { printLogo } from "./utils/logHandler.js";
6
+ import chalk from "chalk";
7
+ import table from "table";
8
+ import { inquirerAccountPassword } from "./utils/interactive.js";
9
+ import { login, getUserInfo } from "./api.js";
10
+ import fs from "fs";
11
+ import {
12
+ getUploadPath,
13
+ getAppId,
14
+ isUrlAccessible,
15
+ preUpload,
16
+ cleanWorkSpaceFolder,
17
+ } from "./utils/common.js";
18
+ import { getAppInfo, uploadVersion, uploadApp } from "./api.js";
19
+
20
+ function createProject(appName, template) {
21
+ printLogo();
22
+ if (template) {
23
+ console.log("\n" + chalk.dim("─".repeat(60)) + "\n");
24
+ info(
25
+ `${template.emoji} 已为 ${chalk.bold(
26
+ appName
27
+ )} 选择模版:${chalk.underline(template.name)}`
28
+ );
29
+ info(` ${chalk.dim("描述:")} ${template.desc}`);
30
+ info(` ${chalk.dim("仓库:")} ${chalk.green(template.value)}`);
31
+ info(`⏳ 正在创建 ${chalk.bold(appName)} 项目...`);
32
+ console.log("\n" + chalk.dim("─".repeat(60)) + "\n");
33
+ download(appName, template.value);
34
+ } else {
35
+ listAction();
36
+ error(`未找到模板`);
37
+ }
38
+ }
39
+
40
+ export const initAction = async (appName, option) => {
41
+ if (!shell.which("git")) {
42
+ error("未安装git");
43
+ }
44
+ const invalidChars = /[\u4E00-\u9FFF`~!@#$%^&*()_+=\[\]{};':"\\|,.<>\/?]/g;
45
+ if (appName.match(invalidChars)) {
46
+ error("名称不能包含中文及特殊字符");
47
+ }
48
+ const inputTemplate = option.template || TEMPLATES[0].name;
49
+ const template = TEMPLATES.find((t) => t.name === inputTemplate);
50
+ await createProject(appName, template);
51
+ };
52
+
53
+ export const listAction = () => {
54
+ console.log(chalk.green.bold("\n📋 可选模版:"));
55
+ const tableData = [
56
+ [
57
+ chalk.cyan.bold("序号"),
58
+ chalk.cyan.bold("模版名称"),
59
+ chalk.cyan.bold("描述"),
60
+ chalk.cyan.bold("仓库路径"),
61
+ ],
62
+ ];
63
+ TEMPLATES.forEach((t, i) => {
64
+ tableData.push([
65
+ chalk.gray(`${i + 1}`),
66
+ `${t.emoji} ${chalk.yellow(t.name)}`,
67
+ chalk.white(t.desc),
68
+ chalk.green(t.value),
69
+ ]);
70
+ });
71
+
72
+ console.log(table.table(tableData));
73
+ };
74
+
75
+ export const loginAction = async () => {
76
+ try {
77
+ const { account, password } = await inquirerAccountPassword();
78
+ const result = await login(account, password);
79
+ if (result.success) {
80
+ success("登录成功");
81
+ } else {
82
+ error("登录失败");
83
+ }
84
+ } catch (e) {
85
+ error(e.message);
86
+ }
87
+ };
88
+ export const getMyInfoAction = async () => {
89
+ try {
90
+ const nickname = await getUserInfo();
91
+ const appId = await getAppId();
92
+ const appInfo = await getAppInfo(appId);
93
+ info(`当前用户:${nickname}`);
94
+ info(`当前授权应用:${appInfo?.name} - ${appId}`);
95
+ } catch (error) {
96
+ error(error?.message);
97
+ }
98
+ };
99
+
100
+ export async function uploadAction() {
101
+ try {
102
+ const appId = await getAppId();
103
+ if (!appId) {
104
+ error("未授权应用,请在根目录下qdmp.json配置对应的appId");
105
+ }
106
+ const remoteApp = await getAppInfo(appId);
107
+ if (!remoteApp) {
108
+ error("未找到应用,请检查appId是否正确");
109
+ }
110
+ const code = +remoteApp.latest + 1;
111
+ const uploadPath = getUploadPath(appId, code);
112
+ const url = `https://crane.qiandaocdn.com/${uploadPath}`;
113
+
114
+ // 预上传
115
+ const targetName = `${appId}_${code}`;
116
+ const localPath = await preUpload(targetName);
117
+ if (!localPath) {
118
+ error("预上传失败");
119
+ }
120
+ // 添加文件存在检查
121
+ if (!fs.existsSync(localPath)) {
122
+ error(`压缩文件 ${localPath} 未生成`);
123
+ }
124
+ // 上传到oss;
125
+ await uploadApp(uploadPath, localPath);
126
+ const isAccessible = await isUrlAccessible(url);
127
+ if (!isAccessible) {
128
+ error("上传链接不可用,请检查网络是否正常");
129
+ }
130
+ // 更新远端版本信息;
131
+ await uploadVersion(appId, url);
132
+ // 清理工作文件夹;
133
+ await cleanWorkSpaceFolder();
134
+ success(
135
+ `上传成功,版本号:${code},可前往官网进行小程序发布:https://mp.qiandao.com`
136
+ );
137
+ } catch (e) {
138
+ error(`${e.message}`);
139
+ }
140
+ }
package/api.js ADDED
@@ -0,0 +1,157 @@
1
+ import { getToken, saveToken } from "./utils/authorized.js";
2
+ import fs from "fs";
3
+ import { promisify } from "util";
4
+
5
+ const readFile = promisify(fs.readFile);
6
+ // API 基础配置
7
+ const API_CONFIG = {
8
+ baseURL: "https://api.qiandao.com",
9
+ timeout: 10000,
10
+ headers: {
11
+ "content-type": "application/json",
12
+ },
13
+ };
14
+
15
+ /**
16
+ * 基础请求函数
17
+ * @param {string} url - 请求地址
18
+ * @param {object} options - 请求选项
19
+ * @returns {Promise} 请求结果
20
+ */
21
+ export const request = async (url, options = {}) => {
22
+ const config = {
23
+ method: "GET",
24
+ headers: { ...API_CONFIG.headers },
25
+ ...options,
26
+ };
27
+
28
+ // 添加认证头(如果有 token)
29
+ const token = getToken();
30
+ if (token) {
31
+ config.headers["authorization"] = `Bearer ${token}`;
32
+ }
33
+
34
+ try {
35
+ const response = await fetch(`${API_CONFIG.baseURL}${url}`, config);
36
+
37
+ // 检查响应状态
38
+ if (!response.ok) {
39
+ if (response.status === 401) {
40
+ throw new Error("未授权,执行 qdmp-cli login 完成登录");
41
+ }
42
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
43
+ }
44
+
45
+ const data = await response.json();
46
+ return data;
47
+ } catch (error) {
48
+ throw new Error(error);
49
+ }
50
+ };
51
+
52
+ /**
53
+ * 用户登录
54
+ * @param {string} username - 用户名
55
+ * @param {string} password - 密码
56
+ * @returns {Promise} 登录结果
57
+ */
58
+ export const login = async (username, password) => {
59
+ try {
60
+ const result = await request("/mp/v1/user/login", {
61
+ method: "POST",
62
+ body: JSON.stringify({ username, password }),
63
+ });
64
+ if (result?.data?.token) {
65
+ saveToken(result.data.token); // 保存token
66
+ return { success: true };
67
+ } else {
68
+ throw new Error(result.error || "登录失败");
69
+ }
70
+ } catch (error) {
71
+ throw new Error(error.message);
72
+ }
73
+ };
74
+ /**
75
+ * 获取用户信息
76
+ * @returns {Promise} 用户信息
77
+ */
78
+ export const getUserInfo = async () => {
79
+ try {
80
+ const result = await request("/mp/v1/user/me");
81
+ return result?.data?.nickname;
82
+ } catch (error) {
83
+ throw new Error(error?.message);
84
+ }
85
+ };
86
+ /**
87
+ * 获取预上传url
88
+ * @returns {Promise} url
89
+ */
90
+ export const getSignInRecords = async (file) => {
91
+ try {
92
+ const result = await request("/mp/v1/platform/version/uploadurl", {
93
+ method: "POST",
94
+ body: JSON.stringify({ file }),
95
+ });
96
+ if (result?.data?.url) {
97
+ return result.data.url;
98
+ } else {
99
+ throw new Error(result.error || "获取上传url失败");
100
+ }
101
+ } catch (error) {
102
+ throw new Error(error?.message);
103
+ }
104
+ };
105
+ /**
106
+ * 上传版本
107
+ * @params {string} filename
108
+ */
109
+ export const uploadApp = async (filename, localPath) => {
110
+ try {
111
+ const signedUrl = await getSignInRecords(filename);
112
+ const fileBuffer = await readFile(localPath);
113
+ const response = await fetch(signedUrl, {
114
+ method: "PUT",
115
+ headers: {
116
+ "Content-Type": "application/octet-stream",
117
+ },
118
+ body: fileBuffer,
119
+ });
120
+ if (response.status === 200) {
121
+ return response;
122
+ }
123
+ } catch (error) {
124
+ throw new Error(error?.message);
125
+ }
126
+ };
127
+ /**
128
+ * 获取app信息
129
+ * @params {string} appId - appId
130
+ */
131
+ export const getAppInfo = async (appId) => {
132
+ try {
133
+ const result = await request("/mp/v1/lookup/app/" + appId);
134
+ return result?.data;
135
+ } catch (error) {
136
+ throw new Error(error?.message);
137
+ }
138
+ };
139
+ /**
140
+ * 更新版本
141
+ * @params {string} appId - appId
142
+ * @params {string} downloadUrl - 上传链接
143
+ */
144
+ export const uploadVersion = async (appId, downloadUrl) => {
145
+ try {
146
+ const result = await request("/mp/v1/platform/version/update", {
147
+ method: "POST",
148
+ body: JSON.stringify({
149
+ appId,
150
+ downloadUrl,
151
+ }),
152
+ });
153
+ return result;
154
+ } catch (error) {
155
+ throw new Error(error?.message);
156
+ }
157
+ };
package/constants.js ADDED
@@ -0,0 +1,16 @@
1
+ export const TEMPLATES = [
2
+ {
3
+ name: "default",
4
+ value: "frontend/echo-uniapp-template",
5
+ desc: "千岛小程序默认模版",
6
+ emoji: "🟢",
7
+ },
8
+ {
9
+ name: "h5",
10
+ value: "frontend/h5-template",
11
+ desc: "h5模版",
12
+ emoji: "🔵",
13
+ },
14
+ ];
15
+
16
+ export const WORKSPACE_DIR = "qdmp-output";
package/index.js ADDED
@@ -0,0 +1,49 @@
1
+ #! /usr/bin/env node
2
+
3
+ import chalk from "chalk";
4
+ import { program } from "commander";
5
+ import { readFileSync } from "fs";
6
+ import { resolve } from "path";
7
+ import { fileURLToPath } from "url";
8
+ import { dirname } from "path";
9
+ import {
10
+ initAction,
11
+ listAction,
12
+ loginAction,
13
+ getMyInfoAction,
14
+ uploadAction,
15
+ } from "./actions.js";
16
+ import { printLogo } from "./utils/logHandler.js";
17
+ // import.meta.url: esm模块化中的属性,获取模块文件的绝对地址
18
+ // __dirname: 获取模块文件所在目录的绝对路径,由nodejs在每个模块注入的特殊变量,只能在commonjs中使用
19
+ // process.cwd(): 获取当前执行nodejs命令的当前工作目录
20
+
21
+ const __filename = fileURLToPath(import.meta.url);
22
+ const __dirname = dirname(__filename);
23
+ const packageJson = JSON.parse(
24
+ readFileSync(resolve(__dirname, "package.json"), "utf8")
25
+ );
26
+
27
+ program.version(chalk.green(`v${packageJson.version}`), "-v,--version");
28
+ program
29
+ .name("qdmp-cli")
30
+ .description("千岛小程序脚手架")
31
+ .usage("<command> [options]")
32
+ .on("--help", () => {
33
+ printLogo();
34
+ });
35
+
36
+ program
37
+ .command("create <app-name>")
38
+ .description("创建项目")
39
+ .option("-t, --template <template>", "项目模版")
40
+ .action(initAction);
41
+
42
+ program.command("list").description("列出可选模版").action(listAction);
43
+ program.command("login").description("登录").action(loginAction);
44
+ program
45
+ .command("getMe")
46
+ .description("查看当前登陆用户")
47
+ .action(getMyInfoAction);
48
+ program.command("upload").description("上传项目").action(uploadAction);
49
+ program.parse(process.argv);
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "qdmp-cli",
3
+ "version": "0.0.1",
4
+ "description": "qdmp-cli",
5
+ "main": "index.js",
6
+ "type": "module",
7
+ "scripts": {
8
+ "test": "echo \"Error: no test specified\" && exit 1"
9
+ },
10
+ "bin": "./index.js",
11
+ "keywords": [],
12
+ "author": "zhangyanran",
13
+ "license": "ISC",
14
+ "dependencies": {
15
+ "chalk": "^5.4.1",
16
+ "commander": "^14.0.0",
17
+ "figlet": "^1.8.1",
18
+ "inquirer": "^12.6.3",
19
+ "is-unicode-supported": "^2.1.0",
20
+ "ora": "^8.2.0",
21
+ "shelljs": "^0.10.0",
22
+ "table": "^6.9.0"
23
+ },
24
+ "publishConfig": {
25
+ "@frontend:registry": " http://g.echo.tech/api/v4/projects/1107/packages/npm/"
26
+ }
27
+
28
+ }
@@ -0,0 +1,43 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import os from "os";
4
+ import { error, info } from "./logHandler.js";
5
+
6
+ // 获取配置文件路径
7
+ const getConfigPath = () => {
8
+ return path.join(os.homedir(), ".qdtool.config.json");
9
+ };
10
+
11
+ /**
12
+ * 保存认证令牌
13
+ * @param {string} token - 认证令牌
14
+ */
15
+ export const saveToken = (token) => {
16
+ const configPath = getConfigPath();
17
+ const config = { token };
18
+
19
+ try {
20
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
21
+ info("🔑 认证令牌已安全存储");
22
+ } catch (err) {
23
+ error("保存认证令牌失败:", err.message);
24
+ }
25
+ };
26
+
27
+ /**
28
+ * 获取认证令牌
29
+ * @returns {string} - 认证令牌
30
+ */
31
+ export const getToken = () => {
32
+ const configPath = getConfigPath();
33
+ try {
34
+ if (fs.existsSync(configPath)) {
35
+ const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
36
+ return config.token || null;
37
+ } else {
38
+ error("认证令牌不存在,请重新登录");
39
+ }
40
+ } catch (err) {
41
+ error("读取认证令牌失败,请重新登录:", err.message);
42
+ }
43
+ };
@@ -0,0 +1,133 @@
1
+ import { info, error, success } from "./logHandler.js";
2
+ import { execSync } from "child_process";
3
+ import chalk from "chalk";
4
+ import { WORKSPACE_DIR } from "../constants.js";
5
+ import fs from "fs";
6
+ import path from "path";
7
+
8
+ /** 获取上传链接
9
+ * @param {*} appId 文件
10
+ * @param {*} code 版本号
11
+ */
12
+ export const getUploadPath = (appId, code) => {
13
+ return `mp/${appId}/${appId}_${code}.zip`;
14
+ };
15
+
16
+ /** 获取AppId
17
+ *
18
+ * @returns appId
19
+ */
20
+ export function getAppId() {
21
+ try {
22
+ const qdmpJsonPath = `${process.cwd()}/qdmp.json`;
23
+ const data = fs.readFileSync(path.resolve(qdmpJsonPath), "utf8");
24
+ const qdmpJson = JSON.parse(data);
25
+ return qdmpJson?.appId || "";
26
+ } catch (e) {
27
+ error("未授权应用,请在根目录下qdmp.json配置对应的appId");
28
+ }
29
+ }
30
+
31
+ /** 验证URL是否可访问
32
+ * @param {*} url
33
+ */
34
+ export const isUrlAccessible = async (url) => {
35
+ try {
36
+ const response = await fetch(url, { method: "HEAD" });
37
+ return response.ok;
38
+ } catch (e) {
39
+ error(e);
40
+ return false;
41
+ }
42
+ };
43
+
44
+ /** 重命名文件夹
45
+ * @param {*} dirname 旧文件夹名
46
+ * @param {*} newname 新文件夹名
47
+ */
48
+ export async function renameDistFolder(dirname, newname) {
49
+ try {
50
+ execSync(`rm -rf ./h5`, { stdio: "inherit" });
51
+ execSync(`mv ${dirname} ${newname}`, { stdio: "inherit" });
52
+ return true;
53
+ } catch (e) {
54
+ error("重命名文件夹失败:", e);
55
+ return false;
56
+ }
57
+ }
58
+ /** 创建工作文件夹
59
+ * @param {*} dirname 文件夹名
60
+ */
61
+ export async function createWorkSpaceFolder(dirname = "") {
62
+ info("创建工作区...");
63
+ try {
64
+ execSync(`rm -rf ${dirname}`);
65
+ execSync(`mkdir ${dirname}`, { stdio: "inherit" });
66
+ success("创建工作区成功!");
67
+ return true;
68
+ } catch (e) {
69
+ error(e);
70
+ return false;
71
+ }
72
+ }
73
+ /** 压缩文件夹
74
+ * @param {*} dirname 文件夹名
75
+ */
76
+ // 压缩项目
77
+ async function compressProject(output, dirname = "") {
78
+ try {
79
+ execSync(`cd ./${dirname} && zip -r ${output}.zip ./h5`, {
80
+ stdio: "inherit",
81
+ });
82
+ return true;
83
+ } catch (error) {
84
+ return false;
85
+ }
86
+ }
87
+ /** 复制文件夹
88
+ * @param {*} dirname 文件夹名
89
+ */
90
+ async function copyDistFolder(dirname) {
91
+ try {
92
+ execSync(`cp -r ${dirname} ./${WORKSPACE_DIR}`, {
93
+ stdio: "inherit",
94
+ });
95
+ return true;
96
+ } catch (e) {
97
+ error(e);
98
+ return false;
99
+ }
100
+ }
101
+
102
+ /**预上传
103
+ *
104
+ */
105
+ export async function preUpload(targetName) {
106
+ try {
107
+ if (!(await createWorkSpaceFolder(WORKSPACE_DIR))) return false;
108
+
109
+ if (!(await copyDistFolder("dist"))) return false;
110
+
111
+ if (
112
+ !(await renameDistFolder(`${WORKSPACE_DIR}/dist`, `${WORKSPACE_DIR}/h5`))
113
+ )
114
+ return false;
115
+
116
+ if (!(await compressProject(targetName, WORKSPACE_DIR))) return false;
117
+ return `./${WORKSPACE_DIR}/${targetName}.zip`;
118
+ } catch (e) {
119
+ error(e);
120
+ }
121
+ }
122
+ /** 清理工作文件夹
123
+ * @param {*} dirname 文件夹名
124
+ */
125
+ export async function cleanWorkSpaceFolder(dirname) {
126
+ try {
127
+ execSync(`rm -rf ./${WORKSPACE_DIR}`, { stdio: "inherit" });
128
+ success("清理工作空间成功!");
129
+ return true;
130
+ } catch (error) {
131
+ return false;
132
+ }
133
+ }
@@ -0,0 +1,62 @@
1
+ import child_process from "child_process";
2
+ import ora from "ora";
3
+ import path from "path";
4
+ import chalk from "chalk";
5
+ import { rm } from "fs/promises";
6
+ import { existsSync } from "fs";
7
+ import { inquirerConfirm } from "./interactive.js";
8
+ import { error } from "./logHandler.js";
9
+ /**
10
+ * 下载项目
11
+ * @param {string} projectName - 项目名称
12
+ * @param {string} repo - 仓库地址
13
+ */
14
+ export const download = async (projectName, repo) => {
15
+ try {
16
+ const targetPath = path.join(process.cwd(), projectName);
17
+ let name = projectName;
18
+ if (existsSync(targetPath)) {
19
+ const randomHash = Math.random().toString(36).substring(2, 8);
20
+ name = `${projectName}_${randomHash}`;
21
+ //询问是否创建新名称
22
+ const confirm = await inquirerConfirm(
23
+ `文件夹 ${projectName} 已存在,是否创建新名称: ${name}`
24
+ );
25
+ if (confirm) {
26
+ clone(repo, name);
27
+ } else {
28
+ error(`文件夹 ${projectName} 已存在, 请更名后重新创建`);
29
+ }
30
+ } else {
31
+ clone(repo, name);
32
+ }
33
+ } catch (error) {
34
+ error(`${error.message}`);
35
+ }
36
+ };
37
+
38
+ const execSync = async (cmd) => {
39
+ return new Promise((resolve, reject) => {
40
+ child_process.exec(cmd, function (err, stdout, stderr) {
41
+ if (err) {
42
+ reject(new Error(stderr || err.message));
43
+ return;
44
+ }
45
+ resolve(stdout);
46
+ });
47
+ });
48
+ };
49
+
50
+ const clone = async (repo, name) => {
51
+ const spinner = ora("正在拉取项目......").start();
52
+ try {
53
+ await execSync(`git clone --depth 1 git@g.echo.tech:${repo}.git ${name}`);
54
+ const gitPath = path.join(process.cwd(), name, ".git");
55
+ await rm(gitPath, { recursive: true, force: true });
56
+ spinner.succeed(chalk.green(`模版创建成功!请进入 ${name} 目录开始开发`));
57
+ } catch (error) {
58
+ spinner.fail(chalk.red(`模版创建失败: ${error.message}`));
59
+ } finally {
60
+ spinner.stop();
61
+ }
62
+ };
@@ -0,0 +1,36 @@
1
+ import inquirer from "inquirer";
2
+ /**
3
+ * 交互式确认
4
+ * @param {string} message - 提示信息
5
+ * @returns {boolean} - 是否确认
6
+ */
7
+ export const inquirerConfirm = async (message) => {
8
+ const { confirm } = await inquirer.prompt([
9
+ {
10
+ type: "confirm",
11
+ name: "confirm",
12
+ message,
13
+ },
14
+ ]);
15
+ return confirm;
16
+ };
17
+ /**
18
+ * 输入用户账号 密码
19
+ * @param {string} message - 提示信息
20
+ * @returns {string} - 用户账号密码
21
+ */
22
+ export const inquirerAccountPassword = async (message) => {
23
+ const { account, password } = await inquirer.prompt([
24
+ {
25
+ type: "input",
26
+ name: "account",
27
+ message: "请输入用户账号",
28
+ },
29
+ {
30
+ type: "password",
31
+ name: "password",
32
+ message: "请输入用户密码",
33
+ },
34
+ ]);
35
+ return { account, password };
36
+ };
@@ -0,0 +1,68 @@
1
+ import chalk from "chalk";
2
+ import isUnicodeSupported from "is-unicode-supported";
3
+ import figlet from "figlet";
4
+ const _isUnicodeSupported = isUnicodeSupported();
5
+
6
+ /**
7
+ * 日志符号集合
8
+ */
9
+ export const logSymbols = {
10
+ error: _isUnicodeSupported ? "✖" : "×",
11
+ warning: _isUnicodeSupported ? "⚠" : "‼",
12
+ success: _isUnicodeSupported ? "✔" : "√",
13
+ info: _isUnicodeSupported ? "ℹ" : "i",
14
+ star: _isUnicodeSupported ? "★" : "*",
15
+ };
16
+
17
+ /**
18
+ * 通用错误处理工具类
19
+ */
20
+ export class ErrorHandler {
21
+ /**
22
+ * 显示错误信息并退出程序
23
+ * @param {string} message - 错误信息
24
+ */
25
+ static fatal(message) {
26
+ console.trace(chalk.red(`${logSymbols.error} 错误:${message}`));
27
+ process.exit(1);
28
+ }
29
+
30
+ /**
31
+ * 显示警告信息,不退出程序
32
+ * @param {string} message - 警告信息
33
+ */
34
+ static warn(message) {
35
+ console.log(chalk.yellow(`${logSymbols.warning} 警告:${message}`));
36
+ }
37
+
38
+ /**
39
+ * 显示成功信息
40
+ * @param {string} message - 成功信息
41
+ */
42
+ static success(message) {
43
+ console.log(chalk.green(`${logSymbols.success} ${message}`));
44
+ }
45
+
46
+ /**
47
+ * 显示普通信息
48
+ * @param {string} message - 信息内容
49
+ */
50
+ static info(message) {
51
+ console.log(chalk.blue(`${logSymbols.info} ${message}`));
52
+ }
53
+ }
54
+
55
+ /**
56
+ * 简化的错误处理函数
57
+ */
58
+ export const error = (message) => ErrorHandler.fatal(message);
59
+ export const warn = (message) => ErrorHandler.warn(message);
60
+ export const success = (message) => ErrorHandler.success(message);
61
+ export const info = (message) => ErrorHandler.info(message);
62
+
63
+ export const printLogo = () =>
64
+ console.log(
65
+ "\r\n" +
66
+ chalk.hex("#7C66FF").bold(figlet.textSync("qiandao-miniapp")) +
67
+ "\r\n"
68
+ );