@module-federation/data-prefetch 0.0.0-next-20240617071542 → 0.0.0-next-20240702090143
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 +6 -5
- package/README.md +1 -2
- package/__tests__/babel.spec.ts +75 -0
- package/__tests__/prefetch.spec.ts +127 -0
- package/__tests__/react.spec.ts +109 -0
- package/dist/cli/index.js +17 -36
- package/dist/esm/{chunk-BN3GGCE5.js → chunk-WLE6YNDH.js} +46 -14
- package/dist/esm/cli/index.js +18 -38
- package/dist/esm/index.js +1 -1
- package/dist/esm/plugin.js +1 -1
- package/dist/esm/react/index.js +2 -2
- package/dist/esm/shared/index.js +25 -0
- package/dist/index.js +46 -14
- package/dist/plugin.js +46 -14
- package/dist/react/index.js +2 -2
- package/dist/shared/index.d.ts +5 -0
- package/dist/shared/index.js +48 -0
- package/package.json +11 -3
- package/src/cli/babel.ts +93 -0
- package/src/cli/index.ts +98 -0
- package/src/common/constant.ts +1 -0
- package/src/common/index.ts +1 -0
- package/src/common/node-utils.ts +24 -0
- package/src/common/runtime-utils.ts +35 -0
- package/src/index.ts +2 -0
- package/src/logger/index.ts +3 -0
- package/src/plugin.ts +210 -0
- package/src/prefetch.ts +210 -0
- package/src/react/hooks.ts +95 -0
- package/src/react/index.ts +1 -0
- package/src/react/utils.ts +11 -0
- package/src/shared/index.ts +26 -0
- package/src/universal/index.ts +27 -0
- package/tsup.config.ts +35 -0
package/dist/index.js
CHANGED
|
@@ -192,6 +192,7 @@ var MFDataPrefetch = class {
|
|
|
192
192
|
};
|
|
193
193
|
|
|
194
194
|
// src/plugin.ts
|
|
195
|
+
var import_runtime2 = require("@module-federation/runtime");
|
|
195
196
|
var import_sdk4 = require("@module-federation/sdk");
|
|
196
197
|
|
|
197
198
|
// src/logger/index.ts
|
|
@@ -200,9 +201,11 @@ var logger_default = new import_sdk3.Logger("[Module Federation Data Prefetch]")
|
|
|
200
201
|
|
|
201
202
|
// src/plugin.ts
|
|
202
203
|
var loadingArray = [];
|
|
204
|
+
var strategy = "loaded-first";
|
|
205
|
+
var sharedFlag = strategy;
|
|
203
206
|
var prefetchPlugin = () => ({
|
|
204
207
|
name: "data-prefetch-runtime-plugin",
|
|
205
|
-
|
|
208
|
+
initContainer(options) {
|
|
206
209
|
const { remoteSnapshot, remoteInfo, id, origin } = options;
|
|
207
210
|
const snapshot = remoteSnapshot;
|
|
208
211
|
const { name } = remoteInfo;
|
|
@@ -216,6 +219,11 @@ var prefetchPlugin = () => ({
|
|
|
216
219
|
if (!signal) {
|
|
217
220
|
return options;
|
|
218
221
|
}
|
|
222
|
+
if (sharedFlag !== strategy) {
|
|
223
|
+
throw new Error(
|
|
224
|
+
`[Module Federation Data Prefetch]: If you want to use data prefetch, the shared strategy must be 'loaded-first'`
|
|
225
|
+
);
|
|
226
|
+
}
|
|
219
227
|
const instance = MFDataPrefetch.getInstance(name) || new MFDataPrefetch(prefetchOptions);
|
|
220
228
|
let prefetchUrl;
|
|
221
229
|
if (snapshot.prefetchEntry) {
|
|
@@ -230,20 +238,22 @@ var prefetchPlugin = () => ({
|
|
|
230
238
|
if (projectExports instanceof Promise) {
|
|
231
239
|
await projectExports;
|
|
232
240
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
|
|
241
|
+
return Promise.resolve().then(() => {
|
|
242
|
+
const exports = instance.getExposeExports(id);
|
|
243
|
+
logger_default.info(`1. Start Prefetch: ${id} - ${performance.now()}`);
|
|
244
|
+
const result = Object.keys(exports).map((k) => {
|
|
245
|
+
const value = instance.prefetch({
|
|
246
|
+
id,
|
|
247
|
+
functionId: k
|
|
248
|
+
});
|
|
249
|
+
const functionId = k;
|
|
250
|
+
return {
|
|
251
|
+
value,
|
|
252
|
+
functionId
|
|
253
|
+
};
|
|
239
254
|
});
|
|
240
|
-
|
|
241
|
-
return {
|
|
242
|
-
value,
|
|
243
|
-
functionId
|
|
244
|
-
};
|
|
255
|
+
return result;
|
|
245
256
|
});
|
|
246
|
-
return result;
|
|
247
257
|
});
|
|
248
258
|
loadingArray.push({
|
|
249
259
|
id,
|
|
@@ -291,12 +301,29 @@ var prefetchPlugin = () => ({
|
|
|
291
301
|
return options;
|
|
292
302
|
}
|
|
293
303
|
const promise = instance.loadEntry(prefetchUrl).then(async () => {
|
|
304
|
+
let module2 = origin.moduleCache.get(remote.name);
|
|
305
|
+
const moduleOptions = {
|
|
306
|
+
host: origin,
|
|
307
|
+
remoteInfo: remote
|
|
308
|
+
};
|
|
309
|
+
if (!module2) {
|
|
310
|
+
module2 = new import_runtime2.Module(moduleOptions);
|
|
311
|
+
origin.moduleCache.set(remote.name, module2);
|
|
312
|
+
}
|
|
313
|
+
const idPart = id.split("/");
|
|
314
|
+
let expose = idPart[idPart.length - 1];
|
|
315
|
+
if (expose !== ".") {
|
|
316
|
+
expose = `./${expose}`;
|
|
317
|
+
}
|
|
318
|
+
await module2.get(id, expose, {}, remoteSnapshot);
|
|
294
319
|
const projectExports = instance.getProjectExports();
|
|
295
320
|
if (projectExports instanceof Promise) {
|
|
296
321
|
await projectExports;
|
|
297
322
|
}
|
|
298
323
|
const exports = instance.getExposeExports(id);
|
|
299
|
-
logger_default.info(
|
|
324
|
+
logger_default.info(
|
|
325
|
+
`1. PreloadRemote Start Prefetch: ${id} - ${performance.now()}`
|
|
326
|
+
);
|
|
300
327
|
const result = Object.keys(exports).map((k) => {
|
|
301
328
|
const value = instance.prefetch({
|
|
302
329
|
id,
|
|
@@ -315,6 +342,11 @@ var prefetchPlugin = () => ({
|
|
|
315
342
|
promise
|
|
316
343
|
});
|
|
317
344
|
return options;
|
|
345
|
+
},
|
|
346
|
+
beforeLoadShare(options) {
|
|
347
|
+
const shareInfo = options.shareInfo;
|
|
348
|
+
sharedFlag = (shareInfo == null ? void 0 : shareInfo.strategy) || sharedFlag;
|
|
349
|
+
return options;
|
|
318
350
|
}
|
|
319
351
|
});
|
|
320
352
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/plugin.js
CHANGED
|
@@ -24,6 +24,7 @@ __export(plugin_exports, {
|
|
|
24
24
|
prefetchPlugin: () => prefetchPlugin
|
|
25
25
|
});
|
|
26
26
|
module.exports = __toCommonJS(plugin_exports);
|
|
27
|
+
var import_runtime2 = require("@module-federation/runtime");
|
|
27
28
|
var import_sdk4 = require("@module-federation/sdk");
|
|
28
29
|
|
|
29
30
|
// src/common/runtime-utils.ts
|
|
@@ -196,9 +197,11 @@ var logger_default = new import_sdk3.Logger("[Module Federation Data Prefetch]")
|
|
|
196
197
|
|
|
197
198
|
// src/plugin.ts
|
|
198
199
|
var loadingArray = [];
|
|
200
|
+
var strategy = "loaded-first";
|
|
201
|
+
var sharedFlag = strategy;
|
|
199
202
|
var prefetchPlugin = () => ({
|
|
200
203
|
name: "data-prefetch-runtime-plugin",
|
|
201
|
-
|
|
204
|
+
initContainer(options) {
|
|
202
205
|
const { remoteSnapshot, remoteInfo, id, origin } = options;
|
|
203
206
|
const snapshot = remoteSnapshot;
|
|
204
207
|
const { name } = remoteInfo;
|
|
@@ -212,6 +215,11 @@ var prefetchPlugin = () => ({
|
|
|
212
215
|
if (!signal) {
|
|
213
216
|
return options;
|
|
214
217
|
}
|
|
218
|
+
if (sharedFlag !== strategy) {
|
|
219
|
+
throw new Error(
|
|
220
|
+
`[Module Federation Data Prefetch]: If you want to use data prefetch, the shared strategy must be 'loaded-first'`
|
|
221
|
+
);
|
|
222
|
+
}
|
|
215
223
|
const instance = MFDataPrefetch.getInstance(name) || new MFDataPrefetch(prefetchOptions);
|
|
216
224
|
let prefetchUrl;
|
|
217
225
|
if (snapshot.prefetchEntry) {
|
|
@@ -226,20 +234,22 @@ var prefetchPlugin = () => ({
|
|
|
226
234
|
if (projectExports instanceof Promise) {
|
|
227
235
|
await projectExports;
|
|
228
236
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
237
|
+
return Promise.resolve().then(() => {
|
|
238
|
+
const exports = instance.getExposeExports(id);
|
|
239
|
+
logger_default.info(`1. Start Prefetch: ${id} - ${performance.now()}`);
|
|
240
|
+
const result = Object.keys(exports).map((k) => {
|
|
241
|
+
const value = instance.prefetch({
|
|
242
|
+
id,
|
|
243
|
+
functionId: k
|
|
244
|
+
});
|
|
245
|
+
const functionId = k;
|
|
246
|
+
return {
|
|
247
|
+
value,
|
|
248
|
+
functionId
|
|
249
|
+
};
|
|
235
250
|
});
|
|
236
|
-
|
|
237
|
-
return {
|
|
238
|
-
value,
|
|
239
|
-
functionId
|
|
240
|
-
};
|
|
251
|
+
return result;
|
|
241
252
|
});
|
|
242
|
-
return result;
|
|
243
253
|
});
|
|
244
254
|
loadingArray.push({
|
|
245
255
|
id,
|
|
@@ -287,12 +297,29 @@ var prefetchPlugin = () => ({
|
|
|
287
297
|
return options;
|
|
288
298
|
}
|
|
289
299
|
const promise = instance.loadEntry(prefetchUrl).then(async () => {
|
|
300
|
+
let module2 = origin.moduleCache.get(remote.name);
|
|
301
|
+
const moduleOptions = {
|
|
302
|
+
host: origin,
|
|
303
|
+
remoteInfo: remote
|
|
304
|
+
};
|
|
305
|
+
if (!module2) {
|
|
306
|
+
module2 = new import_runtime2.Module(moduleOptions);
|
|
307
|
+
origin.moduleCache.set(remote.name, module2);
|
|
308
|
+
}
|
|
309
|
+
const idPart = id.split("/");
|
|
310
|
+
let expose = idPart[idPart.length - 1];
|
|
311
|
+
if (expose !== ".") {
|
|
312
|
+
expose = `./${expose}`;
|
|
313
|
+
}
|
|
314
|
+
await module2.get(id, expose, {}, remoteSnapshot);
|
|
290
315
|
const projectExports = instance.getProjectExports();
|
|
291
316
|
if (projectExports instanceof Promise) {
|
|
292
317
|
await projectExports;
|
|
293
318
|
}
|
|
294
319
|
const exports = instance.getExposeExports(id);
|
|
295
|
-
logger_default.info(
|
|
320
|
+
logger_default.info(
|
|
321
|
+
`1. PreloadRemote Start Prefetch: ${id} - ${performance.now()}`
|
|
322
|
+
);
|
|
296
323
|
const result = Object.keys(exports).map((k) => {
|
|
297
324
|
const value = instance.prefetch({
|
|
298
325
|
id,
|
|
@@ -311,6 +338,11 @@ var prefetchPlugin = () => ({
|
|
|
311
338
|
promise
|
|
312
339
|
});
|
|
313
340
|
return options;
|
|
341
|
+
},
|
|
342
|
+
beforeLoadShare(options) {
|
|
343
|
+
const shareInfo = options.shareInfo;
|
|
344
|
+
sharedFlag = (shareInfo == null ? void 0 : shareInfo.strategy) || sharedFlag;
|
|
345
|
+
return options;
|
|
314
346
|
}
|
|
315
347
|
});
|
|
316
348
|
var plugin_default = prefetchPlugin;
|
package/dist/react/index.js
CHANGED
|
@@ -231,7 +231,7 @@ var usePrefetch = (options) => {
|
|
|
231
231
|
if (isFirstMounted) {
|
|
232
232
|
const startTiming = performance.now();
|
|
233
233
|
logger_default.info(
|
|
234
|
-
`2. Start Get Prefetch Data: ${options.id} - ${options.functionId} - ${startTiming}`
|
|
234
|
+
`2. Start Get Prefetch Data: ${options.id} - ${options.functionId || "default"} - ${startTiming}`
|
|
235
235
|
);
|
|
236
236
|
}
|
|
237
237
|
const { id, functionId, deferId } = options;
|
|
@@ -260,7 +260,7 @@ var usePrefetch = (options) => {
|
|
|
260
260
|
(0, import_react2.useEffect)(() => {
|
|
261
261
|
const useEffectTiming = performance.now();
|
|
262
262
|
logger_default.info(
|
|
263
|
-
`3. Start Execute UseEffect: ${options.id} - ${options.functionId} - ${useEffectTiming}`
|
|
263
|
+
`3. Start Execute UseEffect: ${options.id} - ${options.functionId || "default"} - ${useEffectTiming}`
|
|
264
264
|
);
|
|
265
265
|
return () => {
|
|
266
266
|
prefetchInstance == null ? void 0 : prefetchInstance.markOutdate(prefetchInfo, true);
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/shared/index.ts
|
|
21
|
+
var shared_exports = {};
|
|
22
|
+
__export(shared_exports, {
|
|
23
|
+
default: () => shared_default
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(shared_exports);
|
|
26
|
+
var sharedStrategy = () => ({
|
|
27
|
+
name: "shared-strategy",
|
|
28
|
+
beforeInit(args) {
|
|
29
|
+
const { userOptions } = args;
|
|
30
|
+
const shared = userOptions.shared;
|
|
31
|
+
if (shared) {
|
|
32
|
+
Object.keys(shared).forEach((sharedKey) => {
|
|
33
|
+
const sharedConfigs = shared[sharedKey];
|
|
34
|
+
const arraySharedConfigs = Array.isArray(sharedConfigs) ? sharedConfigs : [sharedConfigs];
|
|
35
|
+
arraySharedConfigs.forEach((s) => {
|
|
36
|
+
s.strategy = "loaded-first";
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
console.warn(
|
|
40
|
+
`[Module Federation Data Prefetch]: Your shared strategy is set to 'loaded-first', this is a necessary condition for data prefetch`
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
return args;
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
var shared_default = sharedStrategy;
|
|
47
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
48
|
+
0 && (module.exports = {});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@module-federation/data-prefetch",
|
|
3
3
|
"description": "Module Federation Data Prefetch",
|
|
4
|
-
"version": "0.0.0-next-
|
|
4
|
+
"version": "0.0.0-next-20240702090143",
|
|
5
5
|
"author": "nieyan <nyqykk@foxmail.com>",
|
|
6
6
|
"homepage": "https://github.com/module-federation/core",
|
|
7
7
|
"license": "MIT",
|
|
@@ -33,6 +33,11 @@
|
|
|
33
33
|
"import": "./dist/esm/universal/index.js",
|
|
34
34
|
"require": "./dist/universal/index.js",
|
|
35
35
|
"types": "./dist/universal/index.d.ts"
|
|
36
|
+
},
|
|
37
|
+
"./shared": {
|
|
38
|
+
"import": "./dist/esm/shared/index.js",
|
|
39
|
+
"require": "./dist/shared/index.js",
|
|
40
|
+
"types": "./dist/shared/index.d.ts"
|
|
36
41
|
}
|
|
37
42
|
},
|
|
38
43
|
"typesVersions": {
|
|
@@ -48,6 +53,9 @@
|
|
|
48
53
|
],
|
|
49
54
|
"universal": [
|
|
50
55
|
"./dist/universal/index.d.ts"
|
|
56
|
+
],
|
|
57
|
+
"shared": [
|
|
58
|
+
"./dist/shared/index.d.ts"
|
|
51
59
|
]
|
|
52
60
|
}
|
|
53
61
|
},
|
|
@@ -77,8 +85,8 @@
|
|
|
77
85
|
},
|
|
78
86
|
"dependencies": {
|
|
79
87
|
"fs-extra": "9.1.0",
|
|
80
|
-
"@module-federation/sdk": "0.0.0-next-
|
|
81
|
-
"@module-federation/runtime": "0.0.0-next-
|
|
88
|
+
"@module-federation/sdk": "0.0.0-next-20240702090143",
|
|
89
|
+
"@module-federation/runtime": "0.0.0-next-20240702090143"
|
|
82
90
|
},
|
|
83
91
|
"scripts": {
|
|
84
92
|
"dev": "cross-env WATCH=true tsup",
|
package/src/cli/babel.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import type { moduleFederationPlugin } from '@module-federation/sdk';
|
|
3
|
+
|
|
4
|
+
const attribute = 'id';
|
|
5
|
+
const hookId = 'usePrefetch';
|
|
6
|
+
const importPackage = '@module-federation/data-prefetch/react';
|
|
7
|
+
|
|
8
|
+
interface BabelPluginOptions {
|
|
9
|
+
hook_id: string;
|
|
10
|
+
import_pkg: string;
|
|
11
|
+
attribute: string;
|
|
12
|
+
mf_name: string;
|
|
13
|
+
exposes: moduleFederationPlugin.ModuleFederationPluginOptions['exposes'];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
17
|
+
export default (babel: { types: any }, options: BabelPluginOptions) => {
|
|
18
|
+
const t = babel.types;
|
|
19
|
+
let shouldHandle = false;
|
|
20
|
+
let scope = '';
|
|
21
|
+
const { mf_name: name, exposes } = options;
|
|
22
|
+
if (!exposes) {
|
|
23
|
+
return {};
|
|
24
|
+
}
|
|
25
|
+
const exposesKey = Object.keys(exposes);
|
|
26
|
+
const processedExposes = exposesKey.map((expose) => ({
|
|
27
|
+
key: expose.replace('.', ''),
|
|
28
|
+
value: path.resolve(
|
|
29
|
+
// @ts-ignore
|
|
30
|
+
typeof exposes[expose] === 'string'
|
|
31
|
+
? // @ts-ignore
|
|
32
|
+
exposes[expose]
|
|
33
|
+
: // @ts-ignore
|
|
34
|
+
exposes[expose].import,
|
|
35
|
+
),
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
visitor: {
|
|
40
|
+
ImportDeclaration(
|
|
41
|
+
nodePath: {
|
|
42
|
+
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
43
|
+
node: { source: { value: any }; specifiers: any };
|
|
44
|
+
},
|
|
45
|
+
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
46
|
+
state: { file: { opts: { filename: any } } },
|
|
47
|
+
) {
|
|
48
|
+
const source = nodePath.node.source.value;
|
|
49
|
+
const { specifiers } = nodePath.node;
|
|
50
|
+
const { filename } = state.file.opts;
|
|
51
|
+
|
|
52
|
+
if (source === importPackage) {
|
|
53
|
+
shouldHandle = specifiers.some(
|
|
54
|
+
(specifier: { imported: { name: string } }) =>
|
|
55
|
+
specifier.imported &&
|
|
56
|
+
specifier.imported.name === hookId &&
|
|
57
|
+
processedExposes.find(
|
|
58
|
+
// biome-ignore lint/suspicious/noAssignInExpressions: <explanation>
|
|
59
|
+
(expose) => expose.value === filename && (scope = expose.key),
|
|
60
|
+
),
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
CallExpression(nodePath: {
|
|
66
|
+
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
67
|
+
node: { callee: any; arguments: string | any[] };
|
|
68
|
+
}) {
|
|
69
|
+
if (
|
|
70
|
+
shouldHandle &&
|
|
71
|
+
t.isIdentifier(nodePath.node.callee, { name: hookId }) &&
|
|
72
|
+
nodePath.node.arguments.length > 0
|
|
73
|
+
) {
|
|
74
|
+
const objectExpression = nodePath.node.arguments[0];
|
|
75
|
+
if (
|
|
76
|
+
objectExpression &&
|
|
77
|
+
t.isObjectExpression(objectExpression) &&
|
|
78
|
+
!objectExpression.properties.find(
|
|
79
|
+
(p: { key: { name: string } }) => p.key.name === attribute,
|
|
80
|
+
)
|
|
81
|
+
) {
|
|
82
|
+
objectExpression.properties.push(
|
|
83
|
+
t.objectProperty(
|
|
84
|
+
t.identifier(attribute),
|
|
85
|
+
t.stringLiteral(name + scope),
|
|
86
|
+
),
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
};
|
package/src/cli/index.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
encodeName,
|
|
6
|
+
moduleFederationPlugin,
|
|
7
|
+
MFPrefetchCommon,
|
|
8
|
+
} from '@module-federation/sdk';
|
|
9
|
+
import type { Compiler, WebpackPluginInstance } from 'webpack';
|
|
10
|
+
|
|
11
|
+
import { TEMP_DIR } from '../common/constant';
|
|
12
|
+
import { fileExistsWithCaseSync, fixPrefetchPath } from '../common/node-utils';
|
|
13
|
+
import { getPrefetchId } from '../common/runtime-utils';
|
|
14
|
+
|
|
15
|
+
export class PrefetchPlugin implements WebpackPluginInstance {
|
|
16
|
+
public options: moduleFederationPlugin.ModuleFederationPluginOptions;
|
|
17
|
+
private _reWriteExports: string;
|
|
18
|
+
|
|
19
|
+
constructor(options: moduleFederationPlugin.ModuleFederationPluginOptions) {
|
|
20
|
+
this.options = options;
|
|
21
|
+
this._reWriteExports = '';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// eslint-disable-next-line max-lines-per-function
|
|
25
|
+
apply(compiler: Compiler) {
|
|
26
|
+
const { name, exposes } = this.options;
|
|
27
|
+
if (!exposes) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (!compiler.options.context) {
|
|
31
|
+
throw new Error('compiler.options.context is not defined');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const { runtimePlugins } = this.options;
|
|
35
|
+
if (!Array.isArray(runtimePlugins)) {
|
|
36
|
+
this.options.runtimePlugins = [];
|
|
37
|
+
}
|
|
38
|
+
this.options.runtimePlugins!.push(
|
|
39
|
+
path.resolve(__dirname, '../esm/plugin.js'),
|
|
40
|
+
);
|
|
41
|
+
this.options.runtimePlugins!.push(
|
|
42
|
+
path.resolve(__dirname, '../esm/shared/index.js'),
|
|
43
|
+
);
|
|
44
|
+
if (!this.options.dataPrefetch) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const prefetchs: Array<string> = [];
|
|
49
|
+
const exposeAlias = Object.keys(exposes);
|
|
50
|
+
exposeAlias.forEach((alias) => {
|
|
51
|
+
let exposePath;
|
|
52
|
+
// @ts-ignore
|
|
53
|
+
const exposeValue = exposes[alias];
|
|
54
|
+
if (typeof exposeValue === 'string') {
|
|
55
|
+
exposePath = exposeValue;
|
|
56
|
+
} else {
|
|
57
|
+
exposePath = exposeValue.import[0];
|
|
58
|
+
}
|
|
59
|
+
const targetPaths = fixPrefetchPath(exposePath);
|
|
60
|
+
for (const pathItem of targetPaths) {
|
|
61
|
+
const absolutePath = path.resolve(compiler.options.context!, pathItem);
|
|
62
|
+
if (fileExistsWithCaseSync(absolutePath)) {
|
|
63
|
+
prefetchs.push(pathItem);
|
|
64
|
+
const absoluteAlias = alias.replace('.', '');
|
|
65
|
+
this._reWriteExports += `export * as ${getPrefetchId(
|
|
66
|
+
`${name}${absoluteAlias}`,
|
|
67
|
+
)} from '${absolutePath}';\n`;
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
if (!this._reWriteExports) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const encodedName = encodeName(name as string);
|
|
78
|
+
const asyncEntryPath = path.resolve(
|
|
79
|
+
compiler.options.context,
|
|
80
|
+
`node_modules/${TEMP_DIR}/${encodedName}/bootstrap.js`,
|
|
81
|
+
);
|
|
82
|
+
const tempDirRealPath = path.resolve(
|
|
83
|
+
compiler.options.context,
|
|
84
|
+
'node_modules',
|
|
85
|
+
TEMP_DIR,
|
|
86
|
+
);
|
|
87
|
+
if (!fs.existsSync(tempDirRealPath)) {
|
|
88
|
+
fs.mkdirSync(tempDirRealPath);
|
|
89
|
+
}
|
|
90
|
+
if (!fs.existsSync(`${tempDirRealPath}/${encodedName}`)) {
|
|
91
|
+
fs.mkdirSync(`${tempDirRealPath}/${encodedName}`);
|
|
92
|
+
}
|
|
93
|
+
fs.writeFileSync(asyncEntryPath, this._reWriteExports);
|
|
94
|
+
new compiler.webpack.DefinePlugin({
|
|
95
|
+
FederationDataPrefetch: JSON.stringify(asyncEntryPath),
|
|
96
|
+
}).apply(compiler);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const TEMP_DIR = '.mf';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './constant';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
|
|
4
|
+
export const fileExistsWithCaseSync = (filepath: string): boolean => {
|
|
5
|
+
const dir = path.dirname(filepath);
|
|
6
|
+
if (filepath === '/' || filepath === '.') {
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
const filenames = fs.readdirSync(dir);
|
|
10
|
+
if (filenames.indexOf(path.basename(filepath)) === -1) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
return fileExistsWithCaseSync(dir);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const fixPrefetchPath = (exposePath: string): Array<string> => {
|
|
17
|
+
const pathExt = ['.js', '.ts'];
|
|
18
|
+
const extReg = /\.(ts|js|tsx|jsx)$/;
|
|
19
|
+
if (extReg.test(exposePath)) {
|
|
20
|
+
return pathExt.map((ext) => exposePath.replace(extReg, `.prefetch${ext}`));
|
|
21
|
+
} else {
|
|
22
|
+
return pathExt.map((ext) => exposePath + `.prefetch${ext}`);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import {
|
|
2
|
+
encodeName,
|
|
3
|
+
ModuleInfo,
|
|
4
|
+
MFPrefetchCommon,
|
|
5
|
+
} from '@module-federation/sdk';
|
|
6
|
+
|
|
7
|
+
export const getScope = (id: string): string => {
|
|
8
|
+
const idArray = id.split('/');
|
|
9
|
+
if (idArray.length >= 2) {
|
|
10
|
+
idArray.pop();
|
|
11
|
+
}
|
|
12
|
+
const name = idArray.join('/');
|
|
13
|
+
return name;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const getPrefetchId = (id: string): string =>
|
|
17
|
+
encodeName(`${id}/${MFPrefetchCommon.identifier}`);
|
|
18
|
+
|
|
19
|
+
export const getSignalFromManifest = (remoteSnapshot: ModuleInfo): boolean => {
|
|
20
|
+
if (!remoteSnapshot) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (
|
|
25
|
+
!('prefetchEntry' in remoteSnapshot) &&
|
|
26
|
+
!('prefetchInterface' in remoteSnapshot)
|
|
27
|
+
) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!remoteSnapshot.prefetchEntry && !remoteSnapshot.prefetchInterface) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
return true;
|
|
35
|
+
};
|
package/src/index.ts
ADDED