generator-mico-cli 0.2.33 → 0.2.34

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 CHANGED
@@ -117,6 +117,12 @@ mico setup multica-desktop
117
117
 
118
118
  所有字段均为可选,未配置时使用默认值或交互式提示。
119
119
 
120
+ ### 组件库版本(`micoPackages`)
121
+
122
+ CLI 根 `package.json` 的 `micoPackages` 是 `@mico-platform/ui` / `@mico-platform/theme` 的**唯一维护点**(semver range,如 `0.x`)。`mico create` 时在该 range 内 resolve 最新版本,写入生成项目为 `^x.y.z`(如 `^0.0.25`);子应用开发环境 CDN 的 `MICO_UI_VERSION` 使用同一 resolved 精确版本。
123
+
124
+ 组件库发 patch(仍在 `0.x`)时通常无需发 CLI;切换到 `1.x` 等 major 策略时,修改 `micoPackages` 并发布新 CLI 即可。
125
+
120
126
  ## Monorepo 项目生成器 (micro-react)
121
127
 
122
128
  创建基于 qiankun 微前端架构的完整 Monorepo 项目:
@@ -31,4 +31,4 @@ mico create micro-react
31
31
  | `cdnPrefix` / `cdnPrefixPath` | string | prompt;`cdnPrefixPath` 为空或带尾斜杠 |
32
32
  | `author` | string | prompt |
33
33
  | `intlTag` | string | prompt |
34
- | `micoUiVersion` / `themeVersion` | string | `npm view @mico-platform/{ui,theme} version`,前缀 `^`,失败回退 `^1.0.0` |
34
+ | `micoUiVersion` / `themeVersion` | string | `^${micoPackages range resolve 的最新版本}` |
@@ -9,7 +9,7 @@ const {
9
9
  collectFiles,
10
10
  transformDestPath,
11
11
  isTemplateFile,
12
- getPackageVersionsParallel,
12
+ getMicoTemplateVersions,
13
13
  setupErrorHandlers,
14
14
  createLogger,
15
15
  loadMicorc,
@@ -162,23 +162,14 @@ module.exports = class extends Generator {
162
162
 
163
163
  // 在 mico_cli 根目录执行 npm view,以使用该目录 .npmrc 中的 Nexus 认证
164
164
  const cliRoot = path.resolve(__dirname, '../..');
165
- this.logger.verbose('Fetching latest package versions (parallel)...');
166
-
167
- // 并行获取版本
168
- const versions = await getPackageVersionsParallel(
169
- [
170
- { name: '@mico-platform/ui', fallback: '1.0.0' },
171
- { name: '@mico-platform/theme', fallback: '1.0.0' },
172
- ],
173
- 8000,
174
- cliRoot,
175
- );
165
+ this.logger.verbose('Fetching mico package versions from manifest...');
176
166
 
177
- const micoUiVer = versions['@mico-platform/ui'];
178
- const themeVer = versions['@mico-platform/theme'];
167
+ const micoVersions = await getMicoTemplateVersions(cliRoot, 8000);
168
+ const ui = micoVersions['@mico-platform/ui'];
169
+ const theme = micoVersions['@mico-platform/theme'];
179
170
 
180
- this.logger.verbose('@mico-platform/ui version:', micoUiVer);
181
- this.logger.verbose('@mico-platform/theme version:', themeVer);
171
+ this.logger.verbose('@mico-platform/ui range:', ui.range, 'resolved:', ui.resolved);
172
+ this.logger.verbose('@mico-platform/theme range:', theme.range, 'resolved:', theme.resolved);
182
173
 
183
174
  // 构建 CDN 路径片段:如果有 prefix 则加上斜杠前缀,否则为空
184
175
  const cdnPrefixPath = this.cdnPrefix ? `${this.cdnPrefix}/` : '';
@@ -191,8 +182,8 @@ module.exports = class extends Generator {
191
182
  appId: this.appId,
192
183
  packageScope: this.packageScope,
193
184
  author: this.author,
194
- micoUiVersion: `^${micoUiVer}`,
195
- themeVersion: `^${themeVer}`,
185
+ micoUiVersion: `^${ui.resolved}`,
186
+ themeVersion: `^${theme.resolved}`,
196
187
  cdnPrefix: this.cdnPrefix,
197
188
  cdnPrefixPath, // 用于拼接路径,已包含尾部斜杠
198
189
  intlTag: this.intlTag,
@@ -23,7 +23,7 @@ function readVerboseDebugQueryOnce(): boolean {
23
23
  }
24
24
  try {
25
25
  const v = new URLSearchParams(window.location.search).get(DEBUG_LOGS_QUERY_KEY);
26
- if (v == null || v === '') {
26
+ if (v === null || v === '') {
27
27
  return false;
28
28
  }
29
29
  const normalized = v.toLowerCase();
@@ -115,8 +115,12 @@ const MicroAppLoader: React.FC<MicroAppLoaderProps> = ({
115
115
  current_tenant: currentUser?.current_tenant,
116
116
  allowed_tenants: allowedTenants,
117
117
  };
118
- const { request: _request, ...loggableProps } = props;
119
- console.log('[MicroAppLoader] buildProps', { appName, ...loggableProps });
118
+ console.log('[MicroAppLoader] buildProps', {
119
+ appName,
120
+ ...Object.fromEntries(
121
+ Object.entries(props).filter(([key]) => key !== 'request'),
122
+ ),
123
+ });
120
124
  return props;
121
125
  }, [base, env, routePath, currentUser, appName]);
122
126
 
@@ -9,7 +9,7 @@ const {
9
9
  collectFiles,
10
10
  transformDestPath,
11
11
  isTemplateFile,
12
- getPackageVersionsParallel,
12
+ getMicoTemplateVersions,
13
13
  detectPackageScope,
14
14
  setupErrorHandlers,
15
15
  createLogger,
@@ -147,23 +147,14 @@ module.exports = class extends Generator {
147
147
 
148
148
  // 在 mico_cli 根目录执行 npm view,以使用该目录 .npmrc 中的 Nexus 认证
149
149
  const cliRoot = path.resolve(__dirname, '../..');
150
- this.logger.verbose('Fetching latest package versions (parallel)...');
151
-
152
- // 并行获取版本
153
- const versions = await getPackageVersionsParallel(
154
- [
155
- { name: '@mico-platform/ui', fallback: '1.0.0' },
156
- { name: '@mico-platform/theme', fallback: '1.0.0' },
157
- ],
158
- 8000,
159
- cliRoot,
160
- );
150
+ this.logger.verbose('Fetching mico package versions from manifest...');
161
151
 
162
- const micoUiVer = versions['@mico-platform/ui'];
163
- const themeVer = versions['@mico-platform/theme'];
152
+ const micoVersions = await getMicoTemplateVersions(cliRoot, 8000);
153
+ const ui = micoVersions['@mico-platform/ui'];
154
+ const theme = micoVersions['@mico-platform/theme'];
164
155
 
165
- this.logger.verbose('@mico-platform/ui version:', micoUiVer);
166
- this.logger.verbose('@mico-platform/theme version:', themeVer);
156
+ this.logger.verbose('@mico-platform/ui range:', ui.range, 'resolved:', ui.resolved);
157
+ this.logger.verbose('@mico-platform/theme range:', theme.range, 'resolved:', theme.resolved);
167
158
 
168
159
  const templateData = {
169
160
  appName: this.appName,
@@ -172,9 +163,9 @@ module.exports = class extends Generator {
172
163
  appNameSnake: this.appName.replace(/-/g, '_'),
173
164
  packageScope: this.packageScope,
174
165
  devPort: this.devPort,
175
- micoUiVersion: `^${micoUiVer}`,
176
- themeVersion: `^${themeVer}`,
177
- micoUiVersionExact: micoUiVer
166
+ micoUiVersion: `^${ui.resolved}`,
167
+ themeVersion: `^${theme.resolved}`,
168
+ micoUiVersionExact: ui.resolved,
178
169
  };
179
170
 
180
171
  this.logger.verbose('Template data:', JSON.stringify(templateData, null, 2));
@@ -23,7 +23,7 @@ function readVerboseDebugQueryOnce(): boolean {
23
23
  }
24
24
  try {
25
25
  const v = new URLSearchParams(window.location.search).get(DEBUG_LOGS_QUERY_KEY);
26
- if (v == null || v === '') {
26
+ if (v === null || v === '') {
27
27
  return false;
28
28
  }
29
29
  const normalized = v.toLowerCase();
@@ -14,6 +14,7 @@ const {
14
14
  setupErrorHandlers,
15
15
  createLogger,
16
16
  loadMicorc,
17
+ getMicoTemplateVersions,
17
18
  } = require('../../lib/utils');
18
19
 
19
20
  const IGNORE_LIST = require('./ignore-list.json');
@@ -145,6 +146,10 @@ module.exports = class extends Generator {
145
146
  this.logger.verbose('Template directory:', this.templateDir);
146
147
  this.logger.verbose('Destination directory:', this.destDir);
147
148
 
149
+ const cliRoot = path.resolve(__dirname, '../..');
150
+ const micoVersions = await getMicoTemplateVersions(cliRoot, 8000);
151
+ const ui = micoVersions['@mico-platform/ui'];
152
+
148
153
  const templateData = {
149
154
  appName: this.appName,
150
155
  AppName: this.appNamePascal,
@@ -152,6 +157,7 @@ module.exports = class extends Generator {
152
157
  umdGlobalName: this.umdGlobalName,
153
158
  packageScope: this.packageScope,
154
159
  devPort: this.devPort,
160
+ micoUiPeerRange: `^${ui.resolved}`,
155
161
  };
156
162
 
157
163
  this.logger.verbose('Template data:', JSON.stringify(templateData, null, 2));
@@ -15,7 +15,7 @@
15
15
  "peerDependencies": {
16
16
  "react": "^18.0.0",
17
17
  "react-dom": "^18.0.0",
18
- "@mico-platform/ui": ">=0.0.13"
18
+ "@mico-platform/ui": "<%= micoUiPeerRange %>"
19
19
  },
20
20
  "devDependencies": {
21
21
  "@types/react": "^18.3.26",
package/lib/utils.js CHANGED
@@ -4,6 +4,7 @@ const fs = require('node:fs');
4
4
  const path = require('node:path');
5
5
  const { exec, execSync, execFileSync, execFile } = require('node:child_process');
6
6
  const os = require('node:os');
7
+ const semver = require('semver');
7
8
 
8
9
  /**
9
10
  * 转换为 kebab-case
@@ -150,6 +151,9 @@ const VALID_PACKAGE_NAME_RE = /^(@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/
150
151
  /** scope 包含 mico 时使用的 registry(如 @mico-platform/*) */
151
152
  const MICO_NPM_REGISTRY = 'https://nexus-vywrajy.micoworld.net/repository/mico-base-common/';
152
153
 
154
+ /** 生成器模板中由 micoPackages 管理的包名 */
155
+ const MICO_TEMPLATE_PACKAGES = ['@mico-platform/ui', '@mico-platform/theme'];
156
+
153
157
  /**
154
158
  * 根据包名 scope 选择 registry:scope 包含 mico 时用 MICO_NPM_REGISTRY,否则用 npm 默认
155
159
  * @param {string} packageName - 包名,如 '@mico-platform/ui'
@@ -287,6 +291,195 @@ async function getPackageVersionsParallel(packages, timeoutMs = 8000, cwd) {
287
291
  }, {});
288
292
  }
289
293
 
294
+ /**
295
+ * 读取 CLI package.json 中的 micoPackages(组件库版本 range 唯一维护点)
296
+ * @param {string} [cliRoot] - CLI 仓库根目录,默认 lib/ 的上级
297
+ * @returns {Record<string, string>} 包名 → semver range
298
+ */
299
+ function loadMicoPackages(cliRoot) {
300
+ const root = cliRoot || path.resolve(__dirname, '..');
301
+ const pkgPath = path.join(root, 'package.json');
302
+ if (!fs.existsSync(pkgPath)) {
303
+ throw new Error(`CLI package.json not found: ${pkgPath}`);
304
+ }
305
+
306
+ let pkg;
307
+ try {
308
+ pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
309
+ } catch (e) {
310
+ throw new Error(`Failed to parse CLI package.json: ${e.message}`);
311
+ }
312
+
313
+ const { micoPackages } = pkg;
314
+ if (!micoPackages || typeof micoPackages !== 'object') {
315
+ throw new Error(
316
+ 'CLI package.json must define "micoPackages" with @mico-platform/ui and @mico-platform/theme ranges',
317
+ );
318
+ }
319
+
320
+ const result = {};
321
+ for (const name of MICO_TEMPLATE_PACKAGES) {
322
+ const range = micoPackages[name];
323
+ if (typeof range !== 'string' || !range.trim()) {
324
+ throw new Error(`micoPackages.${name} must be a non-empty semver range string`);
325
+ }
326
+ const trimmed = range.trim();
327
+ if (!semver.validRange(trimmed)) {
328
+ throw new Error(`micoPackages.${name} is not a valid semver range: ${range}`);
329
+ }
330
+ result[name] = trimmed;
331
+ }
332
+
333
+ return result;
334
+ }
335
+
336
+ /**
337
+ * 执行 npm view <pkg>@<spec> version
338
+ * @returns {Promise<string|undefined>}
339
+ */
340
+ function npmViewVersionAsync(packageName, spec, timeoutMs = 8000, cwd) {
341
+ if (!VALID_PACKAGE_NAME_RE.test(packageName)) {
342
+ return Promise.resolve(undefined);
343
+ }
344
+
345
+ return new Promise((resolve) => {
346
+ let settled = false;
347
+ const done = (value) => {
348
+ if (!settled) {
349
+ settled = true;
350
+ resolve(value);
351
+ }
352
+ };
353
+
354
+ const registry = getRegistryForPackage(packageName);
355
+ const args = ['view', `${packageName}@${spec}`, 'version'];
356
+ if (registry) args.push(`--registry=${registry}`);
357
+
358
+ const child = execFile(
359
+ 'npm',
360
+ args,
361
+ {
362
+ encoding: 'utf-8',
363
+ timeout: timeoutMs,
364
+ ...(cwd && { cwd }),
365
+ },
366
+ (error, stdout) => {
367
+ if (error) {
368
+ done(undefined);
369
+ return;
370
+ }
371
+ const v = stdout.trim();
372
+ done(v && semver.valid(v) ? v : undefined);
373
+ },
374
+ );
375
+
376
+ setTimeout(() => {
377
+ child.kill();
378
+ done(undefined);
379
+ }, timeoutMs + 100);
380
+ });
381
+ }
382
+
383
+ /**
384
+ * 执行 npm view <pkg> versions --json
385
+ * @returns {Promise<string[]>}
386
+ */
387
+ function npmViewAllVersionsAsync(packageName, timeoutMs = 8000, cwd) {
388
+ if (!VALID_PACKAGE_NAME_RE.test(packageName)) {
389
+ return Promise.resolve([]);
390
+ }
391
+
392
+ return new Promise((resolve) => {
393
+ let settled = false;
394
+ const done = (value) => {
395
+ if (!settled) {
396
+ settled = true;
397
+ resolve(value);
398
+ }
399
+ };
400
+
401
+ const registry = getRegistryForPackage(packageName);
402
+ const args = ['view', packageName, 'versions', '--json'];
403
+ if (registry) args.push(`--registry=${registry}`);
404
+
405
+ const child = execFile(
406
+ 'npm',
407
+ args,
408
+ {
409
+ encoding: 'utf-8',
410
+ timeout: timeoutMs,
411
+ ...(cwd && { cwd }),
412
+ },
413
+ (error, stdout) => {
414
+ if (error) {
415
+ done([]);
416
+ return;
417
+ }
418
+ try {
419
+ const list = JSON.parse(stdout.trim());
420
+ done(Array.isArray(list) ? list.filter((v) => semver.valid(v)) : []);
421
+ } catch {
422
+ done([]);
423
+ }
424
+ },
425
+ );
426
+
427
+ setTimeout(() => {
428
+ child.kill();
429
+ done([]);
430
+ }, timeoutMs + 100);
431
+ });
432
+ }
433
+
434
+ /**
435
+ * 在 semver range 内解析包的最新精确版本(用于 CDN / micoUiVersionExact)
436
+ * MICO_PKG_VERSIONS 可覆盖 resolved,但仍须满足 range
437
+ * @param {string} packageName
438
+ * @param {string} range - semver range,如 0.x
439
+ * @param {number} [timeoutMs=8000]
440
+ * @param {string} [cwd]
441
+ * @returns {Promise<string>}
442
+ */
443
+ async function resolveNpmVersionInRangeAsync(packageName, range, timeoutMs = 8000, cwd) {
444
+ const override = getVersionOverride(packageName);
445
+ if (override) {
446
+ if (!semver.satisfies(override, range, { includePrerelease: true })) {
447
+ throw new Error(
448
+ `MICO_PKG_VERSIONS override ${override} does not satisfy range ${range} for ${packageName}`,
449
+ );
450
+ }
451
+ return override;
452
+ }
453
+
454
+ const direct = await npmViewVersionAsync(packageName, range, timeoutMs, cwd);
455
+ if (direct) return direct;
456
+
457
+ const versions = await npmViewAllVersionsAsync(packageName, timeoutMs, cwd);
458
+ const resolved = semver.maxSatisfying(versions, range, { includePrerelease: false });
459
+ if (resolved) return resolved;
460
+
461
+ throw new Error(`No version found for ${packageName} satisfying range ${range}`);
462
+ }
463
+
464
+ /**
465
+ * 读取 micoPackages range,并解析各包在 range 内的最新精确版本
466
+ * @param {string} cliRoot - CLI 仓库根目录
467
+ * @param {number} [timeoutMs=8000]
468
+ * @returns {Promise<Record<string, { range: string, resolved: string }>>}
469
+ */
470
+ async function getMicoTemplateVersions(cliRoot, timeoutMs = 8000) {
471
+ const micoPackages = loadMicoPackages(cliRoot);
472
+ const entries = await Promise.all(
473
+ MICO_TEMPLATE_PACKAGES.map(async (name) => {
474
+ const range = micoPackages[name];
475
+ const resolved = await resolveNpmVersionInRangeAsync(name, range, timeoutMs, cliRoot);
476
+ return [name, { range, resolved }];
477
+ }),
478
+ );
479
+
480
+ return Object.fromEntries(entries);
481
+ }
482
+
290
483
  // ============ 配置文件支持 ============
291
484
 
292
485
  /**
@@ -503,6 +696,10 @@ module.exports = {
503
696
  getLatestNpmVersion,
504
697
  getLatestNpmVersionAsync,
505
698
  getPackageVersionsParallel,
699
+ MICO_TEMPLATE_PACKAGES,
700
+ loadMicoPackages,
701
+ resolveNpmVersionInRangeAsync,
702
+ getMicoTemplateVersions,
506
703
  setupErrorHandlers,
507
704
  TEMPLATE_EXTENSIONS,
508
705
  // 配置文件
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "generator-mico-cli",
3
- "version": "0.2.33",
3
+ "version": "0.2.34",
4
4
  "description": "Yeoman generator for Mico CLI projects",
5
5
  "keywords": [
6
6
  "yeoman-generator",
@@ -35,6 +35,10 @@
35
35
  "engines": {
36
36
  "node": ">=18"
37
37
  },
38
+ "micoPackages": {
39
+ "@mico-platform/ui": "0.x",
40
+ "@mico-platform/theme": "0.x"
41
+ },
38
42
  "dependencies": {
39
43
  "semver": "^7.6.3",
40
44
  "update-notifier": "^7.3.1",