@module-federation/nextjs-mf 5.2.2 → 5.3.1
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/lib/ModuleFederationPlugin.js +80 -0
- package/lib/NextFederationPlugin.js +524 -1
- package/lib/_virtual/_tslib.js +101 -0
- package/lib/client/CombinedPages.d.ts +28 -0
- package/lib/client/CombinedPages.d.ts.map +1 -0
- package/lib/client/CombinedPages.js +60 -0
- package/lib/client/MFClient.d.ts +70 -0
- package/lib/client/MFClient.d.ts.map +1 -0
- package/lib/client/MFClient.js +197 -0
- package/lib/client/RemoteContainer.d.ts +58 -0
- package/lib/client/RemoteContainer.d.ts.map +1 -0
- package/lib/client/RemoteContainer.js +161 -0
- package/lib/client/RemotePages.d.ts +48 -0
- package/lib/client/RemotePages.d.ts.map +1 -0
- package/lib/client/RemotePages.js +168 -0
- package/lib/client/UrlNode.d.ts +18 -0
- package/lib/client/UrlNode.d.ts.map +1 -0
- package/lib/client/UrlNode.js +162 -0
- package/lib/client/helpers.d.ts +17 -0
- package/lib/client/helpers.d.ts.map +1 -0
- package/lib/client/helpers.js +108 -0
- package/lib/client/useMFClient.d.ts +25 -0
- package/lib/client/useMFClient.d.ts.map +1 -0
- package/lib/client/useMFClient.js +79 -0
- package/lib/client/useMFRemote.d.ts +17 -0
- package/lib/client/useMFRemote.d.ts.map +1 -0
- package/lib/client/useMFRemote.js +72 -0
- package/lib/include-defaults.js +1 -3
- package/lib/internal.js +241 -0
- package/lib/loaders/UrlNode.js +209 -0
- package/lib/loaders/nextPageMapLoader.js +63 -13
- package/lib/loaders/patchNextClientPageLoader.js +53 -0
- package/lib/node-plugin/streaming/CommonJsChunkLoadingPlugin.js +86 -0
- package/lib/node-plugin/streaming/LoadFileChunkLoadingRuntimeModule.js +410 -0
- package/lib/node-plugin/streaming/NodeRuntime.js +147 -0
- package/lib/node-plugin/streaming/index.js +44 -0
- package/lib/node-plugin/streaming/loadScript.js +55 -0
- package/lib/plugins/DevHmrFixInvalidPongPlugin.js +60 -0
- package/lib/utils.js +14 -3
- package/node-plugin/README.md +27 -0
- package/node-plugin/package.json +4 -0
- package/node-plugin/streaming/CommonJsChunkLoadingPlugin.js +89 -0
- package/node-plugin/streaming/LoadFileChunkLoadingRuntimeModule.js +410 -0
- package/node-plugin/streaming/NodeRuntime.js +245 -0
- package/node-plugin/streaming/index.js +42 -0
- package/node-plugin/streaming/loadScript.js +51 -0
- package/package.json +22 -5
- package/tsconfig.json +33 -0
- package/lib/NextFederationPlugin2.js +0 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/*
|
|
2
|
+
MIT License http://www.opensource.org/licenses/mit-license.php
|
|
3
|
+
Author Tobias Koppers @sokra and Zackary Jackson @ScriptedAlchemy
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
/*
|
|
8
|
+
plugin was copied over because ContainerPlugin is called in compiler.hooks.afterPlugins which doest seem to work in child compiler
|
|
9
|
+
*/
|
|
10
|
+
/** @typedef {import("../../declarations/plugins/container/ModuleFederationPlugin").ExternalsType} ExternalsType */
|
|
11
|
+
/** @typedef {import("../../declarations/plugins/container/ModuleFederationPlugin").ModuleFederationPluginOptions} ModuleFederationPluginOptions */
|
|
12
|
+
|
|
13
|
+
/** @typedef {import("webpack").Shared} Shared */
|
|
14
|
+
/** @typedef {import("webpack").Compiler} Compiler */
|
|
15
|
+
|
|
16
|
+
class ModuleFederationPlugin {
|
|
17
|
+
/**
|
|
18
|
+
* @param {ModuleFederationPluginOptions} options options
|
|
19
|
+
*/
|
|
20
|
+
constructor(options) {
|
|
21
|
+
this._options = options;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Apply the plugin
|
|
26
|
+
* @param {Compiler} compiler the compiler instance
|
|
27
|
+
* @returns {void}
|
|
28
|
+
*/
|
|
29
|
+
apply(compiler) {
|
|
30
|
+
const { _options: options } = this;
|
|
31
|
+
const webpack = compiler.webpack;
|
|
32
|
+
const { ContainerPlugin, ContainerReferencePlugin } = webpack.container;
|
|
33
|
+
const { SharePlugin } = webpack.sharing;
|
|
34
|
+
const library = options.library || { type: 'var', name: options.name };
|
|
35
|
+
const remoteType =
|
|
36
|
+
options.remoteType ||
|
|
37
|
+
(options.library && /** @type {ExternalsType} */ options.library.type) ||
|
|
38
|
+
'script';
|
|
39
|
+
if (
|
|
40
|
+
library &&
|
|
41
|
+
!compiler.options.output.enabledLibraryTypes.includes(library.type)
|
|
42
|
+
) {
|
|
43
|
+
compiler.options.output.enabledLibraryTypes.push(library.type);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (
|
|
47
|
+
options.exposes &&
|
|
48
|
+
(Array.isArray(options.exposes)
|
|
49
|
+
? options.exposes.length > 0
|
|
50
|
+
: Object.keys(options.exposes).length > 0)
|
|
51
|
+
) {
|
|
52
|
+
new ContainerPlugin({
|
|
53
|
+
name: options.name,
|
|
54
|
+
library,
|
|
55
|
+
filename: options.filename,
|
|
56
|
+
runtime: options.runtime,
|
|
57
|
+
exposes: options.exposes,
|
|
58
|
+
}).apply(compiler);
|
|
59
|
+
}
|
|
60
|
+
if (
|
|
61
|
+
options.remotes &&
|
|
62
|
+
(Array.isArray(options.remotes)
|
|
63
|
+
? options.remotes.length > 0
|
|
64
|
+
: Object.keys(options.remotes).length > 0)
|
|
65
|
+
) {
|
|
66
|
+
new ContainerReferencePlugin({
|
|
67
|
+
remoteType,
|
|
68
|
+
remotes: options.remotes,
|
|
69
|
+
}).apply(compiler);
|
|
70
|
+
}
|
|
71
|
+
if (options.shared) {
|
|
72
|
+
new SharePlugin({
|
|
73
|
+
shared: options.shared,
|
|
74
|
+
shareScope: options.shareScope,
|
|
75
|
+
}).apply(compiler);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export default ModuleFederationPlugin;
|
|
@@ -1,7 +1,530 @@
|
|
|
1
|
+
/*
|
|
2
|
+
MIT License http://www.opensource.org/licenses/mit-license.php
|
|
3
|
+
Author Zackary Jackson @ScriptedAlchemy
|
|
4
|
+
*/
|
|
1
5
|
'use strict';
|
|
2
6
|
|
|
3
|
-
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
const {
|
|
10
|
+
injectRuleLoader,
|
|
11
|
+
hasLoader,
|
|
12
|
+
toDisplayErrors,
|
|
13
|
+
} = require('./loaders/helpers');
|
|
14
|
+
const { exposeNextjsPages } = require('./loaders/nextPageMapLoader');
|
|
15
|
+
const DevHmrFixInvalidPongPlugin = require('./plugins/DevHmrFixInvalidPongPlugin');
|
|
4
16
|
|
|
17
|
+
import {
|
|
18
|
+
reKeyHostShared,
|
|
19
|
+
DEFAULT_SHARE_SCOPE,
|
|
20
|
+
extractUrlAndGlobal,
|
|
21
|
+
generateRemoteTemplate,
|
|
22
|
+
internalizeSharedPackages,
|
|
23
|
+
getOutputPath,
|
|
24
|
+
externalizedShares,
|
|
25
|
+
removePlugins
|
|
26
|
+
} from './internal';
|
|
27
|
+
import StreamingTargetPlugin from '../node-plugin/streaming';
|
|
28
|
+
import NodeFederationPlugin from '../node-plugin/streaming/NodeRuntime';
|
|
29
|
+
import ChildFriendlyModuleFederationPlugin from './ModuleFederationPlugin';
|
|
5
30
|
|
|
31
|
+
const CHILD_PLUGIN_NAME = 'ChildFederationPlugin';
|
|
32
|
+
|
|
33
|
+
class RemoveRRRuntimePlugin {
|
|
34
|
+
/**
|
|
35
|
+
* Apply the plugin
|
|
36
|
+
* @param {Compiler} compiler the compiler instance
|
|
37
|
+
* @returns {void}
|
|
38
|
+
*/
|
|
39
|
+
apply(compiler) {
|
|
40
|
+
const webpack = compiler.webpack;
|
|
41
|
+
// only impacts dev mode - dont waste the memory during prod build
|
|
42
|
+
if (compiler.options.mode === 'development') {
|
|
43
|
+
compiler.hooks.thisCompilation.tap(
|
|
44
|
+
'RemoveRRRuntimePlugin',
|
|
45
|
+
(compilation) => {
|
|
46
|
+
compilation.hooks.processAssets.tap(
|
|
47
|
+
{
|
|
48
|
+
name: 'RemoveRRRuntimePlugin',
|
|
49
|
+
state:
|
|
50
|
+
compilation.constructor.PROCESS_ASSETS_STAGE_OPTIMIZE_INLINE,
|
|
51
|
+
},
|
|
52
|
+
(assets) => {
|
|
53
|
+
//can this be improved? I need react refresh not to cause global collision in dev mode
|
|
54
|
+
Object.keys(assets).forEach((filename) => {
|
|
55
|
+
if (filename.endsWith('.js') || filename.endsWith('.mjs')) {
|
|
56
|
+
const asset = compilation.getAsset(filename);
|
|
57
|
+
// easiest way to solve it is to prevent react refresh helpers from running when its a federated module chunk
|
|
58
|
+
const newSource = asset.source
|
|
59
|
+
.source()
|
|
60
|
+
.replace(/RefreshHelpers/g, 'NoExist');
|
|
61
|
+
const updatedAsset = new webpack.sources.RawSource(newSource);
|
|
62
|
+
|
|
63
|
+
if (asset) {
|
|
64
|
+
compilation.updateAsset(filename, updatedAsset);
|
|
65
|
+
} else {
|
|
66
|
+
compilation.emitAsset(filename, updatedAsset);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const computeRemoteFilename = (isServer, filename) => {
|
|
79
|
+
if (isServer && filename) {
|
|
80
|
+
return path.basename(filename);
|
|
81
|
+
}
|
|
82
|
+
return filename;
|
|
83
|
+
};
|
|
84
|
+
const childCompilers = {};
|
|
85
|
+
class ChildFederationPlugin {
|
|
86
|
+
constructor(options, extraOptions = {}) {
|
|
87
|
+
this._options = options;
|
|
88
|
+
this._extraOptions = extraOptions;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Apply the plugin
|
|
92
|
+
* @param {Compiler} compiler the compiler instance
|
|
93
|
+
* @returns {void}
|
|
94
|
+
*/
|
|
95
|
+
apply(compiler) {
|
|
96
|
+
const webpack = compiler.webpack;
|
|
97
|
+
const LibraryPlugin = webpack.library.EnableLibraryPlugin;
|
|
98
|
+
const LoaderTargetPlugin = webpack.LoaderTargetPlugin;
|
|
99
|
+
const library = compiler.options.output.library;
|
|
100
|
+
const isServer = compiler.options.name === 'server';
|
|
101
|
+
const isDev = compiler.options.mode === 'development';
|
|
102
|
+
let outputPath;
|
|
103
|
+
if (isDev && isServer) {
|
|
104
|
+
outputPath = path.join(getOutputPath(compiler), 'static/ssr');
|
|
105
|
+
} else {
|
|
106
|
+
if (isServer) {
|
|
107
|
+
outputPath = path.join(getOutputPath(compiler), 'static/ssr');
|
|
108
|
+
} else {
|
|
109
|
+
outputPath = compiler.options.output.path;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
compiler.hooks.thisCompilation.tap(CHILD_PLUGIN_NAME, (compilation) => {
|
|
114
|
+
const buildName = this._options.name;
|
|
115
|
+
const childOutput = {
|
|
116
|
+
...compiler.options.output,
|
|
117
|
+
path: outputPath,
|
|
118
|
+
// path: deriveOutputPath(isServer, compiler.options.output.path),
|
|
119
|
+
publicPath: 'auto',
|
|
120
|
+
chunkLoadingGlobal: buildName + 'chunkLoader',
|
|
121
|
+
uniqueName: buildName,
|
|
122
|
+
library: {
|
|
123
|
+
name: buildName,
|
|
124
|
+
type: library.type,
|
|
125
|
+
},
|
|
126
|
+
chunkFilename: compiler.options.output.chunkFilename.replace(
|
|
127
|
+
'.js',
|
|
128
|
+
'-fed.js'
|
|
129
|
+
),
|
|
130
|
+
filename: compiler.options.output.filename.replace('.js', '-fed.js'),
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// using ModuleFederationPlugin does not work, i had to fork because of afterPlugins hook on containerPlugin.
|
|
134
|
+
const FederationPlugin = ChildFriendlyModuleFederationPlugin;
|
|
135
|
+
|
|
136
|
+
const federationPluginOptions = {
|
|
137
|
+
// library: {type: 'var', name: buildName},
|
|
138
|
+
...this._options,
|
|
139
|
+
filename: computeRemoteFilename(isServer, this._options.filename),
|
|
140
|
+
exposes: {
|
|
141
|
+
...this._options.exposes,
|
|
142
|
+
...(this._extraOptions.exposePages
|
|
143
|
+
? exposeNextjsPages(compiler.options.context)
|
|
144
|
+
: {}),
|
|
145
|
+
},
|
|
146
|
+
runtime: false,
|
|
147
|
+
shared: {
|
|
148
|
+
...(this._extraOptions.skipSharingNextInternals
|
|
149
|
+
? {}
|
|
150
|
+
: externalizedShares),
|
|
151
|
+
...this._options.shared,
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
let plugins;
|
|
156
|
+
if (compiler.options.name === 'client') {
|
|
157
|
+
plugins = [
|
|
158
|
+
new FederationPlugin(federationPluginOptions),
|
|
159
|
+
new webpack.web.JsonpTemplatePlugin(childOutput),
|
|
160
|
+
new LoaderTargetPlugin('web'),
|
|
161
|
+
new LibraryPlugin(this._options.library.type),
|
|
162
|
+
new webpack.DefinePlugin({
|
|
163
|
+
'process.env.REMOTES': createRuntimeVariables(
|
|
164
|
+
this._options.remotes
|
|
165
|
+
),
|
|
166
|
+
'process.env.CURRENT_HOST': JSON.stringify(this._options.name),
|
|
167
|
+
}),
|
|
168
|
+
new AddRuntimeRequirementToPromiseExternal(),
|
|
169
|
+
];
|
|
170
|
+
} else if (compiler.options.name === 'server') {
|
|
171
|
+
plugins = [
|
|
172
|
+
new NodeFederationPlugin(federationPluginOptions, {ModuleFederationPlugin: FederationPlugin}),
|
|
173
|
+
new webpack.node.NodeTemplatePlugin(childOutput),
|
|
174
|
+
//TODO: Externals function needs to internalize any shared module for host and remote build
|
|
175
|
+
new webpack.ExternalsPlugin(compiler.options.externalsType, [
|
|
176
|
+
// next dynamic needs to be within webpack, cannot be externalized
|
|
177
|
+
...Object.keys(DEFAULT_SHARE_SCOPE).filter(
|
|
178
|
+
(k) => k !== 'next/dynamic'
|
|
179
|
+
),
|
|
180
|
+
'react/jsx-runtime',
|
|
181
|
+
'react/jsx-dev-runtime',
|
|
182
|
+
]),
|
|
183
|
+
// new LoaderTargetPlugin('async-node'),
|
|
184
|
+
new StreamingTargetPlugin(federationPluginOptions, webpack),
|
|
185
|
+
new LibraryPlugin(federationPluginOptions.library.type),
|
|
186
|
+
// new webpack.DefinePlugin({
|
|
187
|
+
// 'process.env.REMOTES': JSON.stringify(this._options.remotes),
|
|
188
|
+
// 'process.env.CURRENT_HOST': JSON.stringify(this._options.name),
|
|
189
|
+
// }),
|
|
190
|
+
new AddRuntimeRequirementToPromiseExternal(),
|
|
191
|
+
];
|
|
192
|
+
}
|
|
193
|
+
const childCompiler = compilation.createChildCompiler(
|
|
194
|
+
CHILD_PLUGIN_NAME,
|
|
195
|
+
childOutput,
|
|
196
|
+
plugins
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
childCompiler.outputPath = outputPath;
|
|
200
|
+
childCompiler.options.module.rules.forEach((rule) => {
|
|
201
|
+
// next-image-loader fix which adds remote's hostname to the assets url
|
|
202
|
+
if (
|
|
203
|
+
this._extraOptions.enableImageLoaderFix &&
|
|
204
|
+
hasLoader(rule, 'next-image-loader')
|
|
205
|
+
) {
|
|
206
|
+
injectRuleLoader(rule, {
|
|
207
|
+
loader: path.resolve(__dirname, './loaders/fixImageLoader.js'),
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// url-loader fix for which adds remote's hostname to the assets url
|
|
212
|
+
if (
|
|
213
|
+
this._extraOptions.enableUrlLoaderFix &&
|
|
214
|
+
hasLoader(rule, 'url-loader')
|
|
215
|
+
) {
|
|
216
|
+
injectRuleLoader({
|
|
217
|
+
loader: path.resolve(__dirname, './loaders/fixUrlLoader.js'),
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
childCompiler.options.experiments.lazyCompilation = false;
|
|
222
|
+
childCompiler.options.optimization.runtimeChunk = false;
|
|
223
|
+
// no custom chunk splitting should be derived from host (next)
|
|
224
|
+
delete childCompiler.options.optimization.splitChunks;
|
|
225
|
+
childCompiler.outputFileSystem = fs;
|
|
226
|
+
|
|
227
|
+
if (compiler.options.optimization.minimize) {
|
|
228
|
+
for (const minimizer of compiler.options.optimization.minimizer) {
|
|
229
|
+
if (typeof minimizer === "function") {
|
|
230
|
+
minimizer.call(childCompiler, childCompiler)
|
|
231
|
+
} else if (minimizer !== "...") {
|
|
232
|
+
minimizer.apply(childCompiler);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
new RemoveRRRuntimePlugin().apply(childCompiler);
|
|
238
|
+
|
|
239
|
+
const MiniCss = childCompiler.options.plugins.find((p) => {
|
|
240
|
+
return p.constructor.name === 'NextMiniCssExtractPlugin';
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
childCompiler.options.plugins = childCompiler.options.plugins.filter(
|
|
244
|
+
(plugin) => !removePlugins.includes(plugin.constructor.name)
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
if (MiniCss) {
|
|
248
|
+
// grab mini-css and reconfigure it to avoid conflicts with host
|
|
249
|
+
new MiniCss.constructor({
|
|
250
|
+
...MiniCss.options,
|
|
251
|
+
filename: MiniCss.options.filename.replace('.css', '-fed.css'),
|
|
252
|
+
chunkFilename: MiniCss.options.chunkFilename.replace(
|
|
253
|
+
'.css',
|
|
254
|
+
'-fed.css'
|
|
255
|
+
),
|
|
256
|
+
}).apply(childCompiler);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
// TODO: this can likely be deleted now, if running server child compiler under client is the best way to go
|
|
261
|
+
// help wanted for all asset pipeline stuff below
|
|
262
|
+
// let childAssets
|
|
263
|
+
// if (isServer) {
|
|
264
|
+
// childAssets = new Promise((resolve) => {
|
|
265
|
+
// childCompiler.hooks.afterEmit.tap(
|
|
266
|
+
// CHILD_PLUGIN_NAME,
|
|
267
|
+
// (childCompilation) => {
|
|
268
|
+
// console.log('after emit assets server');
|
|
269
|
+
// resolve(childCompilation.assets);
|
|
270
|
+
// }
|
|
271
|
+
// );
|
|
272
|
+
// });
|
|
273
|
+
// } else {
|
|
274
|
+
// if(isDev) {
|
|
275
|
+
// childAssets = new Promise((resolve) => {
|
|
276
|
+
// childCompiler.hooks.afterEmit.tap(
|
|
277
|
+
// CHILD_PLUGIN_NAME,
|
|
278
|
+
// (childCompilation) => {
|
|
279
|
+
// resolve(childCompilation.assets);
|
|
280
|
+
// }
|
|
281
|
+
// );
|
|
282
|
+
// });
|
|
283
|
+
//
|
|
284
|
+
// } else {
|
|
285
|
+
//
|
|
286
|
+
// TODO: improve this
|
|
287
|
+
// childAssets = new Promise((resolve, reject) => {
|
|
288
|
+
// fs.readdir(
|
|
289
|
+
// path.join(childCompiler.context, '.next/ssr'),
|
|
290
|
+
// function (err, files) {
|
|
291
|
+
// if (err) {
|
|
292
|
+
// reject('Unable to scan directory: ' + err);
|
|
293
|
+
// return;
|
|
294
|
+
// }
|
|
295
|
+
//
|
|
296
|
+
// const allFiles = files.map(function (file) {
|
|
297
|
+
// return new Promise((res, rej) => {
|
|
298
|
+
// fs.readFile(
|
|
299
|
+
// path.join(childCompiler.context, '.next/ssr', file),
|
|
300
|
+
// (err, data) => {
|
|
301
|
+
// if (err) rej(err);
|
|
302
|
+
// compilation.assets[path.join('static/ssr', file)] = new compiler.webpack.sources.RawSource(data)
|
|
303
|
+
// res();
|
|
304
|
+
// }
|
|
305
|
+
// );
|
|
306
|
+
// });
|
|
307
|
+
// });
|
|
308
|
+
// Promise.all(allFiles).then(resolve).catch(reject)
|
|
309
|
+
// }
|
|
310
|
+
// );
|
|
311
|
+
// });
|
|
312
|
+
// }
|
|
313
|
+
// }
|
|
314
|
+
// on main compiler add extra assets from server output to browser build
|
|
315
|
+
// compilation.hooks.additionalAssets.tapPromise(CHILD_PLUGIN_NAME, () => {
|
|
316
|
+
// console.log('additional hooks', compiler.options.name);
|
|
317
|
+
// console.log('in additional assets hook for main build');
|
|
318
|
+
// return childAssets
|
|
319
|
+
// });
|
|
320
|
+
|
|
321
|
+
// cache the serer compiler instance, we will run the server child compiler during the client main compilation
|
|
322
|
+
// we need to do this because i need access to data from the client build to inject into the server build
|
|
323
|
+
// in prod builds, server build runs first, followed by client build
|
|
324
|
+
// in dev, client build runs first, followed by server build
|
|
325
|
+
childCompilers[compiler.options.name] = childCompiler;
|
|
326
|
+
|
|
327
|
+
if (isDev) {
|
|
328
|
+
// in dev, run the compilers in the order they are created (client, server)
|
|
329
|
+
childCompiler.run((err, stats) => {
|
|
330
|
+
if (err) {
|
|
331
|
+
compilation.errors.push(err);
|
|
332
|
+
}
|
|
333
|
+
if (stats && stats.hasErrors()) {
|
|
334
|
+
compilation.errors.push(
|
|
335
|
+
new Error(toDisplayErrors(stats.compilation.errors))
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
// in prod, if client
|
|
340
|
+
} else if (!isServer) {
|
|
341
|
+
// if ssr enabled and server in compiler cache
|
|
342
|
+
if(childCompilers['server']) {
|
|
343
|
+
//wrong hook for this
|
|
344
|
+
// add hook for additional assets to prevent compile from sealing.
|
|
345
|
+
compilation.hooks.additionalAssets.tapPromise(CHILD_PLUGIN_NAME, () => {
|
|
346
|
+
return new Promise((res, rej) => {
|
|
347
|
+
// run server child compilation during client main compilation
|
|
348
|
+
childCompilers['server'].run((err, stats) => {
|
|
349
|
+
if (err) {
|
|
350
|
+
compilation.errors.push(err);
|
|
351
|
+
rej()
|
|
352
|
+
}
|
|
353
|
+
if (stats && stats.hasErrors()) {
|
|
354
|
+
compilation.errors.push(
|
|
355
|
+
new Error(toDisplayErrors(stats.compilation.errors))
|
|
356
|
+
);
|
|
357
|
+
rej()
|
|
358
|
+
}
|
|
359
|
+
res()
|
|
360
|
+
});
|
|
361
|
+
});
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
// run client child compiler like normal
|
|
365
|
+
childCompiler.run((err, stats) => {
|
|
366
|
+
if (err) {
|
|
367
|
+
compilation.errors.push(err);
|
|
368
|
+
}
|
|
369
|
+
if (stats && stats.hasErrors()) {
|
|
370
|
+
compilation.errors.push(
|
|
371
|
+
new Error(toDisplayErrors(stats.compilation.errors))
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
class AddRuntimeRequirementToPromiseExternal {
|
|
381
|
+
/**
|
|
382
|
+
* Apply the plugin
|
|
383
|
+
* @param {Compiler} compiler the compiler instance
|
|
384
|
+
* @returns {void}
|
|
385
|
+
*/
|
|
386
|
+
apply(compiler) {
|
|
387
|
+
compiler.hooks.compilation.tap(
|
|
388
|
+
'AddRuntimeRequirementToPromiseExternal',
|
|
389
|
+
(compilation) => {
|
|
390
|
+
const RuntimeGlobals = compiler.webpack.RuntimeGlobals;
|
|
391
|
+
// if (compilation.outputOptions.trustedTypes) {
|
|
392
|
+
compilation.hooks.additionalModuleRuntimeRequirements.tap(
|
|
393
|
+
'AddRuntimeRequirementToPromiseExternal',
|
|
394
|
+
(module, set, context) => {
|
|
395
|
+
if (module.externalType === 'promise') {
|
|
396
|
+
set.add(RuntimeGlobals.loadScript);
|
|
397
|
+
set.add(RuntimeGlobals.require);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
);
|
|
401
|
+
// }
|
|
402
|
+
}
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function createRuntimeVariables(remotes) {
|
|
408
|
+
return Object.entries(remotes).reduce((acc, remote) => {
|
|
409
|
+
// handle promise new promise and external new promise
|
|
410
|
+
if (remote[1].startsWith('promise ') || remote[1].startsWith('external ')) {
|
|
411
|
+
const promiseCall = remote[1]
|
|
412
|
+
.replace('promise ', '')
|
|
413
|
+
.replace('external ', '');
|
|
414
|
+
acc[remote[0]] = `function() {
|
|
415
|
+
return ${promiseCall}
|
|
416
|
+
}`;
|
|
417
|
+
return acc;
|
|
418
|
+
}
|
|
419
|
+
// if somehow its just the @ syntax or something else, pass it through
|
|
420
|
+
acc[remote[0]] = JSON.stringify(remote[1]);
|
|
421
|
+
return acc;
|
|
422
|
+
}, {});
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
class NextFederationPlugin {
|
|
426
|
+
constructor(options) {
|
|
427
|
+
const {extraOptions, ...mainOpts} = options;
|
|
428
|
+
this._options = mainOpts;
|
|
429
|
+
this._extraOptions = extraOptions || {};
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Apply the plugin
|
|
434
|
+
* @param {Compiler} compiler the compiler instance
|
|
435
|
+
* @returns {void}
|
|
436
|
+
*/
|
|
437
|
+
apply(compiler) {
|
|
438
|
+
const isServer = compiler.options.name === 'server';
|
|
439
|
+
const webpack = compiler.webpack;
|
|
440
|
+
if (isServer) {
|
|
441
|
+
console.error('[nextjs-mf] WARNING: SSR IS NOT FULLY SUPPORTED YET, Only use pluign on client builds');
|
|
442
|
+
// target false because we use our own target for node env
|
|
443
|
+
compiler.options.target = false;
|
|
444
|
+
new StreamingTargetPlugin(this._options, webpack).apply(compiler);
|
|
445
|
+
this._options.library = {};
|
|
446
|
+
this._options.library.type = 'commonjs-module';
|
|
447
|
+
this._options.library.name = this._options.name;
|
|
448
|
+
// output remote to ssr if server
|
|
449
|
+
this._options.filename = this._options.filename.replace(
|
|
450
|
+
'/chunks',
|
|
451
|
+
'/ssr'
|
|
452
|
+
);
|
|
453
|
+
|
|
454
|
+
// should this be a plugin that we apply to the compiler?
|
|
455
|
+
internalizeSharedPackages(this._options, compiler);
|
|
456
|
+
} else {
|
|
457
|
+
if (this._extraOptions.automaticPageStitching) {
|
|
458
|
+
compiler.options.module.rules.push({
|
|
459
|
+
test: /next[\\/]dist[\\/]client[\\/]page-loader\.js$/,
|
|
460
|
+
loader: path.resolve(__dirname, './loaders/patchNextClientPageLoader'),
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
if (this._options.remotes) {
|
|
464
|
+
const parsedRemotes = Object.entries(this._options.remotes).reduce(
|
|
465
|
+
(acc, remote) => {
|
|
466
|
+
if (remote[1].includes('@')) {
|
|
467
|
+
const [url, global] = extractUrlAndGlobal(remote[1]);
|
|
468
|
+
acc[remote[0]] = generateRemoteTemplate(url, global);
|
|
469
|
+
return acc;
|
|
470
|
+
}
|
|
471
|
+
acc[remote[0]] = remote[1];
|
|
472
|
+
return acc;
|
|
473
|
+
},
|
|
474
|
+
{}
|
|
475
|
+
);
|
|
476
|
+
this._options.remotes = parsedRemotes;
|
|
477
|
+
}
|
|
478
|
+
if (this._options.library) {
|
|
479
|
+
console.error('[mf] you cannot set custom library');
|
|
480
|
+
}
|
|
481
|
+
this._options.library = {
|
|
482
|
+
// assign remote name to object to avoid SWC mangling top level variable
|
|
483
|
+
type: 'window',
|
|
484
|
+
name: this._options.name,
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
//todo runtime variable creation needs to be applied for server as well. this is just for client
|
|
488
|
+
// todo: this needs to be refactored into something more comprehensive. this is just a quick fix
|
|
489
|
+
new webpack.DefinePlugin({
|
|
490
|
+
'process.env.REMOTES': createRuntimeVariables(this._options.remotes),
|
|
491
|
+
'process.env.CURRENT_HOST': JSON.stringify(this._options.name),
|
|
492
|
+
}).apply(compiler);
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
const ModuleFederationPlugin = {
|
|
496
|
+
client: webpack.container.ModuleFederationPlugin,
|
|
497
|
+
server: NodeFederationPlugin,
|
|
498
|
+
}[compiler.options.name];
|
|
499
|
+
// ignore edge runtime and middleware builds
|
|
500
|
+
if (ModuleFederationPlugin) {
|
|
501
|
+
const internalShare = reKeyHostShared(this._options.shared);
|
|
502
|
+
const hostFederationPluginOptions = {
|
|
503
|
+
...this._options,
|
|
504
|
+
exposes: {},
|
|
505
|
+
shared: {
|
|
506
|
+
noop: {
|
|
507
|
+
import: 'data:text/javascript,module.exports = {};',
|
|
508
|
+
requiredVersion: false,
|
|
509
|
+
eager: true,
|
|
510
|
+
version: '0',
|
|
511
|
+
},
|
|
512
|
+
...internalShare,
|
|
513
|
+
},
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
compiler.options.optimization.chunkIds = 'named';
|
|
517
|
+
|
|
518
|
+
new ModuleFederationPlugin(hostFederationPluginOptions, {
|
|
519
|
+
ModuleFederationPlugin,
|
|
520
|
+
}).apply(compiler);
|
|
521
|
+
new ChildFederationPlugin(this._options, this._extraOptions).apply(compiler);
|
|
522
|
+
new AddRuntimeRequirementToPromiseExternal().apply(compiler);
|
|
523
|
+
if (compiler.options.mode === 'development') {
|
|
524
|
+
new DevHmrFixInvalidPongPlugin().apply(compiler);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
6
529
|
|
|
7
530
|
module.exports = NextFederationPlugin;
|