@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/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
- afterResolve(options) {
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
- 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
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
- const functionId = k;
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(`1. Start Prefetch: ${id} - ${performance.now()}`);
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
- afterResolve(options) {
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
- 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
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
- const functionId = k;
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(`1. Start Prefetch: ${id} - ${performance.now()}`);
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;
@@ -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-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-20240617071542",
81
- "@module-federation/runtime": "0.0.0-next-20240617071542"
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",
@@ -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]');