@module-federation/nextjs-mf 5.3.0 → 5.3.2

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.
@@ -1,6 +1,4 @@
1
- // if(process.browser && (typeof __webpack_share_scopes__ === "undefined" || !__webpack_share_scopes__.default)) {
2
- // __webpack_init_sharing__('default');
3
- // }
1
+ // this is needed to ensure webpack does not attempt to tree shake unused modules. Since these should always come from host
4
2
  require('react');
5
3
  require('react-dom');
6
4
  require('next/link');
@@ -0,0 +1,241 @@
1
+ import { parseOptions } from 'webpack/lib/container/options';
2
+ import { isRequiredVersion } from 'webpack/lib/sharing/utils';
3
+ import path from 'path';
4
+
5
+ // the share scope we attach by default
6
+ // in hosts we re-key them to prevent webpack moving the modules into their own chunks (cause eager error)
7
+ // in remote these are marked as import:false as we always expect the host to prove them
8
+ export const DEFAULT_SHARE_SCOPE = {
9
+ react: {
10
+ singleton: true,
11
+ requiredVersion: false,
12
+ },
13
+ 'react/jsx-runtime': {
14
+ singleton: true,
15
+ requiredVersion: false,
16
+ },
17
+ 'react-dom': {
18
+ singleton: true,
19
+ requiredVersion: false,
20
+ },
21
+ 'next/dynamic': {
22
+ requiredVersion: false,
23
+ singleton: true,
24
+ },
25
+ 'styled-jsx': {
26
+ requiredVersion: false,
27
+ singleton: true,
28
+ },
29
+ 'next/link': {
30
+ requiredVersion: false,
31
+ singleton: true,
32
+ },
33
+ 'next/router': {
34
+ requiredVersion: false,
35
+ singleton: true,
36
+ },
37
+ 'next/script': {
38
+ requiredVersion: false,
39
+ singleton: true,
40
+ },
41
+ 'next/head': {
42
+ requiredVersion: false,
43
+ singleton: true,
44
+ },
45
+ };
46
+ // put host infront of any shared module key, so "hostreact"
47
+ export const reKeyHostShared = (options) => {
48
+ return Object.entries({
49
+ ...(options || {}),
50
+ ...DEFAULT_SHARE_SCOPE,
51
+ }).reduce((acc, item) => {
52
+ const [itemKey, shareOptions] = item;
53
+
54
+ const shareKey = 'host' + (item.shareKey || itemKey);
55
+ acc[shareKey] = shareOptions;
56
+ if (!shareOptions.import) {
57
+ acc[shareKey].import = itemKey;
58
+ }
59
+ if (!shareOptions.shareKey) {
60
+ acc[shareKey].shareKey = itemKey;
61
+ }
62
+
63
+ if (DEFAULT_SHARE_SCOPE[itemKey]) {
64
+ acc[shareKey].packageName = itemKey;
65
+ }
66
+ return acc;
67
+ }, {});
68
+ };
69
+
70
+ // split the @ syntax into url and global
71
+ export const extractUrlAndGlobal = (urlAndGlobal) => {
72
+ const index = urlAndGlobal.indexOf('@');
73
+ if (index <= 0 || index === urlAndGlobal.length - 1) {
74
+ throw new Error(`Invalid request "${urlAndGlobal}"`);
75
+ }
76
+ return [urlAndGlobal.substring(index + 1), urlAndGlobal.substring(0, index)];
77
+ };
78
+
79
+ // browser template to convert remote into promise new promise and use require.loadChunk to load the chunk
80
+ export const generateRemoteTemplate = (url, global) => {
81
+ return `promise new Promise(function (resolve, reject) {
82
+ var __webpack_error__ = new Error();
83
+ if (typeof ${global} !== 'undefined') return resolve();
84
+ __webpack_require__.l(
85
+ ${JSON.stringify(url)},
86
+ function (event) {
87
+ if (typeof ${global} !== 'undefined') return resolve();
88
+ var errorType = event && (event.type === 'load' ? 'missing' : event.type);
89
+ var realSrc = event && event.target && event.target.src;
90
+ __webpack_error__.message =
91
+ 'Loading script failed.\\n(' + errorType + ': ' + realSrc + ')';
92
+ __webpack_error__.name = 'ScriptExternalLoadError';
93
+ __webpack_error__.type = errorType;
94
+ __webpack_error__.request = realSrc;
95
+ reject(__webpack_error__);
96
+ },
97
+ ${JSON.stringify(global)},
98
+ );
99
+ }).then(function () {
100
+ const proxy = {
101
+ get: ${global}.get,
102
+ init: function(shareScope) {
103
+ const handler = {
104
+ get(target, prop) {
105
+ if (target[prop]) {
106
+ Object.values(target[prop]).forEach(function(o) {
107
+ if(o.from === '_N_E') {
108
+ o.loaded = 1
109
+ }
110
+ })
111
+ }
112
+ return target[prop]
113
+ },
114
+ set(target, property, value, receiver) {
115
+ if (target[property]) {
116
+ return target[property]
117
+ }
118
+ target[property] = value
119
+ return true
120
+ }
121
+ }
122
+ try {
123
+ ${global}.init(new Proxy(shareScope, handler))
124
+ } catch (e) {
125
+
126
+ }
127
+ ${global}.__initialized = true
128
+ }
129
+ }
130
+ if (!${global}.__initialized) {
131
+ proxy.init()
132
+ }
133
+ return proxy
134
+ })`;
135
+ }
136
+
137
+ const parseShareOptions = (options) => {
138
+ const sharedOptions = parseOptions(
139
+ options.shared,
140
+ (item, key) => {
141
+ if (typeof item !== 'string')
142
+ throw new Error('Unexpected array in shared');
143
+ /** @type {SharedConfig} */
144
+ const config =
145
+ item === key || !isRequiredVersion(item)
146
+ ? {
147
+ import: item,
148
+ }
149
+ : {
150
+ import: key,
151
+ requiredVersion: item,
152
+ };
153
+ return config;
154
+ },
155
+ (item) => item
156
+ );
157
+ return sharedOptions.reduce((acc, [key, options]) => {
158
+ acc[key] = {
159
+ import: options.import,
160
+ shareKey: options.shareKey || key,
161
+ shareScope: options.shareScope,
162
+ requiredVersion: options.requiredVersion,
163
+ strictVersion: options.strictVersion,
164
+ singleton: options.singleton,
165
+ packageName: options.packageName,
166
+ eager: options.eager,
167
+ };
168
+ return acc;
169
+ }, {});
170
+ };
171
+
172
+ // shared packages must be compiled into webpack bundle, not require() pass through
173
+ export const internalizeSharedPackages = (options, compiler) => {
174
+ //TODO: should use this util for other areas where we read MF options from userland
175
+ if (!options.shared) {
176
+ return;
177
+ }
178
+ const sharedOptions = parseShareOptions(options);
179
+ // get share keys from user, filter out ones that need to be external
180
+ const internalizableKeys = Object.keys(sharedOptions).filter((key) => {
181
+ if (!DEFAULT_SHARE_SCOPE[key]) {
182
+ return true;
183
+ }
184
+ if (!DEFAULT_SHARE_SCOPE[sharedOptions[key].import]) {
185
+ return true;
186
+ }
187
+ });
188
+ // take original externals regex
189
+ const backupExternals = compiler.options.externals[0];
190
+ // if externals is a function (like when you're not running in serverless mode or creating a single build)
191
+ if (typeof backupExternals === 'function') {
192
+ // replace externals function with short-circuit, or fall back to original algo
193
+ compiler.options.externals[0] = (mod, callback) => {
194
+ if (!internalizableKeys.some((v) => mod.request.includes(v))) {
195
+ return backupExternals(mod, callback);
196
+ }
197
+ // bundle it
198
+ return Promise.resolve();
199
+ };
200
+ }
201
+ };
202
+
203
+ export const externalizedShares = Object.entries(DEFAULT_SHARE_SCOPE).reduce(
204
+ (acc, item) => {
205
+ const [key, value] = item;
206
+ acc[key] = { ...value, import: false };
207
+ if (key === 'react/jsx-runtime') {
208
+ delete acc[key].import;
209
+ }
210
+ return acc;
211
+ },
212
+ {}
213
+ );
214
+
215
+ // determine output base path, derives .next folder location
216
+ export const getOutputPath = (compiler) => {
217
+ const isServer = compiler.options.target !== 'client';
218
+ let outputPath = compiler.options.output.path.split(path.sep);
219
+ const foundIndex = outputPath.findIndex((i) => {
220
+ return i === (isServer ? 'server' : 'static');
221
+ });
222
+ outputPath = outputPath
223
+ .slice(0, foundIndex > 0 ? foundIndex : outputPath.length)
224
+ .join(path.sep);
225
+
226
+ return outputPath;
227
+ };
228
+
229
+ export const removePlugins = [
230
+ 'NextJsRequireCacheHotReloader',
231
+ 'BuildManifestPlugin',
232
+ 'WellKnownErrorsPlugin',
233
+ 'WebpackBuildEventsPlugin',
234
+ 'HotModuleReplacementPlugin',
235
+ 'NextMiniCssExtractPlugin',
236
+ 'NextFederationPlugin',
237
+ 'CopyFilePlugin',
238
+ 'ProfilingPlugin',
239
+ 'DropClientPage',
240
+ 'ReactFreshWebpackPlugin',
241
+ ];
@@ -1,7 +1,3 @@
1
- 'use strict';
2
-
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
1
  /**
6
2
  * 🛑🛑🛑 Attention! 🛑🛑🛑
7
3
  * Do not add type definitions to this file!!
@@ -19,7 +15,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
19
15
  * It was copied from
20
16
  * @see https://github.com/vercel/next.js/blob/canary/packages/next/shared/lib/router/utils/sorted-routes.ts
21
17
  */
22
- class UrlNode {
18
+ export class UrlNode {
23
19
  placeholder = true;
24
20
  children = new Map();
25
21
  slugName = null;
@@ -211,5 +207,3 @@ class UrlNode {
211
207
  ._insert(urlPaths.slice(1), slugNames, isCatchAll);
212
208
  }
213
209
  }
214
-
215
- exports.UrlNode = UrlNode;
@@ -1,13 +1,8 @@
1
- 'use strict';
2
-
3
- var helpers = require('../_virtual/helpers.js');
4
-
5
1
  /**
6
2
  * Inject a loader into the current module rule.
7
3
  * This function mutates `rule` argument!
8
4
  */
9
-
10
- helpers.__exports.injectRuleLoader = function injectRuleLoader(rule, loader) {
5
+ module.exports.injectRuleLoader = function injectRuleLoader(rule, loader) {
11
6
  if (rule.loader) {
12
7
  rule.use = [loader, { loader: rule.loader, options: rule.options }];
13
8
  delete rule.loader;
@@ -20,7 +15,7 @@ helpers.__exports.injectRuleLoader = function injectRuleLoader(rule, loader) {
20
15
  /**
21
16
  * Check that current module rule has a loader with the provided name.
22
17
  */
23
- helpers.__exports.hasLoader = function hasLoader(rule, loaderName) {
18
+ module.exports.hasLoader = function hasLoader(rule, loaderName) {
24
19
  if (rule.loader === loaderName) {
25
20
  return true;
26
21
  } else if (rule.use) {
@@ -39,7 +34,7 @@ helpers.__exports.hasLoader = function hasLoader(rule, loaderName) {
39
34
  return false;
40
35
  };
41
36
 
42
- helpers.__exports.toDisplayErrors = function toDisplayErrors(err) {
37
+ module.exports.toDisplayErrors = function toDisplayErrors(err) {
43
38
  return err
44
39
  .map((error) => {
45
40
  let message = error.message;
@@ -50,5 +45,3 @@ helpers.__exports.toDisplayErrors = function toDisplayErrors(err) {
50
45
  })
51
46
  .join('\n');
52
47
  };
53
-
54
- module.exports = helpers.__exports;
@@ -1,21 +1,9 @@
1
- 'use strict';
2
-
3
- var nextPageMapLoader$1 = require('../_virtual/nextPageMapLoader.js');
4
- var require$$0 = require('fast-glob');
5
- var require$$1 = require('fs');
6
- var UrlNode$1 = require('../_virtual/UrlNode.js');
7
-
8
- function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
9
-
10
- var require$$0__default = /*#__PURE__*/_interopDefaultLegacy(require$$0);
11
- var require$$1__default = /*#__PURE__*/_interopDefaultLegacy(require$$1);
12
-
13
- const fg = require$$0__default["default"];
14
- const fs = require$$1__default["default"];
1
+ const fg = require('fast-glob');
2
+ const fs = require('fs');
15
3
 
16
4
  // TODO: import UrlNode from ./client folder when whole project migrates on TypeScript (but right now using JS copy of this class)
17
5
  // const UrlNode = require('../client/UrlNode').UrlNode;
18
- const UrlNode = UrlNode$1.UrlNode;
6
+ const UrlNode = require('./UrlNode').UrlNode;
19
7
 
20
8
  /**
21
9
  * Webpack loader which prepares MF map for NextJS pages
@@ -187,7 +175,5 @@ function preparePageMapV2(pages) {
187
175
  return result;
188
176
  }
189
177
 
190
- nextPageMapLoader$1.nextPageMapLoader.exports = nextPageMapLoader;
191
- nextPageMapLoader$1.nextPageMapLoader.exports.exposeNextjsPages = exposeNextjsPages;
192
-
193
- module.exports = nextPageMapLoader$1.nextPageMapLoader.exports;
178
+ module.exports = nextPageMapLoader;
179
+ module.exports.exposeNextjsPages = exposeNextjsPages;
@@ -1,12 +1,9 @@
1
- 'use strict';
2
-
3
1
  /**
4
2
  * If HMR through websocket received {"invalid":true, "event":"pong"} event
5
3
  * then pages reloads. But for federated page this is unwanted behavior.
6
4
  *
7
5
  * So this plugin in DEV mode disables page.reload() in HMR for federated pages.
8
6
  */
9
-
10
7
  class DevHmrFixInvalidPongPlugin {
11
8
  /**
12
9
  * Apply the plugin
@@ -60,6 +57,4 @@ function escapeRegExp(string) {
60
57
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
61
58
  }
62
59
 
63
- var DevHmrFixInvalidPongPlugin_1 = DevHmrFixInvalidPongPlugin;
64
-
65
- module.exports = DevHmrFixInvalidPongPlugin_1;
60
+ module.exports = DevHmrFixInvalidPongPlugin;
package/lib/utils.js CHANGED
@@ -7,11 +7,18 @@ const remoteVars = process.env.REMOTES || {};
7
7
  const runtimeRemotes = Object.entries(remoteVars).reduce(function (acc, item) {
8
8
  const [key, value] = item;
9
9
  if (typeof value === 'object' && typeof value.then === 'function') {
10
+ // if its an object with a thennable (eagerly executing function)
10
11
  acc[key] = { asyncContainer: value };
12
+ } else if (typeof value === 'function') {
13
+ // if its a function that must be called (lazily executing function)
14
+ acc[key] = { asyncContainer: value() };
11
15
  } else if (typeof value === 'string') {
16
+ // if its just a string (global@url)
12
17
  const [global, url] = value.split('@');
13
18
  acc[key] = { global, url };
14
19
  } else {
20
+ // we dont know or currently support this type
21
+ console.log('remotes process', process.env.REMOTES);
15
22
  throw new Error(`[mf] Invalid value received for runtime_remote "${key}"`);
16
23
  }
17
24
  return acc;
@@ -0,0 +1,27 @@
1
+ # node
2
+
3
+ Software streaming to enable node.js support for browser-like chunk loading
4
+
5
+ ```js
6
+ const StreamingRuntime = require('../node/streaming');
7
+ const NodeFederation = require('../node/streaming/NodeRuntime');
8
+
9
+ plugins: [
10
+ new StreamingRuntime({
11
+ name: 'website2',
12
+ library: { type: 'commonjs' },
13
+ filename: 'remoteEntry.js',
14
+ exposes: {
15
+ './SharedComponent': './remoteServer/SharedComponent',
16
+ },
17
+ }),
18
+ new NodeFederation({
19
+ name: 'website2',
20
+ library: { type: 'commonjs' },
21
+ filename: 'remoteEntry.js',
22
+ exposes: {
23
+ './SharedComponent': './remoteServer/SharedComponent',
24
+ },
25
+ }),
26
+ ];
27
+ ```
@@ -0,0 +1,4 @@
1
+ {
2
+ "name": "@module-federation/node",
3
+ "version": "1.0.0"
4
+ }
@@ -0,0 +1,89 @@
1
+ const RuntimeGlobals = require('webpack/lib/RuntimeGlobals');
2
+ const StartupChunkDependenciesPlugin = require('webpack/lib/runtime/StartupChunkDependenciesPlugin');
3
+ import ChunkLoadingRuntimeModule from './LoadFileChunkLoadingRuntimeModule';
4
+ // const ChunkLoadingRuntimeModule = require('webpack/lib/node/ReadFileChunkLoadingRuntimeModule')
5
+ class CommonJsChunkLoadingPlugin {
6
+ constructor(options) {
7
+ this.options = options || {};
8
+ this._asyncChunkLoading = this.options.asyncChunkLoading;
9
+ }
10
+
11
+ /**
12
+ * Apply the plugin
13
+ * @param {Compiler} compiler the compiler instance
14
+ * @returns {void}
15
+ */
16
+ apply(compiler) {
17
+ const chunkLoadingValue = this._asyncChunkLoading
18
+ ? 'async-node'
19
+ : 'require';
20
+ new StartupChunkDependenciesPlugin({
21
+ chunkLoading: chunkLoadingValue,
22
+ asyncChunkLoading: this._asyncChunkLoading,
23
+ }).apply(compiler);
24
+ compiler.hooks.thisCompilation.tap(
25
+ 'CommonJsChunkLoadingPlugin',
26
+ (compilation) => {
27
+ // Always enabled
28
+ const isEnabledForChunk = () => true;
29
+ const onceForChunkSet = new WeakSet();
30
+ const handler = (chunk, set) => {
31
+ if (onceForChunkSet.has(chunk)) return;
32
+ onceForChunkSet.add(chunk);
33
+ if (!isEnabledForChunk(chunk)) return;
34
+ set.add(RuntimeGlobals.moduleFactoriesAddOnly);
35
+ set.add(RuntimeGlobals.hasOwnProperty);
36
+ compilation.addRuntimeModule(
37
+ chunk,
38
+ new ChunkLoadingRuntimeModule(set, this.options, {
39
+ webpack: compiler.webpack,
40
+ })
41
+ );
42
+ };
43
+
44
+ compilation.hooks.runtimeRequirementInTree
45
+ .for(RuntimeGlobals.ensureChunkHandlers)
46
+ .tap('CommonJsChunkLoadingPlugin', handler);
47
+ compilation.hooks.runtimeRequirementInTree
48
+ .for(RuntimeGlobals.hmrDownloadUpdateHandlers)
49
+ .tap('CommonJsChunkLoadingPlugin', handler);
50
+ compilation.hooks.runtimeRequirementInTree
51
+ .for(RuntimeGlobals.hmrDownloadManifest)
52
+ .tap('CommonJsChunkLoadingPlugin', handler);
53
+ compilation.hooks.runtimeRequirementInTree
54
+ .for(RuntimeGlobals.baseURI)
55
+ .tap('CommonJsChunkLoadingPlugin', handler);
56
+ compilation.hooks.runtimeRequirementInTree
57
+ .for(RuntimeGlobals.externalInstallChunk)
58
+ .tap('CommonJsChunkLoadingPlugin', handler);
59
+ compilation.hooks.runtimeRequirementInTree
60
+ .for(RuntimeGlobals.onChunksLoaded)
61
+ .tap('CommonJsChunkLoadingPlugin', handler);
62
+
63
+ compilation.hooks.runtimeRequirementInTree
64
+ .for(RuntimeGlobals.ensureChunkHandlers)
65
+ .tap('CommonJsChunkLoadingPlugin', (chunk, set) => {
66
+ if (!isEnabledForChunk(chunk)) return;
67
+ set.add(RuntimeGlobals.getChunkScriptFilename);
68
+ });
69
+ compilation.hooks.runtimeRequirementInTree
70
+ .for(RuntimeGlobals.hmrDownloadUpdateHandlers)
71
+ .tap('CommonJsChunkLoadingPlugin', (chunk, set) => {
72
+ if (!isEnabledForChunk(chunk)) return;
73
+ set.add(RuntimeGlobals.getChunkUpdateScriptFilename);
74
+ set.add(RuntimeGlobals.moduleCache);
75
+ set.add(RuntimeGlobals.hmrModuleData);
76
+ set.add(RuntimeGlobals.moduleFactoriesAddOnly);
77
+ });
78
+ compilation.hooks.runtimeRequirementInTree
79
+ .for(RuntimeGlobals.hmrDownloadManifest)
80
+ .tap('CommonJsChunkLoadingPlugin', (chunk, set) => {
81
+ if (!isEnabledForChunk(chunk)) return;
82
+ set.add(RuntimeGlobals.getUpdateManifestFilename);
83
+ });
84
+ }
85
+ );
86
+ }
87
+ }
88
+
89
+ export default CommonJsChunkLoadingPlugin;