generator-mico-cli 0.2.26 → 0.2.28
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 +2 -0
- package/generators/micro-react/templates/apps/layout/config/config.dev.ts +3 -3
- package/generators/micro-react/templates/apps/layout/config/config.prod.development.ts +0 -3
- package/generators/micro-react/templates/apps/layout/config/config.prod.testing.ts +0 -3
- package/generators/micro-react/templates/apps/layout/config/config.prod.ts +1 -4
- package/generators/micro-react/templates/apps/layout/docs/feat-/346/236/204/345/273/272define/344/270/216/345/205/215/350/256/244/350/257/201/345/210/235/345/247/213/346/200/201.md +44 -0
- 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 +51 -2
- package/generators/micro-react/templates/apps/layout/mock/menus.ts +1 -1
- package/generators/micro-react/templates/apps/layout/mock/pages.ts +3 -0
- package/generators/micro-react/templates/apps/layout/src/app.tsx +3 -1
- package/generators/micro-react/templates/apps/layout/src/common/menu/parser.ts +2 -0
- package/generators/micro-react/templates/apps/layout/src/common/menu/types.ts +4 -0
- package/generators/micro-react/templates/apps/layout/src/common/request/config.ts +27 -4
- package/generators/micro-react/templates/apps/layout/src/components/MicroAppLoader/index.tsx +10 -2
- package/generators/micro-react/templates/apps/layout/src/layouts/index.tsx +1 -0
- package/generators/micro-react/templates/packages/common-intl/src/intl.ts +7 -1
- package/generators/subapp-react/index.js +1 -0
- package/generators/subapp-react/templates/homepage/config/config.prod.development.ts +1 -0
- package/generators/subapp-react/templates/homepage/config/config.prod.testing.ts +1 -0
- package/generators/subapp-react/templates/homepage/config/config.prod.ts +1 -0
- package/generators/subapp-react/templates/homepage/config/config.ts +0 -7
- package/generators/subapp-react/templates/homepage/config/routes.ts +1 -1
- package/generators/subapp-react/templates/homepage/src/app.tsx +9 -4
- package/lib/utils.js +1 -1
- 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
|
|
|
@@ -33,6 +33,9 @@ const config: ReturnType<typeof defineConfig> = {
|
|
|
33
33
|
// noLayoutRouteList: [],
|
|
34
34
|
// 关闭权限控制(调试用)
|
|
35
35
|
disableAuth: false,
|
|
36
|
+
// SSO 外跳地址(与 resolveExternalLoginPath 读取的 externalLoginPath 一致;生产由注入的 __MICO_CONFIG__ 提供)
|
|
37
|
+
externalLoginPath:
|
|
38
|
+
'https://micous-idp.cig.tencentcs.com/sso/xxxxxxx/cas',
|
|
36
39
|
};
|
|
37
40
|
`,
|
|
38
41
|
},
|
|
@@ -64,13 +67,10 @@ const config: ReturnType<typeof defineConfig> = {
|
|
|
64
67
|
define: {
|
|
65
68
|
'process.env.NODE_ENV': 'development',
|
|
66
69
|
'process.env.API_BASE_URL': '',
|
|
67
|
-
'process.env.PROXY_SUFFIX': '/proxy/<%= projectName %>_svr',
|
|
68
70
|
'process.env.LOGIN_ENDPOINT':
|
|
69
71
|
'https://dashboard-api-test.micoplatform.com/api/yufu_login/',
|
|
70
72
|
'process.env.REFRESH_ENDPOINT':
|
|
71
73
|
'https://dashboard-api-test.micoplatform.com/api/yufu_login/refresh/',
|
|
72
|
-
'process.env.EXTERNAL_LOGIN_PATH':
|
|
73
|
-
'https://micous-idp.cig.tencentcs.com/sso/xxxxxxx/cas',
|
|
74
74
|
},
|
|
75
75
|
};
|
|
76
76
|
|
|
@@ -29,13 +29,10 @@ const config: ReturnType<typeof defineConfig> = {
|
|
|
29
29
|
define: {
|
|
30
30
|
'process.env.NODE_ENV': 'development',
|
|
31
31
|
'process.env.API_BASE_URL': 'https://dashboard-api-test.micoplatform.com',
|
|
32
|
-
'process.env.PROXY_SUFFIX': '/proxy/<%= projectName %>_svr',
|
|
33
32
|
'process.env.LOGIN_ENDPOINT':
|
|
34
33
|
'https://dashboard-api-test.micoplatform.com/api/yufu_login/',
|
|
35
34
|
'process.env.REFRESH_ENDPOINT':
|
|
36
35
|
'https://dashboard-api-test.micoplatform.com/api/yufu_login/refresh/',
|
|
37
|
-
'process.env.EXTERNAL_LOGIN_PATH':
|
|
38
|
-
'https://micous-idp.cig.tencentcs.com/sso/xxxxxxx/cas',
|
|
39
36
|
},
|
|
40
37
|
};
|
|
41
38
|
|
|
@@ -29,13 +29,10 @@ const config: ReturnType<typeof defineConfig> = {
|
|
|
29
29
|
define: {
|
|
30
30
|
'process.env.NODE_ENV': 'testing',
|
|
31
31
|
'process.env.API_BASE_URL': 'https://dashboard-api-test.micoplatform.com',
|
|
32
|
-
'process.env.PROXY_SUFFIX': '/proxy/<%= projectName %>_svr',
|
|
33
32
|
'process.env.LOGIN_ENDPOINT':
|
|
34
33
|
'https://dashboard-api-test.micoplatform.com/api/yufu_login/',
|
|
35
34
|
'process.env.REFRESH_ENDPOINT':
|
|
36
35
|
'https://dashboard-api-test.micoplatform.com/api/yufu_login/refresh/',
|
|
37
|
-
'process.env.EXTERNAL_LOGIN_PATH':
|
|
38
|
-
'https://micous-idp.cig.tencentcs.com/sso/xxxxxxx/cas',
|
|
39
36
|
},
|
|
40
37
|
};
|
|
41
38
|
|
|
@@ -17,13 +17,10 @@ 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':
|
|
24
23
|
'https://dashboard-api.micoplatform.com/api/yufu_login/refresh/',
|
|
25
|
-
'process.env.EXTERNAL_LOGIN_PATH':
|
|
26
|
-
'https://micous-idp.cig.tencentcs.com/sso/xxxxxxx/cas',
|
|
27
24
|
},
|
|
28
25
|
externals: {
|
|
29
26
|
react: 'React',
|
|
@@ -34,7 +31,7 @@ const config: ReturnType<typeof defineConfig> = {
|
|
|
34
31
|
extraBabelPlugins: ['babel-plugin-dynamic-import-node'],
|
|
35
32
|
|
|
36
33
|
// 禁用代码分割,只输出一个 JS 和一个 CSS
|
|
37
|
-
chainWebpack(memo) {
|
|
34
|
+
chainWebpack(memo: any) {
|
|
38
35
|
// 禁用 splitChunks
|
|
39
36
|
memo.optimization.splitChunks(false);
|
|
40
37
|
// 禁用 runtimeChunk
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# 构建 define 精简与免认证页初始态
|
|
2
|
+
|
|
3
|
+
> 创建时间:2026-03-24
|
|
4
|
+
|
|
5
|
+
## 功能概述
|
|
6
|
+
|
|
7
|
+
1. **Umi `define`**:不再通过构建期注入 `process.env.EXTERNAL_LOGIN_PATH`(本模板从未注入 `APP_ID`);SSO 外跳等依赖运行时 `window.__MICO_CONFIG__`。
|
|
8
|
+
2. **`getInitialState`**:免认证路由下即使本地仍有 token,也不请求用户信息接口,减少无效调用。
|
|
9
|
+
|
|
10
|
+
## 技术方案
|
|
11
|
+
|
|
12
|
+
### 开发环境
|
|
13
|
+
|
|
14
|
+
`config/config.dev.ts` 的 `headScripts` 中 `window.__MICO_CONFIG__` 增加 **`externalLoginPath`**(模板默认为占位 URL,生成项目后请按实际 IdP 修改)。
|
|
15
|
+
|
|
16
|
+
`define` 仅保留 `NODE_ENV`、`API_BASE_URL`、`LOGIN_ENDPOINT`、`REFRESH_ENDPOINT`。
|
|
17
|
+
|
|
18
|
+
### 生产 / 测试构建
|
|
19
|
+
|
|
20
|
+
`config.prod.ts`、`config.prod.development.ts`、`config.prod.testing.ts` 的 `define` 中同样**不包含** `EXTERNAL_LOGIN_PATH`,由部署时注入的 `__MICO_CONFIG__.externalLoginPath`(或 `__MICO_WORKSPACE__.casServerLoginUrl`)提供。
|
|
21
|
+
|
|
22
|
+
### 初始态
|
|
23
|
+
|
|
24
|
+
`src/app.tsx` 中仅在 `getStoredAuthToken() && !skipAuth` 时调用 `fetchUserInfoFn()`;`skipAuth = isNoAuthRoute || isPageAuthFree`。
|
|
25
|
+
|
|
26
|
+
## 文件清单
|
|
27
|
+
|
|
28
|
+
| 文件路径 | 说明 |
|
|
29
|
+
| --- | --- |
|
|
30
|
+
| `config/config.dev.ts` | `__MICO_CONFIG__.externalLoginPath`;移除 `EXTERNAL_LOGIN_PATH` define |
|
|
31
|
+
| `config/config.prod.ts` | 移除 `EXTERNAL_LOGIN_PATH` define |
|
|
32
|
+
| `config/config.prod.development.ts` | 同上 |
|
|
33
|
+
| `config/config.prod.testing.ts` | 同上 |
|
|
34
|
+
| `src/app.tsx` | `fetchUserInfo` 条件增加 `!skipAuth` |
|
|
35
|
+
|
|
36
|
+
## 部署注意
|
|
37
|
+
|
|
38
|
+
- 生产 HTML 或网关需保证 `window.__MICO_CONFIG__` 含 **`appId`**、**`externalLoginPath`**(或与现有网关字段对齐),否则代理前缀、SSO 外跳可能异常。
|
|
39
|
+
- 详见 `src/common/request/config.ts` 中 `buildDefaultClientOptions` 的解析顺序。
|
|
40
|
+
|
|
41
|
+
## 相关文档
|
|
42
|
+
|
|
43
|
+
- [fix-SSO无限重定向](./fix-SSO无限重定向.md)
|
|
44
|
+
- [arch-请求模块](./arch-请求模块.md)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# 微前端模式
|
|
2
2
|
|
|
3
3
|
> 创建时间:2025-12-26
|
|
4
|
-
> 更新时间:2025-
|
|
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
|
+
/** 微应用在主应用中的挂载路径前缀(来自 ParsedRoute.base / 页面配置 base),经 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** | 该微应用在主应用中的**挂载前缀**,来自中台 **`page.base`**,解析为 **`ParsedRoute.base`**(缺省时解析为 **`'/'`**);可与动态路由 **`path`** 不同(如 `path` 为 `/subapp/*` 时 `base` 可为 `/subapp`)。 |
|
|
80
|
+
| **routePath** | 当前激活的**完整路由路径**,随浏览器 URL 变化,用于通知子应用做**内部路由同步**。 |
|
|
81
|
+
|
|
82
|
+
`routePath` 在布局内为 **`currentRoute.path`**。`base` 在 **`patchClientRoutes`** 与布局内均为 **`route.base` / `currentRoute.base`**。**`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.base, ... })` |
|
|
100
|
+
| `src/layouts/index.tsx` | 布局内渲染:`<MicroAppLoader base={currentRoute.base} 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 实例,子应用可直接使用 */
|
|
@@ -125,6 +165,8 @@ export async function update(props: MicroAppProps) {
|
|
|
125
165
|
```typescript
|
|
126
166
|
interface ParsedRoute {
|
|
127
167
|
path: string;
|
|
168
|
+
/** 微应用挂载前缀(来自 page.base,解析缺省为 '/') */
|
|
169
|
+
base: string;
|
|
128
170
|
name: string;
|
|
129
171
|
icon: string;
|
|
130
172
|
/** 加载类型: internal(内部路由) | microapp(qiankun微应用) */
|
|
@@ -142,6 +184,8 @@ interface PageConfig {
|
|
|
142
184
|
id: number;
|
|
143
185
|
name: string;
|
|
144
186
|
route: string;
|
|
187
|
+
/** 微应用挂载前缀(可与 route 不同) */
|
|
188
|
+
base: string;
|
|
145
189
|
enabled: boolean;
|
|
146
190
|
/** 微应用 HTML 入口 URL (优先使用) */
|
|
147
191
|
htmlUrl: string | null;
|
|
@@ -211,7 +255,12 @@ const menus: MenuItem[] = [
|
|
|
211
255
|
const renderContent = () => {
|
|
212
256
|
if (currentRoute?.loadType === 'microapp' && currentRoute.entry) {
|
|
213
257
|
return (
|
|
214
|
-
<MicroAppLoader
|
|
258
|
+
<MicroAppLoader
|
|
259
|
+
entry={currentRoute.entry}
|
|
260
|
+
name={currentRoute.name}
|
|
261
|
+
base={currentRoute.base}
|
|
262
|
+
routePath={currentRoute.path}
|
|
263
|
+
/>
|
|
215
264
|
);
|
|
216
265
|
}
|
|
217
266
|
return <Outlet />;
|
|
@@ -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: [],
|
|
@@ -138,7 +138,8 @@ export async function getInitialState(): Promise<{
|
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
// 有 token 就获取用户信息(无论在哪个页面,支持登录后 refresh 场景)
|
|
141
|
-
|
|
141
|
+
// 当页面免认证时,不获取用户信息
|
|
142
|
+
if (getStoredAuthToken() && !skipAuth) {
|
|
142
143
|
const userInfo = await fetchUserInfoFn();
|
|
143
144
|
if (userInfo) {
|
|
144
145
|
clearRedirectCount();
|
|
@@ -226,6 +227,7 @@ export function patchClientRoutes({ routes }: { routes: UmiRoute[] }) {
|
|
|
226
227
|
? React.createElement(MicroAppLoader, {
|
|
227
228
|
entry: route.entry,
|
|
228
229
|
name: route.path,
|
|
230
|
+
base: route.base,
|
|
229
231
|
displayName: route.name,
|
|
230
232
|
})
|
|
231
233
|
: 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
|
|
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:
|
|
64
|
+
appId: resolvedAppId,
|
|
45
65
|
apiBaseUrl: mico?.apiBaseUrl ?? envDefaults.apiBaseUrl,
|
|
46
|
-
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:
|
package/generators/micro-react/templates/apps/layout/src/components/MicroAppLoader/index.tsx
CHANGED
|
@@ -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">
|
|
@@ -37,7 +37,13 @@ const intlProxy = new Proxy(intl, {
|
|
|
37
37
|
}
|
|
38
38
|
// 不存在的 key,返回一个兜底函数
|
|
39
39
|
console.warn(`[common-intl] Missing intl key: ${prop}`);
|
|
40
|
-
|
|
40
|
+
// 未预定义的 key:仍从缓存(接口拉取的多语言数据)中查找,支持动态 key
|
|
41
|
+
return (...interpolations: Array<string | number | undefined>) =>
|
|
42
|
+
i18n({
|
|
43
|
+
key: prop,
|
|
44
|
+
interpolations,
|
|
45
|
+
defaultMessage: prop, // 缓存无值时用 key 兜底
|
|
46
|
+
});
|
|
41
47
|
},
|
|
42
48
|
});
|
|
43
49
|
|
|
@@ -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: [],',
|
|
@@ -25,6 +25,7 @@ const config: ReturnType<typeof defineConfig> = {
|
|
|
25
25
|
memo.optimization.splitChunks(false);
|
|
26
26
|
// 禁用 runtimeChunk
|
|
27
27
|
memo.optimization.runtimeChunk(false);
|
|
28
|
+
memo.plugins.delete('runtimePublicPath');
|
|
28
29
|
// 开发环境,不上传 sourcemap。调试时如有需要再解开
|
|
29
30
|
// applySentryPlugin({ memo, appName: '<%= appName %>' });
|
|
30
31
|
return memo;
|
|
@@ -25,6 +25,7 @@ const config: ReturnType<typeof defineConfig> = {
|
|
|
25
25
|
memo.optimization.splitChunks(false);
|
|
26
26
|
// 禁用 runtimeChunk
|
|
27
27
|
memo.optimization.runtimeChunk(false);
|
|
28
|
+
memo.plugins.delete('runtimePublicPath');
|
|
28
29
|
// 测试环境,不上传 sourcemap。调试时如有需要再解开
|
|
29
30
|
// applySentryPlugin({ memo, appName: '<%= appName %>' });
|
|
30
31
|
return memo;
|
|
@@ -24,6 +24,7 @@ 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,
|
|
@@ -30,13 +30,6 @@ const config: ReturnType<typeof defineConfig> = {
|
|
|
30
30
|
|
|
31
31
|
publicPath: PUBLIC_PATH,
|
|
32
32
|
|
|
33
|
-
// qiankun slave 插件默认开启 runtimePublicPath,会在构建时将 CSS url() 的 publicPath 覆盖为 '/'
|
|
34
|
-
// 生产环境 publicPath 已确定,删除该插件让 cssPublicPath 生效
|
|
35
|
-
chainWebpack(memo) {
|
|
36
|
-
memo.plugins.delete('runtimePublicPath');
|
|
37
|
-
return memo;
|
|
38
|
-
},
|
|
39
|
-
|
|
40
33
|
/**
|
|
41
34
|
* @name 运行时公共路径
|
|
42
35
|
* @description qiankun 微应用运行时动态设置 publicPath
|
|
@@ -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
|
-
|
|
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.
|
|
3
|
+
"version": "0.2.28",
|
|
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"
|