@module-federation/node 0.0.0-feat-node-support-1702930274547 → 0.0.0-next-20231219093413

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.
Files changed (99) hide show
  1. package/.babelrc +10 -0
  2. package/.eslintrc.json +47 -0
  3. package/CHANGELOG.md +1352 -0
  4. package/LICENSE +21 -0
  5. package/README.md +192 -0
  6. package/assets/banner.png +0 -0
  7. package/dist/jest.config.js +19 -0
  8. package/dist/jest.config.js.map +1 -0
  9. package/dist/package.json +62 -0
  10. package/dist/src/filesystem/stratagies.d.ts +11 -0
  11. package/{src → dist/src}/filesystem/stratagies.js +11 -31
  12. package/dist/src/filesystem/stratagies.js.map +1 -0
  13. package/{src → dist/src}/index.d.ts +2 -0
  14. package/{src → dist/src}/index.js +5 -1
  15. package/dist/src/index.js.map +1 -0
  16. package/dist/src/plugins/AutomaticPublicPathPlugin.js.map +1 -0
  17. package/dist/src/plugins/ChunkCorrelationPlugin.js +442 -0
  18. package/dist/src/plugins/ChunkCorrelationPlugin.js.map +1 -0
  19. package/dist/src/plugins/CommonJsChunkLoadingPlugin.js.map +1 -0
  20. package/{src → dist/src}/plugins/DynamicFilesystemChunkLoadingRuntimeModule.js +13 -0
  21. package/dist/src/plugins/DynamicFilesystemChunkLoadingRuntimeModule.js.map +1 -0
  22. package/dist/src/plugins/NodeContainerPlugin.d.ts +57 -0
  23. package/dist/src/plugins/NodeContainerPlugin.js +105 -0
  24. package/dist/src/plugins/NodeContainerPlugin.js.map +1 -0
  25. package/{src → dist/src}/plugins/NodeFederationPlugin.d.ts +20 -0
  26. package/dist/src/plugins/NodeFederationPlugin.js +127 -0
  27. package/dist/src/plugins/NodeFederationPlugin.js.map +1 -0
  28. package/dist/src/plugins/RemotePublicPathRuntimeModule.js.map +1 -0
  29. package/{src → dist/src}/plugins/StreamingTargetPlugin.js.map +1 -1
  30. package/{src → dist/src}/plugins/UniversalFederationPlugin.d.ts +0 -1
  31. package/{src → dist/src}/plugins/UniversalFederationPlugin.js +3 -3
  32. package/dist/src/plugins/UniversalFederationPlugin.js.map +1 -0
  33. package/{src → dist/src}/plugins/webpackChunkUtilities.js +5 -1
  34. package/dist/src/plugins/webpackChunkUtilities.js.map +1 -0
  35. package/dist/src/types/index.js.map +1 -0
  36. package/dist/src/utils/flush-chunks.js.map +1 -0
  37. package/dist/src/utils/hot-reload.js.map +1 -0
  38. package/dist/src/utils/index.js.map +1 -0
  39. package/global.d.ts +27 -0
  40. package/jest.config.js +14 -16
  41. package/package.json +17 -23
  42. package/project.json +58 -0
  43. package/src/filesystem/stratagies.ts +131 -0
  44. package/src/index.ts +7 -0
  45. package/src/plugins/AutomaticPublicPathPlugin.ts +48 -0
  46. package/src/plugins/ChunkCorrelationPlugin.js +474 -307
  47. package/src/plugins/CommonJsChunkLoadingPlugin.ts +168 -0
  48. package/src/plugins/DynamicFilesystemChunkLoadingRuntimeModule.ts +239 -0
  49. package/src/plugins/NodeContainerPlugin.ts +150 -0
  50. package/src/plugins/NodeFederationPlugin.ts +185 -0
  51. package/src/plugins/RemotePublicPathRuntimeModule.ts +137 -0
  52. package/src/plugins/StreamingTargetPlugin.md +88 -0
  53. package/src/plugins/StreamingTargetPlugin.ts +81 -0
  54. package/src/plugins/UniversalFederationPlugin.ts +81 -0
  55. package/src/plugins/webpackChunkUtilities.ts +383 -0
  56. package/src/types/index.ts +7 -0
  57. package/src/utils/flush-chunks.ts +182 -0
  58. package/src/utils/hot-reload.ts +169 -0
  59. package/src/utils/index.ts +2 -0
  60. package/tsconfig.json +28 -0
  61. package/tsconfig.lib.json +17 -0
  62. package/tsconfig.spec.json +9 -0
  63. package/jest.config.js.map +0 -1
  64. package/src/filesystem/stratagies.d.ts +0 -18
  65. package/src/filesystem/stratagies.js.map +0 -1
  66. package/src/index.js.map +0 -1
  67. package/src/plugins/AutomaticPublicPathPlugin.js.map +0 -1
  68. package/src/plugins/ChunkCorrelationPlugin.js.map +0 -1
  69. package/src/plugins/CommonJsChunkLoadingPlugin.js.map +0 -1
  70. package/src/plugins/DynamicFilesystemChunkLoadingRuntimeModule.js.map +0 -1
  71. package/src/plugins/NodeFederationPlugin.js +0 -76
  72. package/src/plugins/NodeFederationPlugin.js.map +0 -1
  73. package/src/plugins/RemotePublicPathRuntimeModule.js.map +0 -1
  74. package/src/plugins/UniversalFederationPlugin.js.map +0 -1
  75. package/src/plugins/webpackChunkUtilities.js.map +0 -1
  76. package/src/types/index.js.map +0 -1
  77. package/src/utils/flush-chunks.js.map +0 -1
  78. package/src/utils/hot-reload.js.map +0 -1
  79. package/src/utils/index.js.map +0 -1
  80. package/{jest.config.d.ts → dist/jest.config.d.ts} +0 -0
  81. package/{src → dist/src}/plugins/AutomaticPublicPathPlugin.d.ts +0 -0
  82. package/{src → dist/src}/plugins/AutomaticPublicPathPlugin.js +0 -0
  83. package/{src → dist/src}/plugins/ChunkCorrelationPlugin.d.ts +6 -6
  84. /package/{src → dist/src}/plugins/CommonJsChunkLoadingPlugin.d.ts +0 -0
  85. /package/{src → dist/src}/plugins/CommonJsChunkLoadingPlugin.js +0 -0
  86. /package/{src → dist/src}/plugins/DynamicFilesystemChunkLoadingRuntimeModule.d.ts +0 -0
  87. /package/{src → dist/src}/plugins/RemotePublicPathRuntimeModule.d.ts +0 -0
  88. /package/{src → dist/src}/plugins/RemotePublicPathRuntimeModule.js +0 -0
  89. /package/{src → dist/src}/plugins/StreamingTargetPlugin.d.ts +0 -0
  90. /package/{src → dist/src}/plugins/StreamingTargetPlugin.js +0 -0
  91. /package/{src → dist/src}/plugins/webpackChunkUtilities.d.ts +0 -0
  92. /package/{src → dist/src}/types/index.d.ts +0 -0
  93. /package/{src → dist/src}/types/index.js +0 -0
  94. /package/{src → dist/src}/utils/flush-chunks.d.ts +0 -0
  95. /package/{src → dist/src}/utils/flush-chunks.js +0 -0
  96. /package/{src → dist/src}/utils/hot-reload.d.ts +0 -0
  97. /package/{src → dist/src}/utils/hot-reload.js +0 -0
  98. /package/{src → dist/src}/utils/index.d.ts +0 -0
  99. /package/{src → dist/src}/utils/index.js +0 -0
@@ -1,8 +1,9 @@
1
- "use strict";
2
1
  const PLUGIN_NAME = 'FederationStatsPlugin';
2
+
3
3
  /** @typedef {import("./webpack-stats-types").WebpackStats} WebpackStats */
4
4
  /** @typedef {import( "./webpack-stats-types").WebpackStatsChunk} WebpackStatsChunk */
5
5
  /** @typedef {import("./webpack-stats-types").WebpackStatsModule} WebpackStatsModule */
6
+
6
7
  /**
7
8
  * @typedef {object} SharedDependency
8
9
  * @property {string} shareScope
@@ -12,16 +13,19 @@ const PLUGIN_NAME = 'FederationStatsPlugin';
12
13
  * @property {boolean} singleton
13
14
  * @property {boolean} eager
14
15
  */
16
+
15
17
  /**
16
18
  * @typedef {object} SharedModule
17
19
  * @property {string[]} chunks
18
20
  * @property {SharedDependency[]} provides
19
21
  */
22
+
20
23
  /**
21
24
  * @typedef {object} Exposed
22
25
  * @property {string[]} chunks
23
26
  * @property {SharedModule[]} sharedModules
24
27
  */
28
+
25
29
  /**
26
30
  * @typedef {object} FederatedContainer
27
31
  * @property {string} remote
@@ -29,26 +33,31 @@ const PLUGIN_NAME = 'FederationStatsPlugin';
29
33
  * @property {SharedModule[]} sharedModules
30
34
  * @property {{ [key: string]: Exposed }} exposes
31
35
  */
36
+
32
37
  /**
33
38
  * @typedef {object} FederatedStats
34
39
  * @property {SharedModule[]} sharedModules
35
40
  * @property {FederatedContainer[]} federatedModules
36
41
  */
42
+
37
43
  const concat = (x, y) => x.concat(y);
44
+
38
45
  const flatMap = (xs, f) => xs.map(f).reduce(concat, []);
46
+
39
47
  /**
40
48
  *
41
49
  * @param {WebpackStats} stats
42
50
  * @returns {}
43
51
  */
44
52
  function getRemoteModules(stats) {
45
- return stats.modules
46
- .filter((mod) => mod.moduleType === 'remote-module')
47
- .reduce((acc, remoteModule) => {
48
- acc[remoteModule.nameForCondition] = remoteModule.id;
49
- return acc;
53
+ return stats.modules
54
+ .filter((mod) => mod.moduleType === 'remote-module')
55
+ .reduce((acc, remoteModule) => {
56
+ acc[remoteModule.nameForCondition] = remoteModule.id;
57
+ return acc;
50
58
  }, {});
51
59
  }
60
+
52
61
  /**
53
62
  *
54
63
  * @param {WebpackStats} stats
@@ -56,16 +65,17 @@ function getRemoteModules(stats) {
56
65
  * @returns {WebpackStatsModule[]}
57
66
  */
58
67
  function getExposedModules(stats, exposedFile) {
59
- return stats.modules.filter((mod) => mod.name?.startsWith(exposedFile));
68
+ return stats.modules.filter((mod) => mod.name?.startsWith(exposedFile));
60
69
  }
61
70
  //eslint-disable-next-line @typescript-eslint/no-unused-vars
62
71
  function getDependenciesOfChunk(stats, chunk) {
63
- return stats.chunks
64
- .filter((c) => c.children.includes(chunk.id))
65
- .reduce((acc, c) => {
66
- return acc.concat(c.modules);
72
+ return stats.chunks
73
+ .filter((c) => c.children.includes(chunk.id))
74
+ .reduce((acc, c) => {
75
+ return acc.concat(c.modules);
67
76
  }, []);
68
77
  }
78
+
69
79
  /**
70
80
  *
71
81
  * @param {WebpackStats} stats
@@ -73,32 +83,38 @@ function getDependenciesOfChunk(stats, chunk) {
73
83
  * @returns {Exposed}
74
84
  */
75
85
  function getExposed(stats, mod) {
76
- const chunks = stats.chunks.filter((chunk) => {
77
- return chunk.modules.find((modsInChunk) => {
78
- return modsInChunk.id === mod.id && !modsInChunk.dependent;
79
- });
86
+ const chunks = stats.chunks.filter((chunk) => {
87
+ return chunk.modules.find((modsInChunk) => {
88
+ return modsInChunk.id === mod.id && !modsInChunk.dependent;
80
89
  });
81
- const dependencies = stats.modules
82
- .filter((sharedModule) => {
83
- if (sharedModule.moduleType !== 'consume-shared-module') {
84
- return false;
85
- }
86
- return sharedModule.issuerId === mod.id;
90
+ });
91
+ const dependencies = stats.modules
92
+ .filter((sharedModule) => {
93
+ if (sharedModule.moduleType !== 'consume-shared-module') {
94
+ return false;
95
+ }
96
+ return sharedModule.issuerId === mod.id;
87
97
  })
88
- .map((sharedModule) => {
89
- return sharedModule.identifier.split('|')[2];
98
+ .map((sharedModule) => {
99
+ return sharedModule.identifier.split('|')[2];
90
100
  });
91
- const flatChunks = flatMap(chunks, (chunk) => ({
92
- [chunk.id]: {
93
- files: chunk.files.map((f) => `${stats.publicPath === 'auto' ? '' : stats.publicPath || ''}${f}`),
94
- requiredModules: dependencies,
95
- },
96
- }));
97
- return flatChunks.reduce((acc, chunk) => {
98
- Object.assign(acc, chunk);
99
- return acc;
100
- }, {});
101
+
102
+ const flatChunks = flatMap(chunks, (chunk) => ({
103
+ [chunk.id]: {
104
+ files: chunk.files.map(
105
+ (f) =>
106
+ `${stats.publicPath === 'auto' ? '' : stats.publicPath || ''}${f}`,
107
+ ),
108
+ requiredModules: dependencies,
109
+ },
110
+ }));
111
+
112
+ return flatChunks.reduce((acc, chunk) => {
113
+ Object.assign(acc, chunk);
114
+ return acc;
115
+ }, {});
101
116
  }
117
+
102
118
  /**
103
119
  *
104
120
  * @param {import("webpack").Module} mod
@@ -106,77 +122,109 @@ function getExposed(stats, mod) {
106
122
  * @returns {boolean}
107
123
  */
108
124
  function searchIssuer(mod, check) {
109
- if (mod.issuer && check(mod.issuer)) {
110
- return true;
111
- }
112
- return !!mod.modules && mod.modules.some((m) => searchIssuer(m, check));
125
+ if (mod.issuer && check(mod.issuer)) {
126
+ return true;
127
+ }
128
+
129
+ return !!mod.modules && mod.modules.some((m) => searchIssuer(m, check));
113
130
  }
131
+
114
132
  function searchReason(mod, check) {
115
- if (mod.reasons && check(mod.reasons)) {
116
- return true;
117
- }
118
- return !!mod.reasons && mod.reasons.some((m) => searchReason(m, check));
133
+ if (mod.reasons && check(mod.reasons)) {
134
+ return true;
135
+ }
136
+
137
+ return !!mod.reasons && mod.reasons.some((m) => searchReason(m, check));
119
138
  }
139
+
120
140
  function searchIssuerAndReason(mod, check) {
121
- const foundIssuer = searchIssuer(mod, (issuer) => check(issuer));
122
- if (foundIssuer) {
123
- return foundIssuer;
124
- }
125
- return searchReason(mod, (reason) => reason.some((r) => check(r?.moduleIdentifier)));
141
+ const foundIssuer = searchIssuer(mod, (issuer) => check(issuer));
142
+ if (foundIssuer) {
143
+ return foundIssuer;
144
+ }
145
+ return searchReason(mod, (reason) =>
146
+ reason.some((r) => check(r?.moduleIdentifier)),
147
+ );
126
148
  }
149
+
127
150
  /**
128
151
  * @param {import("webpack").Module} mod
129
152
  * @param {(issuer: string) => boolean} check
130
153
  * @returns {string[]}
131
154
  */
132
155
  function getIssuers(mod, check) {
133
- if (mod.issuer && check(mod.issuer)) {
134
- return [mod.issuer];
135
- }
136
- return ((mod.modules &&
137
- mod.modules.filter((m) => searchIssuer(m, check)).map((m) => m.issuer)) ||
138
- []);
156
+ if (mod.issuer && check(mod.issuer)) {
157
+ return [mod.issuer];
158
+ }
159
+
160
+ return (
161
+ (mod.modules &&
162
+ mod.modules.filter((m) => searchIssuer(m, check)).map((m) => m.issuer)) ||
163
+ []
164
+ );
139
165
  }
166
+
140
167
  function getIssuersAndReasons(mod, check) {
141
- if (mod.issuer && check(mod.issuer)) {
142
- return [mod.issuer];
143
- }
144
- if (mod.reasons &&
145
- searchReason(mod, (reason) => reason.some((r) => check(r?.moduleIdentifier)))) {
146
- return mod.reasons
147
- .filter((r) => {
148
- return r.moduleIdentifier && check(r.moduleIdentifier);
149
- })
150
- .map((r) => r.moduleIdentifier);
151
- }
152
- return ((mod.modules &&
153
- mod.modules
154
- .filter((m) => searchIssuerAndReason(m, check))
155
- .map((m) => {
156
- return (m.issuer ||
157
- m.reasons.find((r) => check(r?.moduleIdentifier)).moduleIdentifier);
168
+ if (mod.issuer && check(mod.issuer)) {
169
+ return [mod.issuer];
170
+ }
171
+ if (
172
+ mod.reasons &&
173
+ searchReason(mod, (reason) =>
174
+ reason.some((r) => check(r?.moduleIdentifier)),
175
+ )
176
+ ) {
177
+ return mod.reasons
178
+ .filter((r) => {
179
+ return r.moduleIdentifier && check(r.moduleIdentifier);
180
+ })
181
+ .map((r) => r.moduleIdentifier);
182
+ }
183
+
184
+ return (
185
+ (mod.modules &&
186
+ mod.modules
187
+ .filter((m) => searchIssuerAndReason(m, check))
188
+ .map((m) => {
189
+ return (
190
+ m.issuer ||
191
+ m.reasons.find((r) => check(r?.moduleIdentifier)).moduleIdentifier
192
+ );
158
193
  })) ||
159
- []);
194
+ []
195
+ );
160
196
  }
197
+
161
198
  /**
162
199
  * @param {string} issuer
163
200
  * @returns {SharedDependency}
164
201
  */
165
202
  function parseFederatedIssuer(issuer) {
166
- const split = issuer?.split('|') || [];
167
- if (split.length !== 8 || split[0] !== 'consume-shared-module') {
168
- return null;
169
- }
170
- const [, shareScope, shareKey, requiredVersion, strictVersion, , singleton, eager,] = split;
171
- return {
172
- shareScope,
173
- shareKey,
174
- requiredVersion,
175
- strictVersion: JSON.parse(strictVersion),
176
- singleton: JSON.parse(singleton),
177
- eager: JSON.parse(eager),
178
- };
203
+ const split = issuer?.split('|') || [];
204
+ if (split.length !== 8 || split[0] !== 'consume-shared-module') {
205
+ return null;
206
+ }
207
+ const [
208
+ ,
209
+ shareScope,
210
+ shareKey,
211
+ requiredVersion,
212
+ strictVersion,
213
+ ,
214
+ singleton,
215
+ eager,
216
+ ] = split;
217
+
218
+ return {
219
+ shareScope,
220
+ shareKey,
221
+ requiredVersion,
222
+ strictVersion: JSON.parse(strictVersion),
223
+ singleton: JSON.parse(singleton),
224
+ eager: JSON.parse(eager),
225
+ };
179
226
  }
227
+
180
228
  /**
181
229
  *
182
230
  * @param {WebpackStats} stats
@@ -184,48 +232,106 @@ function parseFederatedIssuer(issuer) {
184
232
  * @returns {SharedModule[]}
185
233
  */
186
234
  function getSharedModules(stats, federationPlugin) {
187
- return flatMap(stats.chunks.filter((chunk) => {
188
- if (!stats.entrypoints[federationPlugin.name]) {
189
- return false;
190
- }
191
- return stats.entrypoints[federationPlugin.name].chunks.some((id) => chunk.id === id);
192
- }), (chunk) => flatMap(chunk.children, (id) => stats.chunks.filter((c) => c.id === id &&
193
- c.files.length > 0 &&
194
- c.parents.some((p) => stats.entrypoints[federationPlugin.name].chunks.some((c) => c === p)) &&
195
- c.modules.some((m) => searchIssuer(m, (issuer) => issuer?.startsWith('consume-shared-module'))))))
196
- .map((chunk) => ({
197
- chunks: chunk.files.map((f) => `${stats.publicPath === 'auto' ? '' : stats.publicPath || ''}${f}`),
198
- provides: flatMap(chunk.modules.filter((m) => searchIssuer(m, (issuer) => issuer?.startsWith('consume-shared-module'))), (m) => getIssuers(m, (issuer) => issuer?.startsWith('consume-shared-module')))
199
- .map(parseFederatedIssuer)
200
- .filter((f) => !!f),
235
+ return flatMap(
236
+ stats.chunks.filter((chunk) => {
237
+ if (!stats.entrypoints[federationPlugin.name]) {
238
+ return false;
239
+ }
240
+ return stats.entrypoints[federationPlugin.name].chunks.some(
241
+ (id) => chunk.id === id,
242
+ );
243
+ }),
244
+ (chunk) =>
245
+ flatMap(chunk.children, (id) =>
246
+ stats.chunks.filter(
247
+ (c) =>
248
+ c.id === id &&
249
+ c.files.length > 0 &&
250
+ c.parents.some((p) =>
251
+ stats.entrypoints[federationPlugin.name].chunks.some(
252
+ (c) => c === p,
253
+ ),
254
+ ) &&
255
+ c.modules.some((m) =>
256
+ searchIssuer(m, (issuer) =>
257
+ issuer?.startsWith('consume-shared-module'),
258
+ ),
259
+ ),
260
+ ),
261
+ ),
262
+ )
263
+ .map((chunk) => ({
264
+ chunks: chunk.files.map(
265
+ (f) =>
266
+ `${stats.publicPath === 'auto' ? '' : stats.publicPath || ''}${f}`,
267
+ ),
268
+ provides: flatMap(
269
+ chunk.modules.filter((m) =>
270
+ searchIssuer(m, (issuer) =>
271
+ issuer?.startsWith('consume-shared-module'),
272
+ ),
273
+ ),
274
+ (m) =>
275
+ getIssuers(m, (issuer) =>
276
+ issuer?.startsWith('consume-shared-module'),
277
+ ),
278
+ )
279
+ .map(parseFederatedIssuer)
280
+ .filter((f) => !!f),
201
281
  }))
202
- .filter((c) => c.provides.length > 0);
282
+ .filter((c) => c.provides.length > 0);
203
283
  }
284
+
204
285
  /**
205
286
  * @param {WebpackStats} stats
206
287
  * @returns {SharedModule[]}
207
288
  */
208
289
  function getMainSharedModules(stats) {
209
- const chunks = stats.namedChunkGroups['main']
210
- ? flatMap(stats.namedChunkGroups['main'].chunks, (c) => stats.chunks.filter((chunk) => chunk.id === c))
211
- : [];
212
- return flatMap(chunks, (chunk) => flatMap(chunk.children, (id) => stats.chunks.filter((c) => {
213
- return (c.id === id &&
214
- c.files.length > 0 &&
215
- c.modules.some((m) => {
216
- return searchIssuerAndReason(m, (check) => check?.startsWith('consume-shared-module'));
217
- }));
218
- })))
219
- .map((chunk) => {
220
- return {
221
- chunks: chunk.files.map((f) => `${stats.publicPath === 'auto' ? '' : stats.publicPath || ''}${f}`),
222
- provides: flatMap(chunk.modules.filter((m) => searchIssuerAndReason(m, (check) => check?.startsWith('consume-shared-module'))), (m) => getIssuersAndReasons(m, (issuer) => issuer?.startsWith('consume-shared-module')))
223
- .map(parseFederatedIssuer)
224
- .filter((f) => !!f),
225
- };
290
+ const chunks = stats.namedChunkGroups['main']
291
+ ? flatMap(stats.namedChunkGroups['main'].chunks, (c) =>
292
+ stats.chunks.filter((chunk) => chunk.id === c),
293
+ )
294
+ : [];
295
+
296
+ return flatMap(chunks, (chunk) =>
297
+ flatMap(chunk.children, (id) =>
298
+ stats.chunks.filter((c) => {
299
+ return (
300
+ c.id === id &&
301
+ c.files.length > 0 &&
302
+ c.modules.some((m) => {
303
+ return searchIssuerAndReason(m, (check) =>
304
+ check?.startsWith('consume-shared-module'),
305
+ );
306
+ })
307
+ );
308
+ }),
309
+ ),
310
+ )
311
+ .map((chunk) => {
312
+ return {
313
+ chunks: chunk.files.map(
314
+ (f) =>
315
+ `${stats.publicPath === 'auto' ? '' : stats.publicPath || ''}${f}`,
316
+ ),
317
+ provides: flatMap(
318
+ chunk.modules.filter((m) =>
319
+ searchIssuerAndReason(m, (check) =>
320
+ check?.startsWith('consume-shared-module'),
321
+ ),
322
+ ),
323
+ (m) =>
324
+ getIssuersAndReasons(m, (issuer) =>
325
+ issuer?.startsWith('consume-shared-module'),
326
+ ),
327
+ )
328
+ .map(parseFederatedIssuer)
329
+ .filter((f) => !!f),
330
+ };
226
331
  })
227
- .filter((c) => c.provides.length > 0);
332
+ .filter((c) => c.provides.length > 0);
228
333
  }
334
+
229
335
  /**
230
336
  *
231
337
  * @param {WebpackStats} stats
@@ -233,210 +339,271 @@ function getMainSharedModules(stats) {
233
339
  * @returns {FederatedStats}
234
340
  */
235
341
  function getFederationStats(stats, federationPluginOptions) {
236
- const exposedModules = Object.entries(federationPluginOptions.exposes).reduce((exposedModules, [exposedAs, exposedFile]) => Object.assign(exposedModules, {
342
+ const exposedModules = Object.entries(federationPluginOptions.exposes).reduce(
343
+ (exposedModules, [exposedAs, exposedFile]) =>
344
+ Object.assign(exposedModules, {
237
345
  [exposedAs]: getExposedModules(stats, exposedFile),
238
- }), {});
239
- /** @type {{ [key: string]: Exposed }} */
240
- const exposes = Object.entries(exposedModules).reduce((exposedChunks, [exposedAs, exposedModules]) => Object.assign(exposedChunks, {
346
+ }),
347
+ {},
348
+ );
349
+
350
+ /** @type {{ [key: string]: Exposed }} */
351
+ const exposes = Object.entries(exposedModules).reduce(
352
+ (exposedChunks, [exposedAs, exposedModules]) =>
353
+ Object.assign(exposedChunks, {
241
354
  [exposedAs]: flatMap(exposedModules, (mod) => getExposed(stats, mod)),
242
- }), {});
243
- /** @type {string} */
244
- const remote = federationPluginOptions.library?.name || federationPluginOptions.name;
245
- const sharedModules = getSharedModules(stats, federationPluginOptions);
246
- const remoteModules = getRemoteModules(stats);
247
- return {
248
- remote,
249
- entry: `${stats.publicPath === 'auto' ? '' : stats.publicPath || ''}${stats.assetsByChunkName[remote] &&
250
- stats.assetsByChunkName[remote].length === 1
251
- ? stats.assetsByChunkName[remote][0]
252
- : federationPluginOptions.filename}`,
253
- sharedModules,
254
- exposes,
255
- remoteModules,
256
- };
355
+ }),
356
+ {},
357
+ );
358
+
359
+ /** @type {string} */
360
+ const remote =
361
+ federationPluginOptions.library?.name || federationPluginOptions.name;
362
+
363
+ const sharedModules = getSharedModules(stats, federationPluginOptions);
364
+ const remoteModules = getRemoteModules(stats);
365
+ return {
366
+ remote,
367
+ entry: `${stats.publicPath === 'auto' ? '' : stats.publicPath || ''}${
368
+ stats.assetsByChunkName[remote] &&
369
+ stats.assetsByChunkName[remote].length === 1
370
+ ? stats.assetsByChunkName[remote][0]
371
+ : federationPluginOptions.filename
372
+ }`,
373
+ sharedModules,
374
+ exposes,
375
+ remoteModules,
376
+ };
257
377
  }
378
+
258
379
  /**
259
380
  * @typedef {object} FederationStatsPluginOptions
260
381
  * @property {string | string[]} filename The filename or an array of filenames in the `output.path` directory to write stats to.
261
382
  */
383
+
262
384
  /**
263
385
  * Writes relevant federation stats to a file for further consumption.
264
386
  */
265
387
  class FederationStatsPlugin {
266
- /**
267
- *
268
- * @param {FederationStatsPluginOptions} options
269
- */
270
- constructor(options) {
271
- if (!options || !options.filename) {
272
- throw new Error('filename option is required.');
273
- }
274
- this._options = options;
388
+ /**
389
+ *
390
+ * @param {FederationStatsPluginOptions} options
391
+ */
392
+ constructor(options) {
393
+ if (!options || !options.filename) {
394
+ throw new Error('filename option is required.');
275
395
  }
276
- /**
277
- *
278
- * @param {import("webpack").Compiler} compiler
279
- */
280
- apply(compiler) {
281
- const federationPlugins = compiler.options.plugins?.filter((plugin) => [
282
- 'NextFederationPlugin',
283
- 'UniversalFederationPlugin',
284
- 'NodeFederationPlugin',
285
- 'ModuleFederationPlugin',
286
- ].includes(plugin.constructor.name) && plugin?._options?.exposes);
287
- if (!federationPlugins || federationPlugins.length === 0) {
288
- console.error('No ModuleFederationPlugin(s) found.');
396
+
397
+ this._options = options;
398
+ }
399
+
400
+ /**
401
+ *
402
+ * @param {import("webpack").Compiler} compiler
403
+ */
404
+ apply(compiler) {
405
+ const federationPlugins = compiler.options.plugins?.filter(
406
+ (plugin) =>
407
+ [
408
+ 'NextFederationPlugin',
409
+ 'UniversalFederationPlugin',
410
+ 'NodeFederationPlugin',
411
+ 'ModuleFederationPlugin',
412
+ ].includes(plugin.constructor.name) && plugin?._options?.exposes,
413
+ );
414
+
415
+ if (!federationPlugins || federationPlugins.length === 0) {
416
+ console.error('No ModuleFederationPlugin(s) found.');
417
+ return;
418
+ }
419
+
420
+ // This is where the plugin is tapping into the Webpack compilation lifecycle.
421
+ // It's listening to the 'thisCompilation' event, which is triggered once for each new compilation.
422
+ compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation) => {
423
+ // 'processAssets' is a hook that gets triggered when Webpack has finished the compilation
424
+ // and is about to generate the final assets. It allows plugins to do additional processing on the assets.
425
+ compilation.hooks.processAssets.tap(
426
+ {
427
+ name: PLUGIN_NAME,
428
+ stage: compilation.constructor.PROCESS_ASSETS_STAGE_REPORT,
429
+ },
430
+ () => {
431
+ // Extract the options from the federation plugins.
432
+ const [federationOpts] = federationPlugins.map(
433
+ (federationPlugin) => federationPlugin?._options,
434
+ );
435
+
436
+ let container;
437
+ // Loop through all entry points and if one matches the name of a federation plugin,
438
+ // store the entry point in the 'container' variable.
439
+ for (const [name, entry] of compilation.entrypoints) {
440
+ if (container) {
441
+ break;
442
+ }
443
+ federationOpts.name.includes(name) && (container = entry);
444
+ }
445
+
446
+ // If no matching entry point was found, exit the function early.
447
+ if (!container) {
289
448
  return;
290
- }
291
- // This is where the plugin is tapping into the Webpack compilation lifecycle.
292
- // It's listening to the 'thisCompilation' event, which is triggered once for each new compilation.
293
- compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation) => {
294
- // 'processAssets' is a hook that gets triggered when Webpack has finished the compilation
295
- // and is about to generate the final assets. It allows plugins to do additional processing on the assets.
296
- compilation.hooks.processAssets.tap({
297
- name: PLUGIN_NAME,
298
- stage: compilation.constructor.PROCESS_ASSETS_STAGE_REPORT,
299
- }, () => {
300
- // Extract the options from the federation plugins.
301
- const [federationOpts] = federationPlugins.map((federationPlugin) => federationPlugin?._options);
302
- let container;
303
- // Loop through all entry points and if one matches the name of a federation plugin,
304
- // store the entry point in the 'container' variable.
305
- for (const [name, entry] of compilation.entrypoints) {
306
- if (container) {
307
- break;
308
- }
309
- federationOpts.name.includes(name) && (container = entry);
310
- }
311
- // If no matching entry point was found, exit the function early.
312
- if (!container) {
313
- return;
314
- }
315
- // Get the chunk associated with the entry point.
316
- container = container?.getEntrypointChunk();
317
- // Get the module associated with the chunk.
318
- const [containerEntryModule] = Array.from(compilation.chunkGraph.getChunkEntryModulesIterable(container));
319
- const { blocks } = containerEntryModule;
320
- const exposedResolved = {};
321
- const builtExposes = {};
322
- // Iterate over each dependency block associated with the entry module.
323
- for (let block of blocks) {
324
- const blockmodule = block;
325
- // Iterate over each dependency within the block.
326
- for (const dep of blockmodule.dependencies) {
327
- // Get the module that corresponds to the dependency.
328
- const connection = compilation.moduleGraph.getConnection(dep);
329
- if (!connection) {
330
- continue;
331
- }
332
- const { module } = connection;
333
- const moduleChunks = compilation.chunkGraph.getModuleChunksIterable(module);
334
- // Iterate over each chunk associated with the module.
335
- for (let exposedChunk of moduleChunks) {
336
- // Determine the runtime for the chunk.
337
- const runtime = typeof exposedChunk.runtime === 'string'
338
- ? new Set([exposedChunk.runtime])
339
- : exposedChunk.runtime;
340
- // Check if the chunk is meant for the same runtime as the entry module.
341
- const isForThisRuntime = runtime.has(containerEntryModule._name);
342
- // Get the root modules for the chunk.
343
- const rootModules = compilation.chunkGraph.getChunkRootModules(exposedChunk);
344
- // Check if the module associated with the dependency is one of the root modules for the chunk.
345
- const moduleActuallyNeedsChunk = rootModules.includes(module);
346
- // If the chunk is not meant for this runtime or the module doesn't need the chunk, skip the rest of this iteration.
347
- if (!isForThisRuntime || !moduleActuallyNeedsChunk) {
348
- continue;
349
- }
350
- // Add the files associated with the chunk to the 'builtExposes' object under the name of the exposed module.
351
- builtExposes[dep.exposedName] = [
352
- ...(builtExposes[dep.exposedName] || []),
353
- ...(exposedChunk.files || []),
354
- ];
355
- }
356
- // Add the module to the 'exposedResolved' object under the name of the exposed module.
357
- exposedResolved[dep.exposedName] = module;
358
- }
359
- }
360
- // Generate a JSON object that contains detailed information about the compilation.
361
- const stats = compilation.getStats().toJson({
362
- all: false,
363
- assets: true,
364
- reasons: true,
365
- modules: true,
366
- children: true,
367
- chunkGroups: true,
368
- chunkModules: true,
369
- chunkOrigins: false,
370
- entrypoints: true,
371
- namedChunkGroups: false,
372
- chunkRelations: true,
373
- chunks: true,
374
- ids: true,
375
- nestedModules: false,
376
- outputPath: true,
377
- publicPath: true,
378
- });
379
- // Apply a function 'getFederationStats' on the stats with the federation plugin options as the second argument.
380
- const federatedModules = getFederationStats(stats, federationOpts);
381
- // Assign the 'builtExposes' object to the 'exposes' property of the 'federatedModules' object.
382
- federatedModules.exposes = builtExposes;
383
- // Apply a function 'getMainSharedModules' on the stats.
384
- const sharedModules = getMainSharedModules(stats);
385
- // Create a Set to hold the vendor chunks.
386
- const vendorChunks = new Set();
387
- // Iterate over the shared modules.
388
- for (const share of sharedModules) {
389
- if (share?.chunks) {
390
- // If a shared module has chunks, add them to the 'vendorChunks' Set.
391
- for (const file of share.chunks) {
392
- vendorChunks.add(file);
393
- }
394
- }
449
+ }
450
+
451
+ // Get the chunk associated with the entry point.
452
+ container = container?.getEntrypointChunk();
453
+
454
+ // Get the module associated with the chunk.
455
+ const [containerEntryModule] = Array.from(
456
+ compilation.chunkGraph.getChunkEntryModulesIterable(container),
457
+ );
458
+
459
+ const { blocks } = containerEntryModule;
460
+
461
+ const exposedResolved = {};
462
+ const builtExposes = {};
463
+
464
+ // Iterate over each dependency block associated with the entry module.
465
+ for (let block of blocks) {
466
+ const blockmodule = block;
467
+
468
+ // Iterate over each dependency within the block.
469
+ for (const dep of blockmodule.dependencies) {
470
+ // Get the module that corresponds to the dependency.
471
+ const connection = compilation.moduleGraph.getConnection(dep);
472
+ if (!connection) {
473
+ continue;
474
+ }
475
+ const { module } = connection;
476
+
477
+ const moduleChunks =
478
+ compilation.chunkGraph.getModuleChunksIterable(module);
479
+ // Iterate over each chunk associated with the module.
480
+ for (let exposedChunk of moduleChunks) {
481
+ // Determine the runtime for the chunk.
482
+ const runtime =
483
+ typeof exposedChunk.runtime === 'string'
484
+ ? new Set([exposedChunk.runtime])
485
+ : exposedChunk.runtime;
486
+
487
+ // Check if the chunk is meant for the same runtime as the entry module.
488
+ const isForThisRuntime = runtime.has(
489
+ containerEntryModule._name,
490
+ );
491
+
492
+ // Get the root modules for the chunk.
493
+ const rootModules =
494
+ compilation.chunkGraph.getChunkRootModules(exposedChunk);
495
+
496
+ // Check if the module associated with the dependency is one of the root modules for the chunk.
497
+ const moduleActuallyNeedsChunk = rootModules.includes(module);
498
+
499
+ // If the chunk is not meant for this runtime or the module doesn't need the chunk, skip the rest of this iteration.
500
+ if (!isForThisRuntime || !moduleActuallyNeedsChunk) {
501
+ continue;
395
502
  }
396
- // Construct an object that contains the shared and federated modules.
397
- const statsResult = {
398
- sharedModules,
399
- federatedModules: [federatedModules],
400
- };
401
- // Convert the 'statsResult' object to a JSON string.
402
- const statsJson = JSON.stringify(statsResult);
403
- // Convert the JSON string to a buffer.
404
- const statsBuffer = Buffer.from(statsJson, 'utf-8');
405
- // Construct an object that represents the source of the final asset.
406
- const statsSource = {
407
- source: () => statsBuffer,
408
- size: () => statsBuffer.length,
409
- };
410
- // Get the filename of the final asset from the plugin options.
411
- const { filename } = this._options;
412
- // If filename is an array, loop over it to emit or update assets.
413
- if (Array.isArray(filename)) {
414
- for (const file of filename) {
415
- // Check if an asset with the same filename already exists.
416
- const asset = compilation.getAsset(file);
417
- // If an asset with the same filename already exists, update it. Otherwise, create a new asset.
418
- if (asset) {
419
- compilation.updateAsset(file, statsSource);
420
- }
421
- else {
422
- compilation.emitAsset(file, statsSource);
423
- }
424
- }
425
- }
426
- else {
427
- // Check if an asset with the same filename already exists.
428
- const asset = compilation.getAsset(filename);
429
- // If an asset with the same filename already exists, update it. Otherwise, create a new asset.
430
- if (asset) {
431
- compilation.updateAsset(filename, statsSource);
432
- }
433
- else {
434
- compilation.emitAsset(filename, statsSource);
435
- }
436
- }
437
- });
438
- });
439
- }
503
+
504
+ // Add the files associated with the chunk to the 'builtExposes' object under the name of the exposed module.
505
+ builtExposes[dep.exposedName] = [
506
+ ...(builtExposes[dep.exposedName] || []),
507
+ ...(exposedChunk.files || []),
508
+ ];
509
+ }
510
+
511
+ // Add the module to the 'exposedResolved' object under the name of the exposed module.
512
+ exposedResolved[dep.exposedName] = module;
513
+ }
514
+ }
515
+
516
+ // Generate a JSON object that contains detailed information about the compilation.
517
+ const stats = compilation.getStats().toJson({
518
+ all: false,
519
+ assets: true,
520
+ reasons: true,
521
+ modules: true,
522
+ children: true,
523
+ chunkGroups: true,
524
+ chunkModules: true,
525
+ chunkOrigins: false,
526
+ entrypoints: true,
527
+ namedChunkGroups: false,
528
+ chunkRelations: true,
529
+ chunks: true,
530
+ ids: true,
531
+ nestedModules: false,
532
+ outputPath: true,
533
+ publicPath: true,
534
+ });
535
+
536
+ // Apply a function 'getFederationStats' on the stats with the federation plugin options as the second argument.
537
+ const federatedModules = getFederationStats(stats, federationOpts);
538
+
539
+ // Assign the 'builtExposes' object to the 'exposes' property of the 'federatedModules' object.
540
+ federatedModules.exposes = builtExposes;
541
+
542
+ // Apply a function 'getMainSharedModules' on the stats.
543
+ const sharedModules = getMainSharedModules(stats);
544
+
545
+ // Create a Set to hold the vendor chunks.
546
+ const vendorChunks = new Set();
547
+
548
+ // Iterate over the shared modules.
549
+ for (const share of sharedModules) {
550
+ if (share?.chunks) {
551
+ // If a shared module has chunks, add them to the 'vendorChunks' Set.
552
+ for (const file of share.chunks) {
553
+ vendorChunks.add(file);
554
+ }
555
+ }
556
+ }
557
+
558
+ // Construct an object that contains the shared and federated modules.
559
+ const statsResult = {
560
+ sharedModules,
561
+ federatedModules: [federatedModules],
562
+ };
563
+
564
+ // Convert the 'statsResult' object to a JSON string.
565
+ const statsJson = JSON.stringify(statsResult);
566
+
567
+ // Convert the JSON string to a buffer.
568
+ const statsBuffer = Buffer.from(statsJson, 'utf-8');
569
+
570
+ // Construct an object that represents the source of the final asset.
571
+ const statsSource = {
572
+ source: () => statsBuffer,
573
+ size: () => statsBuffer.length,
574
+ };
575
+
576
+ // Get the filename of the final asset from the plugin options.
577
+ const { filename } = this._options;
578
+
579
+ // If filename is an array, loop over it to emit or update assets.
580
+ if (Array.isArray(filename)) {
581
+ for (const file of filename) {
582
+ // Check if an asset with the same filename already exists.
583
+ const asset = compilation.getAsset(file);
584
+
585
+ // If an asset with the same filename already exists, update it. Otherwise, create a new asset.
586
+ if (asset) {
587
+ compilation.updateAsset(file, statsSource);
588
+ } else {
589
+ compilation.emitAsset(file, statsSource);
590
+ }
591
+ }
592
+ } else {
593
+ // Check if an asset with the same filename already exists.
594
+ const asset = compilation.getAsset(filename);
595
+
596
+ // If an asset with the same filename already exists, update it. Otherwise, create a new asset.
597
+ if (asset) {
598
+ compilation.updateAsset(filename, statsSource);
599
+ } else {
600
+ compilation.emitAsset(filename, statsSource);
601
+ }
602
+ }
603
+ },
604
+ );
605
+ });
606
+ }
440
607
  }
608
+
441
609
  module.exports = FederationStatsPlugin;
442
- //# sourceMappingURL=ChunkCorrelationPlugin.js.map