@umijs/plugins 4.0.0-rc.8 → 4.0.1
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/dist/access.js +79 -17
- package/dist/analytics.js +1 -1
- package/dist/antd.js +25 -4
- package/dist/dva.js +27 -4
- package/dist/initial-state.js +25 -17
- package/dist/layout.js +126 -27
- package/dist/locale.js +40 -43
- package/dist/model.js +16 -35
- package/dist/qiankun/master.js +31 -11
- package/dist/qiankun/slave.js +66 -44
- package/dist/qiankun.js +1 -0
- package/dist/request.js +142 -127
- package/dist/utils/localeUtils.js +5 -14
- package/dist/utils/modelUtils.d.ts +6 -1
- package/dist/utils/modelUtils.js +110 -8
- package/libs/model.tsx +43 -5
- package/libs/qiankun/master/MicroApp.tsx +15 -6
- package/libs/qiankun/master/common.ts +15 -10
- package/libs/qiankun/master/getMicroAppRouteComponent.tsx.tpl +5 -16
- package/libs/qiankun/master/masterRuntimePlugin.tsx +6 -5
- package/libs/qiankun/master/types.ts +2 -0
- package/libs/qiankun/slave/connectMaster.tsx +0 -1
- package/libs/qiankun/slave/lifecycles.ts +14 -8
- package/libs/qiankun/slave/qiankunModel.ts +0 -1
- package/libs/qiankun/slave/slaveRuntimePlugin.ts +9 -15
- package/package.json +13 -13
package/dist/access.js
CHANGED
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
3
|
-
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10
4
|
};
|
|
11
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const fs_1 = __importDefault(require("fs"));
|
|
12
7
|
const path_1 = require("path");
|
|
13
8
|
const withTmpPath_1 = require("./utils/withTmpPath");
|
|
14
9
|
exports.default = (api) => {
|
|
15
|
-
// TODO: route access
|
|
16
10
|
api.describe({
|
|
17
11
|
config: {
|
|
18
12
|
schema(joi) {
|
|
@@ -21,19 +15,29 @@ exports.default = (api) => {
|
|
|
21
15
|
},
|
|
22
16
|
enableBy: api.EnableBy.config,
|
|
23
17
|
});
|
|
24
|
-
api.onGenerateFiles(() =>
|
|
18
|
+
api.onGenerateFiles(async () => {
|
|
19
|
+
// allow enable access without access file
|
|
20
|
+
const hasAccessFile = ['js', 'jsx', 'ts', 'tsx'].some((ext) => fs_1.default.existsSync((0, path_1.join)(api.paths.absSrcPath, `access.${ext}`)));
|
|
25
21
|
// runtime.tsx
|
|
26
22
|
api.writeTmpFile({
|
|
27
23
|
path: 'runtime.tsx',
|
|
28
24
|
content: `
|
|
29
|
-
import React from 'react'
|
|
30
|
-
|
|
25
|
+
import React from 'react';${hasAccessFile
|
|
26
|
+
? `
|
|
27
|
+
import accessFactory from '@/access'
|
|
31
28
|
import { useModel } from '@@/plugin-model';
|
|
29
|
+
`
|
|
30
|
+
: ''}
|
|
32
31
|
import { AccessContext } from './context';
|
|
33
32
|
|
|
34
|
-
function Provider(props) {
|
|
33
|
+
function Provider(props) {${hasAccessFile
|
|
34
|
+
? `
|
|
35
35
|
const { initialState } = useModel('@@initialState');
|
|
36
36
|
const access = React.useMemo(() => accessFactory(initialState), [initialState]);
|
|
37
|
+
`
|
|
38
|
+
: `
|
|
39
|
+
const access = {};
|
|
40
|
+
`}
|
|
37
41
|
return (
|
|
38
42
|
<AccessContext.Provider value={access}>
|
|
39
43
|
{ props.children }
|
|
@@ -46,16 +50,74 @@ export function accessProvider(container) {
|
|
|
46
50
|
}
|
|
47
51
|
`,
|
|
48
52
|
});
|
|
49
|
-
// index.
|
|
53
|
+
// index.tsx
|
|
50
54
|
api.writeTmpFile({
|
|
51
|
-
path: 'index.
|
|
55
|
+
path: 'index.tsx',
|
|
52
56
|
content: `
|
|
53
|
-
import React from 'react';
|
|
57
|
+
import React, { PropsWithChildren } from 'react';
|
|
54
58
|
import { AccessContext } from './context';
|
|
59
|
+
import type { IRoute } from 'umi';
|
|
55
60
|
|
|
56
61
|
export const useAccess = () => {
|
|
57
62
|
return React.useContext(AccessContext);
|
|
58
63
|
};
|
|
64
|
+
|
|
65
|
+
export interface AccessProps {
|
|
66
|
+
accessible: boolean;
|
|
67
|
+
fallback?: React.ReactNode;
|
|
68
|
+
}
|
|
69
|
+
export const Access: React.FC<PropsWithChildren<AccessProps>> = (props) => {
|
|
70
|
+
if (process.env.NODE_ENV === 'development' && typeof props.accessible !== 'boolean') {
|
|
71
|
+
throw new Error('[access] the \`accessible\` property on <Access /> should be a boolean');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return <>{ props.accessible ? props.children : props.fallback }</>;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const useAccessMarkedRoutes = (routes: IRoute[]) => {
|
|
78
|
+
const access = useAccess();
|
|
79
|
+
const markdedRoutes: IRoute[] = React.useMemo(() => {
|
|
80
|
+
const process = (route, parentAccessCode) => {
|
|
81
|
+
const accessCode = route.access || parentAccessCode;
|
|
82
|
+
|
|
83
|
+
// set default status
|
|
84
|
+
route.unaccessible = ${api.config.access.strictMode ? 'true' : 'false'};
|
|
85
|
+
|
|
86
|
+
// check access code
|
|
87
|
+
if (typeof accessCode === 'string') {
|
|
88
|
+
const detector = access[accessCode];
|
|
89
|
+
|
|
90
|
+
if (typeof detector === 'function') {
|
|
91
|
+
route.unaccessible = !detector(route);
|
|
92
|
+
} else if (typeof detector === 'boolean') {
|
|
93
|
+
route.unaccessible = !detector;
|
|
94
|
+
} else if (typeof detector === 'undefined') {
|
|
95
|
+
route.unaccessible = true;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// check children access code
|
|
100
|
+
if (route.children?.length) {
|
|
101
|
+
const isNoAccessibleChild = !route.children.reduce((hasAccessibleChild, child) => {
|
|
102
|
+
process(child, accessCode);
|
|
103
|
+
|
|
104
|
+
return hasAccessibleChild || !child.unaccessible;
|
|
105
|
+
}, false);
|
|
106
|
+
|
|
107
|
+
// make sure parent route is unaccessible if all children are unaccessible
|
|
108
|
+
if (isNoAccessibleChild) {
|
|
109
|
+
route.unaccessible = true;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return route;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return routes.map(route => process(route));
|
|
117
|
+
}, [routes.length]);
|
|
118
|
+
|
|
119
|
+
return markdedRoutes;
|
|
120
|
+
}
|
|
59
121
|
`,
|
|
60
122
|
});
|
|
61
123
|
// context.ts
|
|
@@ -66,7 +128,7 @@ import React from 'react';
|
|
|
66
128
|
export const AccessContext = React.createContext<any>(null);
|
|
67
129
|
`,
|
|
68
130
|
});
|
|
69
|
-
})
|
|
131
|
+
});
|
|
70
132
|
api.addRuntimePlugin(() => {
|
|
71
133
|
return [(0, withTmpPath_1.withTmpPath)({ api, path: 'runtime.tsx' })];
|
|
72
134
|
});
|
package/dist/analytics.js
CHANGED
|
@@ -20,7 +20,7 @@ exports.default = (api) => {
|
|
|
20
20
|
return `
|
|
21
21
|
(function() {
|
|
22
22
|
var hm = document.createElement('script');
|
|
23
|
-
hm.src = '
|
|
23
|
+
hm.src = '//hm.baidu.com/hm.js?${code}';
|
|
24
24
|
var s = document.getElementsByTagName('script')[0];
|
|
25
25
|
s.parentNode.insertBefore(hm, s);
|
|
26
26
|
})();
|
package/dist/antd.js
CHANGED
|
@@ -30,7 +30,12 @@ exports.default = (api) => {
|
|
|
30
30
|
});
|
|
31
31
|
},
|
|
32
32
|
},
|
|
33
|
-
enableBy
|
|
33
|
+
enableBy({ userConfig }) {
|
|
34
|
+
// 由于本插件有 api.modifyConfig 的调用,以及 Umi 框架的限制
|
|
35
|
+
// 在其他插件中通过 api.modifyDefaultConfig 设置 antd 并不能让 api.modifyConfig 生效
|
|
36
|
+
// 所以这里通过环境变量来判断是否启用
|
|
37
|
+
return process.env.UMI_PLUGIN_ANTD_ENABLE || userConfig.antd;
|
|
38
|
+
},
|
|
34
39
|
});
|
|
35
40
|
function checkPkgPath() {
|
|
36
41
|
if (!pkgPath) {
|
|
@@ -48,17 +53,31 @@ exports.default = (api) => {
|
|
|
48
53
|
});
|
|
49
54
|
api.modifyConfig((memo) => {
|
|
50
55
|
checkPkgPath();
|
|
56
|
+
const antd = memo.antd || {};
|
|
57
|
+
// defaultConfig 的取值在 config 之后,所以改用环境变量传默认值
|
|
58
|
+
if (process.env.UMI_PLUGIN_ANTD_ENABLE) {
|
|
59
|
+
const { defaultConfig } = JSON.parse(process.env.UMI_PLUGIN_ANTD_ENABLE);
|
|
60
|
+
Object.assign(antd, defaultConfig);
|
|
61
|
+
}
|
|
51
62
|
// antd import
|
|
52
63
|
memo.alias.antd = pkgPath;
|
|
53
64
|
// moment > dayjs
|
|
54
|
-
if (
|
|
65
|
+
if (antd.dayjs) {
|
|
55
66
|
memo.alias.moment = (0, path_1.dirname)(require.resolve('dayjs/package.json'));
|
|
56
67
|
}
|
|
57
68
|
// dark mode & compact mode
|
|
58
|
-
if (
|
|
69
|
+
if (antd.dark || antd.compact) {
|
|
59
70
|
const { getThemeVariables } = require('antd/dist/theme');
|
|
60
|
-
memo.theme =
|
|
71
|
+
memo.theme = {
|
|
72
|
+
...getThemeVariables(antd),
|
|
73
|
+
...memo.theme,
|
|
74
|
+
};
|
|
61
75
|
}
|
|
76
|
+
// antd theme
|
|
77
|
+
memo.theme = {
|
|
78
|
+
'root-entry-name': 'default',
|
|
79
|
+
...memo.theme,
|
|
80
|
+
};
|
|
62
81
|
return memo;
|
|
63
82
|
});
|
|
64
83
|
// babel-plugin-import
|
|
@@ -73,6 +92,7 @@ exports.default = (api) => {
|
|
|
73
92
|
libraryDirectory: 'es',
|
|
74
93
|
style: style === 'less' ? true : 'css',
|
|
75
94
|
},
|
|
95
|
+
'antd',
|
|
76
96
|
],
|
|
77
97
|
]
|
|
78
98
|
: [];
|
|
@@ -84,6 +104,7 @@ exports.default = (api) => {
|
|
|
84
104
|
api.writeTmpFile({
|
|
85
105
|
path: `runtime.tsx`,
|
|
86
106
|
content: plugin_utils_1.Mustache.render(`
|
|
107
|
+
import React from 'react';
|
|
87
108
|
import { ConfigProvider, Modal, message, notification } from 'antd';
|
|
88
109
|
|
|
89
110
|
export function rootContainer(container) {
|
package/dist/dva.js
CHANGED
|
@@ -25,11 +25,11 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
26
|
exports.getAllModels = exports.getModelUtil = void 0;
|
|
27
27
|
const t = __importStar(require("@umijs/bundler-utils/compiled/babel/types"));
|
|
28
|
+
const utils_1 = require("@umijs/utils");
|
|
28
29
|
const path_1 = require("path");
|
|
29
30
|
const plugin_utils_1 = require("umi/plugin-utils");
|
|
30
31
|
const modelUtils_1 = require("./utils/modelUtils");
|
|
31
32
|
const withTmpPath_1 = require("./utils/withTmpPath");
|
|
32
|
-
const utils_1 = require("@umijs/utils");
|
|
33
33
|
exports.default = (api) => {
|
|
34
34
|
const pkgPath = (0, path_1.join)(__dirname, '../libs/dva.ts');
|
|
35
35
|
api.describe({
|
|
@@ -80,15 +80,24 @@ import dvaImmer, { enableES5, enableAllPlugins } from '${(0, utils_1.winPath)(re
|
|
|
80
80
|
`
|
|
81
81
|
: ''}
|
|
82
82
|
import React, { useRef } from 'react';
|
|
83
|
-
import { history } from 'umi';
|
|
83
|
+
import { history, ApplyPluginsType, useAppData } from 'umi';
|
|
84
84
|
import { models } from './models';
|
|
85
85
|
|
|
86
|
+
let dvaApp: any;
|
|
87
|
+
|
|
86
88
|
export function RootContainer(props: any) {
|
|
89
|
+
const { pluginManager } = useAppData();
|
|
87
90
|
const app = useRef<any>();
|
|
91
|
+
const runtimeDva = pluginManager.applyPlugins({
|
|
92
|
+
key: 'dva',
|
|
93
|
+
type: ApplyPluginsType.modify,
|
|
94
|
+
initialValue: {},
|
|
95
|
+
});
|
|
88
96
|
if (!app.current) {
|
|
89
97
|
app.current = create(
|
|
90
98
|
{
|
|
91
99
|
history,
|
|
100
|
+
...(runtimeDva.config || {}),
|
|
92
101
|
},
|
|
93
102
|
{
|
|
94
103
|
initialReducer: {},
|
|
@@ -100,17 +109,28 @@ export function RootContainer(props: any) {
|
|
|
100
109
|
},
|
|
101
110
|
},
|
|
102
111
|
);
|
|
112
|
+
dvaApp = app.current;
|
|
103
113
|
app.current.use(createLoading());
|
|
104
114
|
${((_b = api.config.dva) === null || _b === void 0 ? void 0 : _b.immer) ? `app.current.use(dvaImmer());` : ''}
|
|
105
115
|
${((_d = (_c = api.config.dva) === null || _c === void 0 ? void 0 : _c.immer) === null || _d === void 0 ? void 0 : _d.enableES5) ? `enableES5();` : ''}
|
|
106
116
|
${((_f = (_e = api.config.dva) === null || _e === void 0 ? void 0 : _e.immer) === null || _f === void 0 ? void 0 : _f.enableAllPlugins) ? `enableAllPlugins();` : ''}
|
|
117
|
+
(runtimeDva.plugins || []).forEach((p) => {
|
|
118
|
+
app.current.use(p);
|
|
119
|
+
});
|
|
107
120
|
for (const id of Object.keys(models)) {
|
|
108
|
-
app.current.model(
|
|
121
|
+
app.current.model({
|
|
122
|
+
namespace: models[id].namespace,
|
|
123
|
+
...models[id].model,
|
|
124
|
+
});
|
|
109
125
|
}
|
|
110
126
|
app.current.start();
|
|
111
127
|
}
|
|
112
128
|
return <Provider store={app.current!._store}>{props.children}</Provider>;
|
|
113
129
|
}
|
|
130
|
+
|
|
131
|
+
export function getDvaApp() {
|
|
132
|
+
return dvaApp;
|
|
133
|
+
}
|
|
114
134
|
`,
|
|
115
135
|
context: {},
|
|
116
136
|
});
|
|
@@ -130,7 +150,9 @@ export function dataflowProvider(container, opts) {
|
|
|
130
150
|
api.writeTmpFile({
|
|
131
151
|
path: 'index.ts',
|
|
132
152
|
content: `
|
|
133
|
-
export { connect, useDispatch, useStore, useSelector } from 'dva'
|
|
153
|
+
export { connect, useDispatch, useStore, useSelector } from 'dva';
|
|
154
|
+
export { getDvaApp } from './dva';
|
|
155
|
+
`,
|
|
134
156
|
});
|
|
135
157
|
});
|
|
136
158
|
api.addTmpGenerateWatcherPaths(() => {
|
|
@@ -139,6 +161,7 @@ export { connect, useDispatch, useStore, useSelector } from 'dva';`,
|
|
|
139
161
|
api.addRuntimePlugin(() => {
|
|
140
162
|
return [(0, withTmpPath_1.withTmpPath)({ api, path: 'runtime.tsx' })];
|
|
141
163
|
});
|
|
164
|
+
api.addRuntimePluginKey(() => ['dva']);
|
|
142
165
|
// dva list model
|
|
143
166
|
api.registerCommand({
|
|
144
167
|
name: 'dva',
|
package/dist/initial-state.js
CHANGED
|
@@ -14,7 +14,12 @@ exports.default = (api) => {
|
|
|
14
14
|
});
|
|
15
15
|
api.register({
|
|
16
16
|
key: 'addExtraModels',
|
|
17
|
-
fn: () => [
|
|
17
|
+
fn: () => [
|
|
18
|
+
(0, withTmpPath_1.withTmpPath)({
|
|
19
|
+
api,
|
|
20
|
+
path: '@@initialState.ts#{"namespace":"@@initialState"}',
|
|
21
|
+
}),
|
|
22
|
+
],
|
|
18
23
|
});
|
|
19
24
|
api.addRuntimePluginKey(() => ['getInitialState']);
|
|
20
25
|
api.addRuntimePlugin(() => {
|
|
@@ -30,8 +35,8 @@ exports.default = (api) => {
|
|
|
30
35
|
import React from 'react';
|
|
31
36
|
import { useModel } from '@@/plugin-model';
|
|
32
37
|
${loading
|
|
33
|
-
? `import Loading from ${loading}`
|
|
34
|
-
: `function Loading() { return <div
|
|
38
|
+
? `import Loading from '${loading}'`
|
|
39
|
+
: `function Loading() { return <div />; }`}
|
|
35
40
|
export default function InitialStateProvider(props: any) {
|
|
36
41
|
const appLoaded = React.useRef(false);
|
|
37
42
|
const { loading = false } = useModel("@@initialState") || {};
|
|
@@ -55,8 +60,10 @@ export default function InitialStateProvider(props: any) {
|
|
|
55
60
|
import { useState, useEffect, useCallback } from 'react';
|
|
56
61
|
import { getInitialState } from '@/app';
|
|
57
62
|
|
|
63
|
+
export type InitialStateType = Awaited<ReturnType<typeof getInitialState>> | undefined;
|
|
64
|
+
|
|
58
65
|
const initState = {
|
|
59
|
-
initialState: undefined,
|
|
66
|
+
initialState: undefined as InitialStateType,
|
|
60
67
|
loading: true,
|
|
61
68
|
error: undefined,
|
|
62
69
|
};
|
|
@@ -71,20 +78,21 @@ export default () => {
|
|
|
71
78
|
} catch (e) {
|
|
72
79
|
setState((s) => ({ ...s, error: e, loading: false }));
|
|
73
80
|
}
|
|
74
|
-
// [?]
|
|
75
|
-
// await sleep(10);
|
|
76
81
|
}, []);
|
|
77
82
|
|
|
78
|
-
const setInitialState = useCallback(
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
83
|
+
const setInitialState = useCallback(
|
|
84
|
+
async (
|
|
85
|
+
initialState: InitialStateType | ((initialState: InitialStateType) => InitialStateType),
|
|
86
|
+
) => {
|
|
87
|
+
setState((s) => {
|
|
88
|
+
if (typeof initialState === 'function') {
|
|
89
|
+
return { ...s, initialState: initialState(s.initialState), loading: false };
|
|
90
|
+
}
|
|
91
|
+
return { ...s, initialState, loading: false };
|
|
92
|
+
});
|
|
93
|
+
},
|
|
94
|
+
[],
|
|
95
|
+
);
|
|
88
96
|
|
|
89
97
|
useEffect(() => {
|
|
90
98
|
refresh();
|
|
@@ -107,7 +115,7 @@ export default () => ({ loading: false, refresh: () => {} })
|
|
|
107
115
|
content: `
|
|
108
116
|
import React from 'react';
|
|
109
117
|
import Provider from './Provider';
|
|
110
|
-
export function
|
|
118
|
+
export function dataflowProvider(container) {
|
|
111
119
|
return <Provider>{ container }</Provider>;
|
|
112
120
|
}
|
|
113
121
|
`,
|
package/dist/layout.js
CHANGED
|
@@ -22,15 +22,11 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
22
22
|
__setModuleDefault(result, mod);
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
-
};
|
|
28
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
26
|
const allIcons = __importStar(require("@ant-design/icons"));
|
|
30
|
-
const
|
|
27
|
+
const fs_1 = require("fs");
|
|
31
28
|
const path_1 = require("path");
|
|
32
29
|
const plugin_utils_1 = require("umi/plugin-utils");
|
|
33
|
-
const resolveProjectDep_1 = require("./utils/resolveProjectDep");
|
|
34
30
|
const withTmpPath_1 = require("./utils/withTmpPath");
|
|
35
31
|
exports.default = (api) => {
|
|
36
32
|
api.describe({
|
|
@@ -43,11 +39,35 @@ exports.default = (api) => {
|
|
|
43
39
|
},
|
|
44
40
|
enableBy: api.EnableBy.config,
|
|
45
41
|
});
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
42
|
+
/**
|
|
43
|
+
* 优先去找 '@alipay/tech-ui',保证稳定性
|
|
44
|
+
*/
|
|
45
|
+
const depList = ['@alipay/tech-ui', '@ant-design/pro-layout'];
|
|
46
|
+
const pkgHasDep = depList.find((dep) => {
|
|
47
|
+
var _a, _b;
|
|
48
|
+
const { pkg } = api;
|
|
49
|
+
if (((_a = pkg.dependencies) === null || _a === void 0 ? void 0 : _a[dep]) || ((_b = pkg.devDependencies) === null || _b === void 0 ? void 0 : _b[dep])) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
return false;
|
|
53
|
+
});
|
|
54
|
+
const getPkgPath = () => {
|
|
55
|
+
// 如果 layout 和 techui 至少有一个在,找到他们的地址
|
|
56
|
+
if (pkgHasDep &&
|
|
57
|
+
(0, fs_1.existsSync)((0, path_1.join)(api.cwd, 'node_modules', pkgHasDep, 'package.json'))) {
|
|
58
|
+
return (0, path_1.join)(api.cwd, 'node_modules', pkgHasDep);
|
|
59
|
+
}
|
|
60
|
+
const cwd = process.cwd();
|
|
61
|
+
// support APP_ROOT
|
|
62
|
+
if (pkgHasDep &&
|
|
63
|
+
api.cwd !== cwd &&
|
|
64
|
+
(0, fs_1.existsSync)((0, path_1.join)(cwd, 'node_modules', pkgHasDep, 'package.json'))) {
|
|
65
|
+
return (0, path_1.join)(cwd, 'node_modules', pkgHasDep);
|
|
66
|
+
}
|
|
67
|
+
// 如果项目中没有去找插件以来的
|
|
68
|
+
return (0, path_1.dirname)(require.resolve('@ant-design/pro-layout/package.json'));
|
|
69
|
+
};
|
|
70
|
+
const pkgPath = (0, plugin_utils_1.winPath)(getPkgPath());
|
|
51
71
|
api.modifyAppData((memo) => {
|
|
52
72
|
const version = require(`${pkgPath}/package.json`).version;
|
|
53
73
|
memo.pluginLayout = {
|
|
@@ -57,8 +77,11 @@ exports.default = (api) => {
|
|
|
57
77
|
return memo;
|
|
58
78
|
});
|
|
59
79
|
api.modifyConfig((memo) => {
|
|
60
|
-
//
|
|
61
|
-
|
|
80
|
+
// 只在没有自行依赖 @ant-design/pro-layout 或 @alipay/tech-ui 时
|
|
81
|
+
// 才使用插件中提供的 @ant-design/pro-layout
|
|
82
|
+
if (!pkgHasDep) {
|
|
83
|
+
memo.alias['@ant-design/pro-layout'] = pkgPath;
|
|
84
|
+
}
|
|
62
85
|
return memo;
|
|
63
86
|
});
|
|
64
87
|
api.onGenerateFiles(() => {
|
|
@@ -67,17 +90,23 @@ exports.default = (api) => {
|
|
|
67
90
|
api.writeTmpFile({
|
|
68
91
|
path: 'Layout.tsx',
|
|
69
92
|
content: `
|
|
70
|
-
import { Link, useLocation, useNavigate, Outlet, useAppData,
|
|
71
|
-
import
|
|
72
|
-
|
|
73
|
-
|
|
93
|
+
import { Link, useLocation, useNavigate, Outlet, useAppData, useRouteData, matchRoutes } from 'umi';
|
|
94
|
+
import React, { useMemo } from 'react';
|
|
95
|
+
import {
|
|
96
|
+
ProLayout,
|
|
97
|
+
} from "${pkgPath || '@ant-design/pro-layout'}";
|
|
74
98
|
import './Layout.less';
|
|
75
99
|
import Logo from './Logo';
|
|
100
|
+
import Exception from './Exception';
|
|
76
101
|
import { getRightRenderContent } from './rightRender';
|
|
77
102
|
${hasInitialStatePlugin
|
|
78
103
|
? `import { useModel } from '@@/plugin-model';`
|
|
79
104
|
: 'const useModel = null;'}
|
|
80
|
-
|
|
105
|
+
${api.config.access
|
|
106
|
+
? `
|
|
107
|
+
import { useAccessMarkedRoutes } from '@@/plugin-access';
|
|
108
|
+
`.trim()
|
|
109
|
+
: 'const useAccessMarkedRoutes = (r) => r;'}
|
|
81
110
|
${api.config.locale
|
|
82
111
|
? `
|
|
83
112
|
import { useIntl } from '@@/plugin-locale';
|
|
@@ -85,7 +114,7 @@ import { useIntl } from '@@/plugin-locale';
|
|
|
85
114
|
: ''}
|
|
86
115
|
|
|
87
116
|
|
|
88
|
-
export default () => {
|
|
117
|
+
export default (props: any) => {
|
|
89
118
|
const location = useLocation();
|
|
90
119
|
const navigate = useNavigate();
|
|
91
120
|
const { clientRoutes, pluginManager } = useAppData();
|
|
@@ -108,9 +137,8 @@ const { formatMessage } = useIntl();
|
|
|
108
137
|
...initialInfo
|
|
109
138
|
},
|
|
110
139
|
});
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
})[0];
|
|
140
|
+
const matchedRoute = useMemo(() => matchRoutes(clientRoutes, location.pathname).pop()?.route, [location.pathname]);
|
|
141
|
+
const [route] = useAccessMarkedRoutes(clientRoutes.filter(({ id }) => id === 'ant-design-pro-layout'));
|
|
114
142
|
return (
|
|
115
143
|
<ProLayout
|
|
116
144
|
route={route}
|
|
@@ -132,7 +160,8 @@ const { formatMessage } = useIntl();
|
|
|
132
160
|
}
|
|
133
161
|
if (menuItemProps.path && location.pathname !== menuItemProps.path) {
|
|
134
162
|
return (
|
|
135
|
-
|
|
163
|
+
// handle wildcard route path, for example /slave/* from qiankun
|
|
164
|
+
<Link to={menuItemProps.path.replace('/*', '')} target={menuItemProps.target}>
|
|
136
165
|
{defaultDom}
|
|
137
166
|
</Link>
|
|
138
167
|
);
|
|
@@ -166,19 +195,50 @@ const { formatMessage } = useIntl();
|
|
|
166
195
|
})
|
|
167
196
|
}
|
|
168
197
|
>
|
|
169
|
-
<
|
|
198
|
+
<Exception
|
|
199
|
+
route={matchedRoute}
|
|
200
|
+
notFound={runtimeConfig.notFound}
|
|
201
|
+
noAccessible={runtimeConfig.noAccessible}
|
|
202
|
+
>
|
|
203
|
+
{runtimeConfig.childrenRender
|
|
204
|
+
? runtimeConfig.childrenRender(<Outlet />, props)
|
|
205
|
+
: <Outlet />
|
|
206
|
+
}
|
|
207
|
+
</Exception>
|
|
170
208
|
</ProLayout>
|
|
171
209
|
);
|
|
172
210
|
}
|
|
173
211
|
`,
|
|
212
|
+
});
|
|
213
|
+
api.writeTmpFile({
|
|
214
|
+
path: 'index.ts',
|
|
215
|
+
content: `export type TempType = string`,
|
|
216
|
+
});
|
|
217
|
+
// 写入类型, RunTimeLayoutConfig 是 app.tsx 中 layout 配置的类型
|
|
218
|
+
// 对于动态 layout 配置很有用
|
|
219
|
+
api.writeTmpFile({
|
|
220
|
+
path: 'types.d.ts',
|
|
221
|
+
content: `
|
|
222
|
+
import type { ProLayoutProps } from "${pkgPath || '@ant-design/pro-layout'}";
|
|
223
|
+
${hasInitialStatePlugin
|
|
224
|
+
? `import type InitialStateType from '@@/plugin-initialState/@@initialState';
|
|
225
|
+
type InitDataType = ReturnType<typeof InitialStateType>;
|
|
226
|
+
`
|
|
227
|
+
: 'type InitDataType = any;'}
|
|
228
|
+
|
|
229
|
+
export type RunTimeLayoutConfig = (
|
|
230
|
+
initData: InitDataType,
|
|
231
|
+
) => ProLayoutProps & {
|
|
232
|
+
childrenRender?: (dom: JSX.Element, props: ProLayoutProps) => React.ReactNode,
|
|
233
|
+
unAccessible?: JSX.Element,
|
|
234
|
+
noFound?: JSX.Element,
|
|
235
|
+
};
|
|
236
|
+
`,
|
|
174
237
|
});
|
|
175
238
|
const iconsMap = Object.keys(api.appData.routes).reduce((memo, id) => {
|
|
176
239
|
const { icon } = api.appData.routes[id];
|
|
177
240
|
if (icon) {
|
|
178
241
|
const upperIcon = plugin_utils_1.lodash.upperFirst(plugin_utils_1.lodash.camelCase(icon));
|
|
179
|
-
(0, assert_1.default)(
|
|
180
|
-
// @ts-ignore
|
|
181
|
-
allIcons[upperIcon] || allIcons[`${upperIcon}Outlined`], `Icon ${upperIcon} is not found`);
|
|
182
242
|
// @ts-ignore
|
|
183
243
|
if (allIcons[upperIcon]) {
|
|
184
244
|
memo[upperIcon] = true;
|
|
@@ -223,7 +283,9 @@ export function patchRoutes({ routes }) {
|
|
|
223
283
|
const { icon } = routes[key];
|
|
224
284
|
if (icon && typeof icon === 'string') {
|
|
225
285
|
const upperIcon = formatIcon(icon);
|
|
226
|
-
|
|
286
|
+
if (icons[upperIcon] || icons[upperIcon + 'Outlined']) {
|
|
287
|
+
routes[key].icon = React.createElement(icons[upperIcon] || icons[upperIcon + 'Outlined']);
|
|
288
|
+
}
|
|
227
289
|
}
|
|
228
290
|
});
|
|
229
291
|
}
|
|
@@ -468,6 +530,43 @@ const LogoIcon: React.FC = () => {
|
|
|
468
530
|
export default LogoIcon;
|
|
469
531
|
`,
|
|
470
532
|
});
|
|
533
|
+
api.writeTmpFile({
|
|
534
|
+
path: 'Exception.tsx',
|
|
535
|
+
content: `
|
|
536
|
+
import React from 'react';
|
|
537
|
+
import { history, type IRoute } from 'umi';
|
|
538
|
+
import { Result, Button } from 'antd';
|
|
539
|
+
|
|
540
|
+
const Exception: React.FC<{
|
|
541
|
+
children: React.ReactNode;
|
|
542
|
+
route?: IRoute;
|
|
543
|
+
notFound?: React.ReactNode;
|
|
544
|
+
noAccessible?: React.ReactNode;
|
|
545
|
+
}> = (props) => (
|
|
546
|
+
// render custom 404
|
|
547
|
+
(!props.route && props.notFound) ||
|
|
548
|
+
// render custom 403
|
|
549
|
+
(props.route.unaccessible && props.noAccessible) ||
|
|
550
|
+
// render default exception
|
|
551
|
+
((!props.route || props.route.unaccessible) && (
|
|
552
|
+
<Result
|
|
553
|
+
status={props.route ? '403' : '404'}
|
|
554
|
+
title={props.route ? '403' : '404'}
|
|
555
|
+
subTitle={props.route ? '抱歉,你无权访问该页面' : '抱歉,你访问的页面不存在'}
|
|
556
|
+
extra={
|
|
557
|
+
<Button type="primary" onClick={() => history.push('/')}>
|
|
558
|
+
返回首页
|
|
559
|
+
</Button>
|
|
560
|
+
}
|
|
561
|
+
/>
|
|
562
|
+
)) ||
|
|
563
|
+
// normal render
|
|
564
|
+
props.children
|
|
565
|
+
);
|
|
566
|
+
|
|
567
|
+
export default Exception;
|
|
568
|
+
`,
|
|
569
|
+
});
|
|
471
570
|
});
|
|
472
571
|
api.addLayouts(() => {
|
|
473
572
|
return [
|