@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/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
@module-federation/data-prefetch
|
|
2
2
|
|
|
3
|
-
## 0.0.0-next-
|
|
3
|
+
## 0.0.0-next-20240702090143
|
|
4
4
|
|
|
5
5
|
### Patch Changes
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
- Updated dependencies [
|
|
9
|
-
|
|
10
|
-
- @module-federation/sdk@0.0.0-next-
|
|
7
|
+
- 3ddab21: feat(@module-federation/data-prefetch): support data prefetch in Module Federation
|
|
8
|
+
- Updated dependencies [09b792d]
|
|
9
|
+
- Updated dependencies [09b792d]
|
|
10
|
+
- @module-federation/sdk@0.0.0-next-20240702090143
|
|
11
|
+
- @module-federation/runtime@0.0.0-next-20240702090143
|
package/README.md
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
https://vmok.bytedance.net/guide/performance/prefetch.html
|
|
1
|
+
Module Federation Data Prefetch
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/// <reference types="jest" />
|
|
2
|
+
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import { BabelFileResult, transformFileSync } from '@babel/core';
|
|
6
|
+
// @ts-ignore no use
|
|
7
|
+
import type from '@types/jest';
|
|
8
|
+
|
|
9
|
+
import babelPlugin from '../src/cli/babel';
|
|
10
|
+
|
|
11
|
+
const emptyRegexp = /\s+/g;
|
|
12
|
+
describe('Babel Plugin Test', () => {
|
|
13
|
+
const options = {
|
|
14
|
+
name: '@mf/test',
|
|
15
|
+
exposes: { './expose': path.resolve(__dirname, 'test') },
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const testFilePath = path.join(__dirname, './test');
|
|
19
|
+
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
fs.mkdirSync(path.dirname(testFilePath), { recursive: true });
|
|
22
|
+
fs.closeSync(fs.openSync(testFilePath, 'w'));
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
afterEach(() => {
|
|
26
|
+
fs.unlinkSync(testFilePath);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Check if the plugin will add the id parameter when it is not present
|
|
30
|
+
test('it adds id argument to usePrefetch call when it is absent', () => {
|
|
31
|
+
const input = `
|
|
32
|
+
import { usePrefetch } from "@module-federation/data-prefetch/react";
|
|
33
|
+
usePrefetch({});
|
|
34
|
+
`;
|
|
35
|
+
|
|
36
|
+
const expected = `
|
|
37
|
+
import { usePrefetch } from "@module-federation/data-prefetch/react";
|
|
38
|
+
usePrefetch({ id: "@mf/test/expose" });
|
|
39
|
+
`;
|
|
40
|
+
|
|
41
|
+
fs.writeFileSync(testFilePath, input);
|
|
42
|
+
const { code } = transformFileSync(testFilePath, {
|
|
43
|
+
plugins: [[babelPlugin, options]],
|
|
44
|
+
configFile: false,
|
|
45
|
+
}) as BabelFileResult;
|
|
46
|
+
|
|
47
|
+
expect(code?.replace(emptyRegexp, '')).toBe(
|
|
48
|
+
expected.replace(emptyRegexp, ''),
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// The plugin should retain the existing id parameter in the usePrefetch call
|
|
53
|
+
test('it does not overwrite existing id argument in usePrefetch call', () => {
|
|
54
|
+
const input = `
|
|
55
|
+
import { usePrefetch } from "@module-federation/data-prefetch/react";
|
|
56
|
+
usePrefetch({ id: "existingId" });
|
|
57
|
+
`;
|
|
58
|
+
|
|
59
|
+
const expected = `
|
|
60
|
+
import { usePrefetch } from "@module-federation/data-prefetch/react";
|
|
61
|
+
usePrefetch({ id: "existingId" });
|
|
62
|
+
`;
|
|
63
|
+
|
|
64
|
+
fs.writeFileSync(testFilePath, input);
|
|
65
|
+
const { code } = transformFileSync(testFilePath, {
|
|
66
|
+
plugins: [[babelPlugin, options]],
|
|
67
|
+
configFile: false,
|
|
68
|
+
babelrc: false,
|
|
69
|
+
}) as BabelFileResult;
|
|
70
|
+
|
|
71
|
+
expect(code?.replace(emptyRegexp, '')).toBe(
|
|
72
|
+
expected.replace(emptyRegexp, ''),
|
|
73
|
+
);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// Import the necessary modules and functions
|
|
2
|
+
import { MFDataPrefetch } from '../src/prefetch';
|
|
3
|
+
import { loadScript } from '@module-federation/sdk';
|
|
4
|
+
|
|
5
|
+
// Mock loadScript function from SDK
|
|
6
|
+
jest.mock('@module-federation/sdk', () => ({
|
|
7
|
+
loadScript: jest.fn(() => Promise.resolve()),
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
describe('MF Data Prefetch', () => {
|
|
11
|
+
let prefetch: MFDataPrefetch;
|
|
12
|
+
|
|
13
|
+
const options = {
|
|
14
|
+
name: '@mf/test',
|
|
15
|
+
remoteSnapshot: {
|
|
16
|
+
buildVersion: '1.0.0',
|
|
17
|
+
globalName: 'TestGlobalName',
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
globalThis.__FEDERATION__.__PREFETCH__ = {
|
|
23
|
+
entryLoading: {},
|
|
24
|
+
instance: new Map(),
|
|
25
|
+
__PREFETCH_EXPORTS__: {},
|
|
26
|
+
};
|
|
27
|
+
// @ts-ignore
|
|
28
|
+
prefetch = new MFDataPrefetch(options);
|
|
29
|
+
});
|
|
30
|
+
afterAll(() => {
|
|
31
|
+
// @ts-ignore
|
|
32
|
+
delete globalThis.__FEDERATION__;
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Instance gets added to global memory on creation
|
|
36
|
+
it('adds itself to global instances on creation', () => {
|
|
37
|
+
expect(prefetch.global.instance.get(options.name)).toBe(prefetch);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Loads entry script using loadScript from sdk
|
|
41
|
+
it('loads entry script using loadScript from sdk', async () => {
|
|
42
|
+
const url = 'testUrl'; // Url of the script to be loaded
|
|
43
|
+
|
|
44
|
+
await prefetch.loadEntry(url); // Call `loadEntry` function
|
|
45
|
+
|
|
46
|
+
// Expect that the loadScript function is called with the correct url
|
|
47
|
+
expect(loadScript).toHaveBeenCalledWith(url, expect.any(Object));
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Retrieves project exports
|
|
51
|
+
it('gets project exports', async () => {
|
|
52
|
+
const exposeExport = {
|
|
53
|
+
nyPrefetch: () => {},
|
|
54
|
+
};
|
|
55
|
+
const projectExport = {
|
|
56
|
+
[options.name]: exposeExport,
|
|
57
|
+
};
|
|
58
|
+
globalThis.__FEDERATION__.__PREFETCH__.__PREFETCH_EXPORTS__[options.name] =
|
|
59
|
+
Promise.resolve(projectExport);
|
|
60
|
+
|
|
61
|
+
await prefetch.getProjectExports();
|
|
62
|
+
expect(prefetch.getExposeExports(options.name)).toEqual(exposeExport);
|
|
63
|
+
});
|
|
64
|
+
// Prefetching with memory and executing prefetch function
|
|
65
|
+
it('executes prefetch using prefetch function with and without memory', async () => {
|
|
66
|
+
const id = options.name;
|
|
67
|
+
const functionId = 'nyPrefetch';
|
|
68
|
+
const refetchParams = 'testParams';
|
|
69
|
+
const prefetchOptions = { id, functionId, refetchParams };
|
|
70
|
+
|
|
71
|
+
// Creating a mock prefetch function
|
|
72
|
+
const executePrefetch = jest.fn(() => 'Expected Result');
|
|
73
|
+
const prefetchExports = { [functionId]: executePrefetch };
|
|
74
|
+
|
|
75
|
+
// Mock Project Exports
|
|
76
|
+
globalThis.__FEDERATION__.__PREFETCH__.__PREFETCH_EXPORTS__[id] =
|
|
77
|
+
Promise.resolve({
|
|
78
|
+
[id]: prefetchExports,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
await prefetch.getProjectExports();
|
|
82
|
+
// Call the prefetch function first time
|
|
83
|
+
let result = await prefetch.prefetch(prefetchOptions);
|
|
84
|
+
|
|
85
|
+
// Verify that executePrefetch function is correctly executed
|
|
86
|
+
expect(executePrefetch).toHaveBeenCalled();
|
|
87
|
+
|
|
88
|
+
// Clear mock function calls data
|
|
89
|
+
executePrefetch.mockClear();
|
|
90
|
+
|
|
91
|
+
// Call the prefetch function again
|
|
92
|
+
result = await prefetch.prefetch(prefetchOptions);
|
|
93
|
+
|
|
94
|
+
// Verify that executePrefetch function is NOT called this time (since the result should come from memory)
|
|
95
|
+
expect(executePrefetch).not.toHaveBeenCalled();
|
|
96
|
+
|
|
97
|
+
// Clear mock function calls data
|
|
98
|
+
executePrefetch.mockClear();
|
|
99
|
+
|
|
100
|
+
prefetch.markOutdate(prefetchOptions, true);
|
|
101
|
+
|
|
102
|
+
// Call the prefetch function first time
|
|
103
|
+
result = await prefetch.prefetch(prefetchOptions);
|
|
104
|
+
|
|
105
|
+
// Verify that executePrefetch function is correctly executed
|
|
106
|
+
expect(executePrefetch).toHaveBeenCalled();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// Checking outdate marking
|
|
110
|
+
it('checks outdate marking', () => {
|
|
111
|
+
const markOptions = { id: 'testId', functionId: 'testFunction' };
|
|
112
|
+
|
|
113
|
+
// Mark the function as outdated
|
|
114
|
+
prefetch.markOutdate(markOptions, true);
|
|
115
|
+
|
|
116
|
+
// Verify that the function is marked as outdated
|
|
117
|
+
let isOutdated = prefetch.checkOutdate(markOptions);
|
|
118
|
+
expect(isOutdated).toBe(true);
|
|
119
|
+
|
|
120
|
+
// Mark the function as up-to-date
|
|
121
|
+
prefetch.markOutdate(markOptions, false);
|
|
122
|
+
|
|
123
|
+
// Verify that the function is marked as up-to-date
|
|
124
|
+
isOutdated = prefetch.checkOutdate(markOptions);
|
|
125
|
+
expect(isOutdated).toBe(false);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { renderHook, act } from '@testing-library/react-hooks';
|
|
2
|
+
import * as ModuleFederationSDK from '@module-federation/sdk';
|
|
3
|
+
import { usePrefetch } from '../src/react';
|
|
4
|
+
import { MFDataPrefetch } from '../src/prefetch';
|
|
5
|
+
|
|
6
|
+
const mockLoadScript = jest.spyOn(ModuleFederationSDK, 'loadScript');
|
|
7
|
+
mockLoadScript.mockImplementation(() => Promise.resolve());
|
|
8
|
+
|
|
9
|
+
describe('usePrefetch', () => {
|
|
10
|
+
// Mock prefetch function
|
|
11
|
+
global.fetch = jest.fn(() =>
|
|
12
|
+
Promise.resolve({
|
|
13
|
+
ok: true,
|
|
14
|
+
status: 200,
|
|
15
|
+
statusText: 'OK',
|
|
16
|
+
redirected: false,
|
|
17
|
+
type: 'basic',
|
|
18
|
+
url: '',
|
|
19
|
+
clone: jest.fn(),
|
|
20
|
+
headers: new Headers(),
|
|
21
|
+
body: null,
|
|
22
|
+
bodyUsed: false,
|
|
23
|
+
text: () => Promise.resolve(JSON.stringify({ data: 'testData' })),
|
|
24
|
+
json: () => Promise.resolve({ data: 'testData' }),
|
|
25
|
+
formData: () => Promise.resolve(new FormData()),
|
|
26
|
+
arrayBuffer: () => Promise.resolve(new ArrayBuffer(0)),
|
|
27
|
+
blob: () => Promise.resolve(new Blob()),
|
|
28
|
+
}),
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
let prefetch: MFDataPrefetch;
|
|
32
|
+
|
|
33
|
+
const options = {
|
|
34
|
+
// Options that will be passed to the `MFDataPrefetch` constructor
|
|
35
|
+
name: '@mf/test',
|
|
36
|
+
remoteSnapshot: {
|
|
37
|
+
buildVersion: '1.0.0',
|
|
38
|
+
globalName: 'TestGlobalName',
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
const testData = 'testData';
|
|
42
|
+
const newTestData = 'newTestData';
|
|
43
|
+
const functionId = 'testPrefetch';
|
|
44
|
+
const executePrefetch = jest.fn((params: string) =>
|
|
45
|
+
Promise.resolve(params ? params : testData),
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
beforeEach(() => {
|
|
49
|
+
globalThis.__FEDERATION__.__PREFETCH__ = {
|
|
50
|
+
entryLoading: {},
|
|
51
|
+
instance: new Map(),
|
|
52
|
+
__PREFETCH_EXPORTS__: {},
|
|
53
|
+
};
|
|
54
|
+
// @ts-ignore
|
|
55
|
+
prefetch = new MFDataPrefetch(options); // Create a new instance of `MFDataPrefetch` class before each test
|
|
56
|
+
|
|
57
|
+
const exposeExport = {
|
|
58
|
+
[functionId]: executePrefetch,
|
|
59
|
+
};
|
|
60
|
+
const projectExport = {
|
|
61
|
+
[options.name]: exposeExport,
|
|
62
|
+
};
|
|
63
|
+
globalThis.__FEDERATION__.__PREFETCH__.__PREFETCH_EXPORTS__[options.name] =
|
|
64
|
+
Promise.resolve(projectExport);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
afterEach(() => {
|
|
68
|
+
executePrefetch.mockClear();
|
|
69
|
+
mockLoadScript.mockRestore();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
afterAll(() => {
|
|
73
|
+
// @ts-ignore
|
|
74
|
+
delete globalThis.__FEDERATION__;
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should prefetch data on first mount', async () => {
|
|
78
|
+
const { result } = renderHook(() =>
|
|
79
|
+
usePrefetch({ id: options.name, functionId }),
|
|
80
|
+
);
|
|
81
|
+
await result.current[0];
|
|
82
|
+
expect(executePrefetch).toHaveBeenCalled();
|
|
83
|
+
// Verify the prefechState
|
|
84
|
+
expect(result.current[0]).resolves.toEqual(testData);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should refetch data when refreshExecutor is called', async () => {
|
|
88
|
+
const { result } = renderHook(() =>
|
|
89
|
+
usePrefetch({ id: options.name, functionId }),
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
await result.current[0];
|
|
93
|
+
expect(executePrefetch).toHaveBeenCalled();
|
|
94
|
+
executePrefetch.mockClear();
|
|
95
|
+
const { result: newCallResult } = renderHook(() =>
|
|
96
|
+
usePrefetch({ id: options.name, functionId }),
|
|
97
|
+
);
|
|
98
|
+
await newCallResult.current[0];
|
|
99
|
+
expect(executePrefetch).not.toHaveBeenCalled();
|
|
100
|
+
// Call refreshExecutor
|
|
101
|
+
act(() => {
|
|
102
|
+
result.current[1](newTestData);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
expect(executePrefetch).toHaveBeenCalled();
|
|
106
|
+
// // Verify the prefetchState after refetch
|
|
107
|
+
expect(result.current[0]).resolves.toEqual(newTestData);
|
|
108
|
+
});
|
|
109
|
+
});
|
package/dist/cli/index.js
CHANGED
|
@@ -65,28 +65,6 @@ var import_sdk = require("@module-federation/sdk");
|
|
|
65
65
|
var getPrefetchId = (id) => (0, import_sdk.encodeName)(`${id}/${import_sdk.MFPrefetchCommon.identifier}`);
|
|
66
66
|
|
|
67
67
|
// src/cli/index.ts
|
|
68
|
-
var addTemplate = (name) => `
|
|
69
|
-
export default function () {
|
|
70
|
-
let hasInit = false;
|
|
71
|
-
return {
|
|
72
|
-
name: 'data-prefetch-init-plugin',
|
|
73
|
-
beforeInit(args) {
|
|
74
|
-
if (!hasInit) {
|
|
75
|
-
hasInit = true;
|
|
76
|
-
globalThis.__FEDERATION__ = globalThis.__FEDERATION__ || {};
|
|
77
|
-
globalThis.__FEDERATION__['${import_sdk2.MFPrefetchCommon.globalKey}'] = globalThis.__FEDERATION__['${import_sdk2.MFPrefetchCommon.globalKey}'] || {
|
|
78
|
-
entryLoading: {},
|
|
79
|
-
instance: new Map(),
|
|
80
|
-
__PREFETCH_EXPORTS__: {},
|
|
81
|
-
};
|
|
82
|
-
globalThis.__FEDERATION__['${import_sdk2.MFPrefetchCommon.globalKey}']['${import_sdk2.MFPrefetchCommon.exportsKey}'] = globalThis.__FEDERATION__['${import_sdk2.MFPrefetchCommon.globalKey}']['${import_sdk2.MFPrefetchCommon.exportsKey}'] || {};
|
|
83
|
-
globalThis.__FEDERATION__['${import_sdk2.MFPrefetchCommon.globalKey}']['${import_sdk2.MFPrefetchCommon.exportsKey}']['${name}'] = import('./bootstrap');
|
|
84
|
-
}
|
|
85
|
-
return args;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
`;
|
|
90
68
|
var PrefetchPlugin = class {
|
|
91
69
|
constructor(options) {
|
|
92
70
|
this.options = options;
|
|
@@ -100,6 +78,19 @@ var PrefetchPlugin = class {
|
|
|
100
78
|
if (!compiler.options.context) {
|
|
101
79
|
throw new Error("compiler.options.context is not defined");
|
|
102
80
|
}
|
|
81
|
+
const { runtimePlugins } = this.options;
|
|
82
|
+
if (!Array.isArray(runtimePlugins)) {
|
|
83
|
+
this.options.runtimePlugins = [];
|
|
84
|
+
}
|
|
85
|
+
this.options.runtimePlugins.push(
|
|
86
|
+
import_path2.default.resolve(__dirname, "../esm/plugin.js")
|
|
87
|
+
);
|
|
88
|
+
this.options.runtimePlugins.push(
|
|
89
|
+
import_path2.default.resolve(__dirname, "../esm/shared/index.js")
|
|
90
|
+
);
|
|
91
|
+
if (!this.options.dataPrefetch) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
103
94
|
const prefetchs = [];
|
|
104
95
|
const exposeAlias = Object.keys(exposes);
|
|
105
96
|
exposeAlias.forEach((alias) => {
|
|
@@ -108,7 +99,7 @@ var PrefetchPlugin = class {
|
|
|
108
99
|
if (typeof exposeValue === "string") {
|
|
109
100
|
exposePath = exposeValue;
|
|
110
101
|
} else {
|
|
111
|
-
exposePath = exposeValue.import;
|
|
102
|
+
exposePath = exposeValue.import[0];
|
|
112
103
|
}
|
|
113
104
|
const targetPaths = fixPrefetchPath(exposePath);
|
|
114
105
|
for (const pathItem of targetPaths) {
|
|
@@ -124,13 +115,6 @@ var PrefetchPlugin = class {
|
|
|
124
115
|
}
|
|
125
116
|
}
|
|
126
117
|
});
|
|
127
|
-
const { runtimePlugins } = this.options;
|
|
128
|
-
if (!Array.isArray(runtimePlugins)) {
|
|
129
|
-
this.options.runtimePlugins = [];
|
|
130
|
-
}
|
|
131
|
-
this.options.runtimePlugins.push(
|
|
132
|
-
import_path2.default.resolve(__dirname, "../esm/plugin.js")
|
|
133
|
-
);
|
|
134
118
|
if (!this._reWriteExports) {
|
|
135
119
|
return;
|
|
136
120
|
}
|
|
@@ -151,12 +135,9 @@ var PrefetchPlugin = class {
|
|
|
151
135
|
import_fs_extra2.default.mkdirSync(`${tempDirRealPath}/${encodedName}`);
|
|
152
136
|
}
|
|
153
137
|
import_fs_extra2.default.writeFileSync(asyncEntryPath, this._reWriteExports);
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
);
|
|
158
|
-
import_fs_extra2.default.writeFileSync(prefetchEntry, addTemplate(name));
|
|
159
|
-
this.options.runtimePlugins.push(prefetchEntry);
|
|
138
|
+
new compiler.webpack.DefinePlugin({
|
|
139
|
+
FederationDataPrefetch: JSON.stringify(asyncEntryPath)
|
|
140
|
+
}).apply(compiler);
|
|
160
141
|
}
|
|
161
142
|
};
|
|
162
143
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -9,11 +9,14 @@ import {
|
|
|
9
9
|
} from "./chunk-EWCGK4XA.js";
|
|
10
10
|
|
|
11
11
|
// src/plugin.ts
|
|
12
|
+
import { Module } from "@module-federation/runtime";
|
|
12
13
|
import { getResourceUrl } from "@module-federation/sdk";
|
|
13
14
|
var loadingArray = [];
|
|
15
|
+
var strategy = "loaded-first";
|
|
16
|
+
var sharedFlag = strategy;
|
|
14
17
|
var prefetchPlugin = () => ({
|
|
15
18
|
name: "data-prefetch-runtime-plugin",
|
|
16
|
-
|
|
19
|
+
initContainer(options) {
|
|
17
20
|
const { remoteSnapshot, remoteInfo, id, origin } = options;
|
|
18
21
|
const snapshot = remoteSnapshot;
|
|
19
22
|
const { name } = remoteInfo;
|
|
@@ -27,6 +30,11 @@ var prefetchPlugin = () => ({
|
|
|
27
30
|
if (!signal) {
|
|
28
31
|
return options;
|
|
29
32
|
}
|
|
33
|
+
if (sharedFlag !== strategy) {
|
|
34
|
+
throw new Error(
|
|
35
|
+
`[Module Federation Data Prefetch]: If you want to use data prefetch, the shared strategy must be 'loaded-first'`
|
|
36
|
+
);
|
|
37
|
+
}
|
|
30
38
|
const instance = MFDataPrefetch.getInstance(name) || new MFDataPrefetch(prefetchOptions);
|
|
31
39
|
let prefetchUrl;
|
|
32
40
|
if (snapshot.prefetchEntry) {
|
|
@@ -41,20 +49,22 @@ var prefetchPlugin = () => ({
|
|
|
41
49
|
if (projectExports instanceof Promise) {
|
|
42
50
|
await projectExports;
|
|
43
51
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
52
|
+
return Promise.resolve().then(() => {
|
|
53
|
+
const exports = instance.getExposeExports(id);
|
|
54
|
+
logger_default.info(`1. Start Prefetch: ${id} - ${performance.now()}`);
|
|
55
|
+
const result = Object.keys(exports).map((k) => {
|
|
56
|
+
const value = instance.prefetch({
|
|
57
|
+
id,
|
|
58
|
+
functionId: k
|
|
59
|
+
});
|
|
60
|
+
const functionId = k;
|
|
61
|
+
return {
|
|
62
|
+
value,
|
|
63
|
+
functionId
|
|
64
|
+
};
|
|
50
65
|
});
|
|
51
|
-
|
|
52
|
-
return {
|
|
53
|
-
value,
|
|
54
|
-
functionId
|
|
55
|
-
};
|
|
66
|
+
return result;
|
|
56
67
|
});
|
|
57
|
-
return result;
|
|
58
68
|
});
|
|
59
69
|
loadingArray.push({
|
|
60
70
|
id,
|
|
@@ -102,12 +112,29 @@ var prefetchPlugin = () => ({
|
|
|
102
112
|
return options;
|
|
103
113
|
}
|
|
104
114
|
const promise = instance.loadEntry(prefetchUrl).then(async () => {
|
|
115
|
+
let module = origin.moduleCache.get(remote.name);
|
|
116
|
+
const moduleOptions = {
|
|
117
|
+
host: origin,
|
|
118
|
+
remoteInfo: remote
|
|
119
|
+
};
|
|
120
|
+
if (!module) {
|
|
121
|
+
module = new Module(moduleOptions);
|
|
122
|
+
origin.moduleCache.set(remote.name, module);
|
|
123
|
+
}
|
|
124
|
+
const idPart = id.split("/");
|
|
125
|
+
let expose = idPart[idPart.length - 1];
|
|
126
|
+
if (expose !== ".") {
|
|
127
|
+
expose = `./${expose}`;
|
|
128
|
+
}
|
|
129
|
+
await module.get(id, expose, {}, remoteSnapshot);
|
|
105
130
|
const projectExports = instance.getProjectExports();
|
|
106
131
|
if (projectExports instanceof Promise) {
|
|
107
132
|
await projectExports;
|
|
108
133
|
}
|
|
109
134
|
const exports = instance.getExposeExports(id);
|
|
110
|
-
logger_default.info(
|
|
135
|
+
logger_default.info(
|
|
136
|
+
`1. PreloadRemote Start Prefetch: ${id} - ${performance.now()}`
|
|
137
|
+
);
|
|
111
138
|
const result = Object.keys(exports).map((k) => {
|
|
112
139
|
const value = instance.prefetch({
|
|
113
140
|
id,
|
|
@@ -126,6 +153,11 @@ var prefetchPlugin = () => ({
|
|
|
126
153
|
promise
|
|
127
154
|
});
|
|
128
155
|
return options;
|
|
156
|
+
},
|
|
157
|
+
beforeLoadShare(options) {
|
|
158
|
+
const shareInfo = options.shareInfo;
|
|
159
|
+
sharedFlag = (shareInfo == null ? void 0 : shareInfo.strategy) || sharedFlag;
|
|
160
|
+
return options;
|
|
129
161
|
}
|
|
130
162
|
});
|
|
131
163
|
var plugin_default = prefetchPlugin;
|
package/dist/esm/cli/index.js
CHANGED
|
@@ -6,8 +6,7 @@ import {
|
|
|
6
6
|
import path2 from "path";
|
|
7
7
|
import fs2 from "fs-extra";
|
|
8
8
|
import {
|
|
9
|
-
encodeName
|
|
10
|
-
MFPrefetchCommon
|
|
9
|
+
encodeName
|
|
11
10
|
} from "@module-federation/sdk";
|
|
12
11
|
|
|
13
12
|
// src/common/constant.ts
|
|
@@ -38,28 +37,6 @@ var fixPrefetchPath = (exposePath) => {
|
|
|
38
37
|
};
|
|
39
38
|
|
|
40
39
|
// src/cli/index.ts
|
|
41
|
-
var addTemplate = (name) => `
|
|
42
|
-
export default function () {
|
|
43
|
-
let hasInit = false;
|
|
44
|
-
return {
|
|
45
|
-
name: 'data-prefetch-init-plugin',
|
|
46
|
-
beforeInit(args) {
|
|
47
|
-
if (!hasInit) {
|
|
48
|
-
hasInit = true;
|
|
49
|
-
globalThis.__FEDERATION__ = globalThis.__FEDERATION__ || {};
|
|
50
|
-
globalThis.__FEDERATION__['${MFPrefetchCommon.globalKey}'] = globalThis.__FEDERATION__['${MFPrefetchCommon.globalKey}'] || {
|
|
51
|
-
entryLoading: {},
|
|
52
|
-
instance: new Map(),
|
|
53
|
-
__PREFETCH_EXPORTS__: {},
|
|
54
|
-
};
|
|
55
|
-
globalThis.__FEDERATION__['${MFPrefetchCommon.globalKey}']['${MFPrefetchCommon.exportsKey}'] = globalThis.__FEDERATION__['${MFPrefetchCommon.globalKey}']['${MFPrefetchCommon.exportsKey}'] || {};
|
|
56
|
-
globalThis.__FEDERATION__['${MFPrefetchCommon.globalKey}']['${MFPrefetchCommon.exportsKey}']['${name}'] = import('./bootstrap');
|
|
57
|
-
}
|
|
58
|
-
return args;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
`;
|
|
63
40
|
var PrefetchPlugin = class {
|
|
64
41
|
constructor(options) {
|
|
65
42
|
this.options = options;
|
|
@@ -73,6 +50,19 @@ var PrefetchPlugin = class {
|
|
|
73
50
|
if (!compiler.options.context) {
|
|
74
51
|
throw new Error("compiler.options.context is not defined");
|
|
75
52
|
}
|
|
53
|
+
const { runtimePlugins } = this.options;
|
|
54
|
+
if (!Array.isArray(runtimePlugins)) {
|
|
55
|
+
this.options.runtimePlugins = [];
|
|
56
|
+
}
|
|
57
|
+
this.options.runtimePlugins.push(
|
|
58
|
+
path2.resolve(__dirname, "../esm/plugin.js")
|
|
59
|
+
);
|
|
60
|
+
this.options.runtimePlugins.push(
|
|
61
|
+
path2.resolve(__dirname, "../esm/shared/index.js")
|
|
62
|
+
);
|
|
63
|
+
if (!this.options.dataPrefetch) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
76
66
|
const prefetchs = [];
|
|
77
67
|
const exposeAlias = Object.keys(exposes);
|
|
78
68
|
exposeAlias.forEach((alias) => {
|
|
@@ -81,7 +71,7 @@ var PrefetchPlugin = class {
|
|
|
81
71
|
if (typeof exposeValue === "string") {
|
|
82
72
|
exposePath = exposeValue;
|
|
83
73
|
} else {
|
|
84
|
-
exposePath = exposeValue.import;
|
|
74
|
+
exposePath = exposeValue.import[0];
|
|
85
75
|
}
|
|
86
76
|
const targetPaths = fixPrefetchPath(exposePath);
|
|
87
77
|
for (const pathItem of targetPaths) {
|
|
@@ -97,13 +87,6 @@ var PrefetchPlugin = class {
|
|
|
97
87
|
}
|
|
98
88
|
}
|
|
99
89
|
});
|
|
100
|
-
const { runtimePlugins } = this.options;
|
|
101
|
-
if (!Array.isArray(runtimePlugins)) {
|
|
102
|
-
this.options.runtimePlugins = [];
|
|
103
|
-
}
|
|
104
|
-
this.options.runtimePlugins.push(
|
|
105
|
-
path2.resolve(__dirname, "../esm/plugin.js")
|
|
106
|
-
);
|
|
107
90
|
if (!this._reWriteExports) {
|
|
108
91
|
return;
|
|
109
92
|
}
|
|
@@ -124,12 +107,9 @@ var PrefetchPlugin = class {
|
|
|
124
107
|
fs2.mkdirSync(`${tempDirRealPath}/${encodedName}`);
|
|
125
108
|
}
|
|
126
109
|
fs2.writeFileSync(asyncEntryPath, this._reWriteExports);
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
);
|
|
131
|
-
fs2.writeFileSync(prefetchEntry, addTemplate(name));
|
|
132
|
-
this.options.runtimePlugins.push(prefetchEntry);
|
|
110
|
+
new compiler.webpack.DefinePlugin({
|
|
111
|
+
FederationDataPrefetch: JSON.stringify(asyncEntryPath)
|
|
112
|
+
}).apply(compiler);
|
|
133
113
|
}
|
|
134
114
|
};
|
|
135
115
|
export {
|
package/dist/esm/index.js
CHANGED
package/dist/esm/plugin.js
CHANGED
package/dist/esm/react/index.js
CHANGED
|
@@ -30,7 +30,7 @@ var usePrefetch = (options) => {
|
|
|
30
30
|
if (isFirstMounted) {
|
|
31
31
|
const startTiming = performance.now();
|
|
32
32
|
logger_default.info(
|
|
33
|
-
`2. Start Get Prefetch Data: ${options.id} - ${options.functionId} - ${startTiming}`
|
|
33
|
+
`2. Start Get Prefetch Data: ${options.id} - ${options.functionId || "default"} - ${startTiming}`
|
|
34
34
|
);
|
|
35
35
|
}
|
|
36
36
|
const { id, functionId, deferId } = options;
|
|
@@ -59,7 +59,7 @@ var usePrefetch = (options) => {
|
|
|
59
59
|
useEffect2(() => {
|
|
60
60
|
const useEffectTiming = performance.now();
|
|
61
61
|
logger_default.info(
|
|
62
|
-
`3. Start Execute UseEffect: ${options.id} - ${options.functionId} - ${useEffectTiming}`
|
|
62
|
+
`3. Start Execute UseEffect: ${options.id} - ${options.functionId || "default"} - ${useEffectTiming}`
|
|
63
63
|
);
|
|
64
64
|
return () => {
|
|
65
65
|
prefetchInstance == null ? void 0 : prefetchInstance.markOutdate(prefetchInfo, true);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// src/shared/index.ts
|
|
2
|
+
var sharedStrategy = () => ({
|
|
3
|
+
name: "shared-strategy",
|
|
4
|
+
beforeInit(args) {
|
|
5
|
+
const { userOptions } = args;
|
|
6
|
+
const shared = userOptions.shared;
|
|
7
|
+
if (shared) {
|
|
8
|
+
Object.keys(shared).forEach((sharedKey) => {
|
|
9
|
+
const sharedConfigs = shared[sharedKey];
|
|
10
|
+
const arraySharedConfigs = Array.isArray(sharedConfigs) ? sharedConfigs : [sharedConfigs];
|
|
11
|
+
arraySharedConfigs.forEach((s) => {
|
|
12
|
+
s.strategy = "loaded-first";
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
console.warn(
|
|
16
|
+
`[Module Federation Data Prefetch]: Your shared strategy is set to 'loaded-first', this is a necessary condition for data prefetch`
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
return args;
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
var shared_default = sharedStrategy;
|
|
23
|
+
export {
|
|
24
|
+
shared_default as default
|
|
25
|
+
};
|