flu-cli 2.0.6 → 2.1.0
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 +23 -0
- package/README.md +17 -4
- package/config/dev.config.js +11 -11
- package/config/templates.js +10 -10
- package/index.js +554 -102
- package/lib/commands/add.js +365 -266
- package/lib/commands/assets.js +77 -78
- package/lib/commands/cache.js +29 -52
- package/lib/commands/completion.js +13 -11
- package/lib/commands/config.js +150 -44
- package/lib/commands/init-ai-base.js +89 -0
- package/lib/commands/newClack.js +269 -178
- package/lib/commands/snippets.js +58 -43
- package/lib/commands/template.js +98 -58
- package/lib/commands/templates.js +101 -57
- package/lib/commands/upload.js +313 -0
- package/lib/commands/vnext-options.js +206 -0
- package/lib/generators/model_generator.js +91 -88
- package/lib/generators/page_generator.js +100 -93
- package/lib/generators/service_generator.js +44 -39
- package/lib/generators/viewmodel_generator.js +25 -29
- package/lib/generators/widget_generator.js +30 -35
- package/lib/templates/templateCopier.js +14 -15
- package/lib/templates/templateManager.js +22 -21
- package/lib/utils/config.js +37 -20
- package/lib/utils/flutterHelper.js +2 -2
- package/lib/utils/i18n.js +3 -3
- package/lib/utils/index_updater.js +22 -23
- package/lib/utils/json-output.js +59 -0
- package/lib/utils/logger.js +17 -17
- package/lib/utils/project_detector.js +66 -66
- package/lib/utils/snippet_loader.js +21 -19
- package/lib/utils/string_helper.js +13 -13
- package/lib/utils/templateSelectorEnquirer.js +94 -108
- package/locales/en-US.json +1 -1
- package/locales/zh-CN.json +2 -2
- package/package.json +60 -57
- package/scripts/smoke-vnext-generate.mjs +1934 -0
- package/scripts/smoke-vnext-params.mjs +92 -0
- package/CLI.md +0 -513
- package/release.sh +0 -529
- package/scripts/e2e-state-tests.js +0 -116
- package/scripts/sync-base-to-templates.js +0 -108
- package/scripts/workspace-clone-all.sh +0 -101
- package/scripts/workspace-status-all.sh +0 -112
package/lib/commands/assets.js
CHANGED
|
@@ -1,38 +1,38 @@
|
|
|
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'
|
|
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
7
|
|
|
8
|
-
const logger = new ConsoleLogger()
|
|
8
|
+
const logger = new ConsoleLogger()
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* 交互式配置应用资源
|
|
12
12
|
*/
|
|
13
|
-
export async function configAssets
|
|
14
|
-
const projectPath = options.dir || process.cwd()
|
|
13
|
+
export async function configAssets(options) {
|
|
14
|
+
const projectPath = options.dir || process.cwd()
|
|
15
15
|
|
|
16
16
|
// 检查是否在 Flutter 项目中
|
|
17
17
|
if (!existsSync(join(projectPath, 'pubspec.yaml'))) {
|
|
18
|
-
logger.error(t('assets.not_flutter'))
|
|
19
|
-
return
|
|
18
|
+
logger.error(t('assets.not_flutter'))
|
|
19
|
+
return
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
p.intro(chalk.cyan.bold(t('assets.intro')))
|
|
22
|
+
p.intro(chalk.cyan.bold(t('assets.intro')))
|
|
23
23
|
|
|
24
24
|
try {
|
|
25
|
-
const assets = {}
|
|
25
|
+
const assets = {}
|
|
26
26
|
|
|
27
27
|
// 1. 配置应用图标
|
|
28
28
|
const setupIcon = await p.confirm({
|
|
29
29
|
message: t('assets.setup_icon'),
|
|
30
|
-
initialValue: true
|
|
31
|
-
})
|
|
30
|
+
initialValue: true,
|
|
31
|
+
})
|
|
32
32
|
|
|
33
33
|
if (p.isCancel(setupIcon)) {
|
|
34
|
-
p.cancel(t('common.cancel'))
|
|
35
|
-
return
|
|
34
|
+
p.cancel(t('common.cancel'))
|
|
35
|
+
return
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
if (setupIcon) {
|
|
@@ -40,29 +40,29 @@ export async function configAssets (options) {
|
|
|
40
40
|
message: t('assets.icon_path'),
|
|
41
41
|
placeholder: 'assets/logo.png',
|
|
42
42
|
validate: (value) => {
|
|
43
|
-
if (!value) return '路径不能为空'
|
|
43
|
+
if (!value) return '路径不能为空'
|
|
44
44
|
if (!existsSync(join(projectPath, value)) && !existsSync(value)) {
|
|
45
|
-
return '找不到该文件,请确认路径是否正确'
|
|
45
|
+
return '找不到该文件,请确认路径是否正确'
|
|
46
46
|
}
|
|
47
|
-
}
|
|
48
|
-
})
|
|
47
|
+
},
|
|
48
|
+
})
|
|
49
49
|
|
|
50
50
|
if (p.isCancel(iconPath)) {
|
|
51
|
-
p.cancel(t('common.cancel'))
|
|
52
|
-
return
|
|
51
|
+
p.cancel(t('common.cancel'))
|
|
52
|
+
return
|
|
53
53
|
}
|
|
54
|
-
assets.appIcon = iconPath
|
|
54
|
+
assets.appIcon = iconPath
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
// 2. 配置启动图
|
|
58
58
|
const setupSplash = await p.confirm({
|
|
59
59
|
message: t('assets.setup_splash'),
|
|
60
|
-
initialValue: true
|
|
61
|
-
})
|
|
60
|
+
initialValue: true,
|
|
61
|
+
})
|
|
62
62
|
|
|
63
63
|
if (p.isCancel(setupSplash)) {
|
|
64
|
-
p.cancel(t('common.cancel'))
|
|
65
|
-
return
|
|
64
|
+
p.cancel(t('common.cancel'))
|
|
65
|
+
return
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
if (setupSplash) {
|
|
@@ -70,114 +70,113 @@ export async function configAssets (options) {
|
|
|
70
70
|
message: t('assets.splash_logo'),
|
|
71
71
|
placeholder: 'assets/logo.png',
|
|
72
72
|
validate: (value) => {
|
|
73
|
-
if (!value) return '路径不能为空'
|
|
73
|
+
if (!value) return '路径不能为空'
|
|
74
74
|
if (!existsSync(join(projectPath, value)) && !existsSync(value)) {
|
|
75
|
-
return '找不到该文件,请确认路径是否正确'
|
|
75
|
+
return '找不到该文件,请确认路径是否正确'
|
|
76
76
|
}
|
|
77
|
-
}
|
|
78
|
-
})
|
|
77
|
+
},
|
|
78
|
+
})
|
|
79
79
|
|
|
80
80
|
if (p.isCancel(splashLogo)) {
|
|
81
|
-
p.cancel(t('common.cancel'))
|
|
82
|
-
return
|
|
81
|
+
p.cancel(t('common.cancel'))
|
|
82
|
+
return
|
|
83
83
|
}
|
|
84
|
-
assets.splashLogo = splashLogo
|
|
84
|
+
assets.splashLogo = splashLogo
|
|
85
85
|
|
|
86
86
|
const bgColor = await p.text({
|
|
87
87
|
message: t('assets.bg_color'),
|
|
88
88
|
placeholder: '#FFFFFF',
|
|
89
|
-
initialValue: '#FFFFFF'
|
|
90
|
-
})
|
|
89
|
+
initialValue: '#FFFFFF',
|
|
90
|
+
})
|
|
91
91
|
|
|
92
92
|
if (p.isCancel(bgColor)) {
|
|
93
|
-
p.cancel(t('common.cancel'))
|
|
94
|
-
return
|
|
93
|
+
p.cancel(t('common.cancel'))
|
|
94
|
+
return
|
|
95
95
|
}
|
|
96
|
-
assets.splashBackgroundColor = bgColor
|
|
96
|
+
assets.splashBackgroundColor = bgColor
|
|
97
97
|
|
|
98
98
|
const useBgImage = await p.confirm({
|
|
99
99
|
message: t('assets.use_bg_image'),
|
|
100
|
-
initialValue: false
|
|
101
|
-
})
|
|
100
|
+
initialValue: false,
|
|
101
|
+
})
|
|
102
102
|
|
|
103
103
|
if (p.isCancel(useBgImage)) {
|
|
104
|
-
p.cancel(t('common.cancel'))
|
|
105
|
-
return
|
|
104
|
+
p.cancel(t('common.cancel'))
|
|
105
|
+
return
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
if (useBgImage) {
|
|
109
109
|
const bgImagePath = await p.text({
|
|
110
110
|
message: t('assets.bg_image_path'),
|
|
111
111
|
validate: (value) => {
|
|
112
|
-
if (!value) return '路径不能为空'
|
|
112
|
+
if (!value) return '路径不能为空'
|
|
113
113
|
if (!existsSync(join(projectPath, value)) && !existsSync(value)) {
|
|
114
|
-
return '找不到该文件'
|
|
114
|
+
return '找不到该文件'
|
|
115
115
|
}
|
|
116
|
-
}
|
|
117
|
-
})
|
|
116
|
+
},
|
|
117
|
+
})
|
|
118
118
|
if (p.isCancel(bgImagePath)) {
|
|
119
|
-
p.cancel(t('common.cancel'))
|
|
120
|
-
return
|
|
119
|
+
p.cancel(t('common.cancel'))
|
|
120
|
+
return
|
|
121
121
|
}
|
|
122
|
-
assets.splashBackground = bgImagePath
|
|
122
|
+
assets.splashBackground = bgImagePath
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
// 暗黑模式支持
|
|
126
126
|
const enableDark = await p.confirm({
|
|
127
127
|
message: t('assets.setup_dark'),
|
|
128
|
-
initialValue: false
|
|
129
|
-
})
|
|
128
|
+
initialValue: false,
|
|
129
|
+
})
|
|
130
130
|
|
|
131
131
|
if (p.isCancel(enableDark)) {
|
|
132
|
-
p.cancel(t('common.cancel'))
|
|
133
|
-
return
|
|
132
|
+
p.cancel(t('common.cancel'))
|
|
133
|
+
return
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
if (enableDark) {
|
|
137
|
-
assets.enableDarkMode = true
|
|
137
|
+
assets.enableDarkMode = true
|
|
138
138
|
|
|
139
139
|
const darkBgColor = await p.text({
|
|
140
140
|
message: t('assets.dark_bg_color'),
|
|
141
141
|
placeholder: '#000000',
|
|
142
|
-
initialValue: '#000000'
|
|
143
|
-
})
|
|
144
|
-
if (!p.isCancel(darkBgColor)) assets.splashBackgroundColorDark = darkBgColor
|
|
142
|
+
initialValue: '#000000',
|
|
143
|
+
})
|
|
144
|
+
if (!p.isCancel(darkBgColor)) assets.splashBackgroundColorDark = darkBgColor
|
|
145
145
|
|
|
146
146
|
const useDarkLogo = await p.confirm({
|
|
147
147
|
message: t('assets.use_dark_logo'),
|
|
148
|
-
initialValue: false
|
|
149
|
-
})
|
|
148
|
+
initialValue: false,
|
|
149
|
+
})
|
|
150
150
|
if (useDarkLogo) {
|
|
151
151
|
const darkLogoPath = await p.text({
|
|
152
|
-
message: t('assets.dark_logo_path')
|
|
153
|
-
})
|
|
154
|
-
if (!p.isCancel(darkLogoPath)) assets.splashLogoDark = darkLogoPath
|
|
152
|
+
message: t('assets.dark_logo_path'),
|
|
153
|
+
})
|
|
154
|
+
if (!p.isCancel(darkLogoPath)) assets.splashLogoDark = darkLogoPath
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
if (Object.keys(assets).length === 0) {
|
|
160
|
-
p.note(t('assets.no_assets'))
|
|
161
|
-
p.outro(chalk.gray(t('common.bye')))
|
|
162
|
-
return
|
|
160
|
+
p.note(t('assets.no_assets'))
|
|
161
|
+
p.outro(chalk.gray(t('common.bye')))
|
|
162
|
+
return
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
-
const s = p.spinner()
|
|
166
|
-
s.start(t('assets.configuring'))
|
|
165
|
+
const s = p.spinner()
|
|
166
|
+
s.start(t('assets.configuring'))
|
|
167
167
|
|
|
168
|
-
const assetsManager = new AppAssetsManager()
|
|
169
|
-
const success = await assetsManager.setupAppAssets(projectPath, assets, logger)
|
|
168
|
+
const assetsManager = new AppAssetsManager()
|
|
169
|
+
const success = await assetsManager.setupAppAssets(projectPath, assets, logger)
|
|
170
170
|
|
|
171
171
|
if (success) {
|
|
172
|
-
s.stop(t('assets.success'))
|
|
173
|
-
p.outro(chalk.green.bold(t('assets.done')))
|
|
172
|
+
s.stop(t('assets.success'))
|
|
173
|
+
p.outro(chalk.green.bold(t('assets.done')))
|
|
174
174
|
} else {
|
|
175
|
-
s.stop(t('assets.failed'))
|
|
176
|
-
p.outro(chalk.red(t('assets.error_outro')))
|
|
175
|
+
s.stop(t('assets.failed'))
|
|
176
|
+
p.outro(chalk.red(t('assets.error_outro')))
|
|
177
177
|
}
|
|
178
|
-
|
|
179
178
|
} catch (error) {
|
|
180
|
-
p.cancel(`发生错误: ${error.message}`)
|
|
181
|
-
process.exit(1)
|
|
179
|
+
p.cancel(`发生错误: ${error.message}`)
|
|
180
|
+
process.exit(1)
|
|
182
181
|
}
|
|
183
182
|
}
|
package/lib/commands/cache.js
CHANGED
|
@@ -3,97 +3,74 @@
|
|
|
3
3
|
* 更新和清理模板缓存
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { logger } from '../utils/logger.js'
|
|
7
|
-
import { getAllTemplates } from '../../config/templates.js'
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
10
|
-
import ora from 'ora'
|
|
11
|
-
|
|
12
|
-
const { removeSync, existsSync } = fsExtra;
|
|
6
|
+
import { logger } from '../utils/logger.js'
|
|
7
|
+
import { getAllTemplates } from '../../config/templates.js'
|
|
8
|
+
import { TemplateManager } from 'flu-cli-core'
|
|
9
|
+
import { cloneOrUpdateTemplate, checkTemplateUpdate } from '../templates/templateManager.js'
|
|
10
|
+
import ora from 'ora'
|
|
13
11
|
|
|
14
12
|
/**
|
|
15
13
|
* 更新模板缓存
|
|
16
14
|
*/
|
|
17
|
-
export async function updateTemplates
|
|
18
|
-
logger.title('🔄 更新模板缓存')
|
|
15
|
+
export async function updateTemplates(templateName, options = {}) {
|
|
16
|
+
logger.title('🔄 更新模板缓存')
|
|
19
17
|
|
|
20
18
|
if (templateName) {
|
|
21
19
|
// 更新指定模板
|
|
22
|
-
await updateSingleTemplate(templateName, options)
|
|
20
|
+
await updateSingleTemplate(templateName, options)
|
|
23
21
|
} else {
|
|
24
22
|
// 更新所有模板
|
|
25
|
-
const templates = getAllTemplates()
|
|
23
|
+
const templates = getAllTemplates()
|
|
26
24
|
|
|
27
25
|
for (const template of templates) {
|
|
28
|
-
await updateSingleTemplate(template.name.toLowerCase(), options)
|
|
26
|
+
await updateSingleTemplate(template.name.toLowerCase(), options)
|
|
29
27
|
}
|
|
30
28
|
}
|
|
31
29
|
|
|
32
|
-
logger.newLine()
|
|
33
|
-
logger.success('模板更新完成')
|
|
30
|
+
logger.newLine()
|
|
31
|
+
logger.success('模板更新完成')
|
|
34
32
|
}
|
|
35
33
|
|
|
36
34
|
/**
|
|
37
35
|
* 更新单个模板
|
|
38
36
|
*/
|
|
39
|
-
async function updateSingleTemplate
|
|
40
|
-
const templates = getAllTemplates()
|
|
41
|
-
const template = templates.find(t => t.name.toLowerCase() === templateName)
|
|
37
|
+
async function updateSingleTemplate(templateName, options = {}) {
|
|
38
|
+
const templates = getAllTemplates()
|
|
39
|
+
const template = templates.find((t) => t.name.toLowerCase() === templateName)
|
|
42
40
|
|
|
43
41
|
if (!template) {
|
|
44
|
-
logger.error(`模板 "${templateName}" 不存在`)
|
|
45
|
-
return
|
|
42
|
+
logger.error(`模板 "${templateName}" 不存在`)
|
|
43
|
+
return
|
|
46
44
|
}
|
|
47
45
|
|
|
48
|
-
logger.info(`检查模板 ${template.displayName}...`)
|
|
46
|
+
logger.info(`检查模板 ${template.displayName}...`)
|
|
49
47
|
|
|
50
|
-
const updateInfo = await checkTemplateUpdate(templateName)
|
|
48
|
+
const updateInfo = await checkTemplateUpdate(templateName)
|
|
51
49
|
|
|
52
50
|
// 更新模板
|
|
53
|
-
const spinner = ora(`更新 ${template.displayName}...`).start()
|
|
51
|
+
const spinner = ora(`更新 ${template.displayName}...`).start()
|
|
54
52
|
|
|
55
|
-
const result = await cloneOrUpdateTemplate(
|
|
56
|
-
templateName,
|
|
57
|
-
template.repo,
|
|
58
|
-
template.branch,
|
|
59
|
-
true
|
|
60
|
-
);
|
|
53
|
+
const result = await cloneOrUpdateTemplate(templateName, template.repo, template.branch, true)
|
|
61
54
|
|
|
62
55
|
if (result) {
|
|
63
|
-
spinner.succeed(`${template.displayName} 更新成功`)
|
|
56
|
+
spinner.succeed(`${template.displayName} 更新成功`)
|
|
64
57
|
} else {
|
|
65
|
-
spinner.fail(`${template.displayName} 更新失败`)
|
|
58
|
+
spinner.fail(`${template.displayName} 更新失败`)
|
|
66
59
|
}
|
|
67
60
|
}
|
|
68
61
|
|
|
69
62
|
/**
|
|
70
63
|
* 清理缓存
|
|
71
64
|
*/
|
|
72
|
-
export async function cleanCache
|
|
73
|
-
logger.title('🗑️ 清理模板缓存')
|
|
74
|
-
|
|
75
|
-
const templates = getAllTemplates();
|
|
76
|
-
let cleaned = 0;
|
|
77
|
-
|
|
78
|
-
for (const template of templates) {
|
|
79
|
-
const templateName = template.name.toLowerCase();
|
|
80
|
-
const cachePath = getTemplateCachePath(templateName);
|
|
65
|
+
export async function cleanCache() {
|
|
66
|
+
logger.title('🗑️ 清理模板缓存')
|
|
81
67
|
|
|
82
|
-
|
|
83
|
-
try {
|
|
84
|
-
removeSync(cachePath);
|
|
85
|
-
logger.success(`已清理: ${template.displayName}`);
|
|
86
|
-
cleaned++;
|
|
87
|
-
} catch (error) {
|
|
88
|
-
logger.error(`清理失败: ${template.displayName} - ${error.message}`);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
68
|
+
const cleaned = await TemplateManager.getInstance().cleanBuiltinTemplateCache(logger)
|
|
92
69
|
|
|
93
|
-
logger.newLine()
|
|
70
|
+
logger.newLine()
|
|
94
71
|
if (cleaned > 0) {
|
|
95
|
-
logger.success(`已清理 ${cleaned}
|
|
72
|
+
logger.success(`已清理 ${cleaned} 个内置模板缓存`)
|
|
96
73
|
} else {
|
|
97
|
-
logger.info('没有需要清理的缓存')
|
|
74
|
+
logger.info('没有需要清理的缓存')
|
|
98
75
|
}
|
|
99
76
|
}
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import chalk from 'chalk'
|
|
1
|
+
import chalk from 'chalk'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
*/
|
|
6
|
-
export function completion () {
|
|
7
|
-
const script = `
|
|
3
|
+
export function getCompletionScript() {
|
|
4
|
+
return `
|
|
8
5
|
###-begin-flu-cli-completion-###
|
|
9
6
|
#
|
|
10
7
|
# flu-cli command completion script
|
|
@@ -83,10 +80,15 @@ if type compdef &>/dev/null; then
|
|
|
83
80
|
fi
|
|
84
81
|
|
|
85
82
|
###-end-flu-cli-completion-###
|
|
86
|
-
|
|
83
|
+
`
|
|
84
|
+
}
|
|
87
85
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
86
|
+
/**
|
|
87
|
+
* 生成 Shell 自动补全脚本
|
|
88
|
+
*/
|
|
89
|
+
export function completion() {
|
|
90
|
+
console.log(getCompletionScript())
|
|
91
|
+
console.error(chalk.green('✅ 补全脚本已生成'))
|
|
92
|
+
console.error(chalk.gray('请将其添加到您的 shell 配置文件中 (例如 ~/.zshrc 或 ~/.bashrc)'))
|
|
93
|
+
console.error(chalk.gray('用法: flu-cli completion >> ~/.zshrc && source ~/.zshrc'))
|
|
92
94
|
}
|
package/lib/commands/config.js
CHANGED
|
@@ -3,108 +3,214 @@
|
|
|
3
3
|
* 职责:初始化和管理项目配置文件
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
import {
|
|
7
|
+
ProjectConfigManager,
|
|
8
|
+
detectProjectTemplate,
|
|
9
|
+
ConfigManager,
|
|
10
|
+
listGeneratorSelectableOptions,
|
|
11
|
+
} from 'flu-cli-core'
|
|
12
|
+
import { writeFileSync, existsSync } from 'fs'
|
|
13
|
+
import { join } from 'path'
|
|
14
|
+
import chalk from 'chalk'
|
|
15
|
+
|
|
16
|
+
export function initConfigWithReport(options) {
|
|
17
|
+
const diagnostics = []
|
|
18
|
+
try {
|
|
19
|
+
const projectDir = options.dir || process.cwd()
|
|
20
|
+
const configPath = join(projectDir, '.flu-cli.json')
|
|
21
|
+
|
|
22
|
+
if (existsSync(configPath) && !options.force) {
|
|
23
|
+
diagnostics.push('配置文件已存在 (.flu-cli.json),使用 --force 覆盖')
|
|
24
|
+
return { ok: false, diagnostics, configPath, template: null, written: false }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const template = detectProjectTemplate(projectDir) || 'custom'
|
|
28
|
+
const config = ProjectConfigManager.getDefaultConfigTemplate(template)
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
const pageConfig = config.generators && config.generators.page
|
|
32
|
+
if (pageConfig && pageConfig.withBasePage) {
|
|
33
|
+
const possiblePaths = [
|
|
34
|
+
join(projectDir, 'lib/base/base_page.dart'),
|
|
35
|
+
join(projectDir, 'lib/core/base/base_page.dart'),
|
|
36
|
+
join(projectDir, 'lib/core/presentation/base/base_page.dart'),
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
if (pageConfig.basePageImport && pageConfig.basePageImport.startsWith('lib/')) {
|
|
40
|
+
possiblePaths.unshift(join(projectDir, pageConfig.basePageImport))
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const basePageExists = possiblePaths.some((p) => existsSync(p))
|
|
44
|
+
if (!basePageExists) {
|
|
45
|
+
diagnostics.push('Smart Init: 未检测到 BasePage 文件,降级为原生模式 (Native Mode)')
|
|
46
|
+
if (config.generators && config.generators.page) {
|
|
47
|
+
config.generators.page.withBasePage = false
|
|
48
|
+
config.generators.page.withViewModel = false
|
|
49
|
+
}
|
|
50
|
+
if (config.generators && config.generators.viewModel) {
|
|
51
|
+
config.generators.viewModel.withBaseViewModel = false
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
} catch (e) {
|
|
56
|
+
diagnostics.push(`Smart Init 检查失败: ${e.message || e}`)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf8')
|
|
60
|
+
return { ok: true, diagnostics, configPath, template, written: true }
|
|
61
|
+
} catch (error) {
|
|
62
|
+
return { ok: false, diagnostics: [`初始化配置失败: ${error.message}`], configPath: null }
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function setConfigWithReport(key, value) {
|
|
67
|
+
const diagnostics = []
|
|
68
|
+
try {
|
|
69
|
+
const config = ConfigManager.getInstance()
|
|
70
|
+
if (key === 'locale') {
|
|
71
|
+
if (!['zh-CN', 'en-US'].includes(value)) {
|
|
72
|
+
diagnostics.push(`不支持的语言: ${value}`)
|
|
73
|
+
diagnostics.push('支持: zh-CN, en-US')
|
|
74
|
+
return { ok: false, diagnostics, key, value }
|
|
75
|
+
}
|
|
76
|
+
config.setLocale(value)
|
|
77
|
+
return { ok: true, diagnostics, key, value }
|
|
78
|
+
}
|
|
79
|
+
diagnostics.push(`不支持的配置项: ${key}`)
|
|
80
|
+
return { ok: false, diagnostics, key, value }
|
|
81
|
+
} catch (e) {
|
|
82
|
+
return { ok: false, diagnostics: [String(e?.message || e)], key, value }
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function getConfigWithReport(key) {
|
|
87
|
+
const config = ConfigManager.getInstance()
|
|
88
|
+
if (key === 'locale') {
|
|
89
|
+
return { ok: true, diagnostics: [], key, value: config.getLocale() }
|
|
90
|
+
}
|
|
91
|
+
if (key) {
|
|
92
|
+
return { ok: false, diagnostics: [`未知配置项: ${key}`], key, value: null }
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
ok: true,
|
|
96
|
+
diagnostics: [],
|
|
97
|
+
key: null,
|
|
98
|
+
value: {
|
|
99
|
+
locale: config.getLocale(),
|
|
100
|
+
author: config.getAuthorName(),
|
|
101
|
+
},
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function listGeneratorSelectableOptionsWithReport(options) {
|
|
106
|
+
const projectDir = options.dir || process.cwd()
|
|
107
|
+
const target = options.target || 'page'
|
|
108
|
+
try {
|
|
109
|
+
const report = listGeneratorSelectableOptions(projectDir, target)
|
|
110
|
+
return { ok: true, diagnostics: report.diagnostics ?? [], report }
|
|
111
|
+
} catch (e) {
|
|
112
|
+
return { ok: false, diagnostics: [String(e?.message || e)], report: null }
|
|
113
|
+
}
|
|
114
|
+
}
|
|
10
115
|
|
|
11
116
|
/**
|
|
12
117
|
* 初始化配置文件
|
|
13
118
|
*/
|
|
14
|
-
export function initConfig
|
|
119
|
+
export function initConfig(options) {
|
|
15
120
|
try {
|
|
16
|
-
const projectDir = options.dir || process.cwd()
|
|
17
|
-
const configPath = join(projectDir, '.flu-cli.json')
|
|
121
|
+
const projectDir = options.dir || process.cwd()
|
|
122
|
+
const configPath = join(projectDir, '.flu-cli.json')
|
|
18
123
|
|
|
19
124
|
if (existsSync(configPath) && !options.force) {
|
|
20
|
-
console.log(chalk.yellow('⚠️ 配置文件已存在 (.flu-cli.json)'))
|
|
21
|
-
console.log(chalk.gray('使用 --force 覆盖'))
|
|
22
|
-
return
|
|
125
|
+
console.log(chalk.yellow('⚠️ 配置文件已存在 (.flu-cli.json)'))
|
|
126
|
+
console.log(chalk.gray('使用 --force 覆盖'))
|
|
127
|
+
return
|
|
23
128
|
}
|
|
24
129
|
|
|
25
130
|
// 检测当前项目模板,生成对应的默认配置
|
|
26
|
-
const template = detectProjectTemplate(projectDir) || 'custom'
|
|
27
|
-
let config = ProjectConfigManager.getDefaultConfigTemplate(template)
|
|
131
|
+
const template = detectProjectTemplate(projectDir) || 'custom'
|
|
132
|
+
let config = ProjectConfigManager.getDefaultConfigTemplate(template)
|
|
28
133
|
|
|
29
134
|
// Smart Init: 现实检查,防止默认配置依赖的 BasePage/BaseViewModel 在项目中不存在
|
|
30
135
|
try {
|
|
31
|
-
const pageConfig = config.generators && config.generators.page
|
|
136
|
+
const pageConfig = config.generators && config.generators.page
|
|
32
137
|
if (pageConfig && pageConfig.withBasePage) {
|
|
33
138
|
const possiblePaths = [
|
|
34
139
|
join(projectDir, 'lib/base/base_page.dart'),
|
|
35
140
|
join(projectDir, 'lib/core/base/base_page.dart'),
|
|
36
|
-
join(projectDir, 'lib/core/presentation/base/base_page.dart')
|
|
37
|
-
]
|
|
141
|
+
join(projectDir, 'lib/core/presentation/base/base_page.dart'),
|
|
142
|
+
]
|
|
38
143
|
|
|
39
144
|
if (pageConfig.basePageImport && pageConfig.basePageImport.startsWith('lib/')) {
|
|
40
|
-
possiblePaths.unshift(join(projectDir, pageConfig.basePageImport))
|
|
145
|
+
possiblePaths.unshift(join(projectDir, pageConfig.basePageImport))
|
|
41
146
|
}
|
|
42
147
|
|
|
43
|
-
const basePageExists = possiblePaths.some(p => existsSync(p))
|
|
148
|
+
const basePageExists = possiblePaths.some((p) => existsSync(p))
|
|
44
149
|
|
|
45
150
|
if (!basePageExists) {
|
|
46
|
-
console.log(
|
|
151
|
+
console.log(
|
|
152
|
+
chalk.yellow('Smart Init: 未检测到 BasePage 文件,降级为原生模式 (Native Mode)'),
|
|
153
|
+
)
|
|
47
154
|
if (config.generators && config.generators.page) {
|
|
48
|
-
config.generators.page.withBasePage = false
|
|
49
|
-
config.generators.page.withViewModel = false
|
|
155
|
+
config.generators.page.withBasePage = false
|
|
156
|
+
config.generators.page.withViewModel = false
|
|
50
157
|
}
|
|
51
158
|
if (config.generators && config.generators.viewModel) {
|
|
52
|
-
config.generators.viewModel.withBaseViewModel = false
|
|
159
|
+
config.generators.viewModel.withBaseViewModel = false
|
|
53
160
|
}
|
|
54
161
|
}
|
|
55
162
|
}
|
|
56
163
|
} catch (e) {
|
|
57
|
-
console.log(chalk.gray(`Smart Init 检查失败: ${e.message || e}`))
|
|
164
|
+
console.log(chalk.gray(`Smart Init 检查失败: ${e.message || e}`))
|
|
58
165
|
}
|
|
59
166
|
|
|
60
|
-
writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf8')
|
|
61
|
-
|
|
62
|
-
console.log(chalk.green(`✓ 配置文件已生成: ${configPath}`));
|
|
63
|
-
console.log(chalk.cyan(`ℹ️ 基于检测到的模板类型: ${template}`));
|
|
64
|
-
console.log(chalk.gray('你可以编辑 .flu-cli.json 来自定义生成规则'));
|
|
167
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf8')
|
|
65
168
|
|
|
169
|
+
console.log(chalk.green(`✓ 配置文件已生成: ${configPath}`))
|
|
170
|
+
console.log(chalk.cyan(`ℹ️ 基于检测到的模板类型: ${template}`))
|
|
171
|
+
console.log(chalk.gray('你可以编辑 .flu-cli.json 来自定义生成规则'))
|
|
66
172
|
} catch (error) {
|
|
67
|
-
console.error(chalk.red(`初始化配置失败: ${error.message}`))
|
|
68
|
-
process.exit(1)
|
|
173
|
+
console.error(chalk.red(`初始化配置失败: ${error.message}`))
|
|
174
|
+
process.exit(1)
|
|
69
175
|
}
|
|
70
176
|
}
|
|
71
177
|
|
|
72
178
|
/**
|
|
73
179
|
* 设置全局配置
|
|
74
180
|
*/
|
|
75
|
-
export function setConfig
|
|
76
|
-
const config = ConfigManager.getInstance()
|
|
181
|
+
export function setConfig(key, value) {
|
|
182
|
+
const config = ConfigManager.getInstance()
|
|
77
183
|
|
|
78
184
|
// 目前只支持 locale
|
|
79
185
|
if (key === 'locale') {
|
|
80
186
|
if (!['zh-CN', 'en-US'].includes(value)) {
|
|
81
|
-
console.log(chalk.red(`不支持的语言: ${value}`))
|
|
82
|
-
console.log(chalk.gray('支持: zh-CN, en-US'))
|
|
83
|
-
return
|
|
187
|
+
console.log(chalk.red(`不支持的语言: ${value}`))
|
|
188
|
+
console.log(chalk.gray('支持: zh-CN, en-US'))
|
|
189
|
+
return
|
|
84
190
|
}
|
|
85
|
-
config.setLocale(value)
|
|
86
|
-
console.log(chalk.green(`✓ 全局配置已更新: ${key} = ${value}`))
|
|
191
|
+
config.setLocale(value)
|
|
192
|
+
console.log(chalk.green(`✓ 全局配置已更新: ${key} = ${value}`))
|
|
87
193
|
} else {
|
|
88
|
-
console.log(chalk.yellow(`不支持的配置项: ${key}`))
|
|
194
|
+
console.log(chalk.yellow(`不支持的配置项: ${key}`))
|
|
89
195
|
}
|
|
90
196
|
}
|
|
91
197
|
|
|
92
198
|
/**
|
|
93
199
|
* 获取全局配置
|
|
94
200
|
*/
|
|
95
|
-
export function getConfig
|
|
96
|
-
const config = ConfigManager.getInstance()
|
|
201
|
+
export function getConfig(key) {
|
|
202
|
+
const config = ConfigManager.getInstance()
|
|
97
203
|
|
|
98
204
|
if (key === 'locale') {
|
|
99
|
-
const val = config.getLocale()
|
|
100
|
-
console.log(`${key} = ${val}`)
|
|
205
|
+
const val = config.getLocale()
|
|
206
|
+
console.log(`${key} = ${val}`)
|
|
101
207
|
} else {
|
|
102
208
|
// 简单起见,如果 key 未知,显示 list
|
|
103
209
|
if (key) {
|
|
104
|
-
console.log(chalk.yellow(`未知配置项: ${key}`))
|
|
210
|
+
console.log(chalk.yellow(`未知配置项: ${key}`))
|
|
105
211
|
} else {
|
|
106
|
-
console.log(`locale = ${config.getLocale()}`)
|
|
107
|
-
console.log(`author = ${config.getAuthorName()}`)
|
|
212
|
+
console.log(`locale = ${config.getLocale()}`)
|
|
213
|
+
console.log(`author = ${config.getAuthorName()}`)
|
|
108
214
|
}
|
|
109
215
|
}
|
|
110
216
|
}
|