generator-mico-cli 0.2.20 → 0.2.22
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 +29 -0
- package/bin/mico.js +124 -5
- package/generators/micro-react/index.js +76 -17
- package/generators/micro-react/templates/.cursor/rules/always-read-docs.mdc +14 -4
- package/generators/micro-react/templates/.cursor/rules/layout-app.mdc +36 -26
- package/generators/micro-react/templates/.cursor/rules/project-overview.mdc +5 -2
- package/generators/micro-react/templates/CLAUDE.md +15 -7
- package/generators/micro-react/templates/_gitignore +2 -0
- package/generators/micro-react/templates/apps/layout/config/config.dev.ts +7 -3
- package/generators/micro-react/templates/apps/layout/config/config.ts +21 -0
- package/generators/micro-react/templates/apps/layout/config/routes.ts +0 -5
- package/generators/micro-react/templates/apps/layout/docs/common-intl.md +8 -6
- package/generators/micro-react/templates/apps/layout/docs/feature-/345/276/256/345/211/215/347/253/257/346/250/241/345/274/217.md +65 -37
- package/generators/micro-react/templates/apps/layout/docs/feature-/350/217/234/345/215/225/346/235/203/351/231/220/346/216/247/345/210/266.md +112 -48
- package/generators/micro-react/templates/apps/layout/docs/feature-/350/267/257/347/224/261/344/270/216/350/217/234/345/215/225/350/247/243/350/200/246.md +179 -0
- package/generators/micro-react/templates/apps/layout/docs/utils-timezone.md +4 -2
- package/generators/micro-react/templates/apps/layout/mock/menus.ts +89 -139
- package/generators/micro-react/templates/apps/layout/mock/pages.ts +83 -0
- package/generators/micro-react/templates/apps/layout/package.json +3 -2
- package/generators/micro-react/templates/apps/layout/src/app.tsx +10 -8
- package/generators/micro-react/templates/apps/layout/src/common/menu/parser.ts +121 -58
- package/generators/micro-react/templates/apps/layout/src/common/menu/types.ts +35 -4
- package/generators/micro-react/templates/apps/layout/src/common/micro-prefetch.ts +3 -2
- package/generators/micro-react/templates/apps/layout/src/common/portal-data.ts +45 -0
- package/generators/micro-react/templates/apps/layout/src/common/request/config.ts +49 -10
- package/generators/micro-react/templates/apps/layout/src/common/request/interceptors.ts +1 -1
- package/generators/micro-react/templates/apps/layout/src/common/request/sso.ts +6 -0
- package/generators/micro-react/templates/apps/layout/src/common/theme.ts +0 -2
- package/generators/micro-react/templates/apps/layout/src/components/AppTabs/index.less +0 -1
- package/generators/micro-react/templates/apps/layout/src/components/AppTabs/index.tsx +4 -4
- package/generators/micro-react/templates/apps/layout/src/components/IconFont/index.tsx +4 -5
- package/generators/micro-react/templates/apps/layout/src/components/MicroAppLoader/index.less +20 -1
- package/generators/micro-react/templates/apps/layout/src/components/MicroAppLoader/index.tsx +4 -3
- package/generators/micro-react/templates/apps/layout/src/components/MicroAppLoader/micro-app-manager.ts +7 -1
- package/generators/micro-react/templates/apps/layout/src/global.less +15 -3
- package/generators/micro-react/templates/apps/layout/src/hooks/useMenu.ts +3 -2
- package/generators/micro-react/templates/apps/layout/src/layouts/components/menu/index.less +30 -3
- package/generators/micro-react/templates/apps/layout/src/layouts/components/menu/index.tsx +15 -4
- package/generators/micro-react/templates/apps/layout/src/layouts/index.tsx +75 -38
- package/generators/micro-react/templates/apps/layout/src/pages/404/index.tsx +3 -7
- package/generators/micro-react/templates/apps/layout/src/services/user.ts +2 -2
- package/generators/micro-react/templates/dev.preset.json +1 -1
- package/generators/micro-react/templates/package.json +2 -1
- package/generators/subapp-react/index.js +240 -14
- package/generators/subapp-react/templates/homepage/.env +2 -1
- package/generators/subapp-react/templates/homepage/config/config.dev.ts +9 -1
- package/generators/subapp-react/templates/homepage/config/config.prod.development.ts +2 -1
- package/generators/subapp-react/templates/homepage/config/config.prod.testing.ts +2 -1
- package/generators/subapp-react/templates/homepage/config/config.prod.ts +2 -1
- package/generators/subapp-react/templates/homepage/config/config.ts +21 -0
- package/generators/subapp-react/templates/homepage/config/routes.ts +1 -1
- package/generators/subapp-react/templates/homepage/mock/api.mock.ts +2 -2
- package/generators/subapp-react/templates/homepage/package.json +3 -2
- package/generators/subapp-react/templates/homepage/src/app.tsx +1 -1
- package/generators/subapp-react/templates/homepage/src/common/request.ts +2 -2
- package/generators/subapp-react/templates/homepage/src/global.less +2 -1
- package/generators/subapp-react/templates/homepage/src/pages/index.less +1 -1
- package/generators/subapp-react/templates/homepage/src/pages/index.tsx +27 -27
- package/lib/utils.js +200 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -48,12 +48,41 @@ mico list
|
|
|
48
48
|
mico create micro-react --verbose
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
+
预览模式(只显示将创建的文件,不实际创建):
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
mico create micro-react --dry-run
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
检查环境依赖:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
mico doctor
|
|
61
|
+
```
|
|
62
|
+
|
|
51
63
|
将额外参数传递给生成器:
|
|
52
64
|
|
|
53
65
|
```bash
|
|
54
66
|
mico create micro-react -- --help
|
|
55
67
|
```
|
|
56
68
|
|
|
69
|
+
## 配置文件
|
|
70
|
+
|
|
71
|
+
可以在项目目录或用户主目录创建 `.micorc` 或 `.micorc.json` 文件来预设默认值:
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"packageScope": "@my-company",
|
|
76
|
+
"cdnPrefix": "portal",
|
|
77
|
+
"author": "Team <team@example.com>",
|
|
78
|
+
"defaultSubappName": "subapp"
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
配置查找顺序:
|
|
83
|
+
1. 当前目录的 `.micorc` 或 `.micorc.json`
|
|
84
|
+
2. 用户主目录的 `.micorc` 或 `.micorc.json`
|
|
85
|
+
|
|
57
86
|
## Monorepo 项目生成器 (micro-react)
|
|
58
87
|
|
|
59
88
|
创建基于 qiankun 微前端架构的完整 Monorepo 项目:
|
package/bin/mico.js
CHANGED
|
@@ -22,18 +22,32 @@ Commands:
|
|
|
22
22
|
create <generator> Run a generator (e.g., mico create subapp-react)
|
|
23
23
|
list List all available generators
|
|
24
24
|
update Update mico-cli to the latest version
|
|
25
|
+
doctor Check environment dependencies
|
|
25
26
|
|
|
26
27
|
Options:
|
|
27
28
|
--help, -h Show this help message
|
|
28
29
|
--version, -v Show version number
|
|
29
30
|
--verbose Show detailed output during generation
|
|
31
|
+
--dry-run Preview files without creating them
|
|
30
32
|
--no-update-check Skip update check
|
|
31
33
|
|
|
32
34
|
Examples:
|
|
33
35
|
mico create subapp-react
|
|
34
36
|
mico create micro-react --verbose
|
|
37
|
+
mico create micro-react --dry-run
|
|
35
38
|
mico list
|
|
36
39
|
mico update
|
|
40
|
+
mico doctor
|
|
41
|
+
|
|
42
|
+
Configuration:
|
|
43
|
+
Create a .micorc or .micorc.json file in your project or home directory
|
|
44
|
+
to set default values for prompts:
|
|
45
|
+
|
|
46
|
+
{
|
|
47
|
+
"packageScope": "@my-company",
|
|
48
|
+
"cdnPrefix": "portal",
|
|
49
|
+
"author": "Team <team@example.com>"
|
|
50
|
+
}
|
|
37
51
|
|
|
38
52
|
Notes:
|
|
39
53
|
Requires Yeoman CLI (yo) installed globally.
|
|
@@ -118,6 +132,85 @@ function printVersion() {
|
|
|
118
132
|
console.log(`mico-cli v${pkg.version}`);
|
|
119
133
|
}
|
|
120
134
|
|
|
135
|
+
/**
|
|
136
|
+
* 运行 doctor 检查
|
|
137
|
+
*/
|
|
138
|
+
async function runDoctor() {
|
|
139
|
+
const { runDoctorChecks, loadMicorc } = require('../lib/utils');
|
|
140
|
+
|
|
141
|
+
console.log('');
|
|
142
|
+
console.log(` \x1b[1mMico CLI Environment Check\x1b[0m (v${pkg.version})`);
|
|
143
|
+
console.log('');
|
|
144
|
+
|
|
145
|
+
const results = await runDoctorChecks();
|
|
146
|
+
|
|
147
|
+
// Node.js
|
|
148
|
+
const nodeIcon = results.node.satisfied ? '\x1b[32m✓\x1b[0m' : '\x1b[31m✗\x1b[0m';
|
|
149
|
+
const nodeStatus = results.node.satisfied ? 'OK' : `Required: >= ${results.node.required}`;
|
|
150
|
+
console.log(` ${nodeIcon} Node.js ${results.node.current} (${nodeStatus})`);
|
|
151
|
+
|
|
152
|
+
// pnpm
|
|
153
|
+
const pnpmIcon = results.pnpm.available ? '\x1b[32m✓\x1b[0m' : '\x1b[31m✗\x1b[0m';
|
|
154
|
+
if (results.pnpm.available) {
|
|
155
|
+
console.log(` ${pnpmIcon} pnpm ${results.pnpm.version}`);
|
|
156
|
+
} else {
|
|
157
|
+
console.log(` ${pnpmIcon} pnpm not found`);
|
|
158
|
+
console.log(' Install: npm install -g pnpm');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Yeoman
|
|
162
|
+
const yoIcon = results.yo.available ? '\x1b[32m✓\x1b[0m' : '\x1b[31m✗\x1b[0m';
|
|
163
|
+
if (results.yo.available) {
|
|
164
|
+
console.log(` ${yoIcon} yo ${results.yo.version}`);
|
|
165
|
+
} else {
|
|
166
|
+
console.log(` ${yoIcon} yo not found`);
|
|
167
|
+
console.log(' Install: npm install -g yo');
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Git
|
|
171
|
+
const gitIcon = results.git.available ? '\x1b[32m✓\x1b[0m' : '\x1b[33m⚠\x1b[0m';
|
|
172
|
+
if (results.git.available) {
|
|
173
|
+
console.log(` ${gitIcon} git ${results.git.version}`);
|
|
174
|
+
} else {
|
|
175
|
+
console.log(` ${gitIcon} git not found (optional)`);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// npm registry
|
|
179
|
+
const npmIcon = results.npm.reachable ? '\x1b[32m✓\x1b[0m' : '\x1b[33m⚠\x1b[0m';
|
|
180
|
+
if (results.npm.reachable) {
|
|
181
|
+
console.log(` ${npmIcon} npm registry reachable`);
|
|
182
|
+
} else {
|
|
183
|
+
console.log(` ${npmIcon} npm registry unreachable (${results.npm.error})`);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// .micorc 配置
|
|
187
|
+
console.log('');
|
|
188
|
+
const { config, configPath } = loadMicorc();
|
|
189
|
+
if (configPath) {
|
|
190
|
+
console.log(` \x1b[32m✓\x1b[0m Config loaded from: ${configPath}`);
|
|
191
|
+
const keys = Object.keys(config);
|
|
192
|
+
if (keys.length > 0) {
|
|
193
|
+
console.log(` Keys: ${keys.join(', ')}`);
|
|
194
|
+
}
|
|
195
|
+
} else {
|
|
196
|
+
console.log(' \x1b[2m○\x1b[0m No .micorc config found (optional)');
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
console.log('');
|
|
200
|
+
|
|
201
|
+
// 总结
|
|
202
|
+
const allGood = results.node.satisfied && results.pnpm.available && results.yo.available;
|
|
203
|
+
if (allGood) {
|
|
204
|
+
console.log(' \x1b[32m✅ All required dependencies are installed!\x1b[0m');
|
|
205
|
+
} else {
|
|
206
|
+
console.log(' \x1b[31m❌ Some required dependencies are missing.\x1b[0m');
|
|
207
|
+
console.log(' Please install them before using mico-cli.');
|
|
208
|
+
}
|
|
209
|
+
console.log('');
|
|
210
|
+
|
|
211
|
+
return allGood;
|
|
212
|
+
}
|
|
213
|
+
|
|
121
214
|
/**
|
|
122
215
|
* 检查是否有新版本
|
|
123
216
|
* @returns {Promise<{current: string, latest: string, hasUpdate: boolean} | null>}
|
|
@@ -256,6 +349,7 @@ function performUpdate(latestVersion) {
|
|
|
256
349
|
* @param {string[]} passthroughArgs - 透传参数
|
|
257
350
|
* @param {object} options - 选项
|
|
258
351
|
* @param {boolean} options.verbose - 是否启用详细输出
|
|
352
|
+
* @param {boolean} options.dryRun - 是否启用 dry-run 模式
|
|
259
353
|
*/
|
|
260
354
|
function runGenerator(generator, rest, passthroughArgs, options = {}) {
|
|
261
355
|
const localGeneratorEntry = path.join(
|
|
@@ -275,17 +369,31 @@ function runGenerator(generator, rest, passthroughArgs, options = {}) {
|
|
|
275
369
|
|
|
276
370
|
const yoArgs = [yoGenerator, ...rest];
|
|
277
371
|
|
|
372
|
+
// 添加 dry-run 参数给 yeoman
|
|
373
|
+
if (options.dryRun) {
|
|
374
|
+
yoArgs.push('--dry-run');
|
|
375
|
+
}
|
|
376
|
+
|
|
278
377
|
if (passthroughArgs.length > 0) {
|
|
279
378
|
yoArgs.push('--', ...passthroughArgs);
|
|
280
379
|
}
|
|
281
380
|
|
|
282
|
-
//
|
|
381
|
+
// 通过环境变量传递标志给生成器
|
|
283
382
|
const env = { ...process.env };
|
|
284
383
|
if (options.verbose) {
|
|
285
384
|
env.MICO_VERBOSE = '1';
|
|
385
|
+
}
|
|
386
|
+
if (options.dryRun) {
|
|
387
|
+
env.MICO_DRY_RUN = '1';
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (options.verbose) {
|
|
286
391
|
console.log('');
|
|
287
392
|
console.log(' \x1b[2m[verbose] Running generator:', yoGenerator, '\x1b[0m');
|
|
288
393
|
console.log(' \x1b[2m[verbose] Arguments:', yoArgs.join(' '), '\x1b[0m');
|
|
394
|
+
if (options.dryRun) {
|
|
395
|
+
console.log(' \x1b[2m[verbose] Dry-run mode enabled\x1b[0m');
|
|
396
|
+
}
|
|
289
397
|
console.log('');
|
|
290
398
|
}
|
|
291
399
|
|
|
@@ -323,6 +431,7 @@ async function main() {
|
|
|
323
431
|
const hasVersion = mainArgs.includes('--version') || mainArgs.includes('-v');
|
|
324
432
|
const skipUpdateCheck = mainArgs.includes('--no-update-check');
|
|
325
433
|
const isVerbose = mainArgs.includes('--verbose');
|
|
434
|
+
const isDryRun = mainArgs.includes('--dry-run');
|
|
326
435
|
|
|
327
436
|
// 过滤掉标志参数
|
|
328
437
|
const filteredArgs = mainArgs.filter(
|
|
@@ -332,7 +441,8 @@ async function main() {
|
|
|
332
441
|
arg !== '--version' &&
|
|
333
442
|
arg !== '-v' &&
|
|
334
443
|
arg !== '--no-update-check' &&
|
|
335
|
-
arg !== '--verbose'
|
|
444
|
+
arg !== '--verbose' &&
|
|
445
|
+
arg !== '--dry-run'
|
|
336
446
|
);
|
|
337
447
|
|
|
338
448
|
// 处理 --version
|
|
@@ -355,6 +465,12 @@ async function main() {
|
|
|
355
465
|
process.exit(0);
|
|
356
466
|
}
|
|
357
467
|
|
|
468
|
+
// 处理 doctor 命令
|
|
469
|
+
if (command === 'doctor') {
|
|
470
|
+
const success = await runDoctor();
|
|
471
|
+
process.exit(success ? 0 : 1);
|
|
472
|
+
}
|
|
473
|
+
|
|
358
474
|
// 处理 update 命令
|
|
359
475
|
if (command === 'update') {
|
|
360
476
|
const updateInfo = await checkForUpdate();
|
|
@@ -376,8 +492,8 @@ async function main() {
|
|
|
376
492
|
process.exit(1);
|
|
377
493
|
}
|
|
378
494
|
|
|
379
|
-
//
|
|
380
|
-
if (!skipUpdateCheck) {
|
|
495
|
+
// 检查更新(除非跳过或 dry-run)
|
|
496
|
+
if (!skipUpdateCheck && !isDryRun) {
|
|
381
497
|
const updateInfo = await checkForUpdate();
|
|
382
498
|
if (updateInfo && updateInfo.hasUpdate) {
|
|
383
499
|
const shouldUpdate = await askForUpdate(
|
|
@@ -396,7 +512,10 @@ async function main() {
|
|
|
396
512
|
}
|
|
397
513
|
|
|
398
514
|
// 运行生成器
|
|
399
|
-
runGenerator(generator, rest.slice(1), passthroughArgs, {
|
|
515
|
+
runGenerator(generator, rest.slice(1), passthroughArgs, {
|
|
516
|
+
verbose: isVerbose,
|
|
517
|
+
dryRun: isDryRun
|
|
518
|
+
});
|
|
400
519
|
return;
|
|
401
520
|
}
|
|
402
521
|
|
|
@@ -9,10 +9,10 @@ const {
|
|
|
9
9
|
collectFiles,
|
|
10
10
|
transformDestPath,
|
|
11
11
|
isTemplateFile,
|
|
12
|
-
|
|
12
|
+
getPackageVersionsParallel,
|
|
13
13
|
setupErrorHandlers,
|
|
14
14
|
createLogger,
|
|
15
|
-
|
|
15
|
+
loadMicorc,
|
|
16
16
|
} = require('../../lib/utils');
|
|
17
17
|
|
|
18
18
|
const IGNORE_LIST = require('./ignore-list.json');
|
|
@@ -25,6 +25,17 @@ module.exports = class extends Generator {
|
|
|
25
25
|
this.projectRoot = process.cwd();
|
|
26
26
|
this.logger = createLogger(this);
|
|
27
27
|
|
|
28
|
+
// 检查 dry-run 模式
|
|
29
|
+
this.isDryRun = this.options.dryRun || process.env.MICO_DRY_RUN === '1';
|
|
30
|
+
|
|
31
|
+
// 加载 .micorc 配置
|
|
32
|
+
const { config: rcConfig, configPath } = loadMicorc(this.projectRoot);
|
|
33
|
+
this.rcConfig = rcConfig;
|
|
34
|
+
if (configPath) {
|
|
35
|
+
this.logger.verbose('Loaded config from:', configPath);
|
|
36
|
+
this.logger.verbose('Config:', JSON.stringify(rcConfig, null, 2));
|
|
37
|
+
}
|
|
38
|
+
|
|
28
39
|
// 检查当前目录是否已是一个 monorepo
|
|
29
40
|
const workspaceFile = path.join(this.projectRoot, 'pnpm-workspace.yaml');
|
|
30
41
|
if (fs.existsSync(workspaceFile)) {
|
|
@@ -46,12 +57,15 @@ module.exports = class extends Generator {
|
|
|
46
57
|
|
|
47
58
|
async prompting() {
|
|
48
59
|
try {
|
|
60
|
+
// 使用 .micorc 中的值作为默认值
|
|
61
|
+
const rc = this.rcConfig || {};
|
|
62
|
+
|
|
49
63
|
this.answers = await this.prompt([
|
|
50
64
|
{
|
|
51
65
|
type: 'input',
|
|
52
66
|
name: 'projectName',
|
|
53
67
|
message: 'Project name',
|
|
54
|
-
default: path.basename(this.projectRoot),
|
|
68
|
+
default: rc.projectName || path.basename(this.projectRoot),
|
|
55
69
|
filter: (input) => toKebab(input),
|
|
56
70
|
validate: (input) => {
|
|
57
71
|
const value = toKebab(input);
|
|
@@ -63,7 +77,7 @@ module.exports = class extends Generator {
|
|
|
63
77
|
type: 'input',
|
|
64
78
|
name: 'packageScope',
|
|
65
79
|
message: 'Package scope (e.g., @my-project)',
|
|
66
|
-
default: (answers) => `@${toKebab(answers.projectName)}`,
|
|
80
|
+
default: (answers) => rc.packageScope || `@${toKebab(answers.projectName)}`,
|
|
67
81
|
validate: (input) => {
|
|
68
82
|
if (!input) return 'Package scope is required';
|
|
69
83
|
if (!input.startsWith('@'))
|
|
@@ -80,7 +94,7 @@ module.exports = class extends Generator {
|
|
|
80
94
|
- 输入 "portal": https://cdn.example.com/portal/my-project/1.0.0/
|
|
81
95
|
- 输入 "admin/v2": https://cdn.example.com/admin/v2/my-project/1.0.0/
|
|
82
96
|
Prefix`,
|
|
83
|
-
default: '',
|
|
97
|
+
default: rc.cdnPrefix || '',
|
|
84
98
|
filter: (input) => {
|
|
85
99
|
// 移除首尾斜杠,规范化路径
|
|
86
100
|
return input.trim().replace(/^\/+|\/+$/g, '');
|
|
@@ -90,7 +104,7 @@ module.exports = class extends Generator {
|
|
|
90
104
|
type: 'input',
|
|
91
105
|
name: 'author',
|
|
92
106
|
message: 'Author',
|
|
93
|
-
default: 'Your Name <email@example.com>',
|
|
107
|
+
default: rc.author || 'Your Name <email@example.com>',
|
|
94
108
|
},
|
|
95
109
|
]);
|
|
96
110
|
|
|
@@ -110,7 +124,7 @@ module.exports = class extends Generator {
|
|
|
110
124
|
}
|
|
111
125
|
}
|
|
112
126
|
|
|
113
|
-
writing() {
|
|
127
|
+
async writing() {
|
|
114
128
|
try {
|
|
115
129
|
if (!fs.existsSync(this.templateDir)) {
|
|
116
130
|
console.error('');
|
|
@@ -125,21 +139,21 @@ module.exports = class extends Generator {
|
|
|
125
139
|
|
|
126
140
|
// 在 mico_cli 根目录执行 npm view,以使用该目录 .npmrc 中的 Nexus 认证
|
|
127
141
|
const cliRoot = path.resolve(__dirname, '../..');
|
|
128
|
-
this.logger.verbose('Fetching latest package versions...');
|
|
142
|
+
this.logger.verbose('Fetching latest package versions (parallel)...');
|
|
129
143
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
const themeVer = getLatestNpmVersion(
|
|
137
|
-
'@mico-platform/theme',
|
|
138
|
-
'1.0.0',
|
|
144
|
+
// 并行获取版本
|
|
145
|
+
const versions = await getPackageVersionsParallel(
|
|
146
|
+
[
|
|
147
|
+
{ name: '@mico-platform/ui', fallback: '1.0.0' },
|
|
148
|
+
{ name: '@mico-platform/theme', fallback: '1.0.0' },
|
|
149
|
+
],
|
|
139
150
|
8000,
|
|
140
151
|
cliRoot,
|
|
141
152
|
);
|
|
142
153
|
|
|
154
|
+
const micoUiVer = versions['@mico-platform/ui'];
|
|
155
|
+
const themeVer = versions['@mico-platform/theme'];
|
|
156
|
+
|
|
143
157
|
this.logger.verbose('@mico-platform/ui version:', micoUiVer);
|
|
144
158
|
this.logger.verbose('@mico-platform/theme version:', themeVer);
|
|
145
159
|
|
|
@@ -173,6 +187,45 @@ module.exports = class extends Generator {
|
|
|
173
187
|
process.exit(1);
|
|
174
188
|
}
|
|
175
189
|
|
|
190
|
+
// Dry-run 模式:只列出文件,不实际创建
|
|
191
|
+
if (this.isDryRun) {
|
|
192
|
+
this.log('');
|
|
193
|
+
this.log('\x1b[33m📋 Dry run mode - no files will be created\x1b[0m');
|
|
194
|
+
this.log('');
|
|
195
|
+
this.log(` Project: ${this.projectName}`);
|
|
196
|
+
this.log(` Scope: ${this.packageScope}`);
|
|
197
|
+
this.log(` Destination: ${this.destDir}`);
|
|
198
|
+
this.log('');
|
|
199
|
+
this.log(' Would create the following files:');
|
|
200
|
+
this.log('');
|
|
201
|
+
|
|
202
|
+
let templateCount = 0;
|
|
203
|
+
let copyCount = 0;
|
|
204
|
+
|
|
205
|
+
for (const relPath of files) {
|
|
206
|
+
const destRelPath = transformDestPath(relPath);
|
|
207
|
+
const isTemplate = isTemplateFile(relPath);
|
|
208
|
+
const tag = isTemplate ? '\x1b[32m[tpl]\x1b[0m' : '\x1b[36m[cpy]\x1b[0m';
|
|
209
|
+
this.log(` ${tag} ${destRelPath}`);
|
|
210
|
+
|
|
211
|
+
if (isTemplate) {
|
|
212
|
+
templateCount++;
|
|
213
|
+
} else {
|
|
214
|
+
copyCount++;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
this.log('');
|
|
219
|
+
this.log(` Total: ${files.length} files (${templateCount} templates, ${copyCount} static)`);
|
|
220
|
+
this.log('');
|
|
221
|
+
this.log(' Run without --dry-run to actually create these files.');
|
|
222
|
+
this.log('');
|
|
223
|
+
|
|
224
|
+
// 设置标记以跳过后续阶段
|
|
225
|
+
this._skipInstall = true;
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
176
229
|
this.log('');
|
|
177
230
|
this.log(`📦 Creating project: ${this.projectName}`);
|
|
178
231
|
this.log(` Scope: ${this.packageScope}`);
|
|
@@ -210,6 +263,9 @@ module.exports = class extends Generator {
|
|
|
210
263
|
}
|
|
211
264
|
|
|
212
265
|
install() {
|
|
266
|
+
// 跳过 dry-run 模式
|
|
267
|
+
if (this._skipInstall) return;
|
|
268
|
+
|
|
213
269
|
// 检查并初始化 git
|
|
214
270
|
const gitDir = path.join(this.destDir, '.git');
|
|
215
271
|
if (!fs.existsSync(gitDir)) {
|
|
@@ -228,6 +284,9 @@ module.exports = class extends Generator {
|
|
|
228
284
|
}
|
|
229
285
|
|
|
230
286
|
end() {
|
|
287
|
+
// 跳过 dry-run 模式
|
|
288
|
+
if (this._skipInstall) return;
|
|
289
|
+
|
|
231
290
|
this.log('');
|
|
232
291
|
this.log('✅ 项目创建成功!');
|
|
233
292
|
this.log('');
|
|
@@ -20,8 +20,15 @@ docs/
|
|
|
20
20
|
### 2. Layout 应用文档 (`/apps/layout/docs/`)
|
|
21
21
|
```
|
|
22
22
|
apps/layout/docs/
|
|
23
|
-
├── feature-微前端模式.md
|
|
24
|
-
|
|
23
|
+
├── feature-微前端模式.md # qiankun 微前端架构
|
|
24
|
+
├── feature-路由与菜单解耦.md # PAGES/MENUS 数据源分离、双层权限
|
|
25
|
+
├── feature-菜单权限控制.md # sideMenus 白名单、认证与授权分离
|
|
26
|
+
├── feature-主题色切换.md # 主题系统实现
|
|
27
|
+
├── feature-404页面.md # 404 页面
|
|
28
|
+
├── arch-请求模块.md # HTTP 请求层模块化设计
|
|
29
|
+
├── arch-日志与常量.md # Logger 工具与常量管理
|
|
30
|
+
├── common-intl.md # 国际化公共包
|
|
31
|
+
└── utils-timezone.md # 时区工具
|
|
25
32
|
```
|
|
26
33
|
|
|
27
34
|
### 3. 其他应用文档
|
|
@@ -33,8 +40,9 @@ apps/layout/docs/
|
|
|
33
40
|
|
|
34
41
|
### Step 1: 确认需求涉及的模块
|
|
35
42
|
- 微前端相关 → 阅读 `apps/layout/docs/feature-微前端模式.md`
|
|
43
|
+
- 路由/菜单/权限相关 → 阅读 `apps/layout/docs/feature-路由与菜单解耦.md` 和 `apps/layout/docs/feature-菜单权限控制.md`
|
|
36
44
|
- 主题/样式相关 → 阅读 `apps/layout/docs/feature-主题色切换.md`
|
|
37
|
-
- 请求/认证相关 → 阅读 `
|
|
45
|
+
- 请求/认证相关 → 阅读 `apps/layout/docs/arch-请求模块.md` 和 `src/common/auth/` 源码
|
|
38
46
|
- 菜单相关 → 阅读 `src/common/menu/` 源码
|
|
39
47
|
|
|
40
48
|
### Step 2: 查找相关文档
|
|
@@ -54,9 +62,11 @@ find . -name "*.md" -type f | grep -v node_modules
|
|
|
54
62
|
| 需求类型 | 必读文档 |
|
|
55
63
|
|---------|---------|
|
|
56
64
|
| 新建子应用 | `apps/layout/docs/feature-微前端模式.md` |
|
|
65
|
+
| 路由/页面配置 | `apps/layout/docs/feature-路由与菜单解耦.md` |
|
|
66
|
+
| 权限控制 | `apps/layout/docs/feature-菜单权限控制.md` |
|
|
57
67
|
| 主题适配 | `apps/layout/docs/feature-主题色切换.md` |
|
|
58
68
|
| 提交代码 | `docs/commit-message.md` |
|
|
59
|
-
| API 请求 | `
|
|
69
|
+
| API 请求 | `apps/layout/docs/arch-请求模块.md` |
|
|
60
70
|
| 认证登录 | `src/common/auth/` 目录源码 |
|
|
61
71
|
| 菜单配置 | `src/common/menu/types.ts` 类型定义 |
|
|
62
72
|
|
|
@@ -63,24 +63,27 @@ apps/layout/
|
|
|
63
63
|
const BasicLayout: React.FC = () => {
|
|
64
64
|
const location = useLocation();
|
|
65
65
|
|
|
66
|
-
//
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
return extractRoutes(menus);
|
|
66
|
+
// 所有页面路由(优先 PAGES,降级 MENUS)— 用于路由匹配和渲染
|
|
67
|
+
const allPageRoutes = useMemo(() => {
|
|
68
|
+
return getDynamicRoutes();
|
|
70
69
|
}, []);
|
|
71
70
|
|
|
72
|
-
//
|
|
71
|
+
// 菜单路由(从 MENUS)— 用于权限交叉引用
|
|
72
|
+
const allMenuRoutes = useMemo(() => {
|
|
73
|
+
return extractRoutes(getMenus());
|
|
74
|
+
}, []);
|
|
75
|
+
|
|
76
|
+
// 当前路由配置(从所有页面路由中查找)
|
|
73
77
|
const currentRoute = useMemo(() => {
|
|
74
|
-
return findRouteByPath(
|
|
75
|
-
}, [
|
|
78
|
+
return findRouteByPath(allPageRoutes, location.pathname);
|
|
79
|
+
}, [allPageRoutes, location.pathname]);
|
|
76
80
|
|
|
77
81
|
// 渲染页面内容
|
|
78
82
|
const renderContent = () => {
|
|
79
|
-
// 微应用类型使用 MicroAppLoader
|
|
80
83
|
if (currentRoute?.loadType === 'microapp' && currentRoute.entry) {
|
|
81
|
-
|
|
84
|
+
const appName = getAppNameFromEntry(currentRoute.entry);
|
|
85
|
+
return <MicroAppLoader entry={currentRoute.entry} name={appName} />;
|
|
82
86
|
}
|
|
83
|
-
// 内部路由使用 Outlet
|
|
84
87
|
return <Outlet />;
|
|
85
88
|
};
|
|
86
89
|
|
|
@@ -98,28 +101,36 @@ const BasicLayout: React.FC = () => {
|
|
|
98
101
|
};
|
|
99
102
|
```
|
|
100
103
|
|
|
101
|
-
### 2. 菜单系统 (common/menu/)
|
|
104
|
+
### 2. 全局数据源 (common/portal-data.ts) + 菜单系统 (common/menu/)
|
|
102
105
|
|
|
103
106
|
```typescript
|
|
104
|
-
//
|
|
105
|
-
const
|
|
107
|
+
// 获取页面列表 (window.__MICO_PAGES__)
|
|
108
|
+
const pages = getPages();
|
|
109
|
+
|
|
110
|
+
// 获取菜单树 (window.__MICO_MENUS__)
|
|
111
|
+
const menus = getMenus();
|
|
106
112
|
|
|
107
|
-
//
|
|
108
|
-
const routes =
|
|
113
|
+
// 从页面列表提取动态路由(优先数据源)
|
|
114
|
+
const routes = getDynamicRoutes();
|
|
109
115
|
|
|
110
|
-
//
|
|
116
|
+
// 从菜单树提取路由(用于权限交叉引用)
|
|
117
|
+
const menuRoutes = extractRoutes(menus);
|
|
118
|
+
|
|
119
|
+
// 解析菜单项(用于侧边栏渲染)
|
|
111
120
|
const menuItems = parseMenuItems(menus);
|
|
112
121
|
|
|
113
122
|
// 查找当前路由
|
|
114
123
|
const currentRoute = findRouteByPath(routes, pathname);
|
|
124
|
+
|
|
125
|
+
// 通过 pageId 查找页面(O(1))
|
|
126
|
+
const page = getPageById(pageId);
|
|
115
127
|
```
|
|
116
128
|
|
|
117
129
|
### 3. 动态路由 (app.tsx)
|
|
118
130
|
|
|
119
131
|
```typescript
|
|
120
132
|
export function patchClientRoutes({ routes }: { routes: any[] }) {
|
|
121
|
-
const
|
|
122
|
-
const dynamicRoutes = extractRoutes(menus);
|
|
133
|
+
const dynamicRoutes = getDynamicRoutes(); // 从 __MICO_PAGES__ 获取
|
|
123
134
|
|
|
124
135
|
const rootRoute = routes.find((r) => r.path === '/');
|
|
125
136
|
dynamicRoutes.forEach((route) => {
|
|
@@ -128,9 +139,7 @@ export function patchClientRoutes({ routes }: { routes: any[] }) {
|
|
|
128
139
|
name: route.name,
|
|
129
140
|
meta: {
|
|
130
141
|
loadType: route.loadType,
|
|
131
|
-
|
|
132
|
-
jsUrls: route.jsUrls,
|
|
133
|
-
cssUrls: route.cssUrls,
|
|
142
|
+
entry: route.entry,
|
|
134
143
|
},
|
|
135
144
|
});
|
|
136
145
|
});
|
|
@@ -265,8 +274,9 @@ const { collapsed } = useModel('global');
|
|
|
265
274
|
|
|
266
275
|
## 注意事项
|
|
267
276
|
|
|
268
|
-
1.
|
|
269
|
-
2. **路由动态生成**: 在 `patchClientRoutes`
|
|
270
|
-
3.
|
|
271
|
-
4.
|
|
272
|
-
5.
|
|
277
|
+
1. **数据来源**: 页面列表通过 `window.__MICO_PAGES__` 注入,菜单树通过 `window.__MICO_MENUS__` 注入
|
|
278
|
+
2. **路由动态生成**: 在 `patchClientRoutes` 中优先从 `__MICO_PAGES__` 生成路由
|
|
279
|
+
3. **权限交叉引用**: 菜单路由(`__MICO_MENUS__`)用于权限判断,页面路由(`__MICO_PAGES__`)用于渲染
|
|
280
|
+
4. **微应用判断**: 根据 `htmlUrl` 或 `jsUrls` 判断是否为微应用
|
|
281
|
+
5. **主题初始化**: 在 `app.tsx` 中调用 `initTheme()` 避免闪烁
|
|
282
|
+
6. **认证共享**: 子应用使用主应用传递的 `request` 实例
|
|
@@ -61,15 +61,16 @@ alwaysApply: true
|
|
|
61
61
|
- **关键文件**:
|
|
62
62
|
- `src/layouts/index.tsx` - 主布局组件
|
|
63
63
|
- `src/components/MicroAppLoader/` - 微应用加载器
|
|
64
|
-
- `src/common/menu/parser.ts` -
|
|
64
|
+
- `src/common/menu/parser.ts` - 菜单解析、页面数据提取与路由生成
|
|
65
65
|
- `src/common/request/index.ts` - 统一请求封装
|
|
66
66
|
- `src/hooks/useTheme.ts` - 主题管理 Hook
|
|
67
67
|
|
|
68
68
|
### 2. 微前端架构
|
|
69
|
-
-
|
|
69
|
+
- 路由注册优先使用 `window.__MICO_PAGES__`(页面列表),无数据时降级到 `window.__MICO_MENUS__`(菜单树)
|
|
70
70
|
- 根据 `htmlUrl` 或 `jsUrls` 判断是否为微应用
|
|
71
71
|
- 使用 `loadMicroApp` API 动态挂载子应用
|
|
72
72
|
- 子应用共享主应用的 `request` 实例
|
|
73
|
+
- 权限通过菜单交叉引用 + 页面级兜底的双层策略控制
|
|
73
74
|
|
|
74
75
|
### 3. 主题系统
|
|
75
76
|
- 支持亮色/暗黑主题切换
|
|
@@ -123,6 +124,8 @@ Jenkins 根据环境执行 `CICD/` 下的构建脚本:
|
|
|
123
124
|
## 相关文档
|
|
124
125
|
|
|
125
126
|
- [微前端模式](mdc:apps/layout/docs/feature-微前端模式.md)
|
|
127
|
+
- [路由与菜单解耦](mdc:apps/layout/docs/feature-路由与菜单解耦.md)
|
|
128
|
+
- [菜单权限控制](mdc:apps/layout/docs/feature-菜单权限控制.md)
|
|
126
129
|
- [主题色切换](mdc:apps/layout/docs/feature-主题色切换.md)
|
|
127
130
|
- [提交规范](mdc:docs/commit-message.md)
|
|
128
131
|
- [部署说明](mdc:deployDesc.md)
|
|
@@ -53,15 +53,21 @@ apps/ # Workspace 应用目录(微前端子应用)
|
|
|
53
53
|
│ │ ├── app.tsx # Umi 运行时配置
|
|
54
54
|
│ │ ├── layouts/ # 布局组件
|
|
55
55
|
│ │ ├── pages/ # 路由页面
|
|
56
|
-
│ │ ├── components/ #
|
|
56
|
+
│ │ ├── components/ # 公共组件(MicroAppLoader 等)
|
|
57
57
|
│ │ ├── common/ # 工具模块(auth、request、upload、menu)
|
|
58
|
+
│ │ ├── constants/ # 常量定义(路由、主题、时区等)
|
|
58
59
|
│ │ ├── hooks/ # 自定义 Hooks
|
|
59
60
|
│ │ ├── services/ # API 服务层
|
|
60
61
|
│ │ ├── models/ # Umi model(全局状态)
|
|
61
|
-
│ │
|
|
62
|
-
│ │ └── styles/ # 主题变量与样式覆盖
|
|
62
|
+
│ │ └── locales/ # 国际化文件(zh-CN、en-US)
|
|
63
63
|
│ └── config/ # Umi 配置
|
|
64
|
-
|
|
64
|
+
├── basis/ # 基础数据子应用(货币管理等)
|
|
65
|
+
│ ├── src/
|
|
66
|
+
│ │ ├── pages/ # 路由页面
|
|
67
|
+
│ │ ├── locales/ # 国际化文件
|
|
68
|
+
│ │ └── common/ # 工具模块
|
|
69
|
+
│ └── config/ # Umi 配置
|
|
70
|
+
packages/ # 共享包(common-intl 等)
|
|
65
71
|
scripts/ # 构建与开发脚本
|
|
66
72
|
CICD/ # 部署资源
|
|
67
73
|
```
|
|
@@ -97,13 +103,13 @@ refactor(auth): 重构认证服务
|
|
|
97
103
|
API 请求配置在 `src/common/request/`(模块化拆分为 6 个文件),错误处理在 `src/requestErrorConfig.ts`。详见 [请求模块架构](./apps/layout/docs/arch-请求模块.md)。
|
|
98
104
|
|
|
99
105
|
### 日志与常量
|
|
100
|
-
Logger 工具在 `src/common/logger.ts`,常量定义在 `src/
|
|
106
|
+
Logger 工具在 `src/common/logger.ts`,常量定义在 `src/constants/index.ts`。详见 [日志与常量](./apps/layout/docs/arch-日志与常量.md)。
|
|
101
107
|
|
|
102
108
|
### 认证模块
|
|
103
109
|
认证逻辑在 `src/common/auth/`,核心文件 `auth-manager.ts` 管理 Token 存储和用户信息。SSO 登录在 `getInitialState()` 中完成,确保 UI 渲染前认证已就绪。
|
|
104
110
|
|
|
105
|
-
###
|
|
106
|
-
菜单解析与类型定义在 `src/common/menu
|
|
111
|
+
### 菜单与路由系统
|
|
112
|
+
菜单解析与类型定义在 `src/common/menu/`。路由注册消费 `window.__MICO_PAGES__`(页面列表),菜单栏消费 `window.__MICO_MENUS__`(菜单树),两者解耦运作。权限通过菜单交叉引用 + 页面级兜底的双层策略控制。详见 [路由与菜单解耦](./apps/layout/docs/feature-路由与菜单解耦.md) 和 [菜单权限控制](./apps/layout/docs/feature-菜单权限控制.md)。
|
|
107
113
|
|
|
108
114
|
### 微前端
|
|
109
115
|
layout 应用作为 qiankun 主应用,子应用通过 Umi 配置中的 qiankun 配置注册。详见 [微前端模式](./apps/layout/docs/feature-微前端模式.md)。
|
|
@@ -115,6 +121,8 @@ layout 应用作为 qiankun 主应用,子应用通过 Umi 配置中的 qiankun
|
|
|
115
121
|
| [README.md](./README.md) | 项目入口、技术栈、常用命令 |
|
|
116
122
|
| [提交规范](./docs/commit-message.md) | Git Commit 详细规范 |
|
|
117
123
|
| [微前端模式](./apps/layout/docs/feature-微前端模式.md) | qiankun 架构、MicroAppLoader、子应用配置 |
|
|
124
|
+
| [路由与菜单解耦](./apps/layout/docs/feature-路由与菜单解耦.md) | PAGES/MENUS 数据源分离、双层权限控制 |
|
|
125
|
+
| [菜单权限控制](./apps/layout/docs/feature-菜单权限控制.md) | sideMenus 白名单、认证与授权分离 |
|
|
118
126
|
| [主题色切换](./apps/layout/docs/feature-主题色切换.md) | useTheme Hook、CSS 变量系统 |
|
|
119
127
|
| [请求模块架构](./apps/layout/docs/arch-请求模块.md) | HTTP 请求层模块化设计 |
|
|
120
128
|
| [日志与常量](./apps/layout/docs/arch-日志与常量.md) | Logger 工具与常量管理 |
|