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
|
@@ -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');
|
|
@@ -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 =
|
|
148
|
-
const themeVer =
|
|
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
|
-
|
|
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('');
|
|
@@ -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
|
-
|
|
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 Mock API
|
|
3
|
-
* @description
|
|
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
|
|
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
|
}
|
|
@@ -26,12 +26,12 @@ export async function request<T = any>(
|
|
|
26
26
|
|
|
27
27
|
if (mainAppRequest) {
|
|
28
28
|
// 微前端模式:使用主应用的 request
|
|
29
|
-
console.log('[
|
|
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('[
|
|
34
|
+
console.log('[<%= appName %>] Using umi request:', url);
|
|
35
35
|
// umi request 默认返回 data,skipErrorHandler 可选
|
|
36
36
|
return umiRequest<T>(url, {
|
|
37
37
|
skipErrorHandler: true,
|
|
@@ -135,7 +135,7 @@ export default function HomePage() {
|
|
|
135
135
|
};
|
|
136
136
|
|
|
137
137
|
return (
|
|
138
|
-
<div className="
|
|
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="
|
|
320
|
-
<div className="
|
|
321
|
-
<div className="
|
|
322
|
-
<span className="
|
|
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="
|
|
325
|
-
<div className="
|
|
326
|
-
<span className="
|
|
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="
|
|
329
|
-
<div className="
|
|
330
|
-
<span className="
|
|
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="
|
|
333
|
-
<div className="
|
|
334
|
-
<span className="
|
|
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="
|
|
337
|
-
<div className="
|
|
338
|
-
<span className="
|
|
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="
|
|
346
|
-
<div className="
|
|
347
|
-
<div className="
|
|
348
|
-
<span className="
|
|
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="
|
|
351
|
-
<div className="
|
|
352
|
-
<span className="
|
|
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="
|
|
355
|
-
<div className="
|
|
356
|
-
<span className="
|
|
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>
|