@module-federation/data-prefetch 0.0.0-next-20240617071542 → 0.0.0-next-20240701101956

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/index.js CHANGED
@@ -200,9 +200,11 @@ var logger_default = new import_sdk3.Logger("[Module Federation Data Prefetch]")
200
200
 
201
201
  // src/plugin.ts
202
202
  var loadingArray = [];
203
+ var strategy = "loaded-first";
204
+ var sharedFlag = strategy;
203
205
  var prefetchPlugin = () => ({
204
206
  name: "data-prefetch-runtime-plugin",
205
- afterResolve(options) {
207
+ initContainer(options) {
206
208
  const { remoteSnapshot, remoteInfo, id, origin } = options;
207
209
  const snapshot = remoteSnapshot;
208
210
  const { name } = remoteInfo;
@@ -216,6 +218,11 @@ var prefetchPlugin = () => ({
216
218
  if (!signal) {
217
219
  return options;
218
220
  }
221
+ if (sharedFlag !== strategy) {
222
+ throw new Error(
223
+ `[Module Federation Data Prefetch]: If you want to use data prefetch, the shared strategy must be 'loaded-first'`
224
+ );
225
+ }
219
226
  const instance = MFDataPrefetch.getInstance(name) || new MFDataPrefetch(prefetchOptions);
220
227
  let prefetchUrl;
221
228
  if (snapshot.prefetchEntry) {
@@ -230,20 +237,22 @@ var prefetchPlugin = () => ({
230
237
  if (projectExports instanceof Promise) {
231
238
  await projectExports;
232
239
  }
233
- const exports = instance.getExposeExports(id);
234
- logger_default.info(`1. Start Prefetch: ${id} - ${performance.now()}`);
235
- const result = Object.keys(exports).map((k) => {
236
- const value = instance.prefetch({
237
- id,
238
- functionId: k
240
+ return Promise.resolve().then(() => {
241
+ const exports = instance.getExposeExports(id);
242
+ logger_default.info(`1. Start Prefetch: ${id} - ${performance.now()}`);
243
+ const result = Object.keys(exports).map((k) => {
244
+ const value = instance.prefetch({
245
+ id,
246
+ functionId: k
247
+ });
248
+ const functionId = k;
249
+ return {
250
+ value,
251
+ functionId
252
+ };
239
253
  });
240
- const functionId = k;
241
- return {
242
- value,
243
- functionId
244
- };
254
+ return result;
245
255
  });
246
- return result;
247
256
  });
248
257
  loadingArray.push({
249
258
  id,
@@ -296,7 +305,9 @@ var prefetchPlugin = () => ({
296
305
  await projectExports;
297
306
  }
298
307
  const exports = instance.getExposeExports(id);
299
- logger_default.info(`1. Start Prefetch: ${id} - ${performance.now()}`);
308
+ logger_default.info(
309
+ `1. PreloadRemote Start Prefetch: ${id} - ${performance.now()}`
310
+ );
300
311
  const result = Object.keys(exports).map((k) => {
301
312
  const value = instance.prefetch({
302
313
  id,
@@ -315,6 +326,11 @@ var prefetchPlugin = () => ({
315
326
  promise
316
327
  });
317
328
  return options;
329
+ },
330
+ beforeLoadShare(options) {
331
+ const shareInfo = options.shareInfo;
332
+ sharedFlag = (shareInfo == null ? void 0 : shareInfo.strategy) || sharedFlag;
333
+ return options;
318
334
  }
319
335
  });
320
336
  // Annotate the CommonJS export names for ESM import in node:
package/dist/plugin.js CHANGED
@@ -196,9 +196,11 @@ var logger_default = new import_sdk3.Logger("[Module Federation Data Prefetch]")
196
196
 
197
197
  // src/plugin.ts
198
198
  var loadingArray = [];
199
+ var strategy = "loaded-first";
200
+ var sharedFlag = strategy;
199
201
  var prefetchPlugin = () => ({
200
202
  name: "data-prefetch-runtime-plugin",
201
- afterResolve(options) {
203
+ initContainer(options) {
202
204
  const { remoteSnapshot, remoteInfo, id, origin } = options;
203
205
  const snapshot = remoteSnapshot;
204
206
  const { name } = remoteInfo;
@@ -212,6 +214,11 @@ var prefetchPlugin = () => ({
212
214
  if (!signal) {
213
215
  return options;
214
216
  }
217
+ if (sharedFlag !== strategy) {
218
+ throw new Error(
219
+ `[Module Federation Data Prefetch]: If you want to use data prefetch, the shared strategy must be 'loaded-first'`
220
+ );
221
+ }
215
222
  const instance = MFDataPrefetch.getInstance(name) || new MFDataPrefetch(prefetchOptions);
216
223
  let prefetchUrl;
217
224
  if (snapshot.prefetchEntry) {
@@ -226,20 +233,22 @@ var prefetchPlugin = () => ({
226
233
  if (projectExports instanceof Promise) {
227
234
  await projectExports;
228
235
  }
229
- const exports = instance.getExposeExports(id);
230
- logger_default.info(`1. Start Prefetch: ${id} - ${performance.now()}`);
231
- const result = Object.keys(exports).map((k) => {
232
- const value = instance.prefetch({
233
- id,
234
- functionId: k
236
+ return Promise.resolve().then(() => {
237
+ const exports = instance.getExposeExports(id);
238
+ logger_default.info(`1. Start Prefetch: ${id} - ${performance.now()}`);
239
+ const result = Object.keys(exports).map((k) => {
240
+ const value = instance.prefetch({
241
+ id,
242
+ functionId: k
243
+ });
244
+ const functionId = k;
245
+ return {
246
+ value,
247
+ functionId
248
+ };
235
249
  });
236
- const functionId = k;
237
- return {
238
- value,
239
- functionId
240
- };
250
+ return result;
241
251
  });
242
- return result;
243
252
  });
244
253
  loadingArray.push({
245
254
  id,
@@ -292,7 +301,9 @@ var prefetchPlugin = () => ({
292
301
  await projectExports;
293
302
  }
294
303
  const exports = instance.getExposeExports(id);
295
- logger_default.info(`1. Start Prefetch: ${id} - ${performance.now()}`);
304
+ logger_default.info(
305
+ `1. PreloadRemote Start Prefetch: ${id} - ${performance.now()}`
306
+ );
296
307
  const result = Object.keys(exports).map((k) => {
297
308
  const value = instance.prefetch({
298
309
  id,
@@ -311,6 +322,11 @@ var prefetchPlugin = () => ({
311
322
  promise
312
323
  });
313
324
  return options;
325
+ },
326
+ beforeLoadShare(options) {
327
+ const shareInfo = options.shareInfo;
328
+ sharedFlag = (shareInfo == null ? void 0 : shareInfo.strategy) || sharedFlag;
329
+ return options;
314
330
  }
315
331
  });
316
332
  var plugin_default = prefetchPlugin;
@@ -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,5 @@
1
+ import { FederationRuntimePlugin } from '@module-federation/runtime';
2
+
3
+ declare const sharedStrategy: () => FederationRuntimePlugin;
4
+
5
+ export { sharedStrategy as default };
@@ -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-20240617071542",
4
+ "version": "0.0.0-next-20240701101956",
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-20240617071542",
81
- "@module-federation/runtime": "0.0.0-next-20240617071542"
88
+ "@module-federation/sdk": "0.0.0-next-20240701101956",
89
+ "@module-federation/runtime": "0.0.0-next-20240701101956"
82
90
  },
83
91
  "scripts": {
84
92
  "dev": "cross-env WATCH=true tsup",
@@ -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
+ };
@@ -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
@@ -0,0 +1,2 @@
1
+ export * from './prefetch';
2
+ export * from './plugin';
@@ -0,0 +1,3 @@
1
+ import { Logger } from '@module-federation/sdk';
2
+
3
+ export default new Logger('[Module Federation Data Prefetch]');