Skip to content

Commit 4e1d84c

Browse files
fix(node): refactor public path runtime module
1 parent d75dfde commit 4e1d84c

File tree

2 files changed

+112
-138
lines changed

2 files changed

+112
-138
lines changed

.changeset/modern-snails-chew.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@module-federation/node': patch
3+
---
4+
5+
Refactor AutomaticPublicPath plugin to use federation global manager for auto path resolution in node.js

packages/node/src/plugins/RemotePublicPathRuntimeModule.ts

Lines changed: 107 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { normalizeWebpackPath } from '@module-federation/sdk/normalize-webpack-path';
2-
32
const { RuntimeGlobals, RuntimeModule, Template, javascript } = require(
43
normalizeWebpackPath('webpack'),
54
) as typeof import('webpack');
@@ -34,15 +33,7 @@ class AutoPublicPathRuntimeModule extends RuntimeModule {
3433
compilation?.getPath(publicPath || '', {
3534
hash: compilation?.hash || 'XXXX',
3635
});
37-
// If publicPath is not "auto", return the static value
38-
// if (publicPath !== 'auto') {
39-
// const path = getPath();
40-
// return Template.asString([
41-
// `${RuntimeGlobals.publicPath} = ${JSON.stringify(path)};`,
42-
// 'var addProtocol = (url)=> url.startsWith(\'//\') ? \'https:\' + url : url;',
43-
// `globalThis.currentVmokPublicPath = addProtocol(${RuntimeGlobals.publicPath}) || '/';`,
44-
// ]);
45-
// }
36+
4637
const chunkName = compilation?.getPath(
4738
javascript.JavascriptModulesPlugin.getChunkFilenameTemplate(
4839
this.chunk,
@@ -53,138 +44,116 @@ class AutoPublicPathRuntimeModule extends RuntimeModule {
5344
contentHashType: 'javascript',
5445
},
5546
);
56-
let undoPath;
47+
48+
let undoPath: string | null = null;
5749
if (chunkName && path) {
5850
undoPath = getUndoPath(chunkName, path, false);
5951
}
60-
const ident = Template.toIdentifier(uniqueName || '');
61-
62-
// Define potential lookup keys
63-
const potentialLookups = [this.chunk?.name, ident, uniqueName];
64-
65-
// Generate lookup string using potential keys
66-
const lookupString = potentialLookups
67-
.filter(Boolean)
68-
.map((lookup) => {
69-
return `remoteReg[${JSON.stringify(lookup)}]`;
70-
})
71-
.join(' || ');
72-
73-
const remotesFromFederation = Template.indent([
74-
'var result = {};',
75-
'// Assuming the federationController is already defined on globalThis',
76-
'const federationController = globalThis.__FEDERATION__;',
77-
'// Function to convert Map to Object',
78-
'function mapToObject(map) {',
79-
Template.indent([
80-
'const obj = {};',
81-
'map.forEach((value, key) => {',
82-
Template.indent('obj[key] = value;'),
83-
'});',
84-
'return obj;',
85-
]),
86-
'}',
87-
"console.log('instance', __webpack_require__.federation.instance.name);",
88-
'// Iterate over each instance in federationController',
89-
'federationController.__INSTANCES__.forEach(instance => {',
90-
Template.indent([
91-
"// Check if the current instance has a moduleCache and it's a Map",
92-
'if (instance.moduleCache) {',
93-
Template.indent([
94-
'// Convert Map keys and values to an object and merge it with the result',
95-
'result = {...result, ...mapToObject(instance.moduleCache)};',
96-
]),
97-
'}',
98-
]),
99-
'});',
100-
"console.log('RESULTS', result);",
101-
'// Logic to determine the value of p, using result',
102-
`if(!result[${JSON.stringify(lookupString)}]) return false`,
103-
`return result[${JSON.stringify(lookupString)}]`,
104-
]);
105-
106-
const importMetaLookup = Template.indent([
107-
`scriptUrl = new Function('return typeof ${importMetaName}.url === "string" ? ${importMetaName}.url : undefined;')();`,
108-
]);
109-
const federationLookup = Template.asString([
110-
'Object.defineProperty(__webpack_require__, "p", {',
111-
Template.indent([
112-
'get: function() {',
113-
Template.indent([
114-
'try {',
115-
importMetaLookup,
116-
'} catch(e) {',
117-
remotesFromFederation,
118-
'}',
119-
]),
120-
'}',
121-
]),
122-
'});',
123-
]);
124-
125-
return Template.asString([
126-
'var scriptUrl;',
127-
// its an esproxy so nesting into _config directly is not possible
128-
`
129-
let remoteContainerRegistry = {
130-
get url() {
131-
var remoteReg = globalThis.__remote_scope__ ? globalThis.__remote_scope__._config : {};
132-
return ${lookupString}
52+
53+
const getPathFromFederation = `
54+
function getPathFromFederation() {
55+
// Access the global federation manager or create a fallback object
56+
var federationManager = globalThis.__FEDERATION__ || {};
57+
// Access the current Webpack instance's federation details or create a fallback object
58+
var instance = __webpack_require__.federation.instance || {};
59+
60+
// Function to aggregate all known remote module paths
61+
var getAllKnownRemotes = function() {
62+
var found = {};
63+
// Iterate over all federation instances to collect module cache entries
64+
(federationManager.__INSTANCES__ || []).forEach((instance) => {
65+
instance.moduleCache.forEach((value, key) => {
66+
found[key] = value;
67+
});
68+
});
69+
return found;
70+
};
71+
72+
// Retrieve the combined remote cache from all federation instances
73+
const combinedRemoteCache = getAllKnownRemotes();
74+
// Get the name of the current host from the instance
75+
const hostName = instance.name;
76+
// Find the path for the current host in the remote cache
77+
const foundPath = combinedRemoteCache[hostName];
78+
// If a path is not found, return undefined to indicate the absence of an entry path
79+
if (!foundPath) { return undefined; }
80+
// Return the entry path for the found remote module
81+
const entryPath = foundPath.remoteInfo.entry;
82+
return entryPath;
83+
}
84+
`;
85+
const definePropertyCode = `
86+
Object.defineProperty(__webpack_require__, "p", {
87+
get: function() {
88+
var scriptUrl;
89+
90+
// Attempt to get the script URL based on the environment
91+
var scriptType = ${JSON.stringify(scriptType)};
92+
var chunkLoading = ${JSON.stringify(chunkLoading)};
93+
var isModuleEnvironment = ['module', 'node', 'async-node', 'require'].includes(scriptType) || chunkLoading;
94+
95+
if (isModuleEnvironment) {
96+
try {
97+
// Use Function constructor to avoid direct reference to import.meta in environments that do not support it
98+
scriptUrl = (new Function('return typeof ${importMetaName}.url === "string" ? ${importMetaName}.url : undefined;'))();
99+
} catch (e) {
100+
// Handle cases where import.meta is not available or other errors occur
101+
var scriptPath = getPathFromFederation();
102+
if (scriptPath) {
103+
scriptUrl = scriptPath;
104+
} else if (typeof __filename !== "undefined") {
105+
scriptUrl = __filename;
106+
} else {
107+
scriptUrl = ${
108+
publicPath !== 'auto' ? JSON.stringify(getPath()) : 'undefined'
109+
};
110+
}
111+
}
112+
} else {
113+
// Fallback for non-module environments, such as browsers
114+
if (${RuntimeGlobals.global}.importScripts) {
115+
scriptUrl = ${RuntimeGlobals.global}.location + "";
116+
}
117+
var document = ${RuntimeGlobals.global}.document;
118+
if (!scriptUrl && document) {
119+
if (document.currentScript) {
120+
scriptUrl = document.currentScript.src;
121+
} else {
122+
var scripts = document.getElementsByTagName("script");
123+
if (scripts.length) {
124+
scriptUrl = scripts[scripts.length - 1].src;
125+
}
133126
}
134-
};
135-
`,
136-
137-
['module', 'node', 'async-node', 'require'].includes(scriptType || '') ||
138-
chunkLoading
139-
? Template.asString([
140-
'try {',
141-
142-
'} catch (e) {',
143-
Template.indent([
144-
'if (typeof remoteContainerRegistry.url === "string") {',
145-
Template.indent('scriptUrl = remoteContainerRegistry.url;'),
146-
'} else if(typeof __filename !== "undefined") {',
147-
Template.indent('scriptUrl = __filename;'),
148-
'} else {',
149-
Template.indent([
150-
`scriptUrl = ${
151-
publicPath !== 'auto'
152-
? JSON.stringify(getPath())
153-
: 'undefined'
154-
}`,
155-
]),
156-
'}',
157-
]),
158-
'}',
159-
])
160-
: Template.asString([
161-
`if (${RuntimeGlobals.global}.importScripts) scriptUrl = ${RuntimeGlobals.global}.location + "";`,
162-
`var document = ${RuntimeGlobals.global}.document;`,
163-
'if (!scriptUrl && document) {',
164-
Template.indent([
165-
'if (document.currentScript)',
166-
Template.indent('scriptUrl = document.currentScript.src'),
167-
'if (!scriptUrl) {',
168-
Template.indent([
169-
'var scripts = document.getElementsByTagName("script");',
170-
'if(scripts.length) scriptUrl = scripts[scripts.length - 1].src',
171-
]),
172-
'}',
173-
]),
174-
'}',
175-
]),
176-
'// When supporting server environments where an automatic publicPath is not supported, you must specify an output.publicPath manually via configuration',
177-
'// or pass an empty string ("") and set the __webpack_public_path__ variable from your code to use your own logic.',
178-
'if (!scriptUrl) throw new Error("Unable to calculate automatic public path");',
179-
'scriptUrl = scriptUrl.replace(/#.*$/, "").replace(/\\?.*$/, "").replace(/\\/[^\\/]+$/, "/");',
180-
!undoPath
181-
? `${RuntimeGlobals.publicPath} = scriptUrl;`
182-
: `${RuntimeGlobals.publicPath} = scriptUrl + ${JSON.stringify(
183-
undoPath,
184-
)};`,
185-
"var addProtocol = (url)=> url.startsWith('//') ? 'https:' + url : url;",
186-
`globalThis.currentVmokPublicPath = addProtocol(${RuntimeGlobals.publicPath}) || '/';`,
187-
]);
127+
}
128+
}
129+
130+
if (!scriptUrl) {
131+
throw new Error("Unable to calculate automatic public path");
132+
}
133+
134+
// Clean up the script URL by removing any hash or query parameters
135+
scriptUrl = scriptUrl.replace(/#.*$/, "").replace(/\\?.*$/, "").replace(/\\/[^\\/]+$/, "/");
136+
137+
// Apply any undo path that might be necessary for nested public paths
138+
var finalScript = ${JSON.stringify(
139+
undoPath,
140+
)} ? scriptUrl + ${JSON.stringify(undoPath)} : scriptUrl;
141+
142+
// Helper function to ensure the URL has a protocol if it starts with '//'
143+
var addProtocol = function(url) {
144+
return url.startsWith('//') ? 'https:' + url : url;
145+
};
146+
147+
// Set the global variable for the public path
148+
globalThis.currentVmokPublicPath = addProtocol(finalScript) || '/';
149+
150+
// Return the final public path
151+
return finalScript
152+
}
153+
});
154+
`;
155+
156+
return Template.asString([getPathFromFederation, definePropertyCode]);
188157
}
189158
}
190159

0 commit comments

Comments
 (0)