flu-cli 2.0.2 → 2.0.4
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/CHANGELOG.md +36 -0
- package/CLI.md +1 -0
- package/README.md +139 -67
- package/config/templates.js +6 -127
- package/index.js +98 -50
- package/lib/commands/add.js +7 -7
- package/lib/commands/assets.js +183 -0
- package/lib/commands/config.js +41 -1
- package/lib/commands/newClack.js +85 -112
- package/lib/commands/snippets.js +50 -9
- package/lib/templates/templateCopier.js +14 -275
- package/lib/templates/templateManager.js +20 -163
- package/lib/utils/config.js +13 -51
- package/lib/utils/flutterHelper.js +7 -82
- package/lib/utils/i18n.js +7 -0
- package/lib/utils/templateSelectorEnquirer.js +70 -43
- package/locales/en-US.json +59 -0
- package/locales/zh-CN.json +59 -0
- package/package.json +2 -3
- package/release.sh +433 -11
- package/scripts/sync-base-to-templates.js +6 -6
- package/scripts/workspace-clone-all.sh +4 -4
- package/scripts/workspace-status-all.sh +3 -3
- package/lib/generators/project_generator.js +0 -96
- package/lib/generators/state_manager_generator.js +0 -402
- package/templates/README.md +0 -138
- package/templates/base_files/base_list_page.dart.template +0 -174
- package/templates/base_files/base_list_viewmodel.dart.template +0 -134
- package/templates/base_files/base_page.dart.template +0 -251
- package/templates/base_files/base_viewmodel.dart.template +0 -77
- package/templates/base_files/theme/status_views_theme.dart.template +0 -46
- package/templates/snippets/dart.code-snippets +0 -392
package/index.js
CHANGED
|
@@ -4,19 +4,53 @@
|
|
|
4
4
|
* flu-cli v2.0
|
|
5
5
|
* Flutter MVVM 脚手架工具
|
|
6
6
|
*
|
|
7
|
-
*
|
|
7
|
+
* 支持多种架构模板 and 代码生成功能
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
// ========== 加载 .env 文件 ==========
|
|
11
|
+
import { readFileSync, existsSync } from 'fs';
|
|
12
|
+
import { resolve } from 'path';
|
|
13
|
+
|
|
14
|
+
function loadEnvFile () {
|
|
15
|
+
try {
|
|
16
|
+
const envPath = resolve(process.cwd(), '.env');
|
|
17
|
+
if (existsSync(envPath)) {
|
|
18
|
+
const envContent = readFileSync(envPath, 'utf8');
|
|
19
|
+
const lines = envContent.split('\n');
|
|
20
|
+
|
|
21
|
+
for (const line of lines) {
|
|
22
|
+
const trimmed = line.trim();
|
|
23
|
+
// 跳过注释和空行
|
|
24
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
25
|
+
|
|
26
|
+
const [key, ...valueParts] = trimmed.split('=');
|
|
27
|
+
const value = valueParts.join('=').trim();
|
|
28
|
+
|
|
29
|
+
// 移除引号
|
|
30
|
+
const cleanValue = value.replace(/^["']|["']$/g, '');
|
|
31
|
+
|
|
32
|
+
// 只设置未定义的环境变量
|
|
33
|
+
if (!process.env[key]) {
|
|
34
|
+
process.env[key] = cleanValue;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
} catch (error) {
|
|
39
|
+
// 忽略加载错误
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
loadEnvFile();
|
|
44
|
+
|
|
10
45
|
import { program } from 'commander';
|
|
11
46
|
import chalk from 'chalk';
|
|
12
|
-
import { readFileSync } from 'fs';
|
|
13
47
|
import { fileURLToPath } from 'url';
|
|
14
48
|
import { dirname, join } from 'path';
|
|
15
49
|
import { newProjectWithClack } from './lib/commands/newClack.js';
|
|
16
50
|
import { addComponent } from './lib/commands/add.js';
|
|
17
51
|
import { listTemplates, showTemplateDetail } from './lib/commands/templates.js';
|
|
18
52
|
import { updateTemplates, cleanCache } from './lib/commands/cache.js';
|
|
19
|
-
|
|
53
|
+
import { t } from './lib/utils/i18n.js';
|
|
20
54
|
|
|
21
55
|
// 获取 package.json
|
|
22
56
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -25,10 +59,9 @@ const packageJson = JSON.parse(readFileSync(join(__dirname, 'package.json'), 'ut
|
|
|
25
59
|
// 设置版本和描述
|
|
26
60
|
program
|
|
27
61
|
.version(packageJson.version)
|
|
28
|
-
.description(chalk.cyan('
|
|
62
|
+
.description(chalk.cyan(t('cli.description')));
|
|
29
63
|
|
|
30
64
|
// ========== 创建项目命令(V1 已废弃) ==========
|
|
31
|
-
// V1 命令已移除,请使用 'flu new' 命令
|
|
32
65
|
program
|
|
33
66
|
.command('create')
|
|
34
67
|
.description('⚠️ 已废弃:请使用 flu new 命令')
|
|
@@ -36,11 +69,6 @@ program
|
|
|
36
69
|
console.log(chalk.yellow('\n⚠️ create 命令已在 V2 中废弃\n'));
|
|
37
70
|
console.log(chalk.cyan('请使用新命令:'));
|
|
38
71
|
console.log(chalk.green(' flu new <project-name>\n'));
|
|
39
|
-
console.log(chalk.gray('示例:'));
|
|
40
|
-
console.log(chalk.gray(' flu new my_app --template lite'));
|
|
41
|
-
console.log(chalk.gray(' flu new my_app --template modular'));
|
|
42
|
-
console.log(chalk.gray(' flu new my_app --template clean\n'));
|
|
43
|
-
console.log(chalk.blue('查看帮助:flu new --help\n'));
|
|
44
72
|
process.exit(0);
|
|
45
73
|
});
|
|
46
74
|
|
|
@@ -48,12 +76,14 @@ program
|
|
|
48
76
|
program
|
|
49
77
|
.command('new [project-name]')
|
|
50
78
|
.alias('n')
|
|
51
|
-
.description('
|
|
52
|
-
.option('-t, --template <type>', '
|
|
53
|
-
.option('-s, --state <type>', '
|
|
54
|
-
.option('-d, --dir <path>', '
|
|
55
|
-
.option('--no-cache', '
|
|
56
|
-
.option('--
|
|
79
|
+
.description(t('cmd.new.desc'))
|
|
80
|
+
.option('-t, --template <type>', t('cmd.new.opt.template'))
|
|
81
|
+
.option('-s, --state <type>', t('cmd.new.opt.state'), 'default')
|
|
82
|
+
.option('-d, --dir <path>', t('cmd.new.opt.dir'), process.cwd())
|
|
83
|
+
.option('--no-cache', t('cmd.new.opt.no_cache'))
|
|
84
|
+
.option('--no-network', '不包含网络层(默认包含)')
|
|
85
|
+
.option('--remote', t('cmd.new.opt.remote'))
|
|
86
|
+
.option('--flutter-template <type>', 'Flutter official template (app, module, package, plugin)', 'app')
|
|
57
87
|
.action((projectName, options) => {
|
|
58
88
|
newProjectWithClack(projectName, options);
|
|
59
89
|
});
|
|
@@ -62,14 +92,14 @@ program
|
|
|
62
92
|
program
|
|
63
93
|
.command('add [type] [name]')
|
|
64
94
|
.alias('a')
|
|
65
|
-
.description(
|
|
66
|
-
.option('-f, --feature <name>', '
|
|
67
|
-
.option('--stateful', '
|
|
68
|
-
.option('--stateless', '
|
|
69
|
-
.option('--list-page',
|
|
70
|
-
.option('--no-vm', '
|
|
71
|
-
.option('--json <file>', '
|
|
72
|
-
.option('--list', '
|
|
95
|
+
.description(t('cmd.add.desc'))
|
|
96
|
+
.option('-f, --feature <name>', t('cmd.add.opt.feature'))
|
|
97
|
+
.option('--stateful', t('cmd.add.opt.stateful'))
|
|
98
|
+
.option('--stateless', t('cmd.add.opt.stateless'))
|
|
99
|
+
.option('--list-page', t('cmd.add.opt.list_page'))
|
|
100
|
+
.option('--no-vm', t('cmd.add.opt.no_vm'))
|
|
101
|
+
.option('--json <file>', t('cmd.add.opt.json'))
|
|
102
|
+
.option('--list', t('cmd.add.opt.list'))
|
|
73
103
|
.action((type, name, options) => {
|
|
74
104
|
addComponent(type, name, options);
|
|
75
105
|
});
|
|
@@ -78,7 +108,7 @@ program
|
|
|
78
108
|
program
|
|
79
109
|
.command('templates [template-name]')
|
|
80
110
|
.alias('t')
|
|
81
|
-
.description('
|
|
111
|
+
.description(t('cmd.templates.desc'))
|
|
82
112
|
.action((templateName) => {
|
|
83
113
|
if (templateName) {
|
|
84
114
|
showTemplateDetail(templateName);
|
|
@@ -87,15 +117,13 @@ program
|
|
|
87
117
|
}
|
|
88
118
|
});
|
|
89
119
|
|
|
90
|
-
// generate-sm 命令已移除
|
|
91
|
-
|
|
92
120
|
// ========== 缓存管理命令 ==========
|
|
93
121
|
program
|
|
94
122
|
.command('update-templates [template-name]')
|
|
95
123
|
.alias('update')
|
|
96
124
|
.alias('u')
|
|
97
|
-
.description('
|
|
98
|
-
.option('--force', '
|
|
125
|
+
.description(t('cmd.update.desc'))
|
|
126
|
+
.option('--force', t('cmd.update.opt.force'))
|
|
99
127
|
.action((templateName, options) => {
|
|
100
128
|
updateTemplates(templateName, options);
|
|
101
129
|
});
|
|
@@ -103,7 +131,7 @@ program
|
|
|
103
131
|
program
|
|
104
132
|
.command('cache <action>')
|
|
105
133
|
.alias('c')
|
|
106
|
-
.description(
|
|
134
|
+
.description(t('cmd.cache.desc'))
|
|
107
135
|
.action((action) => {
|
|
108
136
|
if (action === 'clean') {
|
|
109
137
|
cleanCache();
|
|
@@ -113,13 +141,25 @@ program
|
|
|
113
141
|
}
|
|
114
142
|
});
|
|
115
143
|
|
|
144
|
+
// ========== 应用资源管理 ==========
|
|
145
|
+
import { configAssets } from './lib/commands/assets.js';
|
|
146
|
+
|
|
147
|
+
program
|
|
148
|
+
.command('assets')
|
|
149
|
+
.alias('as')
|
|
150
|
+
.description(t('cmd.assets.desc'))
|
|
151
|
+
.option('-d, --dir <path>', t('cmd.new.opt.dir'), process.cwd())
|
|
152
|
+
.action((options) => {
|
|
153
|
+
configAssets(options);
|
|
154
|
+
});
|
|
155
|
+
|
|
116
156
|
// ========== 代码片段管理 ==========
|
|
117
157
|
import { syncSnippets } from './lib/commands/snippets.js';
|
|
118
158
|
|
|
119
159
|
program
|
|
120
160
|
.command('sync-snippets')
|
|
121
161
|
.alias('sync')
|
|
122
|
-
.description('
|
|
162
|
+
.description(t('cmd.sync.desc'))
|
|
123
163
|
.action(() => {
|
|
124
164
|
syncSnippets();
|
|
125
165
|
});
|
|
@@ -128,35 +168,34 @@ program
|
|
|
128
168
|
import { listAllTemplates, addTemplate, removeTemplate } from './lib/commands/template.js';
|
|
129
169
|
|
|
130
170
|
const templateCmd = program.command('template');
|
|
131
|
-
|
|
132
171
|
templateCmd
|
|
133
|
-
.description('
|
|
172
|
+
.description(t('cmd.template.desc'))
|
|
134
173
|
.action(() => {
|
|
135
174
|
listAllTemplates();
|
|
136
175
|
});
|
|
137
176
|
|
|
138
177
|
templateCmd
|
|
139
178
|
.command('list')
|
|
140
|
-
.description('
|
|
179
|
+
.description(t('cmd.template.list.desc'))
|
|
141
180
|
.action(() => {
|
|
142
181
|
listAllTemplates();
|
|
143
182
|
});
|
|
144
183
|
|
|
145
184
|
templateCmd
|
|
146
185
|
.command('add <id> <source>')
|
|
147
|
-
.description(
|
|
148
|
-
.option('--local',
|
|
149
|
-
.option('-n, --name <name>', '
|
|
150
|
-
.option('-b, --branch <branch>',
|
|
151
|
-
.option('-d, --description <text>', '
|
|
152
|
-
.option('-f, --force', '
|
|
186
|
+
.description(t('cmd.template.add.desc'))
|
|
187
|
+
.option('--local', t('cmd.template.add.opt.local'))
|
|
188
|
+
.option('-n, --name <name>', t('cmd.template.add.opt.name'))
|
|
189
|
+
.option('-b, --branch <branch>', t('cmd.template.add.opt.branch'))
|
|
190
|
+
.option('-d, --description <text>', t('cmd.template.add.opt.desc'))
|
|
191
|
+
.option('-f, --force', t('cmd.template.add.opt.force'))
|
|
153
192
|
.action((id, source, options) => {
|
|
154
193
|
addTemplate(id, source, options);
|
|
155
194
|
});
|
|
156
195
|
|
|
157
196
|
templateCmd
|
|
158
197
|
.command('remove <id>')
|
|
159
|
-
.description('
|
|
198
|
+
.description(t('cmd.template.remove.desc'))
|
|
160
199
|
.action((id) => {
|
|
161
200
|
removeTemplate(id);
|
|
162
201
|
});
|
|
@@ -167,37 +206,46 @@ import { completion } from './lib/commands/completion.js';
|
|
|
167
206
|
program
|
|
168
207
|
.command('completion')
|
|
169
208
|
.alias('comp')
|
|
170
|
-
.description(
|
|
209
|
+
.description(t('cmd.completion.desc'))
|
|
171
210
|
.action(() => {
|
|
172
211
|
completion();
|
|
173
212
|
});
|
|
174
213
|
|
|
175
|
-
|
|
176
214
|
// ========== 配置管理命令 ==========
|
|
177
|
-
import { initConfig } from './lib/commands/config.js';
|
|
215
|
+
import { initConfig, setConfig, getConfig } from './lib/commands/config.js';
|
|
178
216
|
|
|
179
217
|
const configCmd = program.command('config');
|
|
180
|
-
|
|
181
218
|
configCmd
|
|
182
|
-
.description('
|
|
219
|
+
.description(t('cmd.config.desc'))
|
|
183
220
|
.action(() => {
|
|
184
221
|
configCmd.help();
|
|
185
222
|
});
|
|
186
223
|
|
|
187
224
|
configCmd
|
|
188
225
|
.command('init')
|
|
189
|
-
.description(
|
|
190
|
-
.option('-d, --dir <path>', '
|
|
226
|
+
.description(t('cmd.config.init.desc'))
|
|
227
|
+
.option('-d, --dir <path>', t('cmd.new.opt.dir'), process.cwd())
|
|
191
228
|
.option('-f, --force', '覆盖已存在的配置文件')
|
|
192
229
|
.action((options) => {
|
|
193
230
|
initConfig(options);
|
|
194
231
|
});
|
|
195
232
|
|
|
233
|
+
configCmd
|
|
234
|
+
.command('set <key> <value>')
|
|
235
|
+
.description('Set global configuration (e.g., locale)')
|
|
236
|
+
.action((key, value) => {
|
|
237
|
+
setConfig(key, value);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
configCmd
|
|
241
|
+
.command('get [key]')
|
|
242
|
+
.description('Get global configuration')
|
|
243
|
+
.action((key) => {
|
|
244
|
+
getConfig(key);
|
|
245
|
+
});
|
|
196
246
|
|
|
197
|
-
// 解析命令行参数
|
|
198
247
|
program.parse(process.argv);
|
|
199
248
|
|
|
200
|
-
// 如果没有提供命令,显示帮助信息
|
|
201
249
|
if (process.argv.length === 2) {
|
|
202
250
|
console.log(chalk.bold.cyan('\n🚀 欢迎使用 flu-cli v2.0\n'));
|
|
203
251
|
program.help({ error: false });
|
package/lib/commands/add.js
CHANGED
|
@@ -162,7 +162,7 @@ async function addPage (name, options) {
|
|
|
162
162
|
logger.info(`不生成 ViewModel`);
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
-
const success = generatePage(name, {
|
|
165
|
+
const success = await generatePage(name, {
|
|
166
166
|
feature,
|
|
167
167
|
stateful: finalStateful,
|
|
168
168
|
stateless: finalStateless,
|
|
@@ -209,7 +209,7 @@ async function addWidget (name, options) {
|
|
|
209
209
|
logger.info(`类型: StatefulWidget`);
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
-
const success = generateWidget(name, {
|
|
212
|
+
const success = await generateWidget(name, {
|
|
213
213
|
feature,
|
|
214
214
|
stateful,
|
|
215
215
|
outputDir: process.cwd()
|
|
@@ -234,7 +234,7 @@ async function addComponentItem (name, options) {
|
|
|
234
234
|
logger.info(`功能模块: ${feature}`);
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
-
const success = generateComponent(name, {
|
|
237
|
+
const success = await generateComponent(name, {
|
|
238
238
|
feature,
|
|
239
239
|
outputDir: options.outputDir || process.cwd()
|
|
240
240
|
}, logger);
|
|
@@ -258,7 +258,7 @@ async function addViewModel (name, options) {
|
|
|
258
258
|
logger.info(`功能模块: ${feature}`);
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
-
const success = generateViewModel(name, {
|
|
261
|
+
const success = await generateViewModel(name, {
|
|
262
262
|
feature,
|
|
263
263
|
outputDir: process.cwd()
|
|
264
264
|
}, logger);
|
|
@@ -282,7 +282,7 @@ async function addService (name, options) {
|
|
|
282
282
|
logger.info(`功能模块: ${feature}`);
|
|
283
283
|
}
|
|
284
284
|
|
|
285
|
-
const success = generateService(name, {
|
|
285
|
+
const success = await generateService(name, {
|
|
286
286
|
feature,
|
|
287
287
|
outputDir: process.cwd()
|
|
288
288
|
}, logger);
|
|
@@ -386,7 +386,7 @@ async function addModel (name, options) {
|
|
|
386
386
|
logger.info(`从 JSON 文件生成: ${json}`);
|
|
387
387
|
}
|
|
388
388
|
|
|
389
|
-
const success = generateModel(name, {
|
|
389
|
+
const success = await generateModel(name, {
|
|
390
390
|
feature,
|
|
391
391
|
jsonFile: options.json || json,
|
|
392
392
|
jsonData,
|
|
@@ -405,7 +405,7 @@ async function addModel (name, options) {
|
|
|
405
405
|
async function addModule (name, options) {
|
|
406
406
|
logger.info(`生成模块: ${name}`);
|
|
407
407
|
|
|
408
|
-
const success = generateModule(name, {
|
|
408
|
+
const success = await generateModule(name, {
|
|
409
409
|
outputDir: options.outputDir || process.cwd()
|
|
410
410
|
});
|
|
411
411
|
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import * as p from '@clack/prompts';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { existsSync } from 'fs';
|
|
5
|
+
import { AppAssetsManager, ConsoleLogger } from '@flu-cli/core';
|
|
6
|
+
import { t } from '../utils/i18n.js';
|
|
7
|
+
|
|
8
|
+
const logger = new ConsoleLogger();
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 交互式配置应用资源
|
|
12
|
+
*/
|
|
13
|
+
export async function configAssets (options) {
|
|
14
|
+
const projectPath = options.dir || process.cwd();
|
|
15
|
+
|
|
16
|
+
// 检查是否在 Flutter 项目中
|
|
17
|
+
if (!existsSync(join(projectPath, 'pubspec.yaml'))) {
|
|
18
|
+
logger.error(t('assets.not_flutter'));
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
p.intro(chalk.cyan.bold(t('assets.intro')));
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const assets = {};
|
|
26
|
+
|
|
27
|
+
// 1. 配置应用图标
|
|
28
|
+
const setupIcon = await p.confirm({
|
|
29
|
+
message: t('assets.setup_icon'),
|
|
30
|
+
initialValue: true
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
if (p.isCancel(setupIcon)) {
|
|
34
|
+
p.cancel(t('common.cancel'));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (setupIcon) {
|
|
39
|
+
const iconPath = await p.text({
|
|
40
|
+
message: t('assets.icon_path'),
|
|
41
|
+
placeholder: 'assets/logo.png',
|
|
42
|
+
validate: (value) => {
|
|
43
|
+
if (!value) return '路径不能为空';
|
|
44
|
+
if (!existsSync(join(projectPath, value)) && !existsSync(value)) {
|
|
45
|
+
return '找不到该文件,请确认路径是否正确';
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
if (p.isCancel(iconPath)) {
|
|
51
|
+
p.cancel(t('common.cancel'));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
assets.appIcon = iconPath;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// 2. 配置启动图
|
|
58
|
+
const setupSplash = await p.confirm({
|
|
59
|
+
message: t('assets.setup_splash'),
|
|
60
|
+
initialValue: true
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
if (p.isCancel(setupSplash)) {
|
|
64
|
+
p.cancel(t('common.cancel'));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (setupSplash) {
|
|
69
|
+
const splashLogo = await p.text({
|
|
70
|
+
message: t('assets.splash_logo'),
|
|
71
|
+
placeholder: 'assets/logo.png',
|
|
72
|
+
validate: (value) => {
|
|
73
|
+
if (!value) return '路径不能为空';
|
|
74
|
+
if (!existsSync(join(projectPath, value)) && !existsSync(value)) {
|
|
75
|
+
return '找不到该文件,请确认路径是否正确';
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
if (p.isCancel(splashLogo)) {
|
|
81
|
+
p.cancel(t('common.cancel'));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
assets.splashLogo = splashLogo;
|
|
85
|
+
|
|
86
|
+
const bgColor = await p.text({
|
|
87
|
+
message: t('assets.bg_color'),
|
|
88
|
+
placeholder: '#FFFFFF',
|
|
89
|
+
initialValue: '#FFFFFF'
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
if (p.isCancel(bgColor)) {
|
|
93
|
+
p.cancel(t('common.cancel'));
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
assets.splashBackgroundColor = bgColor;
|
|
97
|
+
|
|
98
|
+
const useBgImage = await p.confirm({
|
|
99
|
+
message: t('assets.use_bg_image'),
|
|
100
|
+
initialValue: false
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
if (p.isCancel(useBgImage)) {
|
|
104
|
+
p.cancel(t('common.cancel'));
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (useBgImage) {
|
|
109
|
+
const bgImagePath = await p.text({
|
|
110
|
+
message: t('assets.bg_image_path'),
|
|
111
|
+
validate: (value) => {
|
|
112
|
+
if (!value) return '路径不能为空';
|
|
113
|
+
if (!existsSync(join(projectPath, value)) && !existsSync(value)) {
|
|
114
|
+
return '找不到该文件';
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
if (p.isCancel(bgImagePath)) {
|
|
119
|
+
p.cancel(t('common.cancel'));
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
assets.splashBackground = bgImagePath;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// 暗黑模式支持
|
|
126
|
+
const enableDark = await p.confirm({
|
|
127
|
+
message: t('assets.setup_dark'),
|
|
128
|
+
initialValue: false
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
if (p.isCancel(enableDark)) {
|
|
132
|
+
p.cancel(t('common.cancel'));
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (enableDark) {
|
|
137
|
+
assets.enableDarkMode = true;
|
|
138
|
+
|
|
139
|
+
const darkBgColor = await p.text({
|
|
140
|
+
message: t('assets.dark_bg_color'),
|
|
141
|
+
placeholder: '#000000',
|
|
142
|
+
initialValue: '#000000'
|
|
143
|
+
});
|
|
144
|
+
if (!p.isCancel(darkBgColor)) assets.splashBackgroundColorDark = darkBgColor;
|
|
145
|
+
|
|
146
|
+
const useDarkLogo = await p.confirm({
|
|
147
|
+
message: t('assets.use_dark_logo'),
|
|
148
|
+
initialValue: false
|
|
149
|
+
});
|
|
150
|
+
if (useDarkLogo) {
|
|
151
|
+
const darkLogoPath = await p.text({
|
|
152
|
+
message: t('assets.dark_logo_path')
|
|
153
|
+
});
|
|
154
|
+
if (!p.isCancel(darkLogoPath)) assets.splashLogoDark = darkLogoPath;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (Object.keys(assets).length === 0) {
|
|
160
|
+
p.note(t('assets.no_assets'));
|
|
161
|
+
p.outro(chalk.gray(t('common.bye')));
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const s = p.spinner();
|
|
166
|
+
s.start(t('assets.configuring'));
|
|
167
|
+
|
|
168
|
+
const assetsManager = new AppAssetsManager();
|
|
169
|
+
const success = await assetsManager.setupAppAssets(projectPath, assets, logger);
|
|
170
|
+
|
|
171
|
+
if (success) {
|
|
172
|
+
s.stop(t('assets.success'));
|
|
173
|
+
p.outro(chalk.green.bold(t('assets.done')));
|
|
174
|
+
} else {
|
|
175
|
+
s.stop(t('assets.failed'));
|
|
176
|
+
p.outro(chalk.red(t('assets.error_outro')));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
} catch (error) {
|
|
180
|
+
p.cancel(`发生错误: ${error.message}`);
|
|
181
|
+
process.exit(1);
|
|
182
|
+
}
|
|
183
|
+
}
|
package/lib/commands/config.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* 职责:初始化和管理项目配置文件
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { ProjectConfigManager, detectProjectTemplate } from '@flu-cli/core';
|
|
6
|
+
import { ProjectConfigManager, detectProjectTemplate, ConfigManager } from '@flu-cli/core';
|
|
7
7
|
import { writeFileSync, existsSync } from 'fs';
|
|
8
8
|
import { join } from 'path';
|
|
9
9
|
import chalk from 'chalk';
|
|
@@ -68,3 +68,43 @@ export function initConfig (options) {
|
|
|
68
68
|
process.exit(1);
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 设置全局配置
|
|
74
|
+
*/
|
|
75
|
+
export function setConfig (key, value) {
|
|
76
|
+
const config = ConfigManager.getInstance();
|
|
77
|
+
|
|
78
|
+
// 目前只支持 locale
|
|
79
|
+
if (key === 'locale') {
|
|
80
|
+
if (!['zh-CN', 'en-US'].includes(value)) {
|
|
81
|
+
console.log(chalk.red(`不支持的语言: ${value}`));
|
|
82
|
+
console.log(chalk.gray('支持: zh-CN, en-US'));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
config.setLocale(value);
|
|
86
|
+
console.log(chalk.green(`✓ 全局配置已更新: ${key} = ${value}`));
|
|
87
|
+
} else {
|
|
88
|
+
console.log(chalk.yellow(`不支持的配置项: ${key}`));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* 获取全局配置
|
|
94
|
+
*/
|
|
95
|
+
export function getConfig (key) {
|
|
96
|
+
const config = ConfigManager.getInstance();
|
|
97
|
+
|
|
98
|
+
if (key === 'locale') {
|
|
99
|
+
const val = config.getLocale();
|
|
100
|
+
console.log(`${key} = ${val}`);
|
|
101
|
+
} else {
|
|
102
|
+
// 简单起见,如果 key 未知,显示 list
|
|
103
|
+
if (key) {
|
|
104
|
+
console.log(chalk.yellow(`未知配置项: ${key}`));
|
|
105
|
+
} else {
|
|
106
|
+
console.log(`locale = ${config.getLocale()}`);
|
|
107
|
+
console.log(`author = ${config.getAuthorName()}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|