@module-federation/bridge-react 0.16.0 → 0.17.0
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/CHANGELOG.md +13 -0
- package/__tests__/bridge.spec.tsx +7 -7
- package/__tests__/createLazyComponent.spec.tsx +210 -0
- package/__tests__/prefetch.spec.ts +153 -0
- package/__tests__/setupTests.ts +3 -0
- package/dist/{bridge-base-P6pEjY1q.js → bridge-base-BoshEggF.mjs} +1 -1
- package/dist/{bridge-base-BBH982Tz.cjs → bridge-base-UGCwcMnG.js} +1 -1
- package/dist/data-fetch-server-middleware.cjs.js +163 -0
- package/dist/data-fetch-server-middleware.d.ts +15 -0
- package/dist/data-fetch-server-middleware.es.js +164 -0
- package/dist/data-fetch-utils.cjs.js +24 -0
- package/dist/data-fetch-utils.d.ts +81 -0
- package/dist/data-fetch-utils.es.js +26 -0
- package/dist/index-C0fDZB5b.js +45 -0
- package/dist/index-CqxytsLY.mjs +46 -0
- package/dist/index.cjs.js +35 -9
- package/dist/index.d.ts +140 -0
- package/dist/index.es.js +38 -12
- package/dist/index.esm-BCeUd-x9.mjs +418 -0
- package/dist/index.esm-j_1sIRzg.js +417 -0
- package/dist/lazy-load-component-plugin-C1tVve-W.js +521 -0
- package/dist/lazy-load-component-plugin-PERjiaFJ.mjs +522 -0
- package/dist/lazy-load-component-plugin.cjs.js +6 -0
- package/dist/lazy-load-component-plugin.d.ts +16 -0
- package/dist/lazy-load-component-plugin.es.js +6 -0
- package/dist/lazy-utils.cjs.js +24 -0
- package/dist/lazy-utils.d.ts +149 -0
- package/dist/lazy-utils.es.js +24 -0
- package/dist/plugin.d.ts +13 -4
- package/dist/prefetch-CZvoIftg.js +1334 -0
- package/dist/prefetch-Dux8GUpr.mjs +1335 -0
- package/dist/router-v5.cjs.js +1 -1
- package/dist/router-v5.d.ts +9 -0
- package/dist/router-v5.es.js +1 -1
- package/dist/router-v6.cjs.js +1 -1
- package/dist/router-v6.d.ts +9 -0
- package/dist/router-v6.es.js +1 -1
- package/dist/router.cjs.js +1 -1
- package/dist/router.d.ts +9 -0
- package/dist/router.es.js +1 -1
- package/dist/utils-Cy-amYU5.mjs +2016 -0
- package/dist/utils-iEVlDmyk.js +2015 -0
- package/dist/v18.cjs.js +1 -1
- package/dist/v18.d.ts +9 -0
- package/dist/v18.es.js +1 -1
- package/dist/v19.cjs.js +1 -1
- package/dist/v19.d.ts +9 -0
- package/dist/v19.es.js +1 -1
- package/package.json +47 -5
- package/src/index.ts +32 -1
- package/src/lazy/AwaitDataFetch.tsx +215 -0
- package/src/lazy/constant.ts +30 -0
- package/src/lazy/createLazyComponent.tsx +411 -0
- package/src/lazy/data-fetch/cache.ts +291 -0
- package/src/lazy/data-fetch/call-data-fetch.ts +13 -0
- package/src/lazy/data-fetch/data-fetch-server-middleware.ts +196 -0
- package/src/lazy/data-fetch/index.ts +16 -0
- package/src/lazy/data-fetch/inject-data-fetch.ts +109 -0
- package/src/lazy/data-fetch/prefetch.ts +106 -0
- package/src/lazy/data-fetch/runtime-plugin.ts +115 -0
- package/src/lazy/index.ts +35 -0
- package/src/lazy/logger.ts +6 -0
- package/src/lazy/types.ts +75 -0
- package/src/lazy/utils.ts +372 -0
- package/src/lazy/wrapNoSSR.tsx +10 -0
- package/src/plugins/lazy-load-component-plugin.spec.ts +21 -0
- package/src/plugins/lazy-load-component-plugin.ts +57 -0
- package/src/provider/plugin.ts +4 -4
- package/src/remote/component.tsx +3 -3
- package/src/remote/create.tsx +17 -4
- package/tsconfig.json +1 -1
- package/vite.config.ts +13 -0
- package/vitest.config.ts +6 -1
- package/dist/index-Cv3p6r66.cjs +0 -235
- package/dist/index-D4yt7Udv.js +0 -236
- package/src/.eslintrc.js +0 -9
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { DATA_FETCH_QUERY, MF_DATA_FETCH_STATUS } from '../constant';
|
|
2
|
+
import {
|
|
3
|
+
getDataFetchMap,
|
|
4
|
+
fetchData,
|
|
5
|
+
initDataFetchMap,
|
|
6
|
+
loadDataFetchModule,
|
|
7
|
+
} from '../utils';
|
|
8
|
+
import { SEPARATOR, MANIFEST_EXT } from '@module-federation/sdk';
|
|
9
|
+
import logger from '../logger';
|
|
10
|
+
|
|
11
|
+
import type { NoSSRRemoteInfo } from '../types';
|
|
12
|
+
import type { MiddlewareHandler } from 'hono';
|
|
13
|
+
|
|
14
|
+
function wrapSetTimeout(
|
|
15
|
+
targetPromise: Promise<unknown>,
|
|
16
|
+
delay = 20000,
|
|
17
|
+
id: string,
|
|
18
|
+
) {
|
|
19
|
+
if (targetPromise && typeof targetPromise.then === 'function') {
|
|
20
|
+
return new Promise((resolve, reject) => {
|
|
21
|
+
const timeoutId = setTimeout(() => {
|
|
22
|
+
logger.warn(`Data fetch for ID ${id} timed out after 20 seconds.`);
|
|
23
|
+
reject(new Error(`Data fetch for ID ${id} timed out after 20 seconds`));
|
|
24
|
+
}, delay);
|
|
25
|
+
|
|
26
|
+
targetPromise
|
|
27
|
+
.then((value: any) => {
|
|
28
|
+
clearTimeout(timeoutId);
|
|
29
|
+
resolve(value);
|
|
30
|
+
})
|
|
31
|
+
.catch((err: any) => {
|
|
32
|
+
clearTimeout(timeoutId);
|
|
33
|
+
reject(err);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function addProtocol(url: string) {
|
|
40
|
+
if (url.startsWith('//')) {
|
|
41
|
+
return 'https:' + url;
|
|
42
|
+
}
|
|
43
|
+
return url;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const getDecodeQuery = (url: URL, name: string) => {
|
|
47
|
+
const res = url.searchParams.get(name);
|
|
48
|
+
if (!res) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
return decodeURIComponent(res);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const dataFetchServerMiddleware: MiddlewareHandler = async (ctx, next) => {
|
|
55
|
+
let url: URL;
|
|
56
|
+
let dataFetchKey: string | null;
|
|
57
|
+
let params: Record<string, unknown>;
|
|
58
|
+
let remoteInfo: NoSSRRemoteInfo;
|
|
59
|
+
try {
|
|
60
|
+
url = new URL(ctx.req.url);
|
|
61
|
+
dataFetchKey = getDecodeQuery(url, DATA_FETCH_QUERY);
|
|
62
|
+
params = JSON.parse(getDecodeQuery(url, 'params') || '{}');
|
|
63
|
+
const remoteInfoQuery = getDecodeQuery(url, 'remoteInfo');
|
|
64
|
+
remoteInfo = remoteInfoQuery ? JSON.parse(remoteInfoQuery) : null;
|
|
65
|
+
} catch (e) {
|
|
66
|
+
logger.error('fetch data from server, error: ', e);
|
|
67
|
+
return next();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (!dataFetchKey) {
|
|
71
|
+
return next();
|
|
72
|
+
}
|
|
73
|
+
logger.log('fetch data from server, dataFetchKey: ', dataFetchKey);
|
|
74
|
+
logger.debug(
|
|
75
|
+
'fetch data from server, moduleInfo: ',
|
|
76
|
+
globalThis.__FEDERATION__?.moduleInfo,
|
|
77
|
+
);
|
|
78
|
+
try {
|
|
79
|
+
const dataFetchMap = getDataFetchMap();
|
|
80
|
+
if (!dataFetchMap) {
|
|
81
|
+
initDataFetchMap();
|
|
82
|
+
}
|
|
83
|
+
const fetchDataPromise = dataFetchMap[dataFetchKey]?.[1];
|
|
84
|
+
logger.debug(
|
|
85
|
+
'fetch data from server, fetchDataPromise: ',
|
|
86
|
+
fetchDataPromise,
|
|
87
|
+
);
|
|
88
|
+
if (
|
|
89
|
+
fetchDataPromise &&
|
|
90
|
+
dataFetchMap[dataFetchKey]?.[2] !== MF_DATA_FETCH_STATUS.ERROR
|
|
91
|
+
) {
|
|
92
|
+
const targetPromise = fetchDataPromise[0];
|
|
93
|
+
// Ensure targetPromise is thenable
|
|
94
|
+
const wrappedPromise = wrapSetTimeout(targetPromise, 20000, dataFetchKey);
|
|
95
|
+
if (wrappedPromise) {
|
|
96
|
+
const res = await wrappedPromise;
|
|
97
|
+
logger.log('fetch data from server, fetchDataPromise res: ', res);
|
|
98
|
+
return ctx.json(res);
|
|
99
|
+
}
|
|
100
|
+
logger.error(
|
|
101
|
+
`Expected a Promise from fetchDataPromise[0] for dataFetchKey ${dataFetchKey}, but received:`,
|
|
102
|
+
targetPromise,
|
|
103
|
+
'Will try call new dataFetch again...',
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (remoteInfo) {
|
|
108
|
+
try {
|
|
109
|
+
const hostInstance = globalThis.__FEDERATION__.__INSTANCES__[0];
|
|
110
|
+
const remoteEntry = `${addProtocol(remoteInfo.ssrPublicPath) + remoteInfo.ssrRemoteEntry}`;
|
|
111
|
+
if (!hostInstance) {
|
|
112
|
+
throw new Error('host instance not found!');
|
|
113
|
+
}
|
|
114
|
+
const remote = hostInstance.options.remotes.find(
|
|
115
|
+
(remote) => remote.name === remoteInfo.name,
|
|
116
|
+
);
|
|
117
|
+
logger.debug('find remote: ', JSON.stringify(remote));
|
|
118
|
+
if (!remote) {
|
|
119
|
+
hostInstance.registerRemotes([
|
|
120
|
+
{
|
|
121
|
+
name: remoteInfo.name,
|
|
122
|
+
entry: remoteEntry,
|
|
123
|
+
entryGlobalName: remoteInfo.globalName,
|
|
124
|
+
},
|
|
125
|
+
]);
|
|
126
|
+
} else if (
|
|
127
|
+
!('entry' in remote) ||
|
|
128
|
+
!remote.entry.includes(MANIFEST_EXT)
|
|
129
|
+
) {
|
|
130
|
+
const { hostGlobalSnapshot, remoteSnapshot } =
|
|
131
|
+
hostInstance.snapshotHandler.getGlobalRemoteInfo(remoteInfo);
|
|
132
|
+
logger.debug(
|
|
133
|
+
'find hostGlobalSnapshot: ',
|
|
134
|
+
JSON.stringify(hostGlobalSnapshot),
|
|
135
|
+
);
|
|
136
|
+
logger.debug('find remoteSnapshot: ', JSON.stringify(remoteSnapshot));
|
|
137
|
+
|
|
138
|
+
if (!hostGlobalSnapshot || !remoteSnapshot) {
|
|
139
|
+
if ('version' in remote) {
|
|
140
|
+
// @ts-ignore
|
|
141
|
+
delete remote.version;
|
|
142
|
+
}
|
|
143
|
+
// @ts-ignore
|
|
144
|
+
remote.entry = remoteEntry;
|
|
145
|
+
remote.entryGlobalName = remoteInfo.globalName;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
} catch (e) {
|
|
149
|
+
ctx.status(500);
|
|
150
|
+
return ctx.text(
|
|
151
|
+
`failed to fetch ${remoteInfo.name} data, error:\n ${e}`,
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const dataFetchItem = dataFetchMap[dataFetchKey];
|
|
157
|
+
logger.debug('fetch data from server, dataFetchItem: ', dataFetchItem);
|
|
158
|
+
if (dataFetchItem) {
|
|
159
|
+
const callFetchDataPromise = fetchData(dataFetchKey, {
|
|
160
|
+
...params,
|
|
161
|
+
isDowngrade: !remoteInfo,
|
|
162
|
+
_id: dataFetchKey,
|
|
163
|
+
});
|
|
164
|
+
const wrappedPromise = wrapSetTimeout(
|
|
165
|
+
callFetchDataPromise,
|
|
166
|
+
20000,
|
|
167
|
+
dataFetchKey,
|
|
168
|
+
);
|
|
169
|
+
if (wrappedPromise) {
|
|
170
|
+
const res = await wrappedPromise;
|
|
171
|
+
logger.log('fetch data from server, dataFetchItem res: ', res);
|
|
172
|
+
return ctx.json(res);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const remoteId = dataFetchKey.split(SEPARATOR)[0];
|
|
177
|
+
const hostInstance = globalThis.__FEDERATION__.__INSTANCES__[0];
|
|
178
|
+
if (!hostInstance) {
|
|
179
|
+
throw new Error('host instance not found!');
|
|
180
|
+
}
|
|
181
|
+
const dataFetchFn = await loadDataFetchModule(hostInstance, remoteId);
|
|
182
|
+
const data = await dataFetchFn({
|
|
183
|
+
...params,
|
|
184
|
+
isDowngrade: !remoteInfo,
|
|
185
|
+
_id: dataFetchKey,
|
|
186
|
+
});
|
|
187
|
+
logger.log('fetch data from server, loadDataFetchModule res: ', data);
|
|
188
|
+
return ctx.json(data);
|
|
189
|
+
} catch (e) {
|
|
190
|
+
logger.error('server plugin data fetch error: ', e);
|
|
191
|
+
ctx.status(500);
|
|
192
|
+
return ctx.text(`failed to fetch ${remoteInfo.name} data, error:\n ${e}`);
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
export default dataFetchServerMiddleware;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export { callDataFetch } from './call-data-fetch';
|
|
2
|
+
export { injectDataFetch } from './inject-data-fetch';
|
|
3
|
+
export {
|
|
4
|
+
CacheSize,
|
|
5
|
+
CacheTime,
|
|
6
|
+
configureCache,
|
|
7
|
+
generateKey,
|
|
8
|
+
cache,
|
|
9
|
+
revalidateTag,
|
|
10
|
+
clearStore,
|
|
11
|
+
} from './cache';
|
|
12
|
+
|
|
13
|
+
export type { CacheStatus, CacheStatsInfo } from './cache';
|
|
14
|
+
|
|
15
|
+
export type { PrefetchOptions } from './prefetch';
|
|
16
|
+
export { prefetch } from './prefetch';
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DATA_FETCH_FUNCTION,
|
|
3
|
+
DOWNGRADE_KEY,
|
|
4
|
+
FS_HREF,
|
|
5
|
+
MF_DATA_FETCH_STATUS,
|
|
6
|
+
MF_DATA_FETCH_TYPE,
|
|
7
|
+
} from '../constant';
|
|
8
|
+
import logger from '../logger';
|
|
9
|
+
import {
|
|
10
|
+
dataFetchFunctionOptions,
|
|
11
|
+
MF_DATA_FETCH_MAP_VALUE_PROMISE_SET,
|
|
12
|
+
} from '../types';
|
|
13
|
+
import {
|
|
14
|
+
callAllDowngrade,
|
|
15
|
+
callDowngrade,
|
|
16
|
+
getDataFetchItem,
|
|
17
|
+
getDataFetchMap,
|
|
18
|
+
getDowngradeTag,
|
|
19
|
+
initDataFetchMap,
|
|
20
|
+
} from '../utils';
|
|
21
|
+
|
|
22
|
+
const dataFetchFunction = async function (options: dataFetchFunctionOptions) {
|
|
23
|
+
const [id, data, downgrade] = options;
|
|
24
|
+
logger.debug('==========call data fetch function!');
|
|
25
|
+
if (data) {
|
|
26
|
+
if (!id) {
|
|
27
|
+
throw new Error('id is required!');
|
|
28
|
+
}
|
|
29
|
+
if (!getDataFetchMap()) {
|
|
30
|
+
initDataFetchMap();
|
|
31
|
+
}
|
|
32
|
+
const dataFetchItem = getDataFetchItem(id);
|
|
33
|
+
if (dataFetchItem) {
|
|
34
|
+
dataFetchItem[1]?.[1]?.(data);
|
|
35
|
+
dataFetchItem[2] = MF_DATA_FETCH_STATUS.LOADED;
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (!dataFetchItem) {
|
|
39
|
+
const dataFetchMap = getDataFetchMap();
|
|
40
|
+
let res: MF_DATA_FETCH_MAP_VALUE_PROMISE_SET[1];
|
|
41
|
+
let rej: MF_DATA_FETCH_MAP_VALUE_PROMISE_SET[2];
|
|
42
|
+
const p = new Promise((resolve, reject) => {
|
|
43
|
+
res = resolve;
|
|
44
|
+
rej = reject;
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
dataFetchMap[id] = [
|
|
48
|
+
[
|
|
49
|
+
async () => async () => {
|
|
50
|
+
return '';
|
|
51
|
+
},
|
|
52
|
+
MF_DATA_FETCH_TYPE.FETCH_SERVER,
|
|
53
|
+
],
|
|
54
|
+
[p, res, rej],
|
|
55
|
+
MF_DATA_FETCH_STATUS.LOADED,
|
|
56
|
+
];
|
|
57
|
+
res && res(data);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (downgrade) {
|
|
63
|
+
const mfDowngrade = getDowngradeTag();
|
|
64
|
+
if (!mfDowngrade) {
|
|
65
|
+
globalThis[DOWNGRADE_KEY] = id ? [id] : true;
|
|
66
|
+
} else if (Array.isArray(mfDowngrade) && id && !mfDowngrade.includes(id)) {
|
|
67
|
+
mfDowngrade.push(id);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const mfDowngrade = getDowngradeTag();
|
|
72
|
+
|
|
73
|
+
if (typeof mfDowngrade === 'boolean') {
|
|
74
|
+
return callAllDowngrade();
|
|
75
|
+
}
|
|
76
|
+
if (Array.isArray(mfDowngrade)) {
|
|
77
|
+
if (!id) {
|
|
78
|
+
globalThis[DOWNGRADE_KEY] = true;
|
|
79
|
+
return callAllDowngrade();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (!mfDowngrade.includes(id)) {
|
|
83
|
+
mfDowngrade.push(id);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return callDowngrade(id);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export function injectDataFetch() {
|
|
91
|
+
globalThis[DATA_FETCH_FUNCTION] ||= [];
|
|
92
|
+
const dataFetch = globalThis[DATA_FETCH_FUNCTION];
|
|
93
|
+
|
|
94
|
+
//@ts-ignore
|
|
95
|
+
if (dataFetch.push === dataFetchFunction) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (typeof window === 'undefined') {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
globalThis[FS_HREF] = window.location.href;
|
|
104
|
+
|
|
105
|
+
//@ts-ignore
|
|
106
|
+
dataFetch.push = dataFetchFunction;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export { dataFetchFunction };
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import type { getInstance } from '@module-federation/runtime';
|
|
2
|
+
import {
|
|
3
|
+
getDataFetchInfo,
|
|
4
|
+
getDataFetchMap,
|
|
5
|
+
getDataFetchMapKey,
|
|
6
|
+
} from '../utils';
|
|
7
|
+
import logger from '../logger';
|
|
8
|
+
import helpers from '@module-federation/runtime/helpers';
|
|
9
|
+
import { DataFetchParams } from '../types';
|
|
10
|
+
|
|
11
|
+
export type PrefetchOptions = {
|
|
12
|
+
id: string;
|
|
13
|
+
instance: ReturnType<typeof getInstance>;
|
|
14
|
+
dataFetchParams?: DataFetchParams;
|
|
15
|
+
preloadComponentResource?: boolean;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export async function prefetch(options: PrefetchOptions) {
|
|
19
|
+
const { instance, id, dataFetchParams, preloadComponentResource } = options;
|
|
20
|
+
if (!id) {
|
|
21
|
+
logger.error('id is required for prefetch!');
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (!instance) {
|
|
25
|
+
logger.error('instance is required for prefetch!');
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const matchedRemoteInfo = helpers.utils.matchRemoteWithNameAndExpose(
|
|
29
|
+
instance.options.remotes,
|
|
30
|
+
id,
|
|
31
|
+
);
|
|
32
|
+
if (!matchedRemoteInfo) {
|
|
33
|
+
logger.error(`Can not found '${id}' in instance.options.remotes!`);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const { remote, expose } = matchedRemoteInfo;
|
|
37
|
+
const { remoteSnapshot, globalSnapshot } =
|
|
38
|
+
await instance.snapshotHandler.loadRemoteSnapshotInfo({
|
|
39
|
+
moduleInfo: remote,
|
|
40
|
+
id,
|
|
41
|
+
expose,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
if (preloadComponentResource) {
|
|
45
|
+
const remoteInfo = helpers.utils.getRemoteInfo(remote);
|
|
46
|
+
|
|
47
|
+
instance.remoteHandler.hooks.lifecycle.generatePreloadAssets.emit({
|
|
48
|
+
origin: instance,
|
|
49
|
+
preloadOptions: {
|
|
50
|
+
remote,
|
|
51
|
+
preloadConfig: {
|
|
52
|
+
nameOrAlias: remote.name,
|
|
53
|
+
exposes: [expose],
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
remote,
|
|
57
|
+
remoteInfo,
|
|
58
|
+
globalSnapshot,
|
|
59
|
+
remoteSnapshot,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const dataFetchMap = getDataFetchMap();
|
|
64
|
+
if (!dataFetchMap) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const dataFetchInfo = getDataFetchInfo({
|
|
68
|
+
name: remote.name,
|
|
69
|
+
alias: remote.alias,
|
|
70
|
+
id,
|
|
71
|
+
remoteSnapshot,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const dataFetchMapKey = getDataFetchMapKey(dataFetchInfo, {
|
|
75
|
+
name: instance.name,
|
|
76
|
+
version: instance.options.version,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
if (!dataFetchMapKey) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const dataFetchItem = dataFetchMap[dataFetchMapKey];
|
|
84
|
+
if (!dataFetchItem) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
89
|
+
const [getDataFetchGetter, _type, getDataFetchPromise] = dataFetchItem[0];
|
|
90
|
+
|
|
91
|
+
let _getDataFetchPromise = getDataFetchPromise;
|
|
92
|
+
if (!getDataFetchPromise) {
|
|
93
|
+
if (!getDataFetchGetter) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
_getDataFetchPromise = getDataFetchGetter();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
_getDataFetchPromise!.then((dataFetchFn) => {
|
|
100
|
+
return dataFetchFn({
|
|
101
|
+
...dataFetchParams,
|
|
102
|
+
_id: dataFetchMapKey,
|
|
103
|
+
isDowngrade: false,
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { injectDataFetch } from './inject-data-fetch';
|
|
2
|
+
import {
|
|
3
|
+
getDataFetchInfo,
|
|
4
|
+
initDataFetchMap,
|
|
5
|
+
getDataFetchItem,
|
|
6
|
+
getDataFetchMap,
|
|
7
|
+
isCSROnly,
|
|
8
|
+
getDataFetchMapKey,
|
|
9
|
+
isDataLoaderExpose,
|
|
10
|
+
loadDataFetchModule,
|
|
11
|
+
isServerEnv,
|
|
12
|
+
} from '../utils';
|
|
13
|
+
import logger from '../logger';
|
|
14
|
+
import {
|
|
15
|
+
MF_DATA_FETCH_TYPE,
|
|
16
|
+
MF_DATA_FETCH_STATUS,
|
|
17
|
+
DATA_FETCH_CLIENT_SUFFIX,
|
|
18
|
+
} from '../constant';
|
|
19
|
+
|
|
20
|
+
import type { MF_DATA_FETCH_MAP_VALUE } from '../types';
|
|
21
|
+
import type { FederationRuntimePlugin } from '@module-federation/runtime';
|
|
22
|
+
|
|
23
|
+
const autoFetchData: () => FederationRuntimePlugin = () => {
|
|
24
|
+
initDataFetchMap();
|
|
25
|
+
injectDataFetch();
|
|
26
|
+
return {
|
|
27
|
+
name: 'auto-fetch-data-plugin',
|
|
28
|
+
afterLoadSnapshot(args) {
|
|
29
|
+
const { id, moduleInfo, remoteSnapshot, host } = args;
|
|
30
|
+
if (typeof id === 'string' && isDataLoaderExpose(id)) {
|
|
31
|
+
return args;
|
|
32
|
+
}
|
|
33
|
+
if (!remoteSnapshot || !id || !('modules' in remoteSnapshot)) {
|
|
34
|
+
return args;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const { name, alias } = moduleInfo;
|
|
38
|
+
const dataFetchInfo = getDataFetchInfo({
|
|
39
|
+
name,
|
|
40
|
+
alias,
|
|
41
|
+
id,
|
|
42
|
+
remoteSnapshot,
|
|
43
|
+
});
|
|
44
|
+
if (!dataFetchInfo) {
|
|
45
|
+
return args;
|
|
46
|
+
}
|
|
47
|
+
const { dataFetchId, dataFetchName } = dataFetchInfo;
|
|
48
|
+
|
|
49
|
+
const dataFetchMapKey = getDataFetchMapKey(dataFetchInfo, {
|
|
50
|
+
name: host.name,
|
|
51
|
+
version: host.options.version,
|
|
52
|
+
});
|
|
53
|
+
logger.debug(
|
|
54
|
+
'======= auto fetch plugin dataFetchMapKey: ',
|
|
55
|
+
dataFetchMapKey,
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
if (!dataFetchMapKey) {
|
|
59
|
+
return args;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const dataFetchItem = getDataFetchItem(dataFetchMapKey);
|
|
63
|
+
if (dataFetchItem) {
|
|
64
|
+
return args;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const dataFetchMap = getDataFetchMap();
|
|
68
|
+
const hasSSRAsset = Boolean(remoteSnapshot.ssrRemoteEntry);
|
|
69
|
+
const hasDataFetchClient = Boolean(
|
|
70
|
+
remoteSnapshot.modules.find(
|
|
71
|
+
(module) =>
|
|
72
|
+
module.moduleName === `${dataFetchName}${DATA_FETCH_CLIENT_SUFFIX}`,
|
|
73
|
+
),
|
|
74
|
+
);
|
|
75
|
+
const downgradeType = hasDataFetchClient
|
|
76
|
+
? MF_DATA_FETCH_TYPE.FETCH_CLIENT
|
|
77
|
+
: hasSSRAsset
|
|
78
|
+
? MF_DATA_FETCH_TYPE.FETCH_SERVER
|
|
79
|
+
: MF_DATA_FETCH_TYPE.FETCH_CLIENT;
|
|
80
|
+
let finalDataFetchId = dataFetchId;
|
|
81
|
+
|
|
82
|
+
if (!isServerEnv()) {
|
|
83
|
+
finalDataFetchId =
|
|
84
|
+
downgradeType === MF_DATA_FETCH_TYPE.FETCH_CLIENT
|
|
85
|
+
? hasDataFetchClient
|
|
86
|
+
? `${dataFetchId}${DATA_FETCH_CLIENT_SUFFIX}`
|
|
87
|
+
: dataFetchId
|
|
88
|
+
: dataFetchId;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const getDataFetchGetter = () =>
|
|
92
|
+
loadDataFetchModule(host, finalDataFetchId);
|
|
93
|
+
|
|
94
|
+
const dataFetchFnItem: MF_DATA_FETCH_MAP_VALUE[0] = [
|
|
95
|
+
getDataFetchGetter,
|
|
96
|
+
downgradeType,
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
// server client must execute
|
|
100
|
+
if (typeof window === 'undefined' || isCSROnly()) {
|
|
101
|
+
dataFetchFnItem.push(getDataFetchGetter());
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
dataFetchMap[dataFetchMapKey] = [
|
|
105
|
+
dataFetchFnItem,
|
|
106
|
+
undefined,
|
|
107
|
+
MF_DATA_FETCH_STATUS.AWAIT,
|
|
108
|
+
];
|
|
109
|
+
|
|
110
|
+
return args;
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export default autoFetchData;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import autoFetchDataPlugin from './data-fetch/runtime-plugin';
|
|
2
|
+
|
|
3
|
+
export { ERROR_TYPE } from './constant';
|
|
4
|
+
export type { DataFetchParams, NoSSRRemoteInfo } from './types';
|
|
5
|
+
export type {
|
|
6
|
+
CreateLazyComponentOptions,
|
|
7
|
+
IProps as CollectSSRAssetsOptions,
|
|
8
|
+
} from './createLazyComponent';
|
|
9
|
+
|
|
10
|
+
export { createLazyComponent, collectSSRAssets } from './createLazyComponent';
|
|
11
|
+
|
|
12
|
+
// export { wrapNoSSR } from './wrapNoSSR';
|
|
13
|
+
|
|
14
|
+
export {
|
|
15
|
+
injectDataFetch,
|
|
16
|
+
callDataFetch,
|
|
17
|
+
CacheSize,
|
|
18
|
+
CacheTime,
|
|
19
|
+
configureCache,
|
|
20
|
+
generateKey,
|
|
21
|
+
cache,
|
|
22
|
+
revalidateTag,
|
|
23
|
+
clearStore,
|
|
24
|
+
prefetch,
|
|
25
|
+
} from './data-fetch';
|
|
26
|
+
|
|
27
|
+
export { setSSREnv } from './utils';
|
|
28
|
+
|
|
29
|
+
export { autoFetchDataPlugin };
|
|
30
|
+
|
|
31
|
+
export type {
|
|
32
|
+
CacheStatus,
|
|
33
|
+
CacheStatsInfo,
|
|
34
|
+
PrefetchOptions,
|
|
35
|
+
} from './data-fetch';
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { MF_DATA_FETCH_TYPE, MF_DATA_FETCH_STATUS } from './constant';
|
|
2
|
+
import type { LRUCache } from 'lru-cache';
|
|
3
|
+
|
|
4
|
+
export type dataFetchFunctionOptions = [
|
|
5
|
+
id?: string,
|
|
6
|
+
data?: unknown,
|
|
7
|
+
downgrade?: boolean,
|
|
8
|
+
];
|
|
9
|
+
|
|
10
|
+
declare global {
|
|
11
|
+
var _mfSSRDowngrade: string[] | true | undefined;
|
|
12
|
+
var __MF_DATA_FETCH_MAP__: MF_DATA_FETCH_MAP | undefined;
|
|
13
|
+
var FEDERATION_SERVER_QUERY: Record<string, unknown> | undefined;
|
|
14
|
+
var FEDERATION_SSR: boolean | undefined;
|
|
15
|
+
var _mfFSHref: string | undefined;
|
|
16
|
+
var _mfDataFetch: Array<dataFetchFunctionOptions> | undefined;
|
|
17
|
+
var __MF_DATA_FETCH_CACHE__: MF_DATA_FETCH_CACHE | undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface CacheItem<T> {
|
|
21
|
+
data: T;
|
|
22
|
+
timestamp: number;
|
|
23
|
+
isRevalidating?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type DataFetchParams = {
|
|
27
|
+
isDowngrade: boolean;
|
|
28
|
+
_id?: string;
|
|
29
|
+
} & Record<string, unknown>;
|
|
30
|
+
export type DataFetch<T> = (params: DataFetchParams) => Promise<T>;
|
|
31
|
+
|
|
32
|
+
// loading, resolve, reject
|
|
33
|
+
export type MF_DATA_FETCH_MAP_VALUE_PROMISE_SET = [
|
|
34
|
+
Promise<unknown>,
|
|
35
|
+
((data: unknown) => void)?,
|
|
36
|
+
((err: unknown) => void)?,
|
|
37
|
+
];
|
|
38
|
+
export type MF_DATA_FETCH_MAP_VALUE = [
|
|
39
|
+
[
|
|
40
|
+
// getDataFetchGetter
|
|
41
|
+
() => Promise<DataFetch<unknown>>,
|
|
42
|
+
// data fetch type
|
|
43
|
+
MF_DATA_FETCH_TYPE,
|
|
44
|
+
// getDataFetchPromise
|
|
45
|
+
Promise<DataFetch<unknown>>?,
|
|
46
|
+
],
|
|
47
|
+
MF_DATA_FETCH_MAP_VALUE_PROMISE_SET?,
|
|
48
|
+
MF_DATA_FETCH_STATUS?,
|
|
49
|
+
];
|
|
50
|
+
export type MF_DATA_FETCH_MAP = Record<string, MF_DATA_FETCH_MAP_VALUE>;
|
|
51
|
+
export type MF_SSR_DOWNGRADE = string[] | true | undefined;
|
|
52
|
+
|
|
53
|
+
export type NoSSRRemoteInfo = {
|
|
54
|
+
name: string;
|
|
55
|
+
version: string;
|
|
56
|
+
ssrPublicPath: string;
|
|
57
|
+
ssrRemoteEntry: string;
|
|
58
|
+
globalName: string;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export interface CacheConfig {
|
|
62
|
+
maxSize?: number;
|
|
63
|
+
unstable_shouldDisable?: ({
|
|
64
|
+
request,
|
|
65
|
+
}: {
|
|
66
|
+
request: Request;
|
|
67
|
+
}) => boolean | Promise<boolean>;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// key is the id, default is the same with dataFetchMapId, but it can be custom by passing dataFetchParams.id
|
|
71
|
+
export type MF_DATA_FETCH_CACHE = {
|
|
72
|
+
cacheStore?: LRUCache<string, Map<string, CacheItem<any>>>;
|
|
73
|
+
tagKeyMap?: Map<string, Set<string>>;
|
|
74
|
+
cacheConfig?: CacheConfig;
|
|
75
|
+
};
|