module-federation requiredVersion/strictVersion has no affect?
Current Behavior
Prior to nx 21, I could return requiredVersion: false from my module federation config by doing:
shared: () => ({
requiredVersion: false,
}),
This added ^ to the requiredVersion in mf-manifest.json of my shared dependencies so they became ^{version}
But since the upgrade to nx 21, this no longer works and seems to turn all my version and requiredVersion to *
"version": "*",
"singleton": true,
"requiredVersion": "^*",
If I tried to set strictVersion instead
shared: () => ({
strictVersion: false,
}),
I get warnings about local shared libraries such as
WARNING in ./src/types.ts
⚠ No required version specified and unable to automatically determine one. Unable to find required version for "@my-project/shared-context" in description file/s
│ /xxx/xxxx/xxxx/xxxx/package.json
│ It need to be in dependencies, devDependencies or peerDependencies. file: shared module @my-project/shared-context
And while all other libraries are now correct again:
Internal libraries now show as * and ^undefined
Even if I check for ^ and handle that, the configuration is not correct.
For example using:
const { requiredVersion } = defaultConfig;
if (requiredVersion && !requiredVersion.startsWith('^')) {
return {
version: requiredVersion,
requiredVersion: `^${requiredVersion}`,
};
}
I get:
"version": "^0.0.1",
"singleton": true,
"requiredVersion": "^0.0.1",
What's the correct way to get back the old behavior?
Expected Behavior
nx 21 should not have broken previous features without a work around or a callout in breaking changes release notes
GitHub Repo
No response
Steps to Reproduce
- Create a shared internal library
- Create new nx federated remote that use said library
- change the configuration to not have a fixed requiredVersion
Nx Report
NX Report complete - copy this into the issue template
Node : 22.15.1
OS : darwin-arm64
Native Target : aarch64-macos
pnpm : 10.11.0
nx : 21.1.2
@nx/js : 21.1.2
@nx/jest : 21.1.2
@nx/eslint : 21.1.2
@nx/workspace : 21.1.2
@nx/cypress : 21.1.2
@nx/devkit : 21.1.2
@nx/esbuild : 21.1.2
@nx/eslint-plugin : 21.1.2
@nx/module-federation : 21.1.2
@nx/next : 21.1.2
@nx/node : 21.1.2
@nx/playwright : 21.1.2
@nx/react : 21.1.2
@nx/rspack : 21.1.2
@nx/storybook : 21.1.2
@nx/vite : 21.1.2
@nx/web : 21.1.2
@nx/webpack : 21.1.2
typescript : 5.8.2
---------------------------------------
Registered Plugins:
@nx/storybook/plugin
@nx/eslint/plugin
@nx/vite/plugin
---------------------------------------
Cache Usage: 302.82 MB / 46.04 GB
Failure Logs
Package Manager Version
No response
Operating System
- [x] macOS
- [ ] Linux
- [ ] Windows
- [ ] Other (Please specify)
Additional Information
No response
@claude can you analyze this issue and determine if a change within packages/module-federation for the withModuleFederation utilities or NxModuleFederationRspackPlugin has introduced this change - or if it is a result of a newer version of the @module-federation/enhanced package (github repo: https://github.com/module-federation/core/tree/main/packages/enhanced) (docs site: https://module-federation.io)
Claude finished @Coly010's task in 2m 28s —— View job
Analysis of Module Federation requiredVersion Issue
I've investigated whether this breaking change in module federation configuration is caused by changes in Nx's packages/module-federation or by a newer version of @module-federation/enhanced.
Todo List:
- [x] Explore the
packages/module-federationdirectory structure - [x] Examine
withModuleFederationutilities implementation - [x] Check
NxModuleFederationRspackPluginimplementation - [x] Look at package.json dependencies for
@module-federation/enhancedversion - [x] Compare current implementation with what might have existed in earlier versions
- [x] Analyze how
requiredVersionandstrictVersionconfigurations are processed - [x] Provide findings and root cause analysis
Root Cause Analysis
The issue appears to be caused by the upgrade to @module-federation/enhanced version ^0.9.0, not by changes in Nx's implementation.
Key Findings:
-
Nx 21 Module Federation Rewrite: Nx 21 introduced a major rewrite of the Module Federation experience (as outlined in the blog post). However, the core shared configuration logic in
packages/module-federation/src/utils/share.tshasn't fundamentally changed in how it processesrequiredVersion: false. -
Upstream Dependency Change: The
@module-federation/enhancedpackage was updated to^0.9.0in Nx 21. This is likely where the behavioral change originates. -
Nx Logic Still Works Correctly: The Nx code in
share.tslines 135-144 still properly handlesrequiredVersion: false:[library.name]: { ...(version ? { requiredVersion: version, singleton: true, } : { requiredVersion: false }), eager, } -
SharedFunction Processing: The
applySharedFunction()inshare.tslines 241-260 correctly processes user-defined shared functions and should properly override the default configuration with the user's{ requiredVersion: false }.
The Problem:
The issue is that @module-federation/enhanced v0.9.0 appears to interpret requiredVersion: false differently than previous versions. Instead of adding a ^ prefix to the version (which was the expected behavior), it's now setting both version and requiredVersion to * and ^* respectively.
Evidence Supporting This Conclusion:
-
No Breaking Changes in Nx 21 Docs: The Nx 21 release notes and module federation blog post don't mention any breaking changes to the shared configuration API.
-
Nx Logic Unchanged: The core logic for processing shared configurations in Nx hasn't been fundamentally altered.
-
Version Correlation: The timing aligns with the
@module-federation/enhancedupgrade.
Recommendation:
This should be reported as either:
- A breaking change in
@module-federation/enhancedv0.9.0 that wasn't properly documented - A bug in how the enhanced plugin processes
requiredVersion: false
The user should also try testing with a specific version constraint for @module-federation/enhanced (downgrading to an earlier version) to confirm this hypothesis.
Potential Workaround:
Instead of using requiredVersion: false, the user might need to explicitly construct the version string with the ^ prefix:
shared: (libraryName, defaultConfig) => {
const { requiredVersion } = defaultConfig;
if (requiredVersion && !requiredVersion.startsWith('^')) {
return {
singleton: true,
requiredVersion: `^${requiredVersion}`,
};
}
return defaultConfig;
}
@h3adache This was not a change done by Nx, and rather an upstream change in Module Federation itself.
The change makes sense. You've explicitly stated that there is no required version by setting requiredVersion: false
Therefore, any version can be used. The Module Federation plugins at build and runtime should handle this correctly, and if they are not, it is worth raising a bug on their repo: https://github.com/module-federation/core
Thanks @Coly010. I can see that by setting a quick provider following https://module-federation.io/guide/start/quick-start.html and adding a npm dependency
"jotai": "2.12.5"
with
'jotai': { singleton: true, requiredVersion: false },
indeed does as you said and uses *.
But if I change it to
'jotai': { singleton: true, strictVersion: false },
I can get the old behavior where ^ is added as a version prefix:
"id": "mf_application:jotai",
"name": "jotai",
"version": "2.12.5",
"singleton": true,
"requiredVersion": "^2.12.5",
For nx, if I do that, I get an warning instead
⚠ No required version specified and unable to automatically determine one. Unable to find required version for "@mf-test/my-library" in description file/s
And the manifest.json is generated with * as the version and ^undefined as the requiredVersion.
"id": "federation_remote_demo:@mf-test/my-library",
"name": "@mf-test/my-library",
"version": "*",
"singleton": true,
"requiredVersion": "^undefined",
If I use my workaround (which Claude also recommended lol)
shared: (libraryName, defaultConfig) => {
const { requiredVersion } = defaultConfig;
if (requiredVersion && !requiredVersion.startsWith('^')) {
return {
requiredVersion: `^${requiredVersion}`,
};
}
return defaultConfig;
}
The errors go away but version also has the ^ prefix:
"name": "@mf-test/my-library",
"version": "^0.0.1",
"singleton": true,
"requiredVersion": "^0.0.1",
I guess this isn't a huge deal as it seems that loading this into a consumer still seems to work.
@Coly010 this breaks nx usages of shared libs. Can you please suggest a better approach?
This gets worse when you migrate to rspack config with inferred tasks.
"name": "@mf-test/my-library",
"version": "*",
"singleton": true,
"requiredVersion": "^*",
This is what is generated now.
Looks like the reason is that requiredVersion was sent for locally buildable/publishable dependencies where as it is not with inferred rspack.
Shared config for @mf-test/my-library: { requiredVersion: '0.0.1', singleton: true, eager: undefined }
vs
Shared config for @mf-test/my-library: { requiredVersion: false, eager: undefined }