Skip to content

Conversation

vinicius-at-webflow
Copy link
Contributor

@vinicius-at-webflow vinicius-at-webflow commented Sep 2, 2025

Description

ConsumeSharedModule does not properly populate its buildMeta and buildInfo properties during its build. This can cause critical issues in shared nested module scenarios where packages have complex dependency structures with nested package.json files (with ESM "type": "module" exports) as seen in #3779 with @mui/styled-engine.

Root cause

Bundlers rely on this metadata to understand Module characteristics such as module type detection (ESM vs CommonJS) and import/export statement generation. In the case of webpack and @mui/styled-engine, without the proper buildMeta info, it generates incorrect import statements, leading to runtime errors like:

TypeError: _emotion_styled__WEBPACK_IMPORTED_MODULE_0__ is not a function

Related Issue

#3779

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.

Copy link

changeset-bot bot commented Sep 2, 2025

⚠️ No Changeset found

Latest commit: 6a82e0f

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

@vinicius-at-webflow vinicius-at-webflow changed the title fix(enhanced): Fix unpopulated build meta and info on ConsumeSharedModule fix(enhanced): Populate buildMeta and buildInfo on ConsumeSharedModule Sep 2, 2025
Copy link

netlify bot commented Sep 2, 2025

Deploy Preview for module-federation-docs ready!

Name Link
🔨 Latest commit 6a82e0f
🔍 Latest deploy log https://app.netlify.com/projects/module-federation-docs/deploys/68bfed9cb06020000852e14f
😎 Deploy Preview https://deploy-preview-4038--module-federation-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@@ -90,7 +90,6 @@
"type": "node",
"request": "launch",
"preLaunchTask": "pnpm-build-enhanced",
"runtimeExecutable": "/Users/bytedance/.nvm/versions/node/v18.20.8/bin/node",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is this hardcoded path really necessary?

Copy link
Member

Choose a reason for hiding this comment

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

probbably not

Comment on lines 193 to 214
// We need to hook into the compilation process to copy metadata
// after the fallback is fully built
compilation.hooks.finishModules.tap('ConsumeSharedModule', () => {
let dependency;

if (this.options.eager) {
// For eager mode, get the fallback directly
dependency = this.dependencies[0];
} else {
// For async mode, get it from the block
dependency = this.blocks[0]?.dependencies[0];
}

if (dependency) {
const fallback = compilation.moduleGraph.getModule(dependency);
if (fallback) {
// Copy its buildMeta and buildInfo from fallback module
this.buildMeta = { ...fallback.buildMeta };
this.buildInfo = { ...fallback.buildInfo };
}
}
});
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Following @ScriptedAlchemy hints 🙇, this is how I got buildMeta to be properly populated. finishModules seemed like the earliest hook to tap into, but there could be better ways of doing it. I'm open to any suggestions.

Copy link
Member

@ScriptedAlchemy ScriptedAlchemy Sep 5, 2025

Choose a reason for hiding this comment

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

Almost.
should not do this in the build method as it will cause massive hook registration per module built and rebuild, instead we should do it in the consume share plugin. Also compilation is not always guaranteed to be available in the build method of modules. For example when using loader.importModule or const plugin who evaluate macros, there is no compilation attached.

Ill push to this branch with the corrections for you to review.

Copy link
Contributor Author

@vinicius-at-webflow vinicius-at-webflow Sep 5, 2025

Choose a reason for hiding this comment

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

Nice! This all makes sense to me. I had a feeling registering in the plugin was the right approach - nowhere across the entire codebase we hook from within a Module, or anything other than a Plugin. I also overlooked the fact that we could

   (modules, callback) => {
            for (const module of modules) {
              // Only process ConsumeSharedModule instances with fallback dependencies
              if (
                !(module instanceof ConsumeSharedModule) ||
                !module.options.import
              ) {
                continue;
              }

Thanks for pushing. I will pull and test everything E2E and report back.


expect(module.dependencies.length).toBe(0);
expect(module.blocks.length).toBe(0);
});

it('should copy buildMeta and buildInfo from fallback', () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since I had to update a few tests on this suite, I'm also adding this unit one to cover the new logic.

Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR fixes an issue in ConsumeSharedModule where buildMeta and buildInfo properties were not properly populated during the build process, causing runtime errors in shared nested module scenarios with complex dependency structures.

Key Changes:

  • Added logic to copy buildMeta and buildInfo from fallback modules to ConsumeSharedModule instances
  • Added comprehensive test coverage for the new metadata copying functionality
  • Created integration test case for consume-nested scenarios

Reviewed Changes

Copilot reviewed 6 out of 11 changed files in this pull request and generated 3 comments.

File Description
packages/enhanced/src/lib/sharing/ConsumeSharedModule.ts Added hook to copy buildMeta and buildInfo from fallback modules after compilation finishes
packages/enhanced/test/unit/sharing/ConsumeSharedModule.test.ts Added unit tests and updated existing tests with proper mock compilation objects
packages/enhanced/test/configCases/sharing/consume-nested/* Added integration test case for nested module consumption scenarios
.vscode/launch.json Removed hardcoded runtime executable path

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

vinicius-at-webflow and others added 7 commits September 3, 2025 11:38
- Move buildMeta/buildInfo copying from ConsumeSharedModule to ConsumeSharedPlugin
- Use finishModules.tapAsync with stage -10 (STAGE_BASIC) instead of seal hook
- Ensure buildMeta is available before FlagDependencyExportsPlugin runs
- Follow webpack's DelegatedModule pattern with spread operator copying
- Add comprehensive test coverage for buildMeta copying functionality
- Fix TypeScript errors in test files and improve type safety
- Maintain ESM/CJS detection inheritance from fallback modules
…-at-webflow/fix-unpopulated-build-meta-and-info
@ScriptedAlchemy
Copy link
Member

@vinicius-at-webflow wanna give it a look now?

@vinicius-at-webflow
Copy link
Contributor Author

vinicius-at-webflow commented Sep 6, 2025

@ScriptedAlchemy I pulled your commits locally and a bunch of enhanced tests involving shared failed (as well as my E2E setup with @mui), with error messages similar to:

moduleIdentifier = packages/enhanced/test/configCases/sharing/shared-strategy/App.js; 
moduleName = ./App.js; loc = 4:31-36; 
message = export 'default' (imported as 'React') was not found in 'react' (module has no exports);
image

After some investigation I noticed it seemed to be a race condition and running at an earlier stage (-10) could be causing the problem. After bumping to 0 (with no success) and finally 10, the test suite was happy again, including the new consume-nested test I introduced, as well as my manual E2E test with @mui.

But what's bothering me is that, per comments you left on the code, it should actually be running before FlagDependencyExportsPlugin, not after... Would have any clues why that is the case?

@vinicius-at-webflow vinicius-at-webflow changed the title fix(enhanced): Populate buildMeta and buildInfo on ConsumeSharedModule fix(enhanced): Populate buildMeta and buildInfo on ConsumeSharedPlugin using fallbacks Sep 6, 2025
@ScriptedAlchemy
Copy link
Member

@ScriptedAlchemy I pulled your commits locally and a bunch of enhanced tests involving shared failed (as well as my E2E setup with @mui), with error messages similar to:

moduleIdentifier = packages/enhanced/test/configCases/sharing/shared-strategy/App.js; 
moduleName = ./App.js; loc = 4:31-36; 
message = export 'default' (imported as 'React') was not found in 'react' (module has no exports);
image After some investigation I noticed it seemed to be a race condition and running at an earlier stage (-10) could be causing the problem. After bumping to 0 (with no success) and [finally 10](https://github.com//pull/4038/commits/8e6a8b0a5c3104b4e9f1205810b3bb784ad29cb7), the test suite was happy again, including the new `consume-nested` test I introduced, as well as my manual E2E test with `@mui`.

But what's bothering me is that, per comments you left on the code, it should actually be running before FlagDependencyExportsPlugin, not after... Would have any clues why that is the case?

Ideally running it before flag dep exports plugin. However im guessing that that phase is actually whats responsible for setting metadata we need. Usually this is used for tree shaking. But if it needs to be stage 10 then no problem.

@ScriptedAlchemy ScriptedAlchemy changed the base branch from main to copy-build-meta September 9, 2025 09:08
@ScriptedAlchemy
Copy link
Member

@codex add changeset for this change

Copy link

@codex add changeset for this change

For now, I can only help with PRs you've created.

@ScriptedAlchemy ScriptedAlchemy merged commit c62e2a8 into module-federation:copy-build-meta Sep 12, 2025
16 checks passed
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.

2 participants