Skip to content

fix: await pending share loads before entry import in bootstrap#866

Merged
gioboa merged 1 commit into
module-federation:mainfrom
LumeWeb:fix/await-pending-share-loads
Jun 28, 2026
Merged

fix: await pending share loads before entry import in bootstrap#866
gioboa merged 1 commit into
module-federation:mainfrom
LumeWeb:fix/await-pending-share-loads

Conversation

@pcfreak30

Copy link
Copy Markdown
Contributor

The bootstrap code generated by pluginAddEntry calls initHost() then immediately imports the entry module. However, some loadShare modules (workspace singletons with lazy fallback, and import:false modules) do not populate their exports synchronously during init().

Instead they schedule an initPromise.then() callback that dynamically imports the real module and assigns exports asynchronously. The bootstrap never waited for these callbacks to complete.

This means the entry module (and any code it transitively runs) could access shared exports (e.g. react, react-dom) before the async initPromise.then() + import() chain resolved, reading undefined.

The fix tracks these deferred share load promises in __mfModuleCache.pendingShareLoads. Two code paths are wrapped:

  1. generateLazyWorkspaceSingletonExports (virtualShared_preBuild.ts): the client-side initPromise.then(() => import(...)) that assigns workspace singleton exports after the ESM import resolves.

  2. generateDeferredHostProvidedExports (virtualShared_preBuild.ts): the initPromise.then() that waits for the host runtime to provide a shared module the remote does not have locally (import: false).

The bootstrap in pluginAddEntry.ts now awaits
Promise.all(__mfModuleCache.pendingShareLoads) after initHost() and before importing the entry.

@pcfreak30 pcfreak30 force-pushed the fix/await-pending-share-loads branch from 38cf655 to aca5779 Compare June 28, 2026 03:28
@pcfreak30

Copy link
Copy Markdown
Contributor Author

@gioboa please approve workflows

@pkg-pr-new

pkg-pr-new Bot commented Jun 28, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@module-federation/vite@866

commit: f09bf4a

@gioboa gioboa left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks 🙏
Do you have a repo to validate these changes?

@pcfreak30

Copy link
Copy Markdown
Contributor Author

Thanks 🙏 Do you have a repo to validate these changes?

No, im submitting this work based on my situation, tests (and a real integration test) were added to the PR.

As I can see CI is oddly failing, ill convert this to a draft until thats solved.

@pcfreak30 pcfreak30 marked this pull request as draft June 28, 2026 05:46

@gioboa gioboa left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These changes should fix the errors


index 4457b0c..a37b88b 100644
--- a/integration/shared-cache-deferred-exports.test.ts
+++ b/integration/shared-cache-deferred-exports.test.ts
@@ -117,8 +117,9 @@ describe('remote loadShare (import: false — host-provided)', () => {
expect(code).toContain('__mfApplyHostProvidedExports');
// The else branch (cache hit) must apply synchronously

  • // Structure: if (exportModule === void 0) initPromise.then(...) else __mfApplyHostProvidedExports(exportModule)
  • const elseMatch = code.match(/else\s+__mfApplyHostProvidedExports(exportModule)/);
  • // Structure: if (exportModule === void 0) initPromise.then(...) else { __mfApplyHostProvidedExports(exportModule) }

  • // Rollup/Vite versions differ on whether they keep braces or whitespace after else.

  • const elseMatch = code.match(/else\s*{?\s*__mfApplyHostProvidedExports(exportModule)/);
    expect(elseMatch).not.toBeNull();

    // Must NOT push to pendingShareLoads in the else branch
    @@ -200,8 +201,9 @@ describe('host loadShare (import: true — workspace singleton)', () => {
    expect(code).toContain('__mfApplyLazyShareExports');

    // The else branch (cache hit) must apply synchronously

  • // Structure: else __mfApplyLazyShareExports(exportModule)
  • const elseMatch = code.match(/else\s+__mfApplyLazyShareExports(exportModule)/);
  • // Structure: else { __mfApplyLazyShareExports(exportModule) }
  • // Rollup/Vite versions differ on whether they keep braces or whitespace after else.
  • const elseMatch = code.match(/else\s*{?\s*__mfApplyLazyShareExports(exportModule)/);
    expect(elseMatch).not.toBeNull();

The bootstrap code generated by pluginAddEntry calls initHost() then
immediately imports the entry module. However, some loadShare modules
(workspace singletons with lazy fallback, and import:false modules)
do not populate their exports synchronously during init().

Instead they schedule an initPromise.then() callback that dynamically
imports the real module and assigns exports asynchronously. The
bootstrap never waited for these callbacks to complete.

This means the entry module (and any code it transitively runs) could
access shared exports (e.g. react, react-dom) before the async
initPromise.then() + import() chain resolved, reading undefined.

The fix tracks these deferred share load promises in
__mfModuleCache.pendingShareLoads. Two code paths are wrapped:

1. generateLazyWorkspaceSingletonExports (virtualShared_preBuild.ts):
   the client-side initPromise.then(() => import(...)) that assigns
   workspace singleton exports after the ESM import resolves.

2. generateDeferredHostProvidedExports (virtualShared_preBuild.ts):
   the initPromise.then() that waits for the host runtime to provide
   a shared module the remote does not have locally (import: false).

The bootstrap in pluginAddEntry.ts now awaits
Promise.all(__mfModuleCache.pendingShareLoads) after initHost() and
before importing the entry.
@pcfreak30 pcfreak30 force-pushed the fix/await-pending-share-loads branch from aca5779 to f09bf4a Compare June 28, 2026 19:41
@pcfreak30 pcfreak30 marked this pull request as ready for review June 28, 2026 19:41
@pcfreak30

Copy link
Copy Markdown
Contributor Author

@gioboa please re-run workflows

@gioboa gioboa left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests With Different Frameworks

Image

@gioboa gioboa left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @pcfreak30 this is a good one 🚀

@gioboa gioboa merged commit 214264d into module-federation:main Jun 28, 2026
19 checks passed
@pcfreak30 pcfreak30 deleted the fix/await-pending-share-loads branch June 28, 2026 20:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants