Skip to content

Commit 49867bb

Browse files
fix(nextjs-mf): fix static method function proxy assignment (#2156)
Co-authored-by: Zhang Hang <[email protected]> Co-authored-by: ScriptedAlchemy <[email protected]>
1 parent b97b0f3 commit 49867bb

File tree

2 files changed

+68
-22
lines changed

2 files changed

+68
-22
lines changed

.changeset/gold-cherries-stare.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@module-federation/nextjs-mf': patch
3+
---
4+
5+
Fix issue with function proxy not returning static methods

packages/nextjs-mf/src/plugins/container/runtimePlugin.ts

Lines changed: 63 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -72,33 +72,74 @@ export default function (): FederationRuntimePlugin {
7272
},
7373
onLoad(args) {
7474
const { exposeModuleFactory, exposeModule, id } = args;
75-
7675
const moduleOrFactory = exposeModuleFactory || exposeModule;
77-
const exposedModuleExports = moduleOrFactory();
78-
const handler = {
79-
//@ts-ignore
80-
get: function (target, prop, receiver) {
81-
const origMethod = target[prop];
82-
if (typeof origMethod === 'function') {
83-
//@ts-ignore
84-
return function (...args) {
85-
globalThis.usedChunks.add(
86-
//@ts-ignore
87-
id,
88-
);
76+
if (!moduleOrFactory) return; // Ensure moduleOrFactory is defined
77+
let exposedModuleExports: any = moduleOrFactory();
78+
79+
if (typeof window === 'undefined') {
80+
const handler: ProxyHandler<any> = {
81+
get(target, prop, receiver) {
82+
// Check if accessing a static property of the function itself
83+
if (
84+
target === exposedModuleExports &&
85+
typeof exposedModuleExports[prop] === 'function'
86+
) {
87+
return function (this: unknown, ...args: any[]) {
88+
globalThis.usedChunks.add(id);
89+
return exposedModuleExports[prop].apply(this, args);
90+
};
91+
}
92+
93+
const originalMethod = target[prop];
94+
if (typeof originalMethod === 'function') {
95+
const proxiedFunction = function (this: unknown, ...args: any[]) {
96+
globalThis.usedChunks.add(id);
97+
return originalMethod.apply(this, args);
98+
};
99+
100+
// Copy all enumerable properties from the original method to the proxied function
101+
Object.keys(originalMethod).forEach((prop) => {
102+
Object.defineProperty(proxiedFunction, prop, {
103+
value: originalMethod[prop],
104+
writable: true,
105+
enumerable: true,
106+
configurable: true,
107+
});
108+
});
109+
110+
return proxiedFunction;
111+
}
89112

90-
// console.log(`function as called to ${prop}`, id);
91-
//@ts-ignore
92-
return origMethod.apply(this, args);
93-
};
94-
} else {
95113
return Reflect.get(target, prop, receiver);
96-
}
97-
},
98-
};
114+
},
115+
};
116+
117+
if (typeof exposedModuleExports === 'function') {
118+
// If the module export is a function, we create a proxy that can handle both its
119+
// call (as a function) and access to its properties (including static methods).
120+
exposedModuleExports = new Proxy(exposedModuleExports, handler);
121+
122+
// Proxy static properties specifically
123+
const staticProps = Object.getOwnPropertyNames(exposedModuleExports);
124+
staticProps.forEach((prop) => {
125+
if (typeof exposedModuleExports[prop] === 'function') {
126+
exposedModuleExports[prop] = new Proxy(
127+
exposedModuleExports[prop],
128+
handler,
129+
);
130+
}
131+
});
132+
} else {
133+
// For objects, just wrap the exported object itself
134+
exposedModuleExports = new Proxy(exposedModuleExports, handler);
135+
}
99136

100-
return () => new Proxy(exposedModuleExports, handler);
137+
return () => exposedModuleExports;
138+
}
139+
140+
return args;
101141
},
142+
102143
resolveShare(args) {
103144
if (
104145
args.pkgName !== 'react' &&

0 commit comments

Comments
 (0)