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.
Files changed (60) hide show
  1. package/README.md +29 -0
  2. package/bin/mico.js +124 -5
  3. package/generators/micro-react/index.js +76 -17
  4. package/generators/micro-react/templates/.cursor/rules/always-read-docs.mdc +14 -4
  5. package/generators/micro-react/templates/.cursor/rules/layout-app.mdc +36 -26
  6. package/generators/micro-react/templates/.cursor/rules/project-overview.mdc +5 -2
  7. package/generators/micro-react/templates/CLAUDE.md +15 -7
  8. package/generators/micro-react/templates/_gitignore +2 -0
  9. package/generators/micro-react/templates/apps/layout/config/config.dev.ts +7 -3
  10. package/generators/micro-react/templates/apps/layout/config/config.ts +21 -0
  11. package/generators/micro-react/templates/apps/layout/config/routes.ts +0 -5
  12. package/generators/micro-react/templates/apps/layout/docs/common-intl.md +8 -6
  13. 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
  14. 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
  15. 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
  16. package/generators/micro-react/templates/apps/layout/docs/utils-timezone.md +4 -2
  17. package/generators/micro-react/templates/apps/layout/mock/menus.ts +89 -139
  18. package/generators/micro-react/templates/apps/layout/mock/pages.ts +83 -0
  19. package/generators/micro-react/templates/apps/layout/package.json +3 -2
  20. package/generators/micro-react/templates/apps/layout/src/app.tsx +10 -8
  21. package/generators/micro-react/templates/apps/layout/src/common/menu/parser.ts +121 -58
  22. package/generators/micro-react/templates/apps/layout/src/common/menu/types.ts +35 -4
  23. package/generators/micro-react/templates/apps/layout/src/common/micro-prefetch.ts +3 -2
  24. package/generators/micro-react/templates/apps/layout/src/common/portal-data.ts +45 -0
  25. package/generators/micro-react/templates/apps/layout/src/common/request/config.ts +49 -10
  26. package/generators/micro-react/templates/apps/layout/src/common/request/interceptors.ts +1 -1
  27. package/generators/micro-react/templates/apps/layout/src/common/request/sso.ts +6 -0
  28. package/generators/micro-react/templates/apps/layout/src/common/theme.ts +0 -2
  29. package/generators/micro-react/templates/apps/layout/src/components/AppTabs/index.less +0 -1
  30. package/generators/micro-react/templates/apps/layout/src/components/AppTabs/index.tsx +4 -4
  31. package/generators/micro-react/templates/apps/layout/src/components/IconFont/index.tsx +4 -5
  32. package/generators/micro-react/templates/apps/layout/src/components/MicroAppLoader/index.less +20 -1
  33. package/generators/micro-react/templates/apps/layout/src/components/MicroAppLoader/index.tsx +4 -3
  34. package/generators/micro-react/templates/apps/layout/src/components/MicroAppLoader/micro-app-manager.ts +7 -1
  35. package/generators/micro-react/templates/apps/layout/src/global.less +15 -3
  36. package/generators/micro-react/templates/apps/layout/src/hooks/useMenu.ts +3 -2
  37. package/generators/micro-react/templates/apps/layout/src/layouts/components/menu/index.less +30 -3
  38. package/generators/micro-react/templates/apps/layout/src/layouts/components/menu/index.tsx +15 -4
  39. package/generators/micro-react/templates/apps/layout/src/layouts/index.tsx +75 -38
  40. package/generators/micro-react/templates/apps/layout/src/pages/404/index.tsx +3 -7
  41. package/generators/micro-react/templates/apps/layout/src/services/user.ts +2 -2
  42. package/generators/micro-react/templates/dev.preset.json +1 -1
  43. package/generators/micro-react/templates/package.json +2 -1
  44. package/generators/subapp-react/index.js +240 -14
  45. package/generators/subapp-react/templates/homepage/.env +2 -1
  46. package/generators/subapp-react/templates/homepage/config/config.dev.ts +9 -1
  47. package/generators/subapp-react/templates/homepage/config/config.prod.development.ts +2 -1
  48. package/generators/subapp-react/templates/homepage/config/config.prod.testing.ts +2 -1
  49. package/generators/subapp-react/templates/homepage/config/config.prod.ts +2 -1
  50. package/generators/subapp-react/templates/homepage/config/config.ts +21 -0
  51. package/generators/subapp-react/templates/homepage/config/routes.ts +1 -1
  52. package/generators/subapp-react/templates/homepage/mock/api.mock.ts +2 -2
  53. package/generators/subapp-react/templates/homepage/package.json +3 -2
  54. package/generators/subapp-react/templates/homepage/src/app.tsx +1 -1
  55. package/generators/subapp-react/templates/homepage/src/common/request.ts +2 -2
  56. package/generators/subapp-react/templates/homepage/src/global.less +2 -1
  57. package/generators/subapp-react/templates/homepage/src/pages/index.less +1 -1
  58. package/generators/subapp-react/templates/homepage/src/pages/index.tsx +27 -27
  59. package/lib/utils.js +200 -2
  60. package/package.json +1 -1
@@ -9,10 +9,10 @@ const {
9
9
  collectFiles,
10
10
  transformDestPath,
11
11
  isTemplateFile,
12
- getLatestNpmVersion,
12
+ getPackageVersionsParallel,
13
13
  setupErrorHandlers,
14
14
  createLogger,
15
- // isVerbose,
15
+ loadMicorc,
16
16
  } = require('../../lib/utils');
17
17
 
18
18
  const IGNORE_LIST = require('./ignore-list.json');
@@ -24,6 +24,18 @@ module.exports = class extends Generator {
24
24
  initializing() {
25
25
  this.monorepoRoot = process.cwd();
26
26
  this.logger = createLogger(this);
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.monorepoRoot);
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
+
27
39
  const appsDir = path.join(this.monorepoRoot, 'apps');
28
40
  const workspaceFile = path.join(this.monorepoRoot, 'pnpm-workspace.yaml');
29
41
 
@@ -77,13 +89,14 @@ module.exports = class extends Generator {
77
89
  }
78
90
 
79
91
  const detectedScope = this._detectPackageScope();
92
+ const rc = this.rcConfig || {};
80
93
 
81
94
  this.answers = await this.prompt([
82
95
  {
83
96
  type: 'input',
84
97
  name: 'appName',
85
98
  message: 'Sub app name',
86
- default: 'subapp',
99
+ default: rc.defaultSubappName || 'subapp',
87
100
  filter: (input) => toKebab(input),
88
101
  validate: (input) => {
89
102
  const value = toKebab(input);
@@ -99,18 +112,32 @@ module.exports = class extends Generator {
99
112
  type: 'input',
100
113
  name: 'packageScope',
101
114
  message: 'Package scope',
102
- default: detectedScope || '@my-project',
115
+ default: rc.packageScope || detectedScope || '@my-project',
103
116
  validate: (input) => {
104
117
  if (!input) return 'Package scope is required';
105
118
  if (!input.startsWith('@')) return 'Package scope must start with @';
106
119
  return true;
107
120
  }
121
+ },
122
+ {
123
+ type: 'input',
124
+ name: 'devPort',
125
+ message: 'Dev server port',
126
+ default: rc.devPort || '8010',
127
+ validate: (input) => {
128
+ const port = Number(input);
129
+ if (!Number.isInteger(port) || port < 1024 || port > 65535) {
130
+ return 'Port must be an integer between 1024 and 65535';
131
+ }
132
+ return true;
133
+ }
108
134
  }
109
135
  ]);
110
136
 
111
137
  this.appName = toKebab(this.answers.appName);
112
138
  this.appNamePascal = toPascal(this.appName);
113
139
  this.packageScope = this.answers.packageScope;
140
+ this.devPort = this.answers.devPort;
114
141
  this.templateDir = this.templatePath('homepage');
115
142
  this.destDir = path.join(this.monorepoRoot, 'apps', this.appName);
116
143
  } catch (error) {
@@ -122,7 +149,7 @@ module.exports = class extends Generator {
122
149
  }
123
150
  }
124
151
 
125
- writing() {
152
+ async writing() {
126
153
  try {
127
154
  if (!fs.existsSync(this.templateDir)) {
128
155
  console.error('');
@@ -132,20 +159,25 @@ module.exports = class extends Generator {
132
159
  process.exit(1);
133
160
  }
134
161
 
135
- this.log('');
136
- this.log(`📦 Creating sub-app: ${this.appName}`);
137
- this.log(` Destination: apps/${this.appName}`);
138
- this.log('');
139
-
140
162
  this.logger.verbose('Template directory:', this.templateDir);
141
163
  this.logger.verbose('Destination directory:', this.destDir);
142
164
 
143
165
  // 在 mico_cli 根目录执行 npm view,以使用该目录 .npmrc 中的 Nexus 认证
144
166
  const cliRoot = path.resolve(__dirname, '../..');
145
- this.logger.verbose('Fetching latest package versions...');
167
+ this.logger.verbose('Fetching latest package versions (parallel)...');
168
+
169
+ // 并行获取版本
170
+ const versions = await getPackageVersionsParallel(
171
+ [
172
+ { name: '@mico-platform/ui', fallback: '1.0.0' },
173
+ { name: '@mico-platform/theme', fallback: '1.0.0' },
174
+ ],
175
+ 8000,
176
+ cliRoot,
177
+ );
146
178
 
147
- const micoUiVer = getLatestNpmVersion('@mico-platform/ui', '1.0.0', 8000, cliRoot);
148
- const themeVer = getLatestNpmVersion('@mico-platform/theme', '1.0.0', 8000, cliRoot);
179
+ const micoUiVer = versions['@mico-platform/ui'];
180
+ const themeVer = versions['@mico-platform/theme'];
149
181
 
150
182
  this.logger.verbose('@mico-platform/ui version:', micoUiVer);
151
183
  this.logger.verbose('@mico-platform/theme version:', themeVer);
@@ -154,6 +186,7 @@ module.exports = class extends Generator {
154
186
  appName: this.appName,
155
187
  AppName: this.appNamePascal,
156
188
  packageScope: this.packageScope,
189
+ devPort: this.devPort,
157
190
  micoUiVersion: `^${micoUiVer}`,
158
191
  themeVersion: `^${themeVer}`,
159
192
  micoUiVersionExact: micoUiVer
@@ -171,6 +204,50 @@ module.exports = class extends Generator {
171
204
  process.exit(1);
172
205
  }
173
206
 
207
+ // Dry-run 模式:只列出文件,不实际创建
208
+ if (this.isDryRun) {
209
+ this.log('');
210
+ this.log('\x1b[33m📋 Dry run mode - no files will be created\x1b[0m');
211
+ this.log('');
212
+ this.log(` Sub-app: ${this.appName}`);
213
+ this.log(` Scope: ${this.packageScope}`);
214
+ this.log(` Destination: apps/${this.appName}`);
215
+ this.log('');
216
+ this.log(' Would create the following files:');
217
+ this.log('');
218
+
219
+ let templateCount = 0;
220
+ let copyCount = 0;
221
+
222
+ for (const relPath of files) {
223
+ const destRelPath = transformDestPath(relPath);
224
+ const isTemplate = isTemplateFile(relPath) || path.basename(relPath) === '.env';
225
+ const tag = isTemplate ? '\x1b[32m[tpl]\x1b[0m' : '\x1b[36m[cpy]\x1b[0m';
226
+ this.log(` ${tag} apps/${this.appName}/${destRelPath}`);
227
+
228
+ if (isTemplate) {
229
+ templateCount++;
230
+ } else {
231
+ copyCount++;
232
+ }
233
+ }
234
+
235
+ this.log('');
236
+ this.log(` Total: ${files.length} files (${templateCount} templates, ${copyCount} static)`);
237
+ this.log('');
238
+ this.log(' Run without --dry-run to actually create these files.');
239
+ this.log('');
240
+
241
+ // 设置标记以跳过后续阶段
242
+ this._skipInstall = true;
243
+ return;
244
+ }
245
+
246
+ this.log('');
247
+ this.log(`📦 Creating sub-app: ${this.appName}`);
248
+ this.log(` Destination: apps/${this.appName}`);
249
+ this.log('');
250
+
174
251
  this.logger.verbose(`Processing ${files.length} files...`);
175
252
 
176
253
  let templateCount = 0;
@@ -181,7 +258,8 @@ module.exports = class extends Generator {
181
258
  const destRelPath = transformDestPath(relPath);
182
259
  const destPath = path.join(this.destDir, destRelPath);
183
260
 
184
- if (isTemplateFile(relPath)) {
261
+ const isEnvFile = path.basename(relPath) === '.env';
262
+ if (isTemplateFile(relPath) || isEnvFile) {
185
263
  this.logger.file('template', destRelPath);
186
264
  this.fs.copyTpl(srcPath, destPath, templateData);
187
265
  templateCount++;
@@ -193,6 +271,10 @@ module.exports = class extends Generator {
193
271
  }
194
272
 
195
273
  this.logger.verbose(`Processed: ${templateCount} templates, ${copyCount} copied`);
274
+
275
+ this._updateDevPreset();
276
+ this._updateMockPages();
277
+ this._updateConfigDev();
196
278
  } catch (error) {
197
279
  console.error('');
198
280
  console.error('❌ Error during file generation:');
@@ -202,7 +284,148 @@ module.exports = class extends Generator {
202
284
  }
203
285
  }
204
286
 
287
+ _updateDevPreset() {
288
+ const presetPath = path.join(this.monorepoRoot, 'dev.preset.json');
289
+ if (!fs.existsSync(presetPath)) {
290
+ this.logger.verbose('dev.preset.json not found, skipping preset update');
291
+ return;
292
+ }
293
+
294
+ try {
295
+ const preset = JSON.parse(fs.readFileSync(presetPath, 'utf-8'));
296
+ const fullApps = preset.presets?.full?.apps;
297
+
298
+ if (!Array.isArray(fullApps)) {
299
+ this.logger.verbose('dev.preset.json has no full.apps array, skipping');
300
+ return;
301
+ }
302
+
303
+ if (fullApps.includes(this.appName)) {
304
+ this.logger.verbose(`"${this.appName}" already in full preset, skipping`);
305
+ return;
306
+ }
307
+
308
+ fullApps.push(this.appName);
309
+ fs.writeFileSync(presetPath, `${JSON.stringify(preset, null, 2)}\n`, 'utf-8');
310
+
311
+ this.log(` 📝 已将 "${this.appName}" 添加到 dev.preset.json 的 full 预设中`);
312
+ } catch (e) {
313
+ console.warn(` ⚠️ 更新 dev.preset.json 失败: ${e.message}`);
314
+ }
315
+ }
316
+
317
+ _updateMockPages() {
318
+ const pagesPath = path.join(this.monorepoRoot, 'apps/layout/mock/pages.ts');
319
+ if (!fs.existsSync(pagesPath)) {
320
+ this.logger.verbose('apps/layout/mock/pages.ts not found, skipping');
321
+ return;
322
+ }
323
+
324
+ try {
325
+ const content = fs.readFileSync(pagesPath, 'utf-8');
326
+
327
+ if (content.includes(`route: '/${this.appName}'`)) {
328
+ this.logger.verbose(`Route "/${this.appName}" already in mock pages, skipping`);
329
+ return;
330
+ }
331
+
332
+ // 找出已有的最大 id
333
+ const idMatches = [...content.matchAll(/id:\s*(\d+)/g)];
334
+ const maxId = idMatches.reduce((max, m) => Math.max(max, Number(m[1])), 0);
335
+
336
+ const newEntry = [
337
+ ' {',
338
+ ` id: ${maxId + 1},`,
339
+ ` name: '${this.appNamePascal}',`,
340
+ ` nameEn: '${this.appNamePascal}',`,
341
+ ` nameKey: 'page.${this.appName}',`,
342
+ ` route: '/${this.appName}',`,
343
+ ` htmlUrl: '//localhost:${this.devPort}',`,
344
+ ' jsUrls: [],',
345
+ ' cssUrls: [],',
346
+ " prefixPath: '',",
347
+ " routeMode: 'prefix',",
348
+ ' enabled: true,',
349
+ ' accessControlEnabled: false,',
350
+ ' adminOnly: false,',
351
+ ' routeKey: null,',
352
+ ` mainDocumentId: ${Math.floor(Math.random() * 900) + 100},`,
353
+ " version: '',",
354
+ ' },',
355
+ ].join('\n');
356
+
357
+ // 插入到兜底路由 (route: '/*') 之前
358
+ const lines = content.split('\n');
359
+ let insertIdx = -1;
360
+
361
+ for (let i = 0; i < lines.length; i++) {
362
+ if (lines[i].includes("route: '/*'")) {
363
+ for (let j = i - 1; j >= 0; j--) {
364
+ if (lines[j].trim() === '{') {
365
+ insertIdx = j;
366
+ break;
367
+ }
368
+ }
369
+ break;
370
+ }
371
+ }
372
+
373
+ if (insertIdx === -1) {
374
+ const closingIdx = lines.findIndex((l) => l.trim() === '];');
375
+ if (closingIdx > 0) insertIdx = closingIdx;
376
+ }
377
+
378
+ if (insertIdx > 0) {
379
+ lines.splice(insertIdx, 0, newEntry);
380
+ fs.writeFileSync(pagesPath, lines.join('\n'), 'utf-8');
381
+ this.log(` 📝 已将 "${this.appName}" 的页面配置添加到 mock/pages.ts`);
382
+ }
383
+ } catch (e) {
384
+ console.warn(` ⚠️ 更新 mock/pages.ts 失败: ${e.message}`);
385
+ }
386
+ }
387
+
388
+ _updateConfigDev() {
389
+ const configPath = path.join(this.monorepoRoot, 'apps/layout/config/config.dev.ts');
390
+ if (!fs.existsSync(configPath)) {
391
+ this.logger.verbose('apps/layout/config/config.dev.ts not found, skipping');
392
+ return;
393
+ }
394
+
395
+ try {
396
+ const content = fs.readFileSync(configPath, 'utf-8');
397
+ const routeEntry = `'/${this.appName}/*'`;
398
+
399
+ if (content.includes(routeEntry)) {
400
+ this.logger.verbose(`Route "${routeEntry}" already in noPermissionRouteList, skipping`);
401
+ return;
402
+ }
403
+
404
+ const updated = content.replace(
405
+ /noPermissionRouteList:\s*\[([^\]]*)\]/,
406
+ (match, inner) => {
407
+ const trimmed = inner.trim();
408
+ const entries = trimmed ? `${trimmed}, ${routeEntry}` : routeEntry;
409
+ return `noPermissionRouteList: [${entries}]`;
410
+ },
411
+ );
412
+
413
+ if (updated === content) {
414
+ this.logger.verbose('noPermissionRouteList not found in config.dev.ts, skipping');
415
+ return;
416
+ }
417
+
418
+ fs.writeFileSync(configPath, updated, 'utf-8');
419
+ this.log(` 📝 已将 "${routeEntry}" 添加到 config.dev.ts 的 noPermissionRouteList 中`);
420
+ } catch (e) {
421
+ console.warn(` ⚠️ 更新 config.dev.ts 失败: ${e.message}`);
422
+ }
423
+ }
424
+
205
425
  install() {
426
+ // 跳过 dry-run 模式
427
+ if (this._skipInstall) return;
428
+
206
429
  this.log('');
207
430
  this.log('📦 正在安装依赖...');
208
431
  this.spawnCommandSync('pnpm', ['install'], {
@@ -211,6 +434,9 @@ module.exports = class extends Generator {
211
434
  }
212
435
 
213
436
  end() {
437
+ // 跳过 dry-run 模式
438
+ if (this._skipInstall) return;
439
+
214
440
  this.log('');
215
441
  this.log('✅ 子应用创建成功!');
216
442
  this.log('');
@@ -1,4 +1,5 @@
1
1
  # Default: 5
2
2
  CHECK_TIMEOUT=10
3
3
  DID_YOU_KNOW=none
4
- PORT=8010
4
+ PORT=<%= devPort %>
5
+ SOCKET_SERVER=http://localhost:<%= devPort %>
@@ -2,6 +2,13 @@
2
2
 
3
3
  import { defineConfig } from '@umijs/max';
4
4
 
5
+ /**
6
+ * ⚠️⚠️ 不要修改这个变量名,版本升级工具会读取这个变量名,进行版本升级
7
+ * @name mico-ui 版本
8
+ * @description 开发环境使用 mico-ui 的最新版本
9
+ */
10
+ const MICO_UI_VERSION = '<%= micoUiVersionExact %>';
11
+
5
12
  /**
6
13
  * 开发环境配置
7
14
  * - 不配置 externals,直接打包依赖,方便独立运行调试
@@ -43,6 +50,7 @@ const config: ReturnType<typeof defineConfig> = {
43
50
  // externals: {
44
51
  // react: 'window.React',
45
52
  // 'react-dom': 'window.ReactDOM',
53
+ // 'react-dom/client': 'window.ReactDOMClient',
46
54
  // '@mico-platform/ui': 'window.micoUI',
47
55
  // },
48
56
  /**
@@ -52,7 +60,7 @@ const config: ReturnType<typeof defineConfig> = {
52
60
  */
53
61
  styles: [
54
62
  // @mico-platform/ui 基础样式(使用运行时解析的最新版本号,避免 latest 标签)
55
- 'https://cdn-portal-test.micoplatform.com/portal-center/mico-ui/<%= micoUiVersionExact %>/ui/dist/css/mico-ui.min.css'
63
+ `https://cdn-portal.micoplatform.com/portal-center/mico-ui/${MICO_UI_VERSION}/ui/dist/css/mico-ui.min.css`,
56
64
  ],
57
65
  };
58
66
 
@@ -21,12 +21,13 @@ const config: ReturnType<typeof defineConfig> = {
21
21
  * @doc https://umijs.org/docs/api/config#externals
22
22
  *
23
23
  * 作为 qiankun 子应用时,这些库由主应用提供:
24
- * - react / react-dom: 主应用已加载,避免多实例问题
24
+ * - react / react-dom / react-dom/client: 主应用已加载,避免多实例问题
25
25
  * - @mico-platform/ui: 主应用已加载,复用组件和样式
26
26
  */
27
27
  externals: {
28
28
  react: 'React',
29
29
  'react-dom': 'ReactDOM',
30
+ 'react-dom/client': 'ReactDOMClient',
30
31
  '@mico-platform/ui': 'micoUI',
31
32
  },
32
33
  };
@@ -21,12 +21,13 @@ const config: ReturnType<typeof defineConfig> = {
21
21
  * @doc https://umijs.org/docs/api/config#externals
22
22
  *
23
23
  * 作为 qiankun 子应用时,这些库由主应用提供:
24
- * - react / react-dom: 主应用已加载,避免多实例问题
24
+ * - react / react-dom / react-dom/client: 主应用已加载,避免多实例问题
25
25
  * - @mico-platform/ui: 主应用已加载,复用组件和样式
26
26
  */
27
27
  externals: {
28
28
  react: 'React',
29
29
  'react-dom': 'ReactDOM',
30
+ 'react-dom/client': 'ReactDOMClient',
30
31
  '@mico-platform/ui': 'micoUI',
31
32
  },
32
33
  };
@@ -27,12 +27,13 @@ const config: ReturnType<typeof defineConfig> = {
27
27
  * @doc https://umijs.org/docs/api/config#externals
28
28
  *
29
29
  * 作为 qiankun 子应用时,这些库由主应用提供:
30
- * - react / react-dom: 主应用已加载,避免多实例问题
30
+ * - react / react-dom / react-dom/client: 主应用已加载,避免多实例问题
31
31
  * - @mico-platform/ui: 主应用已加载,复用组件和样式
32
32
  */
33
33
  externals: {
34
34
  react: 'React',
35
35
  'react-dom': 'ReactDOM',
36
+ 'react-dom/client': 'ReactDOMClient',
36
37
  '@mico-platform/ui': 'micoUI',
37
38
  },
38
39
  };
@@ -102,6 +102,27 @@ const config: ReturnType<typeof defineConfig> = {
102
102
  codeSplitting: {
103
103
  jsStrategy: 'granularChunks',
104
104
  },
105
+
106
+ /**
107
+ * @name 额外 Babel Presets
108
+ * @description 使用 @mico-platform/ui 的 babel preset,实现组件与图标的按需加载
109
+ */
110
+ extraBabelPresets: ['@mico-platform/ui/babel-preset'],
111
+
112
+ /**
113
+ * @name MFSU 配置
114
+ * @description
115
+ * - exclude: theme 子路径解析;ui 尽量与主应用一起编译
116
+ * - shared: React 单例,确保 MFSU 预打包里的组件(如 UI 库)与应用共用同一份 React,否则 useContext 报 null
117
+ * @doc https://umijs.org/docs/guides/mfsu
118
+ */
119
+ mfsu: {
120
+ exclude: ['@mico-platform/theme', '@mico-platform/ui'],
121
+ shared: {
122
+ react: { singleton: true },
123
+ 'react-dom': { singleton: true },
124
+ },
125
+ },
105
126
  };
106
127
 
107
128
  export default defineConfig(config);
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @name 路由配置
3
- * @description homepage 子应用的路由配置
3
+ * @description <%= appName %> 子应用的路由配置
4
4
  */
5
5
  export default [
6
6
  { path: '/<%= appName %>', component: '@/pages/index' },
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @name Mock API
3
- * @description homepage 子应用的 Mock 数据
3
+ * @description <%= appName %> 子应用的 Mock 数据
4
4
  * @doc https://umijs.org/docs/guides/mock
5
5
  */
6
6
 
@@ -12,7 +12,7 @@ export default {
12
12
  // id: 1001,
13
13
  // name: '张三',
14
14
  // email: 'zhangsan@example.com',
15
- // avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=homepage',
15
+ // avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=<%= appName %>',
16
16
  // role: 'admin',
17
17
  // department: '技术部',
18
18
  // },
@@ -17,8 +17,6 @@
17
17
  "dependencies": {
18
18
  "@mico-platform/theme": "<%= themeVersion %>",
19
19
  "@umijs/max": "^4.4.8",
20
- "babel-plugin-dynamic-import-node": "^2.3.3",
21
- "cross-env": "^10.1.0",
22
20
  "react": "^18.2.0",
23
21
  "react-dom": "^18.2.0"
24
22
  },
@@ -26,6 +24,9 @@
26
24
  "@mico-platform/ui": "<%= micoUiVersion %>",
27
25
  "@types/react": "^18.0.33",
28
26
  "@types/react-dom": "^18.0.11",
27
+ "babel-plugin-dynamic-import-node": "^2.3.3",
28
+ "babel-plugin-import": "^1.13.8",
29
+ "cross-env": "^10.1.0",
29
30
  "typescript": "^5.0.3"
30
31
  }
31
32
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @name 应用入口配置
3
- * @description homepage 子应用的运行时配置
3
+ * @description <%= appName %> 子应用的运行时配置
4
4
  * @doc https://umijs.org/docs/max/micro-frontend#子应用配置
5
5
  *
6
6
  * 注意:app.tsx 只能导出 Umi 规定的 API(qiankun、getInitialState 等)
@@ -26,12 +26,12 @@ export async function request<T = any>(
26
26
 
27
27
  if (mainAppRequest) {
28
28
  // 微前端模式:使用主应用的 request
29
- console.log('[homepage] Using main app request:', url);
29
+ console.log('[<%= appName %>] Using main app request:', url);
30
30
  return mainAppRequest<T>(url, options);
31
31
  }
32
32
 
33
33
  // 独立运行模式:使用 umi request
34
- console.log('[homepage] Using umi request:', url);
34
+ console.log('[<%= appName %>] Using umi request:', url);
35
35
  // umi request 默认返回 data,skipErrorHandler 可选
36
36
  return umiRequest<T>(url, {
37
37
  skipErrorHandler: true,
@@ -20,7 +20,8 @@ body {
20
20
  'Noto Sans', sans-serif;
21
21
  }
22
22
 
23
- #homepage-root {
23
+ #<%= appName %>-root {
24
24
  width: 100%;
25
25
  height: 100%;
26
+ background-color: var(--color-fill-1);
26
27
  }
@@ -6,7 +6,7 @@
6
6
  // 导入主题变量(仅 Less 变量,子应用用)
7
7
  @import '@mico-platform/theme/variables';
8
8
 
9
- .homepage {
9
+ .<%= appName %> {
10
10
  padding: @spacing-lg;
11
11
  min-height: 100vh;
12
12
  // 使用主题色变量 - 背景色会随主题切换自动变化
@@ -135,7 +135,7 @@ export default function HomePage() {
135
135
  };
136
136
 
137
137
  return (
138
- <div className="homepage">
138
+ <div className="<%= appName %>">
139
139
  <Title heading={2}>🎨 子应用主题 & 组件示例</Title>
140
140
  <Paragraph type="secondary">
141
141
  子应用复用主应用的 React 和 @mico-platform/ui,无重复打包,主题自动同步
@@ -316,44 +316,44 @@ export default function HomePage() {
316
316
  </Paragraph>
317
317
 
318
318
  <Title heading={6}>品牌色 & 功能色</Title>
319
- <div className="homepage-colors">
320
- <div className="homepage-color-item">
321
- <div className="homepage-color-box brand-1" />
322
- <span className="homepage-color-label">@Brand1-6</span>
319
+ <div className="<%= appName %>-colors">
320
+ <div className="<%= appName %>-color-item">
321
+ <div className="<%= appName %>-color-box brand-1" />
322
+ <span className="<%= appName %>-color-label">@Brand1-6</span>
323
323
  </div>
324
- <div className="homepage-color-item">
325
- <div className="homepage-color-box brand-2" />
326
- <span className="homepage-color-label">@Brand2-6</span>
324
+ <div className="<%= appName %>-color-item">
325
+ <div className="<%= appName %>-color-box brand-2" />
326
+ <span className="<%= appName %>-color-label">@Brand2-6</span>
327
327
  </div>
328
- <div className="homepage-color-item">
329
- <div className="homepage-color-box success" />
330
- <span className="homepage-color-label">@Success-6</span>
328
+ <div className="<%= appName %>-color-item">
329
+ <div className="<%= appName %>-color-box success" />
330
+ <span className="<%= appName %>-color-label">@Success-6</span>
331
331
  </div>
332
- <div className="homepage-color-item">
333
- <div className="homepage-color-box warning" />
334
- <span className="homepage-color-label">@Warning-6</span>
332
+ <div className="<%= appName %>-color-item">
333
+ <div className="<%= appName %>-color-box warning" />
334
+ <span className="<%= appName %>-color-label">@Warning-6</span>
335
335
  </div>
336
- <div className="homepage-color-item">
337
- <div className="homepage-color-box danger" />
338
- <span className="homepage-color-label">@Danger-6</span>
336
+ <div className="<%= appName %>-color-item">
337
+ <div className="<%= appName %>-color-box danger" />
338
+ <span className="<%= appName %>-color-label">@Danger-6</span>
339
339
  </div>
340
340
  </div>
341
341
 
342
342
  <Title heading={6} style={{ marginTop: 16 }}>
343
343
  中性色(随主题自动切换)
344
344
  </Title>
345
- <div className="homepage-colors">
346
- <div className="homepage-color-item">
347
- <div className="homepage-color-box fill-1" />
348
- <span className="homepage-color-label">@color-fill-1</span>
345
+ <div className="<%= appName %>-colors">
346
+ <div className="<%= appName %>-color-item">
347
+ <div className="<%= appName %>-color-box fill-1" />
348
+ <span className="<%= appName %>-color-label">@color-fill-1</span>
349
349
  </div>
350
- <div className="homepage-color-item">
351
- <div className="homepage-color-box fill-2" />
352
- <span className="homepage-color-label">@color-fill-2</span>
350
+ <div className="<%= appName %>-color-item">
351
+ <div className="<%= appName %>-color-box fill-2" />
352
+ <span className="<%= appName %>-color-label">@color-fill-2</span>
353
353
  </div>
354
- <div className="homepage-color-item">
355
- <div className="homepage-color-box fill-3" />
356
- <span className="homepage-color-label">@color-fill-3</span>
354
+ <div className="<%= appName %>-color-item">
355
+ <div className="<%= appName %>-color-box fill-3" />
356
+ <span className="<%= appName %>-color-label">@color-fill-3</span>
357
357
  </div>
358
358
  </div>
359
359
  </Card>