generator-mico-cli 0.2.31 → 0.2.32

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 (162) hide show
  1. package/README.md +145 -18
  2. package/bin/mico.js +76 -0
  3. package/generators/h5-react/ignore-list.json +1 -0
  4. package/generators/h5-react/index.js +349 -0
  5. package/generators/h5-react/meta.json +11 -0
  6. package/generators/h5-react/templates/.commitlintrc.js +7 -0
  7. package/generators/h5-react/templates/.cursor/rules/cicd-deploy.mdc +104 -0
  8. package/generators/h5-react/templates/.cursor/rules/common-intl.mdc +42 -0
  9. package/generators/h5-react/templates/.cursor/rules/git-hooks.mdc +40 -0
  10. package/generators/h5-react/templates/.cursor/rules/internal-packages.mdc +46 -0
  11. package/generators/h5-react/templates/.cursor/rules/monorepo.mdc +64 -0
  12. package/generators/h5-react/templates/.cursor/rules/package-json.mdc +52 -0
  13. package/generators/h5-react/templates/.cursor/rules/tailwind-umi.mdc +60 -0
  14. package/generators/h5-react/templates/.cursor/rules/umi-app.mdc +74 -0
  15. package/generators/h5-react/templates/.cursor/rules/umi-config.mdc +86 -0
  16. package/generators/h5-react/templates/.cursor/rules/umi-mock.mdc +80 -0
  17. package/generators/h5-react/templates/.cursor/rules/workspace-request.mdc +52 -0
  18. package/generators/h5-react/templates/.cursor/skills/biz-app-analyzer/SKILL.md +213 -0
  19. package/generators/h5-react/templates/.cursor/skills/biz-app-analyzer/evals/evals.json +23 -0
  20. package/generators/h5-react/templates/.cursor/skills/biz-app-analyzer/references/cursor-rule-template.md +60 -0
  21. package/generators/h5-react/templates/.cursor/skills/biz-app-analyzer/references/phase-1-scanning.md +102 -0
  22. package/generators/h5-react/templates/.cursor/skills/biz-app-analyzer/references/phase-2-context-analysis.md +102 -0
  23. package/generators/h5-react/templates/.cursor/skills/biz-app-analyzer/references/phase-3-pattern-extraction.md +105 -0
  24. package/generators/h5-react/templates/.cursor/skills/biz-app-analyzer/references/phase-4-module-mapping.md +65 -0
  25. package/generators/h5-react/templates/.cursor/skills/biz-app-analyzer/references/phase-5-glossary.md +63 -0
  26. package/generators/h5-react/templates/.cursor/skills/biz-app-analyzer/references/templates/DEV_PATTERNS.tpl.md +77 -0
  27. package/generators/h5-react/templates/.cursor/skills/biz-app-analyzer/references/templates/GLOSSARY.tpl.md +17 -0
  28. package/generators/h5-react/templates/.cursor/skills/biz-app-analyzer/references/templates/MODULE_MAP.tpl.md +45 -0
  29. package/generators/h5-react/templates/.cursor/skills/biz-app-analyzer/references/templates/PROJECT_CONTEXT.tpl.md +155 -0
  30. package/generators/h5-react/templates/.cursor/skills/biz-app-analyzer/references/update-mode.md +116 -0
  31. package/generators/h5-react/templates/.env.development +5 -0
  32. package/generators/h5-react/templates/.env.production +5 -0
  33. package/generators/h5-react/templates/.env.testing +5 -0
  34. package/generators/h5-react/templates/.husky/commit-msg +2 -0
  35. package/generators/h5-react/templates/.husky/pre-commit +2 -0
  36. package/generators/h5-react/templates/.lintstagedrc.js +8 -0
  37. package/generators/h5-react/templates/.prettierrc.json +7 -0
  38. package/generators/h5-react/templates/CICD/before_build.sh +76 -0
  39. package/generators/h5-react/templates/CICD/start_dev.sh +54 -0
  40. package/generators/h5-react/templates/CICD/start_local.sh +30 -0
  41. package/generators/h5-react/templates/CICD/start_prod.sh +53 -0
  42. package/generators/h5-react/templates/CICD/start_test.sh +55 -0
  43. package/generators/h5-react/templates/CICD/wangsu_fresh_dev.sh +19 -0
  44. package/generators/h5-react/templates/CICD/wangsu_fresh_prod.sh +19 -0
  45. package/generators/h5-react/templates/CICD/wangsu_fresh_test.sh +19 -0
  46. package/generators/h5-react/templates/README.md +301 -0
  47. package/generators/h5-react/templates/_gitignore +30 -0
  48. package/generators/h5-react/templates/_npmrc +6 -0
  49. package/generators/h5-react/templates/apps/.gitkeep +0 -0
  50. package/generators/h5-react/templates/dev.preset.json +10 -0
  51. package/generators/h5-react/templates/package.json +56 -0
  52. package/generators/h5-react/templates/packages/common-intl/README.md +180 -0
  53. package/generators/h5-react/templates/packages/common-intl/eslint.config.ts +12 -0
  54. package/generators/h5-react/templates/packages/common-intl/package.json +31 -0
  55. package/generators/h5-react/templates/packages/common-intl/src/index.ts +3 -0
  56. package/generators/h5-react/templates/packages/common-intl/src/intl.ts +100 -0
  57. package/generators/h5-react/templates/packages/common-intl/tsconfig.json +3 -0
  58. package/generators/h5-react/templates/packages/components/eslint.config.ts +12 -0
  59. package/generators/h5-react/templates/packages/components/package.json +32 -0
  60. package/generators/h5-react/templates/packages/components/src/Layout/ImmersiveHeader.tsx +126 -0
  61. package/generators/h5-react/templates/packages/components/src/Layout/LayoutFooter.tsx +72 -0
  62. package/generators/h5-react/templates/packages/components/src/Layout/index.tsx +121 -0
  63. package/generators/h5-react/templates/packages/components/src/assets/image/back.png +0 -0
  64. package/generators/h5-react/templates/packages/components/src/index.ts +0 -0
  65. package/generators/h5-react/templates/packages/components/tsconfig.json +13 -0
  66. package/generators/h5-react/templates/packages/components/typings.d.ts +1 -0
  67. package/generators/h5-react/templates/packages/constant/eslint.config.ts +12 -0
  68. package/generators/h5-react/templates/packages/constant/package.json +19 -0
  69. package/generators/h5-react/templates/packages/constant/src/index.ts +0 -0
  70. package/generators/h5-react/templates/packages/constant/src/member.ts +8 -0
  71. package/generators/h5-react/templates/packages/constant/tsconfig.json +3 -0
  72. package/generators/h5-react/templates/packages/deeplink/eslint.config.ts +12 -0
  73. package/generators/h5-react/templates/packages/deeplink/package.json +18 -0
  74. package/generators/h5-react/templates/packages/deeplink/src/index.ts +7 -0
  75. package/generators/h5-react/templates/packages/deeplink/tsconfig.json +3 -0
  76. package/generators/h5-react/templates/packages/domain/eslint.config.ts +12 -0
  77. package/generators/h5-react/templates/packages/domain/package.json +18 -0
  78. package/generators/h5-react/templates/packages/domain/src/index.ts +29 -0
  79. package/generators/h5-react/templates/packages/domain/tsconfig.json +3 -0
  80. package/generators/h5-react/templates/packages/domain/types.d.ts +11 -0
  81. package/generators/h5-react/templates/packages/eslint/eslint.config.base.ts +36 -0
  82. package/generators/h5-react/templates/packages/eslint/eslint.config.react.ts +33 -0
  83. package/generators/h5-react/templates/packages/eslint/package.json +22 -0
  84. package/generators/h5-react/templates/packages/js-bridge/eslint.config.ts +17 -0
  85. package/generators/h5-react/templates/packages/js-bridge/package.json +23 -0
  86. package/generators/h5-react/templates/packages/js-bridge/src/call.ts +126 -0
  87. package/generators/h5-react/templates/packages/js-bridge/src/closeH5Page.ts +9 -0
  88. package/generators/h5-react/templates/packages/js-bridge/src/getUserInfo.ts +96 -0
  89. package/generators/h5-react/templates/packages/js-bridge/src/index.ts +15 -0
  90. package/generators/h5-react/templates/packages/js-bridge/tsconfig.json +3 -0
  91. package/generators/h5-react/templates/packages/js-bridge/type.d.ts +24 -0
  92. package/generators/h5-react/templates/packages/request/axios.d.ts +42 -0
  93. package/generators/h5-react/templates/packages/request/eslint.config.ts +17 -0
  94. package/generators/h5-react/templates/packages/request/package.json +22 -0
  95. package/generators/h5-react/templates/packages/request/src/index.ts +165 -0
  96. package/generators/h5-react/templates/packages/request/src/interceptors.ts +126 -0
  97. package/generators/h5-react/templates/packages/request/src/types.ts +101 -0
  98. package/generators/h5-react/templates/packages/request/src/url-resolver.ts +66 -0
  99. package/generators/h5-react/templates/packages/request/src/utils.ts +12 -0
  100. package/generators/h5-react/templates/packages/request/tsconfig.json +3 -0
  101. package/generators/h5-react/templates/packages/request/umi.d.ts +94 -0
  102. package/generators/h5-react/templates/packages/typescript/package.json +11 -0
  103. package/generators/h5-react/templates/packages/typescript/tsconfig.base.json +23 -0
  104. package/generators/h5-react/templates/packages/typescript/tsconfig.react.json +7 -0
  105. package/generators/h5-react/templates/packages/umi-config/eslint.config.ts +12 -0
  106. package/generators/h5-react/templates/packages/umi-config/package.json +31 -0
  107. package/generators/h5-react/templates/packages/umi-config/src/config.dev.ts +34 -0
  108. package/generators/h5-react/templates/packages/umi-config/src/config.prod.development.ts +17 -0
  109. package/generators/h5-react/templates/packages/umi-config/src/config.prod.production.ts +42 -0
  110. package/generators/h5-react/templates/packages/umi-config/src/config.prod.testing.ts +17 -0
  111. package/generators/h5-react/templates/packages/umi-config/src/config.prod.ts +56 -0
  112. package/generators/h5-react/templates/packages/umi-config/src/config.ts +86 -0
  113. package/generators/h5-react/templates/packages/umi-config/src/index.ts +25 -0
  114. package/generators/h5-react/templates/packages/umi-config/src/plugins/apply-sentry-plugin.ts +57 -0
  115. package/generators/h5-react/templates/packages/umi-config/src/type.d.ts +3 -0
  116. package/generators/h5-react/templates/packages/umi-config/tsconfig.json +3 -0
  117. package/generators/h5-react/templates/packages/utils/eslint.config.ts +12 -0
  118. package/generators/h5-react/templates/packages/utils/package.json +27 -0
  119. package/generators/h5-react/templates/packages/utils/src/date.ts +21 -0
  120. package/generators/h5-react/templates/packages/utils/src/env.ts +40 -0
  121. package/generators/h5-react/templates/packages/utils/src/index.ts +3 -0
  122. package/generators/h5-react/templates/packages/utils/src/md5.ts +17 -0
  123. package/generators/h5-react/templates/packages/utils/src/mock.ts +83 -0
  124. package/generators/h5-react/templates/packages/utils/src/number.ts +23 -0
  125. package/generators/h5-react/templates/packages/utils/src/tailwind.ts +12 -0
  126. package/generators/h5-react/templates/packages/utils/src/url.ts +19 -0
  127. package/generators/h5-react/templates/packages/utils/tsconfig.json +9 -0
  128. package/generators/h5-react/templates/page.config.ts +1 -0
  129. package/generators/h5-react/templates/pnpm-workspace.yaml +17 -0
  130. package/generators/h5-react/templates/scripts/collect-dist.js +78 -0
  131. package/generators/h5-react/templates/scripts/dev-preset.js +265 -0
  132. package/generators/h5-react/templates/scripts/dev-preset.schema.json +39 -0
  133. package/generators/h5-react/templates/scripts/dev.js +133 -0
  134. package/generators/h5-react/templates/scripts/gateway.ts +241 -0
  135. package/generators/h5-react/templates/turbo.json +86 -0
  136. package/generators/subapp-h5/ignore-list.json +1 -0
  137. package/generators/subapp-h5/index.js +424 -0
  138. package/generators/subapp-h5/meta.json +10 -0
  139. package/generators/subapp-h5/templates/.env +1 -0
  140. package/generators/subapp-h5/templates/.stylelintrc.js +22 -0
  141. package/generators/subapp-h5/templates/config/config.dev.ts +7 -0
  142. package/generators/subapp-h5/templates/config/config.prod.development.ts +7 -0
  143. package/generators/subapp-h5/templates/config/config.prod.production.ts +10 -0
  144. package/generators/subapp-h5/templates/config/config.prod.testing.ts +7 -0
  145. package/generators/subapp-h5/templates/config/config.prod.ts +7 -0
  146. package/generators/subapp-h5/templates/config/config.ts +6 -0
  147. package/generators/subapp-h5/templates/config/routes.ts +13 -0
  148. package/generators/subapp-h5/templates/eslint.config.ts +12 -0
  149. package/generators/subapp-h5/templates/mock/user.ts +34 -0
  150. package/generators/subapp-h5/templates/package.json +42 -0
  151. package/generators/subapp-h5/templates/src/app.tsx +14 -0
  152. package/generators/subapp-h5/templates/src/assets/yay.jpg +0 -0
  153. package/generators/subapp-h5/templates/src/intl.ts +37 -0
  154. package/generators/subapp-h5/templates/src/layouts/index.tsx +10 -0
  155. package/generators/subapp-h5/templates/src/pages/index.tsx +22 -0
  156. package/generators/subapp-h5/templates/src/services/user.ts +38 -0
  157. package/generators/subapp-h5/templates/tailwind.config.js +16 -0
  158. package/generators/subapp-h5/templates/tailwind.css +7 -0
  159. package/generators/subapp-h5/templates/tsconfig.json +3 -0
  160. package/generators/subapp-h5/templates/typings.d.ts +1 -0
  161. package/lib/setup-multica-desktop.js +154 -0
  162. package/package.json +1 -1
@@ -0,0 +1,424 @@
1
+ 'use strict';
2
+
3
+ const Generator = require('yeoman-generator');
4
+ const fs = require('node:fs');
5
+ const path = require('node:path');
6
+ const {
7
+ toKebab,
8
+ toPascal,
9
+ collectFiles,
10
+ transformDestPath,
11
+ isTemplateFile,
12
+ detectPackageScope,
13
+ setupErrorHandlers,
14
+ createLogger,
15
+ loadMicorc,
16
+ } = require('../../lib/utils');
17
+
18
+ const IGNORE_LIST = require('./ignore-list.json');
19
+
20
+ setupErrorHandlers();
21
+
22
+ module.exports = class extends Generator {
23
+ initializing() {
24
+ this.monorepoRoot = process.cwd();
25
+ this.logger = createLogger(this);
26
+
27
+ this.isDryRun = this.options.dryRun || process.env.MICO_DRY_RUN === '1';
28
+ this.skipInstall =
29
+ this.options.skipInstall === true || this.options['skip-install'] === true;
30
+ this.skipEndMessage = this.options.skipEndMessage === true;
31
+ this.composedFromH5React = this.options.composedFromH5React === true;
32
+
33
+ const { config: rcConfig, configPath } = loadMicorc(this.monorepoRoot);
34
+ this.rcConfig = rcConfig;
35
+ if (configPath) {
36
+ this.logger.verbose('Loaded config from:', configPath);
37
+ this.logger.verbose('Config:', JSON.stringify(rcConfig, null, 2));
38
+ }
39
+
40
+ const appsDir = path.join(this.monorepoRoot, 'apps');
41
+ const workspaceFile = path.join(this.monorepoRoot, 'pnpm-workspace.yaml');
42
+
43
+ // 联动 h5-react 时,父生成器刚把 apps/.gitkeep 等写入 mem-fs,尚未 commit 到磁盘,
44
+ // 仅用 fs.existsSync(apps) 会误判。允许:磁盘已有 apps/,或 mem-fs 中已有 apps 下占位文件。
45
+ const appsGitkeepInStore = this.destinationPath('apps/.gitkeep');
46
+ const hasAppsOnDisk = fs.existsSync(appsDir);
47
+ const hasAppsInMemFs =
48
+ typeof this.fs?.exists === 'function' && this.fs.exists(appsGitkeepInStore);
49
+ if (!hasAppsOnDisk && !hasAppsInMemFs) {
50
+ console.error('');
51
+ console.error('❌ Error: apps directory not found.');
52
+ console.error('');
53
+ console.error(
54
+ ' This generator must be run from an H5 monorepo root that contains an "apps" directory.',
55
+ );
56
+ console.error(` Current directory: ${this.monorepoRoot}`);
57
+ console.error('');
58
+ console.error(' Usage:');
59
+ console.error(' cd /path/to/your-h5-monorepo');
60
+ console.error(' mico create subapp-h5');
61
+ console.error('');
62
+ process.exit(1);
63
+ }
64
+
65
+ if (!fs.existsSync(workspaceFile)) {
66
+ console.warn('');
67
+ console.warn('⚠️ Warning: pnpm-workspace.yaml not found.');
68
+ console.warn(' Make sure you are in the correct monorepo.');
69
+ console.warn('');
70
+ }
71
+ }
72
+
73
+ async prompting() {
74
+ try {
75
+ const compose = this.options.composeAnswers;
76
+ if (compose && typeof compose === 'object') {
77
+ const appName = toKebab(compose.appName);
78
+ if (!appName) {
79
+ console.error('');
80
+ console.error('❌ Error: composeAnswers.appName is required.');
81
+ console.error('');
82
+ process.exit(1);
83
+ }
84
+ const destDir = path.join(this.monorepoRoot, 'apps', appName);
85
+ if (fs.existsSync(destDir)) {
86
+ console.error('');
87
+ console.error(`❌ Error: Target already exists: apps/${appName}`);
88
+ console.error('');
89
+ process.exit(1);
90
+ }
91
+ this.answers = {
92
+ appName,
93
+ packageScope: compose.packageScope,
94
+ devPort: String(compose.devPort ?? '8000'),
95
+ author: compose.author || '',
96
+ };
97
+ if (!this.answers.packageScope || !this.answers.packageScope.startsWith('@')) {
98
+ console.error('');
99
+ console.error('❌ Error: composeAnswers.packageScope must be a scoped name (e.g. @my-org).');
100
+ console.error('');
101
+ process.exit(1);
102
+ }
103
+ } else {
104
+ const detectedScope = detectPackageScope(this.monorepoRoot);
105
+ const rc = this.rcConfig || {};
106
+
107
+ this.answers = await this.prompt([
108
+ {
109
+ type: 'input',
110
+ name: 'appName',
111
+ message: 'Sub app name',
112
+ default: rc.defaultSubappName || 'app',
113
+ filter: (input) => toKebab(input),
114
+ validate: (input) => {
115
+ const value = toKebab(input);
116
+ if (!value) return 'App name is required';
117
+ const destDir = path.join(this.monorepoRoot, 'apps', value);
118
+ if (fs.existsSync(destDir)) {
119
+ return `Target already exists: apps/${value}`;
120
+ }
121
+ return true;
122
+ },
123
+ },
124
+ {
125
+ type: 'input',
126
+ name: 'packageScope',
127
+ message: 'Package scope',
128
+ default: rc.packageScope || detectedScope || '@my-project',
129
+ validate: (input) => {
130
+ if (!input) return 'Package scope is required';
131
+ if (!input.startsWith('@')) return 'Package scope must start with @';
132
+ return true;
133
+ },
134
+ },
135
+ {
136
+ type: 'input',
137
+ name: 'devPort',
138
+ message: 'Dev server port (standalone pnpm dev in this app)',
139
+ default: rc.devPort || '8000',
140
+ validate: (input) => {
141
+ const port = Number(input);
142
+ if (!Number.isInteger(port) || port < 1024 || port > 65535) {
143
+ return 'Port must be an integer between 1024 and 65535';
144
+ }
145
+ return true;
146
+ },
147
+ },
148
+ {
149
+ type: 'input',
150
+ name: 'author',
151
+ message: 'Author (optional)',
152
+ default: rc.author || '',
153
+ },
154
+ ]);
155
+ }
156
+
157
+ this.appName = toKebab(this.answers.appName);
158
+ this.appNamePascal = toPascal(this.appName);
159
+ this.packageScope = this.answers.packageScope;
160
+ this.devPort = String(this.answers.devPort);
161
+ this.author = this.answers.author || '';
162
+ this.templateDir = this.templatePath();
163
+ this.destDir = path.join(this.monorepoRoot, 'apps', this.appName);
164
+ } catch (error) {
165
+ console.error('');
166
+ console.error('❌ Error during prompting:');
167
+ console.error(` ${error.message}`);
168
+ console.error('');
169
+ process.exit(1);
170
+ }
171
+ }
172
+
173
+ async writing() {
174
+ try {
175
+ if (!fs.existsSync(this.templateDir)) {
176
+ console.error('');
177
+ console.error('❌ Error: Template directory not found.');
178
+ console.error(` Expected: ${this.templateDir}`);
179
+ console.error('');
180
+ process.exit(1);
181
+ }
182
+
183
+ this.logger.verbose('Template directory:', this.templateDir);
184
+ this.logger.verbose('Destination directory:', this.destDir);
185
+
186
+ const templateData = {
187
+ appName: this.appName,
188
+ AppName: this.appNamePascal,
189
+ packageScope: this.packageScope,
190
+ devPort: this.devPort,
191
+ author: this.author,
192
+ };
193
+
194
+ this.logger.verbose('Template data:', JSON.stringify(templateData, null, 2));
195
+
196
+ const files = collectFiles(this.templateDir, this.templateDir, IGNORE_LIST);
197
+
198
+ if (files.length === 0) {
199
+ console.error('');
200
+ console.error('❌ Error: No template files found.');
201
+ console.error(` Template directory: ${this.templateDir}`);
202
+ console.error('');
203
+ process.exit(1);
204
+ }
205
+
206
+ if (this.isDryRun) {
207
+ this.log('');
208
+ this.log('\x1b[33m📋 Dry run mode - no files will be created\x1b[0m');
209
+ this.log('');
210
+ this.log(` Sub-app: ${this.appName}`);
211
+ this.log(` Scope: ${this.packageScope}`);
212
+ this.log(` Destination: apps/${this.appName}`);
213
+ this.log('');
214
+ this.log(' Would create the following files:');
215
+ this.log('');
216
+
217
+ let templateCount = 0;
218
+ let copyCount = 0;
219
+
220
+ for (const relPath of files) {
221
+ const destRelPath = transformDestPath(relPath);
222
+ const isTemplate = isTemplateFile(relPath) || path.basename(relPath) === '.env';
223
+ const tag = isTemplate ? '\x1b[32m[tpl]\x1b[0m' : '\x1b[36m[cpy]\x1b[0m';
224
+ this.log(` ${tag} apps/${this.appName}/${destRelPath}`);
225
+
226
+ if (isTemplate) {
227
+ templateCount++;
228
+ } else {
229
+ copyCount++;
230
+ }
231
+ }
232
+
233
+ this.log('');
234
+ this.log(
235
+ ` Total: ${files.length} files (${templateCount} templates, ${copyCount} static)`,
236
+ );
237
+ this.log('');
238
+ this.log(' Run without --dry-run to actually create these files.');
239
+ this.log('');
240
+
241
+ this._skipInstall = true;
242
+ return;
243
+ }
244
+
245
+ this.log('');
246
+ this.log(`📦 Creating sub-app: ${this.appName}`);
247
+ this.log(` Destination: apps/${this.appName}`);
248
+ this.log('');
249
+
250
+ this.logger.verbose(`Processing ${files.length} files...`);
251
+
252
+ let templateCount = 0;
253
+ let copyCount = 0;
254
+
255
+ for (const relPath of files) {
256
+ const srcPath = path.join(this.templateDir, relPath);
257
+ const destRelPath = transformDestPath(relPath);
258
+ const destPath = path.join(this.destDir, destRelPath);
259
+
260
+ if (isTemplateFile(relPath) || path.basename(relPath) === '.env') {
261
+ this.logger.file('template', destRelPath);
262
+ this.fs.copyTpl(srcPath, destPath, templateData);
263
+ templateCount++;
264
+ } else {
265
+ this.logger.file('copy', destRelPath);
266
+ this.fs.copy(srcPath, destPath);
267
+ copyCount++;
268
+ }
269
+ }
270
+
271
+ this.logger.verbose(`Processed: ${templateCount} templates, ${copyCount} copied`);
272
+
273
+ this._updateDevPreset();
274
+ this._updatePageConfig();
275
+ } catch (error) {
276
+ console.error('');
277
+ console.error('❌ Error during file generation:');
278
+ console.error(` ${error.message}`);
279
+ console.error('');
280
+ process.exit(1);
281
+ }
282
+ }
283
+
284
+ /**
285
+ * 读取目标根目录文件:优先 mem-fs(与父生成器联动时尚未落盘),否则读磁盘。
286
+ * @param {string} absolutePath
287
+ * @returns {string|null}
288
+ */
289
+ _readDestinationText(absolutePath) {
290
+ if (this.fs.exists(absolutePath)) {
291
+ const buf = this.fs.read(absolutePath);
292
+ return Buffer.isBuffer(buf) ? buf.toString('utf8') : String(buf);
293
+ }
294
+ if (fs.existsSync(absolutePath)) {
295
+ return fs.readFileSync(absolutePath, 'utf8');
296
+ }
297
+ return null;
298
+ }
299
+
300
+ /**
301
+ * 写入目标根目录文件(走 mem-fs,最终由 Yeoman 统一落盘)。
302
+ * @param {string} absolutePath
303
+ * @param {string} content
304
+ */
305
+ _writeDestinationText(absolutePath, content) {
306
+ this.fs.write(absolutePath, content);
307
+ }
308
+
309
+ _updateDevPreset() {
310
+ const presetPath = this.destinationPath('dev.preset.json');
311
+ const raw = this._readDestinationText(presetPath);
312
+ if (raw == null) {
313
+ this.logger.verbose('dev.preset.json not found, skipping preset update');
314
+ return;
315
+ }
316
+
317
+ try {
318
+ const preset = JSON.parse(raw);
319
+ const fullApps = preset.presets?.full?.apps;
320
+
321
+ if (!Array.isArray(fullApps)) {
322
+ this.logger.verbose('dev.preset.json has no full.apps array, skipping');
323
+ return;
324
+ }
325
+
326
+ if (fullApps.includes(this.appName)) {
327
+ this.logger.verbose(`"${this.appName}" already in full preset, skipping`);
328
+ return;
329
+ }
330
+
331
+ fullApps.push(this.appName);
332
+ this._writeDestinationText(presetPath, `${JSON.stringify(preset, null, 2)}\n`);
333
+
334
+ this.log(` 📝 已将 "${this.appName}" 添加到 dev.preset.json 的 full 预设中`);
335
+ } catch (e) {
336
+ console.warn(` ⚠️ 更新 dev.preset.json 失败: ${e.message}`);
337
+ }
338
+ }
339
+
340
+ _updatePageConfig() {
341
+ const pageConfigPath = this.destinationPath('page.config.ts');
342
+ const content = this._readDestinationText(pageConfigPath);
343
+ if (content == null) {
344
+ this.logger.verbose('page.config.ts not found, skipping');
345
+ return;
346
+ }
347
+
348
+ try {
349
+ if (content.includes(`${this.appName}:`)) {
350
+ this.logger.verbose(`page.config.ts already has ${this.appName}, skipping`);
351
+ return;
352
+ }
353
+
354
+ const keyLine = ` ${this.appName}: true`;
355
+ const lastBraceIdx = content.lastIndexOf('};');
356
+ if (lastBraceIdx === -1) {
357
+ this.logger.verbose('page.config.ts: no closing }; found, skipping');
358
+ return;
359
+ }
360
+
361
+ const before = content.slice(0, lastBraceIdx).trimEnd();
362
+ const after = content.slice(lastBraceIdx);
363
+ let newBefore;
364
+ if (before.endsWith('{')) {
365
+ newBefore = `${before}\n${keyLine}\n`;
366
+ } else {
367
+ newBefore = `${before},\n${keyLine}\n`;
368
+ }
369
+
370
+ this._writeDestinationText(pageConfigPath, newBefore + after);
371
+ this.log(` 📝 已将 "${this.appName}" 写入 page.config.ts`);
372
+ } catch (e) {
373
+ console.warn(` ⚠️ 更新 page.config.ts 失败: ${e.message}`);
374
+ }
375
+ }
376
+
377
+ install() {
378
+ if (this._skipInstall || this.skipInstall) return;
379
+
380
+ this.log('');
381
+ this.log('📦 正在安装依赖...');
382
+ try {
383
+ this.spawnCommandSync('pnpm', ['install', '--filter', this.appName], {
384
+ cwd: this.monorepoRoot,
385
+ });
386
+ } catch (e) {
387
+ this._installFailed = true;
388
+ this.log('');
389
+ this.log('⚠️ 依赖安装失败,但子应用文件已全部生成。');
390
+ this.logger.verbose('Install error:', e.message);
391
+ }
392
+ }
393
+
394
+ end() {
395
+ if (this._skipInstall || this.skipEndMessage) return;
396
+
397
+ this.log('');
398
+
399
+ if (this._installFailed) {
400
+ this.log('⚠️ 子应用文件已创建,但依赖安装未完成。');
401
+ this.log('');
402
+ this.log(' 请手动安装依赖:');
403
+ this.log('');
404
+ this.log(' pnpm install');
405
+ this.log('');
406
+ this.log(' 如果安装仍然失败,可尝试:');
407
+ this.log(' pnpm install --network-concurrency 4');
408
+ } else {
409
+ this.log('✅ 子应用创建成功!');
410
+ }
411
+
412
+ this.log('');
413
+ this.log(' 后续步骤:');
414
+ this.log('');
415
+ this.log(` cd apps/${this.appName}`);
416
+ this.log(' pnpm dev');
417
+ this.log('');
418
+ if (!this.composedFromH5React) {
419
+ this.log(' 继续在 monorepo 根目录新增子应用:');
420
+ this.log(' mico create subapp-h5');
421
+ this.log('');
422
+ }
423
+ }
424
+ };
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "subapp-h5",
3
+ "description": "在 H5 Monorepo 的 apps/ 下新增 Umi 4 子应用",
4
+ "usage": "cd <h5-monorepo-root> && mico create subapp-h5",
5
+ "features": [
6
+ "写入 page.config.ts 与 dev.preset.json",
7
+ "Tailwind + @umijs/plugins 模板",
8
+ "与仓库 packageScope / workspace 包对齐"
9
+ ]
10
+ }
@@ -0,0 +1 @@
1
+ PORT=<%= devPort %>
@@ -0,0 +1,22 @@
1
+ module.exports = {
2
+ extends: [require.resolve('umi/stylelint')],
3
+ rules: {
4
+ 'at-rule-no-unknown': [
5
+ true,
6
+ {
7
+ ignoreAtRules: [
8
+ 'tailwind',
9
+ 'apply',
10
+ 'layer',
11
+ 'config',
12
+ 'plugin',
13
+ 'theme',
14
+ 'utility',
15
+ 'source',
16
+ 'custom-variant',
17
+ ],
18
+ },
19
+ ],
20
+ },
21
+ ignoreFiles: ["dist/**/*", "node_modules/**/*"],
22
+ };
@@ -0,0 +1,7 @@
1
+ // https://umijs.org/config/
2
+
3
+ import { createDevConfig } from '<%= packageScope %>/umi-config/config.dev';
4
+
5
+ export default createDevConfig({
6
+ // custom config
7
+ });
@@ -0,0 +1,7 @@
1
+ // https://umijs.org/config/
2
+
3
+ import { createDevelopmentConfig } from '<%= packageScope %>/umi-config/config.prod.development';
4
+
5
+ export default createDevelopmentConfig({
6
+ // custom config
7
+ });
@@ -0,0 +1,10 @@
1
+ // https://umijs.org/config/
2
+
3
+ import { createProductionConfig } from '<%= packageScope %>/umi-config/config.prod.production';
4
+
5
+ export default createProductionConfig(
6
+ { appName: '<%= appName %>' },
7
+ {
8
+ // custom config
9
+ },
10
+ );
@@ -0,0 +1,7 @@
1
+ // https://umijs.org/config/
2
+
3
+ import { createTestingConfig } from '<%= packageScope %>/umi-config/config.prod.testing';
4
+
5
+ export default createTestingConfig({
6
+ // custom config
7
+ });
@@ -0,0 +1,7 @@
1
+ // https://umijs.org/config/
2
+
3
+ import { createProConfig } from '<%= packageScope %>/umi-config/config.prod';
4
+
5
+ export default createProConfig({
6
+ // custom config
7
+ });
@@ -0,0 +1,6 @@
1
+ // https://umijs.org/config/
2
+
3
+ import { createBaseConfig } from '<%= packageScope %>/umi-config/config';
4
+ import routes from './routes';
5
+
6
+ export default createBaseConfig({ routes });
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @name 基础路由配置
3
+ * @description 这里只配置静态的基础路由,动态路由从 window.__MICO_MENUS__ 获取
4
+ * @doc https://umijs.org/docs/guides/routes
5
+ * @note Umi Max 会自动使用 src/layouts/index.tsx 作为全局布局,无需显式配置
6
+ */
7
+ export default [
8
+ {
9
+ path: '/',
10
+ component: 'index',
11
+ name: '首页',
12
+ },
13
+ ];
@@ -0,0 +1,12 @@
1
+ import baseConfig from '<%= packageScope %>/eslint/react';
2
+ import { defineConfig } from 'eslint/config';
3
+ export default defineConfig([
4
+ ...baseConfig,
5
+ {
6
+ languageOptions: {
7
+ parserOptions: {
8
+ tsconfigRootDir: import.meta.dirname,
9
+ },
10
+ },
11
+ },
12
+ ]);
@@ -0,0 +1,34 @@
1
+ import mockjs from 'mockjs';
2
+ import { z } from 'zod';
3
+ import { defineMock } from 'umi';
4
+ import { baseUrl, tryProxy, validateBody } from '<%= packageScope %>/utils/mock';
5
+
6
+ export default defineMock({
7
+ [`POST ${baseUrl}/user`]: async (req, res) => {
8
+ // 参数校验
9
+ const schema = z.object({
10
+ email: z.string(),
11
+ })
12
+ if (!validateBody(req.body, schema, res)) return;
13
+
14
+ if (await tryProxy(req, res)) return;
15
+
16
+ setTimeout(() => {
17
+ res.json(mockjs.mock({
18
+ 'code': 200,
19
+ 'message': '@cword(10, 20)',
20
+ 'content': {
21
+ 'token': {
22
+ 'accessToken': '@guid()',
23
+ 'refreshToken': '@guid()',
24
+ },
25
+ 'thirdProfile': {
26
+ 'name': '@cname()',
27
+ 'email': '@email()',
28
+ 'phone': /^1(3|5|8|7|9)\d{9}$/,
29
+ },
30
+ },
31
+ }));
32
+ }, 500);
33
+ },
34
+ });
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "<%= appName %>",
3
+ "version": "0.0.1",
4
+ "private": true,
5
+ "author": "<%= author %>",
6
+ "scripts": {
7
+ "dev": "umi dev",
8
+ "build": "npm run build:production",
9
+ "build:development": "cross-env UMI_ENV=development umi build",
10
+ "build:production": "cross-env UMI_ENV=production umi build",
11
+ "build:testing": "cross-env UMI_ENV=testing umi build",
12
+ "postinstall": "umi setup",
13
+ "setup": "umi setup",
14
+ "start": "npm run dev",
15
+ "lint": "eslint && stylelint \"**/*.{css,less}\"",
16
+ "lint:fix": "eslint --fix && stylelint \"**/*.{css,less}\" --fix"
17
+ },
18
+ "dependencies": {
19
+ "<%= packageScope %>/common-intl": "workspace:^",
20
+ "<%= packageScope %>/components": "workspace:^",
21
+ "<%= packageScope %>/constant": "workspace:^",
22
+ "<%= packageScope %>/deeplink": "workspace:^",
23
+ "<%= packageScope %>/domain": "workspace:^",
24
+ "<%= packageScope %>/js-bridge": "workspace:^",
25
+ "<%= packageScope %>/request": "workspace:^",
26
+ "<%= packageScope %>/umi-config": "workspace:^",
27
+ "<%= packageScope %>/utils": "workspace:^",
28
+ "react": "catalog:",
29
+ "react-dom": "catalog:",
30
+ "umi": "catalog:"
31
+ },
32
+ "devDependencies": {
33
+ "@common-web/sentry": "catalog:",
34
+ "<%= packageScope %>/eslint": "workspace:^",
35
+ "@types/react": "catalog:",
36
+ "@types/react-dom": "catalog:",
37
+ "@umijs/plugins": "catalog:",
38
+ "cross-env": "catalog:",
39
+ "tailwindcss": "catalog:",
40
+ "tailwindcss-safe-area": "catalog:"
41
+ }
42
+ }
@@ -0,0 +1,14 @@
1
+ import { fetchMultilingualData } from '@/intl';
2
+
3
+ export function render(oldRender: () => void) {
4
+ /**
5
+ * 在应用渲染前加载多语言数据,可根据需要自己调整逻辑
6
+ */
7
+ fetchMultilingualData()
8
+ .then(oldRender)
9
+ .catch((error: Error) => {
10
+ console.error('获取多语言文案失败', error);
11
+ // 即使失败也继续渲染,使用兜底文案
12
+ oldRender();
13
+ });
14
+ }
@@ -0,0 +1,37 @@
1
+ import { addIntl, init } from '<%= packageScope %>/common-intl';
2
+
3
+ const { fetchMultilingualData, i18n } = init({
4
+ tag: '<%= appName %>',
5
+ indexedDBParams: { dbName: 'i18n_<%= appName %>' },
6
+ });
7
+
8
+ /**
9
+ * 1. 调用方法
10
+ * fetchMultilingualData();
11
+ *
12
+ * 2. 使用文案
13
+ * import intl from '@/intl';
14
+ * intl.common_hello();
15
+ * intl.common_welcome_aa('张三');
16
+ */
17
+ const intl = addIntl(
18
+ {
19
+ // ============ 示例文案(可删除) ============
20
+ common_hello: () =>
21
+ i18n({
22
+ key: 'common_hello',
23
+ defaultMessage: '你好',
24
+ }),
25
+ common_welcome_aa: (name: string) =>
26
+ i18n({
27
+ key: 'common_welcome_aa',
28
+ interpolations: [name],
29
+ defaultMessage: `欢迎, ${name}`,
30
+ }),
31
+ },
32
+ i18n,
33
+ );
34
+
35
+ export { fetchMultilingualData };
36
+
37
+ export default intl;
@@ -0,0 +1,10 @@
1
+ import { SentryErrorBoundary } from '@common-web/sentry';
2
+ import { Outlet } from 'umi';
3
+
4
+ export default function Layout() {
5
+ return (
6
+ <SentryErrorBoundary>
7
+ <Outlet />
8
+ </SentryErrorBoundary>
9
+ );
10
+ }