Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions packages/nextjs-mf/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ const SampleComponent = lazy(() => import('next2/sampleComponent'));

Delegated modules are now a standard feature in module federation, giving you the ability to manage the loading procedure of remote modules via an internally bundled file by webpack. This is facilitated by exporting a promise in the delegate file that resolves to a remote/container interface.

A container interface represents the fundamental `{get, init}` API that remote entries present to a consuming app. Within the browser, a remote container could be `window.app1`, and in Node, it could be `global.__remote_scope__.app1`.
A container interface represents the fundamental `{get, init}` API that remote entries present to a consuming app. Within the browser, a remote container could be `window.app1`, and in Node, it could be `globalThis.__remote_scope__.app1`.

Implementing a method for script loading in the delegate file is necessary for the utilization of delegated modules. Although the built-in `__webpack_require__.l` method of webpack is a prevalent method, any method is suitable. This method is made available to the runtime and is identical to the method webpack employs internally to load remotes.

Expand Down Expand Up @@ -338,9 +338,9 @@ const remotes = {
// remote-delegate.js
module.exports = new Promise((resolve, reject) => {
// some node specific for NodeFederation
if (!global.__remote_scope__) {
if (!globalThis.__remote_scope__) {
// create a global scope for container, similar to how remotes are set on window in the browser
global.__remote_scope__ = {
globalThis.__remote_scope__ = {
_config: {},
};
}
Expand All @@ -351,7 +351,7 @@ module.exports = new Promise((resolve, reject) => {
const [containerGlobal, url] = currentRequest.split('@');
// if node server, register the containers known origins
if (typeof window === 'undefined') {
global.__remote_scope__._config[global] = url;
globalThis.__remote_scope__._config[global] = url;
}
const __webpack_error__ = new Error();
// if you use NodeFederationPlugin, ive build a server-side version of __webpack_require__.l, with the same api.
Expand All @@ -360,7 +360,7 @@ module.exports = new Promise((resolve, reject) => {
url,
function (event) {
// resolve promise with container, for browser env or node env.
const container = typeof window === 'undefined' ? global.__remote_scope__[containerGlobal] : window[containerGlobal];
const container = typeof window === 'undefined' ? globalThis.__remote_scope__[containerGlobal] : window[containerGlobal];
console.log('delegate resolving', container);
if (typeof container !== 'undefined') return resolve(container);
var realSrc = event && event.target && event.target.src;
Expand Down
2 changes: 1 addition & 1 deletion packages/nextjs-mf/src/loaders/fixImageLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export async function fixImageLoader(
Template.asString([
'try {',
Template.indent([
'const remoteEntry = global.__remote_scope__ && global.__remote_scope__._config[__webpack_runtime_id__];',
'const remoteEntry = globalThis.__remote_scope__ && globalThis.__remote_scope__._config[__webpack_runtime_id__];',
`if (remoteEntry) {`,
Template.indent([
`const splitted = remoteEntry.split('/_next')`,
Expand Down
14 changes: 8 additions & 6 deletions packages/node/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,17 @@ declare module 'webpack/lib/Template';
declare module 'webpack/lib/util/compileBooleanMatcher';
declare module 'webpack/lib/util/identifier';

declare const global: typeof globalThis & {
usedChunks: Set<string>;
flushChunks: () => Promise<Array<string>>;
__remote_scope__: {

// globals.d.ts
declare module globalThis {
var usedChunks: Set<string>;
var flushChunks: () => Promise<Array<string>>;
var __remote_scope__: {
_config: Record<string, any>;
_medusa?: Record<string, any>;
[K: string]: {
fake?: boolean;
};
};
webpackChunkLoad;
};
var webpackChunkLoad: ()=> any;
}
281 changes: 132 additions & 149 deletions packages/node/src/plugins/LoadFileChunkLoadingRuntimeModule.ts

Large diffs are not rendered by default.

20 changes: 5 additions & 15 deletions packages/node/src/plugins/NodeFederationPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,31 +47,21 @@ export const generateRemoteTemplate = (
url: string,
global: any
) => `new Promise(function (resolve, reject) {
if(!global.__remote_scope__) {
if(!globalThis.__remote_scope__) {
// create a global scope for container, similar to how remotes are set on window in the browser
global.__remote_scope__ = {
globalThis.__remote_scope__ = {
_config: {},
}
}

if (typeof global.__remote_scope__[${JSON.stringify(
global
)}] !== 'undefined') return resolve(global.__remote_scope__[${JSON.stringify(
global
)}]);
global.__remote_scope__._config[${JSON.stringify(
global
)}] = ${JSON.stringify(url)};
if (typeof globalThis.__remote_scope__[${JSON.stringify(global)}] !== 'undefined') return resolve(globalThis.__remote_scope__[${JSON.stringify(global)}]);
globalThis.__remote_scope__._config[${JSON.stringify(global)}] = ${JSON.stringify(url)};
var __webpack_error__ = new Error();

__webpack_require__.l(
${JSON.stringify(url)},
function (event) {
if (typeof global.__remote_scope__[${JSON.stringify(
global
)}] !== 'undefined') return resolve(global.__remote_scope__[${JSON.stringify(
global
)}]);
if (typeof globalThis.__remote_scope__[${JSON.stringify(global)}] !== 'undefined') return resolve(globalThis.__remote_scope__[${JSON.stringify(global)}]);
var realSrc = event && event.target && event.target.src;
__webpack_error__.message = 'Loading script failed.\\n(' + event.message + ': ' + realSrc + ')';
__webpack_error__.name = 'ScriptExternalLoadError';
Expand Down
2 changes: 2 additions & 0 deletions packages/node/src/plugins/StreamingTargetPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,5 @@ class StreamingTargetPlugin {
}

export default StreamingTargetPlugin;


13 changes: 8 additions & 5 deletions packages/node/src/plugins/loadScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,18 @@ export const executeLoadTemplate = `
return res.text();
}).then(function (scriptContent) {
try {
const vmContext = typeof URLSearchParams === 'undefined' ?{exports, require, module, global, __filename, __dirname, URL, URLSearchParams, console, process,Buffer, ...global, remoteEntryName: name}:
{exports, require, module, global, __filename, __dirname, URL, URLSearchParams, console, process,Buffer, ...global, remoteEntryName: name};
const remote = vm.runInNewContext(scriptContent + '\\nmodule.exports', vmContext, {filename: 'node-federation-loader-' + name + '.vm'});
const m = require('module');

const remoteCapsule = vm.runInThisContext(m.wrap(scriptContent), 'node-federation-loader-' + name + '.vm')
const exp = {};
let remote = {exports:{}};
remoteCapsule(exp,require,remote,'node-federation-loader-' + name + '.vm',__dirname);
remote = remote.exports || remote;
globalThis.__remote_scope__[name] = remote[name] || remote;
globalThis.__remote_scope__._config[name] = url;
callback(globalThis.__remote_scope__[name])
} catch (e) {
console.error('executeLoad hit catch block');
console.error('executeLoad hit catch block', e);
e.target = {src: url};
callback(e);
}
Expand All @@ -96,7 +100,6 @@ export const executeLoadTemplate = `
}).then(function (scriptContent) {
try {
const remote = eval('let module = {};' + scriptContent + '\\nmodule.exports')
// const remote = vm.runInNewContext(scriptContent + '\\nmodule.exports', vmContext, {filename: 'node-federation-loader-' + name + '.vm'});
globalThis.__remote_scope__[name] = remote[name] || remote;
globalThis.__remote_scope__._config[name] = url;
callback(globalThis.__remote_scope__[name])
Expand Down
13 changes: 6 additions & 7 deletions packages/node/src/utils/flush-chunks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ const processChunk = async (chunk, shareMap, hostStats) => {
const [remote, request] = chunk.split('->');

// If the remote is not defined in the global config, return
if (!global.__remote_scope__._config[remote]) {
if (!globalThis.__remote_scope__._config[remote]) {
console.error(
`flush chunks:`,
`Remote ${remote} is not defined in the global config`
Expand All @@ -83,12 +83,13 @@ const processChunk = async (chunk, shareMap, hostStats) => {

try {
// Extract the remote name from the URL
const remoteName = new URL(global.__remote_scope__._config[remote]).pathname
//@ts-ignore
const remoteName = new URL(globalThis.__remote_scope__._config[remote]).pathname
.split('/')
.pop();

// Construct the stats file URL from the remote config
const statsFile = global.__remote_scope__._config[remote]
const statsFile = globalThis.__remote_scope__._config[remote]
.replace(remoteName, 'federated-stats.json')
.replace('ssr', 'chunks');

Expand All @@ -107,7 +108,7 @@ const processChunk = async (chunk, shareMap, hostStats) => {
// );

// Extract the prefix from the remote config
const [prefix] = global.__remote_scope__._config[remote].split('static/');
const [prefix] = globalThis.__remote_scope__._config[remote].split('static/');

// Process federated modules from the stats object
// @ts-ignore
Expand Down Expand Up @@ -138,9 +139,7 @@ const processChunk = async (chunk, shareMap, hostStats) => {
// Check if the module is in the shareMap
if (shareMap[module]) {
// If the module is from the host, log the host stats
if (shareMap[module][0].startsWith('host__')) {
console.log('host', hostStats);
}

}
});
}
Expand Down
11 changes: 7 additions & 4 deletions packages/node/src/utils/hot-reload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const performReload = (shouldReload: any) => {
}

//@ts-ignore
global.__remote_scope__ = {
globalThis.__remote_scope__ = {
_config: {},
_medusa: {},
};
Expand All @@ -39,8 +39,10 @@ const performReload = (shouldReload: any) => {
webpack whether its hash has changed since last time or not
*/
export const revalidate = () => {
if (global.__remote_scope__) {
const remoteScope = global.__remote_scope__;
//@ts-ignore
if (globalThis.__remote_scope__) {
//@ts-ignore
const remoteScope = globalThis.__remote_scope__;

return new Promise((res) => {
const fetches = [];
Expand Down Expand Up @@ -159,7 +161,8 @@ export const revalidate = () => {
allows us to use fetch in our tests without having to mock out nodefetch
*/
function getFetchModule() {
const loadedModule = global.webpackChunkLoad || global.fetch;
//@ts-ignore
const loadedModule = globalThis.webpackChunkLoad || global.webpackChunkLoad || global.fetch;
if (loadedModule) {
return loadedModule;
}
Expand Down
8 changes: 4 additions & 4 deletions packages/utilities/src/utils/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,18 +76,18 @@ export const loadScript = (keyOrRuntimeRemoteItem: string | RuntimeRemote) => {
};

// @ts-ignore
if (!global.__remote_scope__) {
if (!globalThis.__remote_scope__) {
// create a global scope for container, similar to how remotes are set on window in the browser
// @ts-ignore
global.__remote_scope__ = {
globalThis.__remote_scope__ = {
// @ts-ignore
_config: {},
};
}
// @ts-ignore
const globalScope =
// @ts-ignore
typeof window !== 'undefined' ? window : global.__remote_scope__;
typeof window !== 'undefined' ? window : globalThis.__remote_scope__;

if (typeof window === 'undefined') {
globalScope['_config'][containerKey] = reference.url;
Expand Down Expand Up @@ -258,7 +258,7 @@ export const getContainer = async (
// @ts-ignore
const containerScope =
// @ts-ignore
typeof window !== 'undefined' ? window : global.__remote_scope__;
typeof window !== 'undefined' ? window : globalThis.__remote_scope__;

if (typeof remoteContainer === 'string') {
if (containerScope[remoteContainer]) {
Expand Down