generator-mico-cli 0.2.25 → 0.2.27

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 (22) hide show
  1. package/README.md +2 -0
  2. package/generators/micro-react/templates/apps/layout/config/config.dev.ts +0 -1
  3. package/generators/micro-react/templates/apps/layout/config/config.prod.development.ts +7 -1
  4. package/generators/micro-react/templates/apps/layout/config/config.prod.testing.ts +7 -1
  5. package/generators/micro-react/templates/apps/layout/config/config.prod.ts +1 -1
  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 +46 -2
  7. package/generators/micro-react/templates/apps/layout/mock/menus.ts +1 -1
  8. package/generators/micro-react/templates/apps/layout/mock/pages.ts +3 -0
  9. package/generators/micro-react/templates/apps/layout/src/app.tsx +1 -0
  10. package/generators/micro-react/templates/apps/layout/src/common/menu/parser.ts +2 -0
  11. package/generators/micro-react/templates/apps/layout/src/common/menu/types.ts +4 -0
  12. package/generators/micro-react/templates/apps/layout/src/common/request/config.ts +27 -4
  13. package/generators/micro-react/templates/apps/layout/src/components/MicroAppLoader/index.tsx +10 -2
  14. package/generators/micro-react/templates/apps/layout/src/layouts/index.tsx +1 -0
  15. package/generators/subapp-react/index.js +1 -0
  16. package/generators/subapp-react/templates/homepage/config/config.prod.development.ts +9 -0
  17. package/generators/subapp-react/templates/homepage/config/config.prod.testing.ts +9 -0
  18. package/generators/subapp-react/templates/homepage/config/config.prod.ts +2 -0
  19. package/generators/subapp-react/templates/homepage/config/routes.ts +1 -1
  20. package/generators/subapp-react/templates/homepage/src/app.tsx +9 -4
  21. package/lib/utils.js +1 -1
  22. package/package.json +3 -1
package/README.md CHANGED
@@ -216,7 +216,9 @@ pnpm test:watch
216
216
  | `tests/integration/micro-react.test.js` | micro-react 生成器集成测试 |
217
217
  | `tests/integration/subapp-react.test.js` | subapp-react 生成器集成测试 |
218
218
  | `tests/integration/subapp-umd.test.js` | subapp-umd 生成器集成测试 |
219
+ | `tests/integration/mico-cli.test.js` | `mico` 入口子进程测试(help / version / list / create 透传) |
219
220
  | `tests/helpers/setup.js` | 测试共享工具(路径常量、monorepo fixture 工厂) |
221
+ | `tests/helpers/mico-subprocess.js` | 子进程运行 `bin/mico.js`、假 `yo` 工具函数 |
220
222
 
221
223
  ### Scripts 说明
222
224
 
@@ -64,7 +64,6 @@ const config: ReturnType<typeof defineConfig> = {
64
64
  define: {
65
65
  'process.env.NODE_ENV': 'development',
66
66
  'process.env.API_BASE_URL': '',
67
- 'process.env.PROXY_SUFFIX': '/proxy/<%= projectName %>_svr',
68
67
  'process.env.LOGIN_ENDPOINT':
69
68
  'https://dashboard-api-test.micoplatform.com/api/yufu_login/',
70
69
  'process.env.REFRESH_ENDPOINT':
@@ -3,6 +3,11 @@
3
3
  import { defineConfig } from '@umijs/max';
4
4
  // 开发环境,不上传 sourcemap。调试时如有需要再解开
5
5
  // import { applySentryPlugin } from "../../../scripts/apply-sentry-plugin";
6
+ const { CDN_PUBLIC_PATH } = process.env;
7
+
8
+ const PUBLIC_PATH: string = CDN_PUBLIC_PATH
9
+ ? `${CDN_PUBLIC_PATH.replace(/\/?$/, '/')}layout/`
10
+ : '/layout/';
6
11
 
7
12
  const config: ReturnType<typeof defineConfig> = {
8
13
  // 开发环境,不上传 sourcemap。调试时如有需要再解开
@@ -19,10 +24,11 @@ const config: ReturnType<typeof defineConfig> = {
19
24
  'react-dom': 'ReactDOM',
20
25
  '@common-web/sentry': 'CommonWebSentry',
21
26
  },
27
+ publicPath: PUBLIC_PATH,
28
+ cssPublicPath: PUBLIC_PATH,
22
29
  define: {
23
30
  'process.env.NODE_ENV': 'development',
24
31
  'process.env.API_BASE_URL': 'https://dashboard-api-test.micoplatform.com',
25
- 'process.env.PROXY_SUFFIX': '/proxy/<%= projectName %>_svr',
26
32
  'process.env.LOGIN_ENDPOINT':
27
33
  'https://dashboard-api-test.micoplatform.com/api/yufu_login/',
28
34
  'process.env.REFRESH_ENDPOINT':
@@ -3,6 +3,11 @@
3
3
  import { defineConfig } from '@umijs/max';
4
4
  // 测试环境,不上传 sourcemap。调试时如有需要再解开
5
5
  // import { applySentryPlugin } from "../../../scripts/apply-sentry-plugin";
6
+ const { CDN_PUBLIC_PATH } = process.env;
7
+
8
+ const PUBLIC_PATH: string = CDN_PUBLIC_PATH
9
+ ? `${CDN_PUBLIC_PATH.replace(/\/?$/, '/')}layout/`
10
+ : '/layout/';
6
11
 
7
12
  const config: ReturnType<typeof defineConfig> = {
8
13
  // 测试环境,不上传 sourcemap。调试时如有需要再解开
@@ -19,10 +24,11 @@ const config: ReturnType<typeof defineConfig> = {
19
24
  'react-dom': 'ReactDOM',
20
25
  '@common-web/sentry': 'CommonWebSentry',
21
26
  },
27
+ publicPath: PUBLIC_PATH,
28
+ cssPublicPath: PUBLIC_PATH,
22
29
  define: {
23
30
  'process.env.NODE_ENV': 'testing',
24
31
  'process.env.API_BASE_URL': 'https://dashboard-api-test.micoplatform.com',
25
- 'process.env.PROXY_SUFFIX': '/proxy/<%= projectName %>_svr',
26
32
  'process.env.LOGIN_ENDPOINT':
27
33
  'https://dashboard-api-test.micoplatform.com/api/yufu_login/',
28
34
  'process.env.REFRESH_ENDPOINT':
@@ -17,7 +17,6 @@ const config: ReturnType<typeof defineConfig> = {
17
17
  define: {
18
18
  'process.env.NODE_ENV': 'production',
19
19
  'process.env.API_BASE_URL': 'https://dashboard-api.micoplatform.com',
20
- 'process.env.PROXY_SUFFIX': '/proxy/<%= projectName %>_svr',
21
20
  'process.env.LOGIN_ENDPOINT':
22
21
  'https://dashboard-api.micoplatform.com/api/yufu_login/',
23
22
  'process.env.REFRESH_ENDPOINT':
@@ -43,6 +42,7 @@ const config: ReturnType<typeof defineConfig> = {
43
42
  return memo;
44
43
  },
45
44
  publicPath: PUBLIC_PATH,
45
+ cssPublicPath: PUBLIC_PATH,
46
46
  };
47
47
 
48
48
  export default defineConfig(config);
@@ -1,7 +1,7 @@
1
1
  # 微前端模式
2
2
 
3
3
  > 创建时间:2025-12-26
4
- > 更新时间:2025-01-25(加载健壮性增强)
4
+ > 更新时间:2025-03-20(MicroAppLoader 增加 base 与 normalizeMicroAppBase)
5
5
 
6
6
  ## 功能概述
7
7
 
@@ -45,6 +45,8 @@
45
45
 
46
46
  ```typescript
47
47
  interface MicroAppLoaderProps {
48
+ /** 微应用在主应用中的挂载路径前缀(通常与当前页面 route.path 一致),经 normalizeMicroAppBase 后传给子应用 */
49
+ base: string;
48
50
  /** 微应用入口 URL */
49
51
  entry: string;
50
52
  /** 微应用名称 */
@@ -60,11 +62,47 @@ interface MicroAppLoaderProps {
60
62
 
61
63
  | 参数 | 类型 | 必填 | 说明 |
62
64
  |------|------|------|------|
65
+ | base | string | 是 | 挂载路径前缀;经 `normalizeMicroAppBase` 后作为子应用 `props.base` |
63
66
  | entry | string | 是 | qiankun 入口 URL (htmlUrl 或 jsUrls[0]) |
64
67
  | name | string | 是 | 微应用唯一标识 |
65
68
  | displayName | string | 否 | 显示名称,用于加载提示 |
66
69
  | routePath | string | 否 | 当前路由路径,用于子应用内部路由切换 |
67
70
 
71
+ ### MicroAppLoader 路由与 base
72
+
73
+ 主应用为微应用补充 **挂载前缀 `base`**,便于子应用对齐 basename、publicPath、内部路由等与路径相关的逻辑;**是否与免鉴权配置做前缀匹配**以当前实现为准(见下「加载门控」)。
74
+
75
+ #### `base` 与 `routePath` 的区别
76
+
77
+ | 字段 | 含义 |
78
+ | --- | --- |
79
+ | **base** | 该微应用在主应用中的**根路径前缀**,一般等于菜单/动态路由里的 `path`。 |
80
+ | **routePath** | 当前激活的**完整路由路径**,随浏览器 URL 变化,用于通知子应用做**内部路由同步**。 |
81
+
82
+ 二者在 `layouts/index.tsx` 中常同为 `currentRoute.path`;在 `app.tsx` 的 `patchClientRoutes` 里与动态路由项一致,传入 **`base: route.path`**、**`name: route.path`**(`name` 另在布局侧可能改为基于 `entry` 的 `getAppNameFromEntry`,与 `base` 来源不同,需注意)。
83
+
84
+ #### `normalizeMicroAppBase`
85
+
86
+ 组件内导出工具函数(见 `MicroAppLoader/index.tsx`):
87
+
88
+ ```typescript
89
+ export const normalizeMicroAppBase = (path: string): string =>
90
+ path.replace(/\/+$/, '').replace(/\*+$/, '');
91
+ ```
92
+
93
+ 含义:先去掉末尾 `/`,再去掉末尾 `*`(兼容路由表中的 `/foo/*` 等写法)。**传给子应用的 `props.base` 为规范化后的字符串**。
94
+
95
+ #### 调用处传参
96
+
97
+ | 位置 | 说明 |
98
+ | --- | --- |
99
+ | `src/app.tsx` `patchClientRoutes` | 动态注册微应用路由时:`React.createElement(MicroAppLoader, { entry, name: route.path, base: route.path, ... })` |
100
+ | `src/layouts/index.tsx` | 布局内渲染:`<MicroAppLoader base={currentRoute.path} routePath={currentRoute.path} ... />` |
101
+
102
+ #### 加载门控(认证)
103
+
104
+ 微应用开始执行 `switchTo` / 挂载前,需 **`isAuthReady`**(`isAuthDisabled`、`isPageAuthFree`、`isNoAuthRoute(location.pathname)`、`isNoPermissionRoute(location.pathname)` 或已存在 `currentUser` 等,见组件内实现)。**当前以浏览器 `location.pathname` 与全局常量为准**,未单独使用 `base` 与 `window.__MICO_CONFIG__.noAuthRouteList` 做前缀匹配。
105
+
68
106
  ### MicroAppProps (传递给子应用)
69
107
 
70
108
  ```typescript
@@ -87,6 +125,8 @@ interface MicroAppProps {
87
125
  timezone: string;
88
126
  /** 在线状态 */
89
127
  presenceStatus: string;
128
+ /** 挂载路径前缀(为 `normalizeMicroAppBase(MicroAppLoader.base)`) */
129
+ base: string;
90
130
  /** 当前路由路径 */
91
131
  routePath?: string;
92
132
  /** 共享的 request 实例,子应用可直接使用 */
@@ -211,7 +251,11 @@ const menus: MenuItem[] = [
211
251
  const renderContent = () => {
212
252
  if (currentRoute?.loadType === 'microapp' && currentRoute.entry) {
213
253
  return (
214
- <MicroAppLoader entry={currentRoute.entry} name={currentRoute.name} />
254
+ <MicroAppLoader
255
+ entry={currentRoute.entry}
256
+ name={currentRoute.name}
257
+ base={currentRoute.path}
258
+ />
215
259
  );
216
260
  }
217
261
  return <Outlet />;
@@ -80,7 +80,7 @@ const mockMenus: MockMenuItem[] = [
80
80
  nameEn: 'Subapp Page',
81
81
  nameKey: 'cs_web_menu_subapp_page',
82
82
  type: 'page',
83
- path: '/subapp/*',
83
+ path: '/subapp',
84
84
  icon: 'Desktop',
85
85
  enabled: true,
86
86
  sortOrder: 1,
@@ -28,6 +28,7 @@ const mockPages: PublicPageItem[] = [
28
28
  nameEn: 'Login',
29
29
  nameKey: 'page.user.login',
30
30
  route: '/user/login',
31
+ base: '/user/login',
31
32
  htmlUrl:
32
33
  'https://cdn-portal.micoplatform.com/portal-center/common-web/0.0.4/login/index.html',
33
34
  jsUrls: [],
@@ -47,6 +48,7 @@ const mockPages: PublicPageItem[] = [
47
48
  nameEn: 'Permission Management',
48
49
  nameKey: 'page.permission',
49
50
  route: '/permission',
51
+ base: '/',
50
52
  htmlUrl:
51
53
  'https://cdn-portal.micoplatform.com/portal-center/common-web/0.0.4/permission/index.html',
52
54
  jsUrls: [],
@@ -66,6 +68,7 @@ const mockPages: PublicPageItem[] = [
66
68
  nameEn: 'Fallback',
67
69
  nameKey: 'page.fallback',
68
70
  route: '/*',
71
+ base: '/',
69
72
  htmlUrl: '',
70
73
  jsUrls: [],
71
74
  cssUrls: [],
@@ -226,6 +226,7 @@ export function patchClientRoutes({ routes }: { routes: UmiRoute[] }) {
226
226
  ? React.createElement(MicroAppLoader, {
227
227
  entry: route.entry,
228
228
  name: route.path,
229
+ base: route.base,
229
230
  displayName: route.name,
230
231
  })
231
232
  : undefined;
@@ -22,6 +22,7 @@ export const extractRoutesFromPages = (
22
22
  return {
23
23
  path: page.route,
24
24
  name: page.name,
25
+ base: page.base || '/',
25
26
  icon: '',
26
27
  loadType: (hasEntry ? 'microapp' : 'internal') as
27
28
  | 'internal'
@@ -269,6 +270,7 @@ export const extractRoutes = (
269
270
  if (page && page.enabled) {
270
271
  routes.push({
271
272
  path: page.route,
273
+ base: page.base || '/',
272
274
  name: item.name,
273
275
  nameEn: item.nameEn,
274
276
  icon: item.icon,
@@ -16,6 +16,8 @@ export interface PageConfig {
16
16
  nameKey?: string;
17
17
  /** 路由路径 */
18
18
  route: string;
19
+ /** 路由前缀路径 */
20
+ base: string;
19
21
  /** 是否启用 */
20
22
  enabled: boolean;
21
23
  /** 微应用 HTML 入口 URL (用于 qiankun 加载) */
@@ -90,6 +92,8 @@ export interface MenuItem {
90
92
  */
91
93
  export interface ParsedRoute {
92
94
  path: string;
95
+ /** 微应用在主应用中的挂载前缀(来自页面配置 base,可与 path 不同) */
96
+ base: string;
93
97
  /** 菜单显示名称(中文) */
94
98
  name: string;
95
99
  /** 菜单显示名称(英文) */
@@ -3,6 +3,7 @@
3
3
  * appId 优先从 window.__MICO_WORKSPACE__ 获取,其次从 window.__MICO_CONFIG__ 获取;
4
4
  * externalLoginPath 优先从 window.__MICO_WORKSPACE__.casServerLoginUrl 获取;
5
5
  * 其余 apiBaseUrl / proxySuffix / loginEndpoint / refreshEndpoint 优先从 window.__MICO_CONFIG__ 同名字段获取,兜底取 process.env 环境变量。
6
+ * proxySuffix:若 __MICO_CONFIG__.proxySuffix 与 process.env.PROXY_SUFFIX 均未配置,则按已解析的 appId 推导为 /proxy/${appId}_svr。
6
7
  */
7
8
 
8
9
  import { getStoredAuthToken } from '../auth/auth-manager';
@@ -18,6 +19,15 @@ function getMicoWorkspace(): Window['__MICO_WORKSPACE__'] {
18
19
  return window.__MICO_WORKSPACE__ ?? undefined;
19
20
  }
20
21
 
22
+ /** 显式 proxySuffix 优先;否则在有 appId 时使用 /proxy/${appId}_svr */
23
+ function resolveProxySuffixWithFallback(
24
+ explicit: string,
25
+ appId: string,
26
+ ): string {
27
+ if (explicit) return explicit;
28
+ return appId ? `/proxy/${appId}_svr` : '';
29
+ }
30
+
21
31
  function buildDefaultClientOptions(): RequestClientOptions {
22
32
  const envDefaults: RequestClientOptions = {
23
33
  appId: '',
@@ -36,14 +46,27 @@ function buildDefaultClientOptions(): RequestClientOptions {
36
46
  ? String(workspace.appId)
37
47
  : undefined;
38
48
  const externalLoginFromWorkspace = workspace?.casServerLoginUrl;
39
- if (!mico && !appIdFromWorkspace && !externalLoginFromWorkspace)
40
- return envDefaults;
49
+ if (!mico && !appIdFromWorkspace && !externalLoginFromWorkspace) {
50
+ return {
51
+ ...envDefaults,
52
+ proxySuffix: resolveProxySuffixWithFallback(
53
+ envDefaults.proxySuffix,
54
+ envDefaults.appId,
55
+ ),
56
+ };
57
+ }
58
+ const resolvedAppId =
59
+ appIdFromWorkspace ?? mico?.appId ?? envDefaults.appId;
60
+ const explicitProxy = mico?.proxySuffix ?? envDefaults.proxySuffix;
41
61
  return {
42
62
  ...envDefaults,
43
63
  ...(mico?.defaultClientOptions ?? {}),
44
- appId: appIdFromWorkspace ?? mico?.appId ?? envDefaults.appId,
64
+ appId: resolvedAppId,
45
65
  apiBaseUrl: mico?.apiBaseUrl ?? envDefaults.apiBaseUrl,
46
- proxySuffix: mico?.proxySuffix ?? envDefaults.proxySuffix,
66
+ proxySuffix: resolveProxySuffixWithFallback(
67
+ explicitProxy,
68
+ resolvedAppId,
69
+ ),
47
70
  loginEndpoint: mico?.loginEndpoint ?? envDefaults.loginEndpoint,
48
71
  refreshEndpoint: mico?.refreshEndpoint ?? envDefaults.refreshEndpoint,
49
72
  externalLoginPath:
@@ -11,6 +11,8 @@ import './index.less';
11
11
  import { microAppManager, type MicroAppState } from './micro-app-manager';
12
12
 
13
13
  interface MicroAppLoaderProps {
14
+ /** 微应用在主应用中的挂载路径前缀(来自 ParsedRoute.base / 页面配置 base),经 normalizeMicroAppBase 后传给子应用 */
15
+ base: string;
14
16
  entry: string;
15
17
  name: string;
16
18
  displayName?: string;
@@ -18,6 +20,10 @@ interface MicroAppLoaderProps {
18
20
  routePath?: string;
19
21
  }
20
22
 
23
+ /** 微应用 base:先去掉末尾 /,再去掉末尾 *(与路由 path 约定一致) */
24
+ export const normalizeMicroAppBase = (path: string): string =>
25
+ path.replace(/\/+$/, '').replace(/\*+$/, '');
26
+
21
27
  const sanitizeId = (path: string): string => {
22
28
  return (
23
29
  path
@@ -35,6 +41,7 @@ const sanitizeId = (path: string): string => {
35
41
  * 用户意图追踪在 MicroAppManager 内部处理。
36
42
  */
37
43
  const MicroAppLoader: React.FC<MicroAppLoaderProps> = ({
44
+ base,
38
45
  entry,
39
46
  name,
40
47
  displayName,
@@ -77,6 +84,7 @@ const MicroAppLoader: React.FC<MicroAppLoaderProps> = ({
77
84
  uid: authInfo.uid,
78
85
  avatar: authInfo.avatar,
79
86
  nickname: authInfo.nickname,
87
+ base: normalizeMicroAppBase(base || '/'),
80
88
  // 传递当前路由路径,让子应用进行内部路由切换
81
89
  routePath,
82
90
  // 传递主应用的 request 实例,子应用可直接使用
@@ -84,7 +92,7 @@ const MicroAppLoader: React.FC<MicroAppLoaderProps> = ({
84
92
  // 传递当前多语言类型给子应用(zh-CN, en-US)
85
93
  locale: getCurrentLocale(),
86
94
  };
87
- }, [env, routePath]);
95
+ }, [base, env, routePath]);
88
96
 
89
97
  // ref 持有最新的 buildProps,避免加载 effect 依赖它导致子应用被重载
90
98
  const buildPropsRef = useRef(buildProps);
@@ -129,7 +137,7 @@ const MicroAppLoader: React.FC<MicroAppLoaderProps> = ({
129
137
  microAppManager.cancel();
130
138
  microAppManager.setStateCallback(null);
131
139
  };
132
- }, [entry, appName, isAuthReady]);
140
+ }, [entry, appName, isAuthReady, buildProps]);
133
141
 
134
142
  return (
135
143
  <div className="micro-app-container">
@@ -184,6 +184,7 @@ const BasicLayout: React.FC = () => {
184
184
  // 同一个微应用的不同路由共用同一个实例
185
185
  key={appName}
186
186
  entry={currentRoute.entry}
187
+ base={currentRoute.base}
187
188
  // 使用 entry 生成的标识,而不是 path
188
189
  name={appName}
189
190
  // 显示名称用于 loading 提示
@@ -323,6 +323,7 @@ module.exports = class extends Generator {
323
323
  ` nameEn: '${this.appNamePascal}',`,
324
324
  ` nameKey: 'page.${this.appName}',`,
325
325
  ` route: '/${this.appName}',`,
326
+ ` base: '/${this.appName}',`,
326
327
  ` htmlUrl: '//localhost:${this.devPort}',`,
327
328
  ' jsUrls: [],',
328
329
  ' cssUrls: [],',
@@ -3,6 +3,11 @@
3
3
  import { defineConfig } from '@umijs/max';
4
4
  // 开发环境,不上传 sourcemap。调试时如有需要再解开
5
5
  // import { applySentryPlugin } from "../../../scripts/apply-sentry-plugin";
6
+ const { CDN_PUBLIC_PATH } = process.env;
7
+
8
+ const PUBLIC_PATH: string = CDN_PUBLIC_PATH
9
+ ? `${CDN_PUBLIC_PATH.replace(/\/?$/, '/')}<%= appName %>/`
10
+ : '/<%= appName %>/';
6
11
 
7
12
  const config: ReturnType<typeof defineConfig> = {
8
13
  // 开发环境,不上传 sourcemap。调试时如有需要再解开
@@ -11,12 +16,16 @@ const config: ReturnType<typeof defineConfig> = {
11
16
  // 测试环境:将所有代码打包到一个文件
12
17
  extraBabelPlugins: ['babel-plugin-dynamic-import-node'],
13
18
 
19
+ publicPath: PUBLIC_PATH,
20
+ cssPublicPath: PUBLIC_PATH,
21
+
14
22
  // 禁用代码分割,只输出一个 JS 和一个 CSS
15
23
  chainWebpack(memo) {
16
24
  // 禁用 splitChunks
17
25
  memo.optimization.splitChunks(false);
18
26
  // 禁用 runtimeChunk
19
27
  memo.optimization.runtimeChunk(false);
28
+ memo.plugins.delete('runtimePublicPath');
20
29
  // 开发环境,不上传 sourcemap。调试时如有需要再解开
21
30
  // applySentryPlugin({ memo, appName: '<%= appName %>' });
22
31
  return memo;
@@ -3,6 +3,11 @@
3
3
  import { defineConfig } from '@umijs/max';
4
4
  // 测试环境,不上传 sourcemap。调试时如有需要再解开
5
5
  // import { applySentryPlugin } from "../../../scripts/apply-sentry-plugin";
6
+ const { CDN_PUBLIC_PATH } = process.env;
7
+
8
+ const PUBLIC_PATH: string = CDN_PUBLIC_PATH
9
+ ? `${CDN_PUBLIC_PATH.replace(/\/?$/, '/')}<%= appName %>/`
10
+ : '/<%= appName %>/';
6
11
 
7
12
  const config: ReturnType<typeof defineConfig> = {
8
13
  // 测试环境,不上传 sourcemap。调试时如有需要再解开
@@ -11,12 +16,16 @@ const config: ReturnType<typeof defineConfig> = {
11
16
  // 测试环境:将所有代码打包到一个文件
12
17
  extraBabelPlugins: ['babel-plugin-dynamic-import-node'],
13
18
 
19
+ publicPath: PUBLIC_PATH,
20
+ cssPublicPath: PUBLIC_PATH,
21
+
14
22
  // 禁用代码分割,只输出一个 JS 和一个 CSS
15
23
  chainWebpack(memo) {
16
24
  // 禁用 splitChunks
17
25
  memo.optimization.splitChunks(false);
18
26
  // 禁用 runtimeChunk
19
27
  memo.optimization.runtimeChunk(false);
28
+ memo.plugins.delete('runtimePublicPath');
20
29
  // 测试环境,不上传 sourcemap。调试时如有需要再解开
21
30
  // applySentryPlugin({ memo, appName: '<%= appName %>' });
22
31
  return memo;
@@ -24,9 +24,11 @@ const config: ReturnType<typeof defineConfig> = {
24
24
  // 禁用 runtimeChunk
25
25
  memo.optimization.runtimeChunk(false);
26
26
  applySentryPlugin({ memo, appName: '<%= appName %>' });
27
+ memo.plugins.delete('runtimePublicPath');
27
28
  return memo;
28
29
  },
29
30
  publicPath: PUBLIC_PATH,
31
+ cssPublicPath: PUBLIC_PATH,
30
32
 
31
33
  /**
32
34
  * @name 外部依赖配置
@@ -3,5 +3,5 @@
3
3
  * @description <%= appName %> 子应用的路由配置
4
4
  */
5
5
  export default [
6
- { path: '/<%= appName %>', component: '@/pages/index' },
6
+ { path: '/', component: '@/pages/index' },
7
7
  ];
@@ -13,6 +13,11 @@ import { SentryErrorBoundary } from '@common-web/sentry';
13
13
  import { appLogger } from './common/logger';
14
14
  import { type IMicroAppProps, setMainAppProps } from './common/mainApp';
15
15
 
16
+ /** 与 layout parser.findRouteByPath 一致:根路径 "/" 保持不变 */
17
+ function stripTrailingSlash(path: string): string {
18
+ return path.length > 1 && path.endsWith('/') ? path.slice(0, -1) : path;
19
+ }
20
+
16
21
  /**
17
22
  * @name 路由同步工具
18
23
  * @description 处理主应用与子应用之间的路由同步,支持通配符路由模式
@@ -20,12 +25,12 @@ import { type IMicroAppProps, setMainAppProps } from './common/mainApp';
20
25
  function syncRoute(routePath: string, logPrefix = 'route sync') {
21
26
  if (!history) return;
22
27
 
23
- const currentPath = history.location.pathname;
28
+ const currentPath = stripTrailingSlash(history.location.pathname);
24
29
  const isWildcard = routePath.endsWith('/*');
25
- // 通配符模式取基础路径,否则使用原路径
26
- const targetPath = isWildcard ? routePath.slice(0, -2) : routePath;
30
+ const targetPath = stripTrailingSlash(
31
+ isWildcard ? routePath.slice(0, -2) : routePath,
32
+ );
27
33
 
28
- // 判断当前路径是否已匹配目标
29
34
  const isMatched = isWildcard
30
35
  ? currentPath === targetPath || currentPath.startsWith(`${targetPath}/`)
31
36
  : currentPath === targetPath;
package/lib/utils.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  const fs = require('node:fs');
4
4
  const path = require('node:path');
5
- const { execSync, execFileSync, execFile } = require('node:child_process');
5
+ const { exec, execSync, execFileSync, execFile } = require('node:child_process');
6
6
  const os = require('node:os');
7
7
 
8
8
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "generator-mico-cli",
3
- "version": "0.2.25",
3
+ "version": "0.2.27",
4
4
  "description": "Yeoman generator for Mico CLI projects",
5
5
  "keywords": [
6
6
  "yeoman-generator",
@@ -18,6 +18,7 @@
18
18
  "generators"
19
19
  ],
20
20
  "scripts": {
21
+ "prepare": "husky",
21
22
  "format": "biome format --write .",
22
23
  "format:check": "biome format .",
23
24
  "test": "vitest run",
@@ -27,6 +28,7 @@
27
28
  },
28
29
  "devDependencies": {
29
30
  "@biomejs/biome": "^1.9.4",
31
+ "husky": "^9.1.7",
30
32
  "vitest": "^4.0.18",
31
33
  "yeoman-environment": "^3.19.3",
32
34
  "yeoman-test": "^6.3.0"