Skip to content

fix(nextjs-mf): await async factories and return wrapper factory in server onLoad - #4974

Open
shashank-u03 wants to merge 2 commits into
module-federation:mainfrom
shashank-u03:fix/nextjs-mf-onload-async-fact
Open

fix(nextjs-mf): await async factories and return wrapper factory in server onLoad#4974
shashank-u03 wants to merge 2 commits into
module-federation:mainfrom
shashank-u03:fix/nextjs-mf-onload-async-fact

Conversation

@shashank-u03

Copy link
Copy Markdown
Contributor

Description

Remote containers in @module-federation/runtime expose async module factories (RemoteEntryExports.get returns () => Promise<Module>).

During the webpack build/SSR path, runtime-core loads remotes with loadFactory: false and from: 'build' (see packages/webpack-bundler-runtime/src/remotes.ts), passing the unexecuted factory to nextjs-mf's onLoad hook as exposeModuleFactory.

On the server, onLoad synchronously invoked that factory and Proxy-wrapped the return value for chunk-usage tracking. When the factory is async, the sync invocation returned a raw Promise. Proxy-wrapping that Promise caused webpack's async module runtime to fail when calling .then():

TypeError: Method Promise.prototype.then called on incompatible receiver [object Promise]

runtime-core catches this in loadRemote and routes it to errorLoadRemote with lifecycle: 'onLoad' and from: 'build'.

Fix:

  1. Await Promise results from moduleOrFactory() before applying the existing Proxy wrapper on the server.
  2. When the resolved export is a namespace object and the input was exposeModuleFactory, return a wrapper factory so loadRemote can adopt the proxied result (runtime-core only uses onLoad's return when it is a function - see packages/runtime-core/src/remote/index.ts).

runtime-core already handles async factories in module.wraperFactory; this aligns nextjs-mf onLoad with that behavior.

Tests added in runtimePlugin.test.ts:

  • Async factory is awaited before proxy-wrapping on the server
  • Wrapper factory is returned for async namespace exports on the server
  • Promise.prototype.then is not broken after the fix
  • Sync exposeModuleFactory still works
  • Rejected async factories propagate correctly
  • Client path (window present) still returns args unchanged
    Note on async onLoad: loadRemote always awaits onLoad.emit() and AsyncHook.emit wraps listener returns with Promise.resolve(). The client branch still returns args unchanged; only the server branch gained internal await logic.

Related Issue

#2218

Types of changes

  • Docs change / refactoring / dependency upgrade
  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)

Checklist

  • I have added tests to cover my changes.
  • All new and existing tests passed.
  • I have updated the documentation.

@changeset-bot

changeset-bot Bot commented Aug 8, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: cc62ed0

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@module-federation/nextjs-mf Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@shashank-u03 shashank-u03 changed the title fix(nextjs-mf): await async factories in server onLoad hook fix(nextjs-mf): await async factories and return wrapper factory in server onLoad Aug 8, 2026
@shashank-u03
shashank-u03 force-pushed the fix/nextjs-mf-onload-async-fact branch from eb80f2d to 194e558 Compare August 8, 2026 18:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant