@module-federation/runtime 0.0.7 → 0.0.9

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/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  - Only consume part of the export of the remote module and will not fully download the remote module
5
5
  - The runtime calling process can be extended through the module-runtime plug-in mechanism
6
6
 
7
- ## Usage
7
+ ## API
8
8
 
9
9
  ```javascript
10
10
  // Can load modules using only the runtime SDK without relying on build plugins
@@ -16,18 +16,14 @@ init({
16
16
  remotes: [
17
17
  {
18
18
  name: "@demo/app2",
19
- entry: "http://localhost:3006/remoteEntry.js"
20
- },
21
- {
22
- name: "@demo/app3",
23
- alias: "app3",
24
- entry: "http://localhost:2001/module-federation-manifest.json"
19
+ entry: "http://localhost:3006/remoteEntry.js",
20
+ alias: "app2"
25
21
  },
26
22
  ],
27
23
  });
28
24
 
29
25
  // Load by alias
30
- loadRemote<{add: (...args: Array<number>)=> number }>("app3/util").then((md)=>{
26
+ loadRemote<{add: (...args: Array<number>)=> number }>("app2/util").then((md)=>{
31
27
  md.add(1,2,3);
32
28
  });
33
29
  ```
@@ -35,74 +31,65 @@ loadRemote<{add: (...args: Array<number>)=> number }>("app3/util").then((md)=>{
35
31
  ### init
36
32
 
37
33
  - Type: `init(options: InitOptions): void`
38
- - It is used to dynamically register the module at runtime
34
+ - Create runtime instance , it can dynamically register remotes by re-call , but only one instance will exist.
39
35
  - InitOptions:
40
36
 
41
37
  ```ts
42
38
  type InitOptions {
39
+ //The name of the current host
43
40
  name: string;
41
+ // The version of the current host will use the host version of the corresponding version online.
42
+ // remoteInfo in remotes will use the online version
44
43
  version?: string;
44
+ // In the area where the module is deployed, the consumer will transparently transmit this data to the producer
45
+ //After setting, when the producer data fails to be obtained normally, the backup data will be automatically obtained according to this configuration.
46
+ region?: `EnhancedRegion`;
47
+ // List of dependent remote modules
48
+ // tip: The remotes configured at runtime are not completely consistent with the types and data passed in the build plugin, and the passed in * ^ ~ version rules are not supported at runtime.
49
+ remotes: Array<RemoteInfo>;
50
+ // List of dependencies that need to be shared by the current host
51
+ // When using the build plugin, users can configure the dependencies that need to be shared in the build plugin, and the build plugin will inject the dependencies that need to be shared into the runtime shared configuration.
52
+ // Shared must be manually passed in the version instance reference when it is passed in at runtime, because it cannot be directly passed in at runtime.
45
53
  shared?: ShareInfos;
46
54
  };
47
55
 
48
56
  type RemoteInfo = (RemotesWithEntry | RemotesWithVersion) & {
49
- alias?: string;
57
+ alias?: string;
50
58
  };
51
59
 
52
60
  interface RemotesWithVersion {
53
- name: string;
54
- version: string;
61
+ name: string;
62
+ version: string;
55
63
  }
56
64
 
57
65
  interface RemotesWithEntry {
58
- name: string;
59
- entry: string;
66
+ name: string;
67
+ entry: string;
60
68
  }
61
69
 
62
70
  type ShareInfos = {
63
- // Package name and dependency basic information and sharing strategy
64
- [pkgName: string]: Share;
71
+ // Dependent package name, dependent basic information, and sharing strategy
72
+ [pkgName: string]: Share;
65
73
  };
66
74
 
67
75
  type Share = {
68
- // Versions of shared dependencies
69
- version: string;
70
- // Which modules consume the current shared dependencies?
71
- useIn?: Array<string>;
72
- // Which module does the shared dependency come from?
73
- from?: string;
74
- // Get the factory function of the shared dependency instance. When the cached shared instance cannot be loaded, its own shared dependency will be loaded.
75
- lib: () => Module;
76
- // Sharing strategy, what strategy will be used to determine the reuse of shared dependencies
77
- shareConfig?: SharedConfig;
78
- // Dependencies between shares
79
- deps?: Array<string>;
80
- // The scope under which the current shared dependencies are placed, the default is default
81
- scope?: string | Array<string>;
76
+ // Versions of shared dependencies
77
+ version: string;
78
+ //Which modules consume the current shared dependencies?
79
+ useIn?: Array<string>;
80
+ //Which module does the shared dependency come from?
81
+ from?: string;
82
+ // Get the factory function of the shared dependency instance. When the cached shared instance cannot be loaded, its own shared dependency will be loaded.
83
+ lib: () => Module;
84
+ // Sharing strategy, what strategy will be used to determine the reuse of shared dependencies
85
+ shareConfig?: SharedConfig;
86
+ // Dependencies between shares
87
+ deps?: Array<string>;
88
+ // Under what scope the current shared dependencies are placed, the default is default
89
+ scope?: string | Array<string>;
82
90
  };
83
91
  ```
84
92
 
85
- - Example
86
-
87
- ```js
88
- import { init, loadRemote } from '@module-federation/runtime';
89
-
90
- init({
91
- name: '@demo/main-app',
92
- remotes: [
93
- {
94
- name: '@demo/app2',
95
- entry: 'http://localhost:3006/remoteEntry.js',
96
- },
97
- {
98
- name: '@demo/app3',
99
- alias: 'app3',
100
- entry: 'http://localhost:2001/module-federation-manifest.json',
101
- },
102
- ],
103
- });
104
- ```
105
-
106
93
  ### loadRemote
107
94
 
108
95
  - Type: `loadRemote(id: string)`
@@ -117,18 +104,18 @@ init({
117
104
  name: '@demo/main-app',
118
105
  remotes: [
119
106
  {
120
- name: '@demo/app3',
121
- alias: 'app3',
122
- entry: 'http://localhost:2001/module-federation-manifest.json',
107
+ name: '@demo/app2',
108
+ alias: 'app2',
109
+ entry: "http://localhost:3006/remoteEntry.js",
123
110
  },
124
111
  ],
125
112
  });
126
113
 
127
114
  // remoteName + expose
128
- loadRemote('@demo/app3/util').then((m) => m.add(1, 2, 3));
115
+ loadRemote('@demo/app2/util').then((m) => m.add(1, 2, 3));
129
116
 
130
117
  // alias + expose
131
- loadRemote('app3/util').then((m) => m.add(1, 2, 3));
118
+ loadRemote('app2/util').then((m) => m.add(1, 2, 3));
132
119
  ```
133
120
 
134
121
  ### loadShare
@@ -174,75 +161,11 @@ loadShare('react').then((reactFactory) => {
174
161
  });
175
162
  ```
176
163
 
177
- ### usePlugin
178
-
179
- Used to extend the internal loading process of `ModuleFederation`, affecting the entire loading process through hook triggers and return values.
180
-
181
- - Example
182
-
183
- ```ts
184
- import { init } from '@module-federation/runtime';
185
-
186
- // mock get remote data remotes config
187
- function getDataConfig() {
188
- return new Promise((resolve) => {
189
- setTimeout(() => {
190
- resolve({
191
- remotes: [
192
- {
193
- name: '@demo/sub',
194
- alias: 'sub',
195
- entry: 'http://localhost:2001/module-federation-manifest.json',
196
- },
197
- ],
198
- });
199
- }, 2000);
200
- });
201
- }
202
-
203
- function RemotesDataPlugin() {
204
- return {
205
- name: 'data-config',
206
- async beforeRequest(args) {
207
- const remotes = await getDataConfig();
208
- origin.initOptions({
209
- remotes,
210
- });
211
- return args;
212
- },
213
- };
214
- }
215
-
216
- init({
217
- name: '@demo/micro-app',
218
- remotes: [],
219
- pluigns: [RemotesDataPlugin()],
220
- });
221
-
222
- loadRemote('sub/utils').then((m) => {
223
- m.add(1, 2, 3, 4);
224
- });
225
- ```
226
-
227
- - Type
228
-
229
- ```typescript
230
- type Plugin = {
231
- name: string;
232
- core: {
233
- beforeRequest: AsyncWaterfallHook<{
234
- id: string;
235
- options: Options;
236
- origin: VmokHost;
237
- }>;
238
- };
239
- snapshot: {};
240
- };
241
- type usePlugin = (plugin: Plugin) => void;
242
- ```
243
-
244
164
  ### preloadRemote
245
165
 
166
+ - Type: `preloadRemote(preloadOptions: Array<PreloadRemoteArgs>)`
167
+ - Used to preload remote assets like remoteEntry.js .
168
+
246
169
  - Type
247
170
 
248
171
  ```typescript
@@ -270,10 +193,12 @@ type PreloadRemoteArgs = {
270
193
 
271
194
  - Details
272
195
 
196
+ **Info**: Only if the entry is manifest can more resources be loaded. The manifest plugin will be supported in 2024.Q1
197
+
273
198
  Through `preloadRemote`, module resources can be preloaded at an earlier stage to avoid waterfall requests. `preloadRemote` can preload the following content:
274
199
 
275
200
  - The `remoteEntry` of `remote`
276
- - `expose` of `remote`
201
+ - `expose` assets of `remote`
277
202
  - Synchronous resources or asynchronous resources of `remote`
278
203
  - `remote` resources that `remote` depends on
279
204
 
@@ -281,6 +206,7 @@ Through `preloadRemote`, module resources can be preloaded at an earlier stage t
281
206
 
282
207
  ```ts
283
208
  import { init, preloadRemote } from '@module-federation/runtime';
209
+
284
210
  init({
285
211
  name: '@demo/preload-remote',
286
212
  remotes: [
@@ -298,4 +224,467 @@ init({
298
224
  },
299
225
  ],
300
226
  });
227
+
228
+ // Preload @demo/sub1 module
229
+ // Filter resource information that contains ignore in the resource name
230
+ // Only preload sub-dependent @demo/sub1-button modules
231
+ preloadRemote([
232
+ {
233
+ nameOrAlias: '@demo/sub1',
234
+ filter(assetUrl) {
235
+ return assetUrl.indexOf('ignore') === -1;
236
+ },
237
+ depsRemote: [{ nameOrAlias: '@demo/sub1-button' }],
238
+ },
239
+ ]);
240
+
241
+ // Preload @demo/sub2 module
242
+ // Preload all exposes under @demo/sub2
243
+ // Preload the synchronous resources and asynchronous resources of @demo/sub2
244
+ preloadRemote([
245
+ {
246
+ nameOrAlias: '@demo/sub2',
247
+ resourceCategory: 'all',
248
+ },
249
+ ]);
250
+
251
+ // Preload expose of @demo/sub3 module
252
+ preloadRemote([
253
+ {
254
+ nameOrAlias: '@demo/sub3',
255
+ resourceCategory: 'all',
256
+ exposes: ['add'],
257
+ },
258
+ ]);
259
+ ```
260
+
261
+ ## hooks
262
+
263
+ Lifecycle hooks for FederationHost interaction.
264
+
265
+ * example
266
+
267
+ ```ts
268
+ import { init } from '@module-federation/runtime'
269
+ import type { FederationRuntimePlugin } from '@module-federation/runtime';
270
+
271
+ const runtimePlugin: () => FederationRuntimePlugin =
272
+ function () {
273
+ return {
274
+ name: 'my-runtime-plugin',
275
+ beforeInit(args) {
276
+ console.log('beforeInit: ', args);
277
+ return args;
278
+ },
279
+ beforeRequest(args) {
280
+ console.log('beforeRequest: ', args);
281
+ return args;
282
+ },
283
+ afterResolve(args) {
284
+ console.log('afterResolve', args);
285
+ return args;
286
+ },
287
+ onLoad(args) {
288
+ console.log('onLoad: ', args);
289
+ return args;
290
+ },
291
+ async loadShare(args) {
292
+ console.log('loadShare:', args);
293
+ },
294
+ async beforeLoadShare(args) {
295
+ console.log('beforeloadShare:', args);
296
+ return args;
297
+ },
298
+ };
299
+ };
300
+
301
+ init({
302
+ name: '@demo/app-main',
303
+ remotes: [
304
+ {
305
+ name: "@demo/app2",
306
+ entry: "http://localhost:3006/remoteEntry.js",
307
+ alias: "app2"
308
+ },
309
+ ],
310
+ plugins: [runtimePlugin()]
311
+ });
312
+ ```
313
+
314
+ ### beforeInit
315
+
316
+ `SyncWaterfallHook`
317
+
318
+ Updates Federation Host configurations before the initialization process of remote containers.
319
+
320
+ * type
321
+
322
+ ```ts
323
+ function beforeInit(args: BeforeInitOptions): BeforeInitOptions
324
+
325
+ type BeforeInitOptions ={
326
+ userOptions: UserOptions;
327
+ options: FederationRuntimeOptions;
328
+ origin: FederationHost;
329
+ shareInfo: ShareInfos;
330
+ }
331
+
332
+ interface FederationRuntimeOptions {
333
+ id?: string;
334
+ name: string;
335
+ version?: string;
336
+ remotes: Array<Remote>;
337
+ shared: ShareInfos;
338
+ plugins: Array<FederationRuntimePlugin>;
339
+ inBrowser: boolean;
340
+ }
341
+ ```
342
+
343
+ ### init
344
+
345
+ `SyncHook`
346
+
347
+ Called during the initialization of remote containers.
348
+
349
+ * type
350
+
351
+ ```ts
352
+ function init(args: InitOptions): void
353
+
354
+ type InitOptions ={
355
+ options: FederationRuntimeOptions;
356
+ origin: FederationHost;
357
+ }
358
+ ```
359
+
360
+ ### beforeRequest
361
+
362
+ `AsyncWaterfallHook`
363
+
364
+ Invoked before resolving a remote container, useful for injecting the container or updating something ahead of the lookup.
365
+
366
+ * type
367
+
368
+ ```ts
369
+ async function beforeRequest(args: BeforeRequestOptions): Promise<BeforeRequestOptions>
370
+
371
+ type BeforeRequestOptions ={
372
+ id: string;
373
+ options: FederationRuntimeOptions;
374
+ origin: FederationHost;
375
+ }
376
+ ```
377
+
378
+ ### afterResolve
379
+
380
+ `AsyncWaterfallHook`
381
+
382
+ Called after resolving a container, allowing redirection or modification of resolved information.
383
+
384
+ * type
385
+
386
+ ```ts
387
+ async function afterResolve(args: AfterResolveOptions): Promise<AfterResolveOptions>
388
+
389
+ type AfterResolveOptions ={
390
+ id: string;
391
+ pkgNameOrAlias: string;
392
+ expose: string;
393
+ remote: Remote;
394
+ options: FederationRuntimeOptions;
395
+ origin: FederationHost;
396
+ remoteInfo: RemoteInfo;
397
+ remoteSnapshot?: ModuleInfo;
398
+ }
399
+ ```
400
+
401
+ ### onLoad
402
+
403
+ `AsyncHook`
404
+
405
+ Triggered once a federated module is loaded, allowing access and modification to the exports of the loaded file.
406
+
407
+ * type
408
+
409
+ ```ts
410
+ async function onLoad(args: OnLoadOptions): Promise<void>
411
+
412
+ type OnLoadOptions ={
413
+ id: string;
414
+ expose: string;
415
+ pkgNameOrAlias: string;
416
+ remote: Remote;
417
+ options: ModuleOptions;
418
+ origin: FederationHost;
419
+ exposeModule: any;
420
+ exposeModuleFactory: any;
421
+ moduleInstance: Module;
422
+ }
423
+
424
+ type ModuleOptions = {
425
+ remoteInfo: RemoteInfo;
426
+ host: FederationHost;
427
+ }
428
+
429
+ interface RemoteInfo {
430
+ name: string;
431
+ version?: string;
432
+ buildVersion?: string;
433
+ entry: string;
434
+ type: RemoteEntryType;
435
+ entryGlobalName: string;
436
+ shareScope: string;
437
+ }
438
+ ```
439
+
440
+ ### handlePreloadModule
441
+
442
+ `SyncHook`
443
+
444
+ Handles preloading logic for federated modules.
445
+
446
+ * type
447
+
448
+ ```ts
449
+ function handlePreloadModule(args: HandlePreloadModuleOptions): void
450
+
451
+ type HandlePreloadModuleOptions ={
452
+ id: string;
453
+ name: string;
454
+ remoteSnapshot: ModuleInfo;
455
+ preloadConfig: PreloadRemoteArgs;
456
+ }
457
+ ```
458
+
459
+ ### errorLoadRemote
460
+
461
+ `AsyncHook`
462
+
463
+ Invoked if loading a federated module fails, enabling custom error handling.
464
+
465
+ * type
466
+
467
+ ```ts
468
+ async function errorLoadRemote(args: ErrorLoadRemoteOptions): Promise<void | unknown>
469
+
470
+ type ErrorLoadRemoteOptions ={
471
+ id: string;
472
+ error: unknown;
473
+ from: 'build' | 'runtime';
474
+ origin: FederationHost;
475
+ }
476
+ ```
477
+ * example
478
+
479
+ ```ts
480
+ import { init, loadRemote } from '@module-federation/runtime'
481
+
482
+ import type { FederationRuntimePlugin } from '@module-federation/runtime';
483
+
484
+ const fallbackPlugin: () => FederationRuntimePlugin =
485
+ function () {
486
+ return {
487
+ name: 'fallback-plugin',
488
+ errorLoadRemote(args) {
489
+ const fallback = 'fallback'
490
+ return fallback;
491
+ },
492
+ };
493
+ };
494
+
495
+
496
+ init({
497
+ name: '@demo/app-main',
498
+ remotes: [
499
+ {
500
+ name: "@demo/app2",
501
+ entry: "http://localhost:3006/remoteEntry.js",
502
+ alias: "app2"
503
+ },
504
+ ],
505
+ plugins: [fallbackPlugin()]
506
+ });
507
+
508
+ loadRemote('app2/un-existed-module').then(mod=>{
509
+ expect(mod).toEqual('fallback');
510
+ })
511
+ ```
512
+
513
+ ### beforeLoadShare
514
+
515
+ `AsyncWaterfallHook`
516
+
517
+ Called before attempting to load or negotiate shared modules between federated apps.
518
+
519
+ * type
520
+
521
+ ```ts
522
+ async function beforeLoadShare(args: BeforeLoadShareOptions): Promise<BeforeLoadShareOptions>
523
+
524
+ type BeforeLoadShareOptions ={
525
+ pkgName: string;
526
+ shareInfo?: Shared;
527
+ shared: Options['shared'];
528
+ origin: FederationHost;
529
+ }
530
+ ```
531
+
532
+ ### resolveShare
533
+
534
+ `SyncWaterfallHook`
535
+
536
+ Allows manual resolution of shared module requests.
537
+
538
+ * type
539
+
540
+ ```ts
541
+ function resolveShare(args: ResolveShareOptions): ResolveShareOptions
542
+
543
+ type ResolveShareOptions ={
544
+ shareScopeMap: ShareScopeMap;
545
+ scope: string;
546
+ pkgName: string;
547
+ version: string;
548
+ GlobalFederation: Federation;
549
+ resolver: () => Shared | undefined;
550
+ }
551
+ ```
552
+
553
+ * example
554
+
555
+ ```ts
556
+ import { init, loadRemote } from '@module-federation/runtime'
557
+
558
+ import type { FederationRuntimePlugin } from '@module-federation/runtime';
559
+
560
+ const customSharedPlugin: () => FederationRuntimePlugin =
561
+ function () {
562
+ return {
563
+ name: 'custom-shared-plugin',
564
+ resolveShare(args) {
565
+ const { shareScopeMap, scope, pkgName, version, GlobalFederation } = args;
566
+
567
+ if (
568
+ pkgName !== 'react'
569
+ ) {
570
+ return args;
571
+ }
572
+
573
+ args.resolver = function () {
574
+ shareScopeMap[scope][pkgName][version] = window.React; // replace local share scope manually with desired module
575
+ return shareScopeMap[scope][pkgName][version];
576
+ };
577
+ return args;
578
+ },
579
+ };
580
+ };
581
+
582
+
583
+ init({
584
+ name: '@demo/app-main',
585
+ shared: {
586
+ react: {
587
+ version: '17.0.0',
588
+ scope: 'default',
589
+ lib: () => React,
590
+ shareConfig: {
591
+ singleton: true,
592
+ requiredVersion: '^17.0.0',
593
+ },
594
+ },
595
+ },
596
+ plugins: [customSharedPlugin()]
597
+ });
598
+
599
+ window.React = ()=> 'Desired Shared';
600
+
601
+ loadShare("react").then((reactFactory)=>{
602
+ expect(reactFactory()).toEqual(window.React());
603
+ });
604
+ ```
605
+
606
+ ### beforePreloadRemote
607
+
608
+ `AsyncHook`
609
+
610
+ Invoked before any preload logic is executed by the preload handler.
611
+
612
+ * type
613
+
614
+ ```ts
615
+ async function beforePreloadRemote(args: BeforePreloadRemoteOptions): BeforePreloadRemoteOptions
616
+
617
+ type BeforePreloadRemoteOptions ={
618
+ preloadOps: Array<PreloadRemoteArgs>;
619
+ options: Options;
620
+ origin: FederationHost;
621
+ }
622
+ ```
623
+
624
+ ### generatePreloadAssets
625
+
626
+ `AsyncHook`
627
+
628
+ Called for generating preload assets based on configurations.
629
+
630
+ * type
631
+
632
+ ```ts
633
+ async function generatePreloadAssets(args: GeneratePreloadAssetsOptions): Promise<PreloadAssets>
634
+
635
+ type GeneratePreloadAssetsOptions ={
636
+ origin: FederationHost;
637
+ preloadOptions: PreloadOptions[number];
638
+ remote: Remote;
639
+ remoteInfo: RemoteInfo;
640
+ remoteSnapshot: ModuleInfo;
641
+ globalSnapshot: GlobalModuleInfo;
642
+ }
643
+
644
+ interface PreloadAssets {
645
+ cssAssets: Array<string>;
646
+ jsAssetsWithoutEntry: Array<string>;
647
+ entryAssets: Array<EntryAssets>;
648
+ }
649
+ ```
650
+
651
+ ## loaderHook
652
+
653
+ Plugin system for module loading operations.
654
+
655
+ ### createScript
656
+
657
+ `SyncHook`
658
+
659
+ * type
660
+
661
+ ```ts
662
+ function createScript(args: CreateScriptOptions): HTMLScriptElement | void
663
+
664
+ type CreateScriptOptions ={
665
+ url: string;
666
+ }
667
+ ```
668
+
669
+ * example
670
+
671
+ ```ts
672
+ import { init } from '@module-federation/runtime'
673
+ import type { FederationRuntimePlugin } from '@module-federation/runtime';
674
+
675
+ const changeScriptAttributePlugin: () => FederationRuntimePlugin =
676
+ function () {
677
+ return {
678
+ name: 'change-script-attribute',
679
+ createScript({ url }) {
680
+ if (url === testRemoteEntry) {
681
+ let script = document.createElement('script');
682
+ script.src = testRemoteEntry;
683
+ script.setAttribute('loader-hooks', 'isTrue');
684
+ script.setAttribute('crossorigin', 'anonymous');
685
+ return script;
686
+ }
687
+ }
688
+ };
689
+ };
301
690
  ```