@htyf-mp/cli 0.2.0-beta.1 → 0.2.2

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/src/build.mjs ADDED
@@ -0,0 +1,269 @@
1
+ /**
2
+ * 构建模块
3
+ * 负责小程序和 Godot 游戏的打包构建
4
+ *
5
+ * @module build
6
+ */
7
+
8
+ import path from 'path';
9
+ import fse from 'fs-extra';
10
+ import AdmZip from 'adm-zip';
11
+ import { fileURLToPath } from 'node:url';
12
+ import { Logger } from './logger.mjs';
13
+ import { CONSTANTS } from './constants.mjs';
14
+ import { FileSystemUtils } from './file-system.mjs';
15
+ import { CommandExecutor } from './command-executor.mjs';
16
+ import { getMiniAppScriptId } from './utils-functions.mjs';
17
+ import { getProjectRoot, guessGodotProjectDir } from './utils.mjs';
18
+ import { exportGodot, promptGodotOptions } from './export_godot.mjs';
19
+
20
+ // 获取当前文件所在目录
21
+ const __filename = fileURLToPath(import.meta.url);
22
+ const __dirname = path.dirname(__filename);
23
+ const PROJECT_ROOT = getProjectRoot();
24
+
25
+ /**
26
+ * 压缩文件夹为 ZIP 文件
27
+ *
28
+ * 将指定目录压缩为 ZIP 格式,自动过滤 .map 文件(源码映射文件)
29
+ *
30
+ * @param {string} inputPath - 要压缩的目录路径
31
+ * @param {string} outputPath - 输出的 ZIP 文件路径
32
+ * @returns {Promise<string>} 压缩包完整路径
33
+ * @throws {Error} 当输入路径不存在或不是目录时抛出错误
34
+ *
35
+ * @example
36
+ * const zipPath = await handleZip('./dist', './dist.dgz');
37
+ */
38
+ export async function handleZip(inputPath, outputPath) {
39
+ Logger.info(`开始压缩文件夹: ${inputPath}`);
40
+ Logger.info(`输出文件: ${outputPath}`);
41
+
42
+ // 验证输入路径是否存在
43
+ if (!(await fse.pathExists(inputPath))) {
44
+ throw new Error(`输入路径不存在: ${inputPath}`);
45
+ }
46
+
47
+ // 验证是否为目录
48
+ const stats = await fse.stat(inputPath);
49
+ if (!stats.isDirectory()) {
50
+ throw new Error(`输入路径不是目录: ${inputPath}`);
51
+ }
52
+
53
+ // 检查目录是否为空
54
+ const files = await fse.readdir(inputPath);
55
+ Logger.debug(`目录内容: ${files.length} 个文件/文件夹`);
56
+
57
+ if (files.length === 0) {
58
+ Logger.warn(`警告: 目录为空,将创建空的zip文件`);
59
+ }
60
+
61
+ const zipPath = outputPath;
62
+ const admzip = new AdmZip();
63
+
64
+ try {
65
+ // 添加目录到 ZIP,过滤 .map 文件(源码映射文件,不需要打包)
66
+ await admzip.addLocalFolderPromise(inputPath, {
67
+ filter: (filename) => {
68
+ if (filename.endsWith('.map')) {
69
+ Logger.debug(`过滤文件: ${filename}`);
70
+ return false;
71
+ }
72
+ Logger.debug(`添加文件: ${filename}`);
73
+ return true;
74
+ }
75
+ });
76
+
77
+ // 写入 ZIP 文件
78
+ await admzip.writeZipPromise(zipPath);
79
+ Logger.success(`压缩完成: ${zipPath}`);
80
+
81
+ // 显示压缩包大小
82
+ const zipStats = await fse.stat(zipPath);
83
+ Logger.info(`ZIP文件大小: ${FileSystemUtils.formatFileSize(zipStats.size)}`);
84
+
85
+ return zipPath;
86
+ } catch (error) {
87
+ Logger.error(`压缩失败: ${error.message}`);
88
+ throw error;
89
+ }
90
+ }
91
+
92
+ /**
93
+ * 构建小程序包
94
+ *
95
+ * 支持两种类型的项目构建:
96
+ * 1. 普通小程序项目:使用 webpack 打包
97
+ * 2. Godot 游戏项目:使用 Godot 导出工具打包
98
+ *
99
+ * @param {object} newAppInfo - 应用信息对象
100
+ * @param {string} newAppInfo.name - 应用名称
101
+ * @param {string} newAppInfo.appid - 应用ID
102
+ * @param {string} newAppInfo.version - 应用版本
103
+ * @param {string} [newAppInfo.entry] - 入口文件(普通小程序必需)
104
+ * @param {string} [newAppInfo.host] - 服务器地址
105
+ * @param {string} [newAppInfo.appUrlConfig] - 应用配置URL
106
+ * @param {string} [newAppInfo.zipUrl] - 压缩包URL
107
+ * @param {boolean} [isGodot=false] - 是否为 Godot 项目
108
+ * @returns {Promise<string>} 压缩包完整路径
109
+ * @throws {Error} 当必需字段缺失或构建失败时抛出错误
110
+ *
111
+ * @example
112
+ * const zipPath = await mpBuildShell({
113
+ * name: '我的应用',
114
+ * appid: 'com.example.app',
115
+ * version: '1.0.0',
116
+ * entry: './src/index.js'
117
+ * }, false);
118
+ */
119
+ export async function mpBuildShell(newAppInfo, isGodot = false) {
120
+ Logger.info(`开始构建小程序包...`);
121
+ Logger.info(`应用: ${newAppInfo.name}`);
122
+ Logger.info(`应用ID: ${newAppInfo.appid}`);
123
+ Logger.info(`版本: ${newAppInfo.version}`);
124
+ Logger.info(`平台: ${CONSTANTS.BUNDLE_PLATFORM}`);
125
+ Logger.info(`项目类型: ${isGodot ? 'Godot 游戏' : '普通小程序'}`);
126
+
127
+ // ========== 参数验证 ==========
128
+ if (!newAppInfo.name || !newAppInfo.appid || !newAppInfo.version) {
129
+ throw new Error('缺少必需的应用信息: name, appid, version');
130
+ }
131
+
132
+ // ========== 目录准备 ==========
133
+ // 创建临时目录(用于存放构建过程中的临时文件)
134
+ const tempPath = CONSTANTS.TEMP_DIR;
135
+ if (!FileSystemUtils.ensureDirectory(tempPath)) {
136
+ throw new Error('无法创建临时目录');
137
+ }
138
+
139
+ // 创建输出目录(最终构建产物存放位置)
140
+ const mpOutputPath = CONSTANTS.MP_OUTPUT_DIR;
141
+ if (!FileSystemUtils.ensureDirectory(mpOutputPath)) {
142
+ throw new Error('无法创建输出目录');
143
+ }
144
+
145
+ // ========== 应用配置准备 ==========
146
+ const { version, appid } = newAppInfo;
147
+ const scriptName = getMiniAppScriptId(appid, version);
148
+
149
+ // 构建 app.json 配置
150
+ const appJson = {
151
+ type: isGodot ? 'game' : 'app', // Godot 项目类型为 'game',普通小程序为 'app'
152
+ engines: '2.0.0',
153
+ name: newAppInfo.name,
154
+ appid,
155
+ version,
156
+ appUrlConfig: newAppInfo.appUrlConfig || `${newAppInfo.host}/app.json`,
157
+ zipUrl: newAppInfo.zipUrl || `${newAppInfo.host}/dist.dgz`
158
+ };
159
+
160
+ // 输出路径配置
161
+ let outputPath = path.join(mpOutputPath, 'dist'); // 最终输出目录
162
+ let distPackagePath = path.join(mpOutputPath, 'dist.dgz'); // 最终压缩包路径
163
+ const rootIndexPath = path.join(tempPath, '..', 'index.js');
164
+ // Webpack 构建选项(仅普通小程序使用)
165
+ const mpOptions = {
166
+ name: scriptName,
167
+ filename: `${scriptName}.bundle`,
168
+ exposes: {
169
+ 'App': newAppInfo.entry,
170
+ },
171
+ outputPath: path.join(mpOutputPath, 'build'), // webpack 构建输出目录
172
+ extraChunksPath: outputPath,
173
+ manifest: path.join(mpOutputPath, 'manifest.json'),
174
+ };
175
+ Logger.info('mpOptions', mpOptions);
176
+
177
+ // 保存 app.json 到输出目录
178
+ const appJsonPath = path.join(mpOutputPath, 'app.json');
179
+ await fse.writeJson(appJsonPath, appJson, { spaces: 2 });
180
+ const webpackConfigPath = path.join(__dirname, 'webpack.config.mjs');
181
+
182
+ // ========== 清空输出目录 ==========
183
+ // 确保每次构建都是干净的状态,避免旧文件残留
184
+ Logger.info('清空输出目录...');
185
+ await fse.ensureDir(outputPath);
186
+ await fse.emptyDir(outputPath);
187
+
188
+ // 清空 webpack 构建目录
189
+ // 如果 buildPath 是一个文件,先删除它,然后创建目录
190
+ const buildPath = path.join(mpOutputPath, 'build');
191
+ if (await fse.pathExists(buildPath)) {
192
+ const stats = await fse.stat(buildPath);
193
+ if (stats.isFile()) {
194
+ // 如果是文件,删除它
195
+ await fse.remove(buildPath);
196
+ Logger.info(`已删除文件: ${buildPath}`);
197
+ }
198
+ // 确保是目录并清空
199
+ await fse.ensureDir(buildPath);
200
+ await fse.emptyDir(buildPath);
201
+ } else {
202
+ // 如果不存在,直接创建目录
203
+ await fse.ensureDir(buildPath);
204
+ }
205
+
206
+ // ========== 执行构建 ==========
207
+ try {
208
+ if (!isGodot) {
209
+ // ========== 普通小程序构建流程 ==========
210
+ Logger.info('使用 Webpack 构建小程序包...');
211
+ const command = `npx cross-env APP_ROOT_INDEX_PATH=${rootIndexPath} APP_EXPOSES_OPTIONS=${encodeURI(JSON.stringify(mpOptions))} webpack --config ${webpackConfigPath}`;
212
+ await CommandExecutor.execute(command, {
213
+ description: '构建小程序包',
214
+ timeout: 120000, // 2 分钟超时
215
+ showCommand: false
216
+ });
217
+ } else {
218
+ // ========== Godot 游戏构建流程 ==========
219
+ Logger.info('检测到 Godot 项目,开始执行 Godot 导出流程...');
220
+
221
+ // 根据平台常量确定默认平台
222
+ const defaultPlatform = ['android', 'ios'].includes(CONSTANTS.BUNDLE_PLATFORM)
223
+ ? CONSTANTS.BUNDLE_PLATFORM
224
+ : 'ios';
225
+
226
+ // 准备 Godot 导出选项的默认值
227
+ const godotDefaults = {
228
+ targetBaseDir: outputPath,
229
+ projectDir: guessGodotProjectDir(PROJECT_ROOT),
230
+ name: newAppInfo.appid || 'GodotApp',
231
+ preset: defaultPlatform === 'android' ? 'Android' : 'iOS',
232
+ platform: defaultPlatform
233
+ };
234
+
235
+ // 提示用户输入或确认 Godot 导出选项
236
+ const godotOptions = await promptGodotOptions(godotDefaults);
237
+ outputPath = path.resolve(godotOptions.targetBaseDir);
238
+ await fse.ensureDir(outputPath);
239
+
240
+ // 执行 Godot 导出
241
+ await exportGodot(godotOptions);
242
+
243
+ // Godot 项目使用平台特定的文件名(如 dist.ios.dgz 或 dist.android.dgz)
244
+ const platformName = godotOptions.platform === 'ios' ? 'ios' : 'android';
245
+ distPackagePath = path.join(mpOutputPath, `dist.${platformName}.dgz`);
246
+
247
+ // 更新 app.json 中的 zipUrl
248
+ appJson.zipUrl = newAppInfo.zipUrl || `${newAppInfo.host}/dist.${platformName}.dgz`;
249
+ await fse.writeJson(appJsonPath, appJson, { spaces: 2 });
250
+ }
251
+
252
+ // ========== 打包最终产物 ==========
253
+ // 将 app.json 复制到输出目录
254
+ await fse.ensureDir(outputPath);
255
+ await fse.copy(appJsonPath, path.join(outputPath, 'app.json'));
256
+ if (!isGodot) {
257
+ fse.copyFileSync(path.join(outputPath, '../manifest.json'), path.join(outputPath, 'manifest.json'));
258
+ }
259
+
260
+ // 压缩输出目录为最终包
261
+ const zipPath = await handleZip(outputPath, distPackagePath);
262
+ Logger.success(`压缩包已创建: ${zipPath}`);
263
+ return zipPath;
264
+ } catch (error) {
265
+ Logger.error(`小程序构建失败: ${error.message}`);
266
+ throw error;
267
+ }
268
+ }
269
+
package/src/clean.mjs ADDED
@@ -0,0 +1,83 @@
1
+ import path from 'path';
2
+ import { Logger } from './logger.mjs';
3
+ import { CONSTANTS } from './constants.mjs';
4
+ import { FileSystemUtils } from './file-system.mjs';
5
+ import { getProjectRoot } from './utils.mjs';
6
+
7
+ const projectPath = getProjectRoot();
8
+
9
+ /**
10
+ * 清理临时文件
11
+ * @param {string} cleanType - 清理类型 ('all', 'build', 'temp', 'logs', 'cache', 或应用名称)
12
+ */
13
+ export async function cleanShell(cleanType = 'all') {
14
+ Logger.info('开始清理文件...');
15
+
16
+ const pathsToClean = [];
17
+ let totalSizeBefore = 0;
18
+
19
+ if (cleanType === 'build') {
20
+ // 只清理构建输出
21
+ const buildPath = CONSTANTS.MP_OUTPUT_DIR;
22
+ pathsToClean.push(buildPath);
23
+ totalSizeBefore += FileSystemUtils.calculateDirectorySize(buildPath);
24
+ Logger.info('清理构建输出文件...');
25
+ } else if (cleanType === 'temp') {
26
+ // 只清理临时文件
27
+ const tempPath = CONSTANTS.TEMP_DIR;
28
+ pathsToClean.push(tempPath);
29
+ totalSizeBefore += FileSystemUtils.calculateDirectorySize(tempPath);
30
+ Logger.info('清理临时文件...');
31
+ } else if (cleanType === 'logs') {
32
+ // 清理日志文件
33
+ const logsPath = CONSTANTS.LOGS_DIR;
34
+ pathsToClean.push(logsPath);
35
+ totalSizeBefore += FileSystemUtils.calculateDirectorySize(logsPath);
36
+ Logger.info('清理日志文件...');
37
+ } else if (cleanType === 'cache') {
38
+ // 清理缓存文件
39
+ const cachePaths = [
40
+ path.join(projectPath, 'node_modules/.cache'),
41
+ path.join(projectPath, '.cache'),
42
+ path.join(projectPath, 'dist/.cache')
43
+ ];
44
+ pathsToClean.push(...cachePaths);
45
+ cachePaths.forEach(cachePath => {
46
+ totalSizeBefore += FileSystemUtils.calculateDirectorySize(cachePath);
47
+ });
48
+ Logger.info('清理缓存文件...');
49
+ } else if (cleanType && cleanType !== 'all') {
50
+ // 清理指定应用的文件
51
+ const appTempPath = path.join(CONSTANTS.TEMP_DIR, cleanType);
52
+ const appBuildPath = path.join(CONSTANTS.MP_OUTPUT_DIR, cleanType);
53
+ pathsToClean.push(appTempPath, appBuildPath);
54
+ totalSizeBefore += FileSystemUtils.calculateDirectorySize(appTempPath) + FileSystemUtils.calculateDirectorySize(appBuildPath);
55
+ Logger.info(`清理应用 ${cleanType} 的文件...`);
56
+ } else {
57
+ // 清理所有文件
58
+ const allPaths = [
59
+ path.join(CONSTANTS.TEMP_DIR),
60
+ path.join(CONSTANTS.MP_OUTPUT_DIR),
61
+ path.join(CONSTANTS.LOGS_DIR),
62
+ path.join(projectPath, 'node_modules/.cache'),
63
+ path.join(projectPath, '.cache'),
64
+ path.join(projectPath, 'dist/.cache')
65
+ ];
66
+ pathsToClean.push(...allPaths);
67
+ allPaths.forEach(cleanPath => {
68
+ totalSizeBefore += FileSystemUtils.calculateDirectorySize(cleanPath);
69
+ });
70
+ Logger.info('清理所有临时文件...');
71
+ }
72
+
73
+ let cleanedCount = 0;
74
+ for (const cleanPath of pathsToClean) {
75
+ if (FileSystemUtils.cleanDirectory(cleanPath)) {
76
+ cleanedCount++;
77
+ }
78
+ }
79
+
80
+ const freedSpace = FileSystemUtils.formatFileSize(totalSizeBefore);
81
+ Logger.success(`清理完成,已清理 ${cleanedCount} 个目录,释放空间: ${freedSpace}`);
82
+ }
83
+
@@ -0,0 +1,133 @@
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
+ // 信号处理:当收到退出信号时,停止 spinner
24
+ const cleanup = () => {
25
+ spinner.stop();
26
+ };
27
+ process.once('SIGINT', cleanup);
28
+ process.once('SIGTERM', cleanup);
29
+
30
+ try {
31
+ for (let attempt = 1; attempt <= retryCount; attempt++) {
32
+ try {
33
+ Logger.debug(`执行命令 (尝试 ${attempt}/${retryCount}): ${command}`);
34
+
35
+ const result = await new Promise((resolve, reject) => {
36
+ const timeoutId = setTimeout(() => {
37
+ reject(new Error(`命令执行超时 (${timeout}ms)`));
38
+ }, timeout);
39
+
40
+ shell.exec(command, {
41
+ silent: silent,
42
+ fatal: false,
43
+ async: false
44
+ }, (code, stdout, stderr) => {
45
+ clearTimeout(timeoutId);
46
+ resolve({ code, stdout, stderr });
47
+ });
48
+ });
49
+
50
+ if (result.code === 0) {
51
+ process.removeListener('SIGINT', cleanup);
52
+ process.removeListener('SIGTERM', cleanup);
53
+ spinner.succeed(`${description} 成功`);
54
+ Logger.debug(`命令输出: ${result.stdout}`);
55
+ return result;
56
+ } else {
57
+ throw new Error(`命令执行失败,退出码: ${result.code}, 错误: ${result.stderr}`);
58
+ }
59
+ } catch (error) {
60
+ // 如果是用户中断,直接退出
61
+ if (error.message && (error.message.includes('SIGINT') || error.message.includes('SIGTERM'))) {
62
+ process.removeListener('SIGINT', cleanup);
63
+ process.removeListener('SIGTERM', cleanup);
64
+ spinner.stop();
65
+ throw error;
66
+ }
67
+
68
+ if (attempt === retryCount) {
69
+ process.removeListener('SIGINT', cleanup);
70
+ process.removeListener('SIGTERM', cleanup);
71
+ spinner.fail(`${description} 失败 (${retryCount} 次尝试后)`);
72
+ Logger.error(`命令执行失败: ${error.message}`);
73
+
74
+ if (fatal) {
75
+ process.exit(1);
76
+ }
77
+ throw error;
78
+ } else {
79
+ Logger.warn(`命令执行失败,准备重试 (${attempt}/${retryCount}): ${error.message}`);
80
+ await new Promise(resolve => setTimeout(resolve, 1000 * attempt)); // 递增延迟
81
+ }
82
+ }
83
+ }
84
+ } catch (error) {
85
+ process.removeListener('SIGINT', cleanup);
86
+ process.removeListener('SIGTERM', cleanup);
87
+ spinner.stop();
88
+ throw error;
89
+ }
90
+ }
91
+
92
+ static async executeWithProgress(command, options = {}) {
93
+ const { description = '执行命令', progressCallback } = options;
94
+
95
+ return new Promise((resolve, reject) => {
96
+ const child = shell.exec(command, {
97
+ silent: false,
98
+ fatal: false,
99
+ async: true
100
+ });
101
+
102
+ let output = '';
103
+
104
+ child.stdout?.on('data', (data) => {
105
+ output += data;
106
+ if (progressCallback) {
107
+ progressCallback(data.toString());
108
+ }
109
+ });
110
+
111
+ child.stderr?.on('data', (data) => {
112
+ output += data;
113
+ Logger.warn(`命令输出: ${data}`);
114
+ });
115
+
116
+ child.on('close', (code) => {
117
+ if (code === 0) {
118
+ Logger.success(`${description} 完成`);
119
+ resolve({ code, output });
120
+ } else {
121
+ Logger.error(`${description} 失败,退出码: ${code}`);
122
+ reject(new Error(`命令执行失败,退出码: ${code}`));
123
+ }
124
+ });
125
+
126
+ child.on('error', (error) => {
127
+ Logger.error(`${description} 出错: ${error.message}`);
128
+ reject(error);
129
+ });
130
+ });
131
+ }
132
+ }
133
+
package/src/config.mjs ADDED
@@ -0,0 +1,52 @@
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.APP_TEMPLATE]: {
17
+ name: 'app-template',
18
+ tempPath: '_apps_temp_'
19
+ },
20
+ [CONSTANTS.TEMPLATE_TYPES.GAME_TEMPLATE]: {
21
+ name: 'game-template',
22
+ tempPath: '_games_temp_'
23
+ }
24
+ };
25
+ }
26
+
27
+ validateAppName(name) {
28
+ return this.validators.appName.test(name);
29
+ }
30
+
31
+ validateDisplayName(name) {
32
+ return this.validators.displayName.test(name);
33
+ }
34
+
35
+ generateAppId() {
36
+ return md5(uuidv4({
37
+ random: [0x10, 0x91, 0x56, 0xbe, 0xc4, 0xfb, 0xc1, 0xea, 0x71, 0xb4, 0xef, 0xe1, 0x67, 0x1c, 0x58, 0x36]
38
+ }));
39
+ }
40
+
41
+ createProjectConfig(appName, displayName, appid) {
42
+ return {
43
+ type: 'app',
44
+ name: appName,
45
+ projectname: displayName,
46
+ appid: appid,
47
+ appUrlConfig: 'https://xxx.cos.ap-chengdu.myqcloud.com/assets/testMiniApps/htyanimation/app.json',
48
+ zipUrl: 'https://xxxxx.myqcloud.com/assets/testMiniApps/xxxxx/outputs/dist.dgz'
49
+ };
50
+ }
51
+ }
52
+
@@ -0,0 +1,54 @@
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
+ APP_TEMPLATE: 'app-template',
29
+ GAME_TEMPLATE: 'game-template',
30
+ }
31
+ };
32
+
33
+ /**
34
+ * 操作类型枚举
35
+ */
36
+ export const ACTION_TYPES = {
37
+ START: 'start',
38
+ INIT: 'init',
39
+ MP_BUILD: 'mp-build',
40
+ MP_DEBUG: 'mp-debug',
41
+ CLEAN: 'clean',
42
+ QUIT: 'quit'
43
+ };
44
+
45
+ /**
46
+ * 日志级别枚举
47
+ */
48
+ export const LOG_LEVELS = {
49
+ DEBUG: 0,
50
+ INFO: 1,
51
+ WARN: 2,
52
+ ERROR: 3
53
+ };
54
+