@htyf-mp/cli 0.1.3 → 0.2.0-alpha.3

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.
@@ -0,0 +1,107 @@
1
+ import shell from 'shelljs';
2
+ import ora from 'ora';
3
+ import { Logger } from './logger.mjs';
4
+ import { CONSTANTS } from './constants.mjs';
5
+
6
+ /**
7
+ * 命令执行工具类
8
+ */
9
+ export class CommandExecutor {
10
+ static async execute(command, options = {}) {
11
+ const {
12
+ silent = false,
13
+ fatal = true,
14
+ timeout = CONSTANTS.DEFAULT_TIMEOUT,
15
+ retryCount = CONSTANTS.MAX_RETRY_COUNT,
16
+ description = '执行命令',
17
+ showCommand = false
18
+ } = options;
19
+
20
+ const displayCommand = showCommand ? command : description;
21
+ const spinner = ora(displayCommand).start();
22
+
23
+ for (let attempt = 1; attempt <= retryCount; attempt++) {
24
+ try {
25
+ Logger.debug(`执行命令 (尝试 ${attempt}/${retryCount}): ${command}`);
26
+
27
+ const result = await new Promise((resolve, reject) => {
28
+ const timeoutId = setTimeout(() => {
29
+ reject(new Error(`命令执行超时 (${timeout}ms)`));
30
+ }, timeout);
31
+
32
+ shell.exec(command, {
33
+ silent: silent,
34
+ fatal: false,
35
+ async: false
36
+ }, (code, stdout, stderr) => {
37
+ clearTimeout(timeoutId);
38
+ resolve({ code, stdout, stderr });
39
+ });
40
+ });
41
+
42
+ if (result.code === 0) {
43
+ spinner.succeed(`${description} 成功`);
44
+ Logger.debug(`命令输出: ${result.stdout}`);
45
+ return result;
46
+ } else {
47
+ throw new Error(`命令执行失败,退出码: ${result.code}, 错误: ${result.stderr}`);
48
+ }
49
+ } catch (error) {
50
+ if (attempt === retryCount) {
51
+ spinner.fail(`${description} 失败 (${retryCount} 次尝试后)`);
52
+ Logger.error(`命令执行失败: ${error.message}`);
53
+
54
+ if (fatal) {
55
+ process.exit(1);
56
+ }
57
+ throw error;
58
+ } else {
59
+ Logger.warn(`命令执行失败,准备重试 (${attempt}/${retryCount}): ${error.message}`);
60
+ await new Promise(resolve => setTimeout(resolve, 1000 * attempt)); // 递增延迟
61
+ }
62
+ }
63
+ }
64
+ }
65
+
66
+ static async executeWithProgress(command, options = {}) {
67
+ const { description = '执行命令', progressCallback } = options;
68
+
69
+ return new Promise((resolve, reject) => {
70
+ const child = shell.exec(command, {
71
+ silent: false,
72
+ fatal: false,
73
+ async: true
74
+ });
75
+
76
+ let output = '';
77
+
78
+ child.stdout?.on('data', (data) => {
79
+ output += data;
80
+ if (progressCallback) {
81
+ progressCallback(data.toString());
82
+ }
83
+ });
84
+
85
+ child.stderr?.on('data', (data) => {
86
+ output += data;
87
+ Logger.warn(`命令输出: ${data}`);
88
+ });
89
+
90
+ child.on('close', (code) => {
91
+ if (code === 0) {
92
+ Logger.success(`${description} 完成`);
93
+ resolve({ code, output });
94
+ } else {
95
+ Logger.error(`${description} 失败,退出码: ${code}`);
96
+ reject(new Error(`命令执行失败,退出码: ${code}`));
97
+ }
98
+ });
99
+
100
+ child.on('error', (error) => {
101
+ Logger.error(`${description} 出错: ${error.message}`);
102
+ reject(error);
103
+ });
104
+ });
105
+ }
106
+ }
107
+
package/src/config.mjs ADDED
@@ -0,0 +1,55 @@
1
+ import md5 from 'md5';
2
+ import { v4 as uuidv4 } from 'uuid';
3
+ import { CONSTANTS } from './constants.mjs';
4
+
5
+ /**
6
+ * 项目初始化配置类
7
+ */
8
+ export class ProjectConfig {
9
+ constructor() {
10
+ this.validators = {
11
+ appName: /^([a-z]+)(-[a-z0-9]+)*$/,
12
+ displayName: /^[\u4e00-\u9fa5a-zA-Z0-9]{2,4}$/
13
+ };
14
+
15
+ this.templates = {
16
+ [CONSTANTS.TEMPLATE_TYPES.EXPO]: {
17
+ name: 'Expo (https://docs.expo.dev/)',
18
+ tempPath: 'mini-apps-template',
19
+ files: {
20
+ src: 'expo_src',
21
+ app: 'expo.App.tsx',
22
+ index: 'expo.index.js',
23
+ babel: 'expo.babel.config.js',
24
+ config: 'expo.project.dgz.json'
25
+ }
26
+ },
27
+ };
28
+ }
29
+
30
+ validateAppName(name) {
31
+ return this.validators.appName.test(name);
32
+ }
33
+
34
+ validateDisplayName(name) {
35
+ return this.validators.displayName.test(name);
36
+ }
37
+
38
+ generateAppId() {
39
+ return md5(uuidv4({
40
+ random: [0x10, 0x91, 0x56, 0xbe, 0xc4, 0xfb, 0xc1, 0xea, 0x71, 0xb4, 0xef, 0xe1, 0x67, 0x1c, 0x58, 0x36]
41
+ }));
42
+ }
43
+
44
+ createProjectConfig(appName, displayName, appid) {
45
+ return {
46
+ type: 'app',
47
+ name: appName,
48
+ projectname: displayName,
49
+ appid: appid,
50
+ appUrlConfig: 'https://xxx.cos.ap-chengdu.myqcloud.com/assets/testMiniApps/htyanimation/app.json',
51
+ zipUrl: 'https://xxxxx.myqcloud.com/assets/testMiniApps/xxxxx/outputs/dist.dgz'
52
+ };
53
+ }
54
+ }
55
+
@@ -0,0 +1,55 @@
1
+ import path from 'path';
2
+ import { getProjectRoot } from './utils.mjs';
3
+
4
+ const projectPath = getProjectRoot();
5
+
6
+ /**
7
+ * CLI 工具常量定义
8
+ */
9
+ export const CONSTANTS = {
10
+ SRC_DIR: path.join(projectPath, './src'),
11
+ TEMP_DIR: path.join(projectPath, './.htyf'),
12
+ MP_OUTPUT_DIR: path.join(projectPath, './dist'),
13
+ LOGS_DIR: path.join(projectPath, './.htyf/.logs'),
14
+ DEFAULT_VERSION_NAME: '1.0.0',
15
+ DEFAULT_VERSION_CODE: '1',
16
+ VERSION_NAME_REGEX: /^\d+(\.\d+){2}$/g,
17
+ VERSION_CODE_REGEX: /^\d+$/g,
18
+ WEBPACK_PORT: 8081,
19
+ BUNDLE_ENTRY_FILE: 'index.js',
20
+ BUNDLE_PLATFORM: 'ios',
21
+ MAX_RETRY_COUNT: 3,
22
+ DEFAULT_TIMEOUT: 30000,
23
+ TEMPLATE_REPOS: {
24
+ GITHUB: 'https://github.com/htyf-mp-community/htyf-mp.git',
25
+ CODING: 'https://e.coding.net/dagouzhi/hongtangyun_mobile/htyf-mp.git'
26
+ },
27
+ TEMPLATE_TYPES: {
28
+ EXPO: 'expo',
29
+ TARO: 'taro',
30
+ GAME_COCOS: 'game-cocos',
31
+ }
32
+ };
33
+
34
+ /**
35
+ * 操作类型枚举
36
+ */
37
+ export const ACTION_TYPES = {
38
+ START: 'start',
39
+ INIT: 'init',
40
+ MP_BUILD: 'mp-build',
41
+ MP_DEBUG: 'mp-debug',
42
+ CLEAN: 'clean',
43
+ QUIT: 'quit'
44
+ };
45
+
46
+ /**
47
+ * 日志级别枚举
48
+ */
49
+ export const LOG_LEVELS = {
50
+ DEBUG: 0,
51
+ INFO: 1,
52
+ WARN: 2,
53
+ ERROR: 3
54
+ };
55
+
package/src/debug.mjs ADDED
@@ -0,0 +1,111 @@
1
+ import path from 'path';
2
+ import fse from 'fs-extra';
3
+ import superstatic from 'superstatic';
4
+ import portfinder from 'portfinder';
5
+ import chalk from 'chalk';
6
+ import boxen from 'boxen';
7
+ import gradient from 'gradient-string';
8
+ import { Logger } from './logger.mjs';
9
+ import { ConfigValidator } from './validators.mjs';
10
+ import { mpBuildShell } from './build.mjs';
11
+ import { printQrcode } from './utils-functions.mjs';
12
+
13
+ /**
14
+ * 小程序真机调试
15
+ * @param {object} newAppInfo - 应用信息
16
+ */
17
+ export async function mpDebugShell(newAppInfo) {
18
+ try {
19
+ // 验证必需字段
20
+ if (!newAppInfo.entry) {
21
+ throw new Error('缺少必需的应用信息: entry');
22
+ }
23
+
24
+ const zipPath = await mpBuildShell(newAppInfo);
25
+ const assetsPath = path.dirname(zipPath);
26
+ const appJsonPath = path.join(assetsPath, 'app.json');
27
+
28
+ Logger.info(`资源路径: ${assetsPath}`);
29
+ const applocaljson = fse.readJsonSync(appJsonPath);
30
+
31
+ // 验证网络配置
32
+ const networkValidation = await ConfigValidator.validateNetworkConfig();
33
+ if (!networkValidation.isValid) {
34
+ throw new Error(`网络配置验证失败: ${networkValidation.error}`);
35
+ }
36
+
37
+ const host = networkValidation.host;
38
+ const port = await portfinder.getPortPromise();
39
+
40
+ const app = superstatic.server({
41
+ port: port,
42
+ address: host,
43
+ cwd: assetsPath
44
+ });
45
+
46
+ const server = app.listen(function () {
47
+ const address = server.address();
48
+
49
+ const debugerAppUrlConfig = `http://${host}:${address.port}/app.json`;
50
+ const debugerZip = `http://${host}:${address.port}/dist.dgz`;
51
+ const args = {
52
+ ...applocaljson,
53
+ development: true,
54
+ "name": applocaljson.name,
55
+ "appid": applocaljson.appid,
56
+ "version": applocaljson.version,
57
+ appUrlConfig: debugerAppUrlConfig,
58
+ zipUrl: debugerZip,
59
+ };
60
+
61
+ fse.writeJSONSync(appJsonPath, {
62
+ ...fse.readJSONSync(appJsonPath),
63
+ appUrlConfig: debugerAppUrlConfig,
64
+ zipUrl: debugerZip,
65
+ });
66
+
67
+ const qrcodeUrl = `https://share.dagouzhi.com/#/pages/index/index?data=${encodeURIComponent(JSON.stringify(args))}`;
68
+
69
+ // 显示调试信息
70
+ console.log('\n');
71
+ console.log(boxen(
72
+ gradient.rainbow('小程序真机调试') + '\n\n' +
73
+ chalk.cyan('服务器地址: ') + chalk.white(`${host}:${address.port}`) + '\n' +
74
+ chalk.cyan('应用名称: ') + chalk.white(applocaljson.name) + '\n' +
75
+ chalk.cyan('应用ID: ') + chalk.white(applocaljson.appid) + '\n' +
76
+ chalk.cyan('版本: ') + chalk.white(applocaljson.version),
77
+ {
78
+ padding: 1,
79
+ margin: 1,
80
+ borderStyle: 'round',
81
+ borderColor: 'cyan'
82
+ }
83
+ ));
84
+
85
+ console.log('\n' + chalk.yellow('调试配置:'));
86
+ console.log(JSON.stringify(args, undefined, 2));
87
+
88
+ console.log('\n' + chalk.green('调试链接:'));
89
+ console.log(qrcodeUrl);
90
+
91
+ console.log('\n' + chalk.blue('二维码:'));
92
+ printQrcode(qrcodeUrl);
93
+
94
+ Logger.info('调试服务器已启动,按 Ctrl+C 停止');
95
+ });
96
+
97
+ // 优雅关闭
98
+ process.on('SIGINT', () => {
99
+ Logger.info('正在关闭调试服务器...');
100
+ server.close(() => {
101
+ Logger.success('调试服务器已关闭');
102
+ process.exit(0);
103
+ });
104
+ });
105
+
106
+ } catch (error) {
107
+ Logger.error(`小程序调试失败: ${error.message}`);
108
+ process.exit(1);
109
+ }
110
+ }
111
+
@@ -0,0 +1,96 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import fse from 'fs-extra';
4
+ import { Logger } from './logger.mjs';
5
+
6
+ /**
7
+ * 文件系统工具类
8
+ */
9
+ export class FileSystemUtils {
10
+ static ensureDirectory(dirPath) {
11
+ try {
12
+ fse.ensureDirSync(dirPath);
13
+ Logger.debug(`目录已确保存在: ${dirPath}`);
14
+ return true;
15
+ } catch (error) {
16
+ Logger.error(`创建目录失败: ${dirPath}`, error.message);
17
+ return false;
18
+ }
19
+ }
20
+
21
+ static cleanDirectory(dirPath) {
22
+ try {
23
+ if (fs.existsSync(dirPath)) {
24
+ fse.removeSync(dirPath);
25
+ Logger.info(`目录已清理: ${dirPath}`);
26
+ }
27
+ return true;
28
+ } catch (error) {
29
+ Logger.error(`清理目录失败: ${dirPath}`, error.message);
30
+ return false;
31
+ }
32
+ }
33
+
34
+ static backupFile(filePath) {
35
+ try {
36
+ if (fs.existsSync(filePath)) {
37
+ const backupPath = `${filePath}.backup.${Date.now()}`;
38
+ fse.copySync(filePath, backupPath);
39
+ Logger.info(`文件已备份: ${backupPath}`);
40
+ return backupPath;
41
+ }
42
+ return null;
43
+ } catch (error) {
44
+ Logger.error(`备份文件失败: ${filePath}`, error.message);
45
+ return null;
46
+ }
47
+ }
48
+
49
+ static getFileSize(filePath) {
50
+ try {
51
+ const stats = fs.statSync(filePath);
52
+ return stats.size;
53
+ } catch (error) {
54
+ return 0;
55
+ }
56
+ }
57
+
58
+ static formatFileSize(bytes) {
59
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
60
+ if (bytes === 0) return '0 Bytes';
61
+ const i = Math.floor(Math.log(bytes) / Math.log(1024));
62
+ return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i];
63
+ }
64
+
65
+ static validatePath(path) {
66
+ // if (/[ - fa5]/gi.test(path)) {
67
+ // return { isValid: false, error: '路径中包含中文字符' };
68
+ // }
69
+ return { isValid: true };
70
+ }
71
+
72
+ /**
73
+ * 计算目录大小
74
+ * @param {string} dirPath - 目录路径
75
+ * @returns {number} 目录大小(字节)
76
+ */
77
+ static calculateDirectorySize(dirPath) {
78
+ if (!fs.existsSync(dirPath)) return 0;
79
+ let size = 0;
80
+ try {
81
+ const items = fse.readdirSync(dirPath, { withFileTypes: true });
82
+ for (const item of items) {
83
+ const itemPath = path.join(dirPath, item.name);
84
+ if (item.isDirectory()) {
85
+ size += this.calculateDirectorySize(itemPath);
86
+ } else {
87
+ size += fs.statSync(itemPath).size;
88
+ }
89
+ }
90
+ } catch (error) {
91
+ Logger.warn(`计算目录大小失败: ${dirPath}`, error.message);
92
+ }
93
+ return size;
94
+ }
95
+ }
96
+
package/src/index.mjs ADDED
@@ -0,0 +1,255 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * React Native 应用 CLI 工具
5
+ * 用于管理多应用项目的开发、构建和部署
6
+ *
7
+ * @author CLI Team
8
+ * @version 2.2.0
9
+ */
10
+
11
+ import inquirer from 'inquirer';
12
+ import path from 'path';
13
+ import lodash from 'lodash';
14
+ import fse from 'fs-extra';
15
+ import chalk from 'chalk';
16
+ import boxen from 'boxen';
17
+ import gradient from 'gradient-string';
18
+ import { confirm } from '@inquirer/prompts';
19
+ import { getProjectRoot } from './utils.mjs';
20
+ import { Logger, setLogLevel } from './logger.mjs';
21
+ import { CONSTANTS, ACTION_TYPES, LOG_LEVELS } from './constants.mjs';
22
+ import { ProjectInitializer } from './project-initializer.mjs';
23
+ import { mpBuildShell } from './build.mjs';
24
+ import { mpDebugShell } from './debug.mjs';
25
+ import { cleanShell } from './clean.mjs';
26
+ import { incrementVersion, updateAppConfig } from './utils-functions.mjs';
27
+
28
+ const projectPath = getProjectRoot();
29
+
30
+ /**
31
+ * 主启动函数
32
+ * @param {string} action - 操作类型
33
+ */
34
+ async function Start(action) {
35
+ const actionNames = {
36
+ [ACTION_TYPES.MP_BUILD]: '小程序构建',
37
+ [ACTION_TYPES.MP_DEBUG]: '小程序调试',
38
+ [ACTION_TYPES.CLEAN]: '清理模式',
39
+ [ACTION_TYPES.QUIT]: '退出'
40
+ };
41
+
42
+ Logger.info(`当前操作: ${actionNames[action] || '未知操作'}`);
43
+
44
+ if (action === ACTION_TYPES.QUIT) {
45
+ Logger.info('再见!');
46
+ return;
47
+ }
48
+
49
+ if (action === ACTION_TYPES.CLEAN) {
50
+ const { cleanType } = await inquirer.prompt([
51
+ {
52
+ type: 'list',
53
+ name: 'cleanType',
54
+ message: '请选择清理类型:',
55
+ choices: [
56
+ { name: '🧹 清理所有临时文件', value: 'all' },
57
+ { name: '📁 清理构建输出', value: 'build' },
58
+ { name: '🗂️ 清理临时目录', value: 'temp' },
59
+ { name: '📝 清理日志文件', value: 'logs' },
60
+ { name: '💾 清理缓存文件', value: 'cache' },
61
+ { name: '❌ 取消', value: 'cancel' }
62
+ ]
63
+ }
64
+ ]);
65
+
66
+ if (cleanType === 'cancel') {
67
+ Logger.info('已取消清理操作');
68
+ return;
69
+ }
70
+
71
+ const getCleanTypeName = (type) => {
72
+ switch (type) {
73
+ case 'all': return '所有';
74
+ case 'build': return '构建输出';
75
+ case 'temp': return '临时';
76
+ case 'logs': return '日志';
77
+ case 'cache': return '缓存';
78
+ default: return type;
79
+ }
80
+ };
81
+
82
+ const confirmClean = await confirm({
83
+ message: `确定要清理${getCleanTypeName(cleanType)}文件吗?`,
84
+ default: false
85
+ });
86
+
87
+ if (confirmClean) {
88
+ await cleanShell(cleanType);
89
+ } else {
90
+ Logger.info('已取消清理操作');
91
+ }
92
+ return;
93
+ }
94
+
95
+ // 显示应用选择界面
96
+ try {
97
+ const appInfo = fse.readJsonSync(path.join(projectPath, 'app.json')).htyf;
98
+ if (!appInfo) {
99
+ Logger.error('应用配置不存在,请先在app.json中配置htyf');
100
+ return;
101
+ }
102
+
103
+ const version = appInfo?.version;
104
+ const incrementedVersion = version ? incrementVersion(version) : '1.0.0';
105
+ const { versionName } = await inquirer.prompt([
106
+ {
107
+ type: 'input',
108
+ name: 'versionName',
109
+ message: '请输入应用版本名称 (格式: x.y.z):',
110
+ default: incrementedVersion,
111
+ validate: (input) => {
112
+ const _ipt = input || incrementedVersion;
113
+ return (!!_ipt && /^\d+(\.\d+){2}$/g.test(_ipt)) || '请输入正确的版本名称格式 (x.y.z)';
114
+ }
115
+ },
116
+ ]);
117
+
118
+ Logger.info(`用户选择:`);
119
+ Logger.info(`版本名称: ${versionName}`);
120
+
121
+ const newAppInfo = lodash.merge({}, appInfo, {
122
+ version: versionName,
123
+ });
124
+
125
+ if (!updateAppConfig(newAppInfo)) {
126
+ return;
127
+ }
128
+
129
+ // 显示操作信息
130
+ console.log('\n' + boxen(
131
+ chalk.cyan('应用信息') + '\n\n' +
132
+ chalk.white('名称: ') + chalk.yellow(`${newAppInfo.name}`) + '\n' +
133
+ chalk.white('版本: ') + chalk.yellow(`${newAppInfo.version}`),
134
+ {
135
+ padding: 1,
136
+ margin: 1,
137
+ borderStyle: 'round',
138
+ borderColor: 'yellow'
139
+ }
140
+ ));
141
+
142
+ Logger.info('执行初始化...');
143
+
144
+ switch (action) {
145
+ case ACTION_TYPES.MP_BUILD:
146
+ await mpBuildShell(newAppInfo);
147
+ break;
148
+ case ACTION_TYPES.MP_DEBUG:
149
+ await mpDebugShell(newAppInfo);
150
+ break;
151
+ default:
152
+ Logger.error(`未知的操作类型: ${action}`);
153
+ }
154
+
155
+ } catch (error) {
156
+ Logger.error(`操作失败: ${error.message}`);
157
+ Logger.error(`错误详情: ${error.stack}`);
158
+ }
159
+ }
160
+
161
+ // 主程序入口
162
+ console.log('\n' + boxen(
163
+ gradient.rainbow('🎯 红糖云服 应用 CLI 工具 v2.2.0') + '\n' +
164
+ chalk.gray('用于管理多应用项目的开发、构建和部署') + '\n' +
165
+ chalk.gray(`项目目录:`) + '\n\n' +
166
+ chalk.gray(`${projectPath}`) + '\n',
167
+ {
168
+ padding: 1,
169
+ margin: 1,
170
+ borderStyle: 'round',
171
+ borderColor: 'cyan'
172
+ }
173
+ ));
174
+
175
+ // 设置日志级别
176
+ if (process.argv.includes('--debug')) {
177
+ setLogLevel(LOG_LEVELS.DEBUG);
178
+ Logger.debug('调试模式已启用');
179
+ }
180
+
181
+ // 记录启动日志
182
+ Logger.writeToFile('CLI 工具启动', 'INFO');
183
+
184
+ // 处理命令行清理参数
185
+ if (process.argv.includes('--clean')) {
186
+ const cleanIndex = process.argv.indexOf('--clean');
187
+ const cleanType = process.argv[cleanIndex + 1] || 'all';
188
+
189
+ if (['all', 'build', 'temp', 'logs', 'cache'].includes(cleanType)) {
190
+ Logger.info(`执行命令行清理: ${cleanType}`);
191
+ await cleanShell(cleanType);
192
+ process.exit(0);
193
+ } else {
194
+ Logger.error(`无效的清理类型: ${cleanType}`);
195
+ Logger.error('支持的清理类型: all, build, temp, logs, cache');
196
+ process.exit(1);
197
+ }
198
+ }
199
+
200
+ // 显示帮助信息
201
+ if (process.argv.includes('--help') || process.argv.includes('-h')) {
202
+ console.log('\n' + boxen(
203
+ chalk.cyan('CLI 工具使用说明') + '\n\n' +
204
+ chalk.white('基本用法:') + '\n' +
205
+ chalk.gray(' node index.mjs # 启动交互式界面') + '\n' +
206
+ chalk.gray(' node index.mjs --debug # 启用调试模式') + '\n' +
207
+ chalk.gray(' node index.mjs --help # 显示帮助信息') + '\n\n' +
208
+ chalk.white('清理命令:') + '\n' +
209
+ chalk.gray(' node index.mjs --clean all # 清理所有临时文件') + '\n' +
210
+ chalk.gray(' node index.mjs --clean build # 清理构建输出') + '\n' +
211
+ chalk.gray(' node index.mjs --clean temp # 清理临时目录') + '\n' +
212
+ chalk.gray(' node index.mjs --clean logs # 清理日志文件') + '\n' +
213
+ chalk.gray(' node index.mjs --clean cache # 清理缓存文件') + '\n\n' +
214
+ chalk.white('功能说明:') + '\n' +
215
+ chalk.gray(' 🆕 初始化新小程序项目 - 创建新的小程序项目') + '\n' +
216
+ chalk.gray(' 🔍 小程序打包 - 构建小程序包') + '\n' +
217
+ chalk.gray(' 📦 小程序调试 - 启动真机调试服务') + '\n' +
218
+ chalk.gray(' 🧹 清理模式 - 清理临时文件') + '\n',
219
+ {
220
+ padding: 1,
221
+ margin: 1,
222
+ borderStyle: 'round',
223
+ borderColor: 'blue'
224
+ }
225
+ ));
226
+ process.exit(0);
227
+ }
228
+
229
+ inquirer
230
+ .prompt([
231
+ {
232
+ type: 'rawlist',
233
+ name: 'index',
234
+ message: '请选择你想要执行的操作:',
235
+ choices: [
236
+ { name: '🆕 初始化新小程序项目', value: ACTION_TYPES.INIT },
237
+ { name: '🔍 小程序 - 打包小程序', value: ACTION_TYPES.MP_BUILD },
238
+ { name: '📦 小程序 - 真机调试', value: ACTION_TYPES.MP_DEBUG },
239
+ { name: '🧹 清理模式 - 清理临时文件', value: ACTION_TYPES.CLEAN },
240
+ { name: '👋 退出', value: ACTION_TYPES.QUIT },
241
+ ],
242
+ },
243
+ ])
244
+ .then(async ({ index }) => {
245
+ if (index === ACTION_TYPES.INIT) {
246
+ const initializer = new ProjectInitializer();
247
+ await initializer.initialize();
248
+ } else {
249
+ await Start(index);
250
+ }
251
+ })
252
+ .catch((error) => {
253
+ Logger.error(`程序启动失败: ${error.message}`);
254
+ process.exit(1);
255
+ });