nx icon indicating copy to clipboard operation
nx copied to clipboard

module-federation requiredVersion/strictVersion has no affect?

Open h3adache opened this issue 7 months ago • 6 comments

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: Image

Internal libraries now show as * and ^undefined Image

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

  1. Create a shared internal library
  2. Create new nx federated remote that use said library
  3. 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

h3adache avatar May 30 '25 19:05 h3adache

@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)

Coly010 avatar Jun 02 '25 15:06 Coly010

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-federation directory structure
  • [x] Examine withModuleFederation utilities implementation
  • [x] Check NxModuleFederationRspackPlugin implementation
  • [x] Look at package.json dependencies for @module-federation/enhanced version
  • [x] Compare current implementation with what might have existed in earlier versions
  • [x] Analyze how requiredVersion and strictVersion configurations 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:

  1. 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.ts hasn't fundamentally changed in how it processes requiredVersion: false.

  2. Upstream Dependency Change: The @module-federation/enhanced package was updated to ^0.9.0 in Nx 21. This is likely where the behavioral change originates.

  3. Nx Logic Still Works Correctly: The Nx code in share.ts lines 135-144 still properly handles requiredVersion: false:

    [library.name]: {
      ...(version
        ? {
            requiredVersion: version,
            singleton: true,
          }
        : { requiredVersion: false }),
      eager,
    }
    
  4. SharedFunction Processing: The applySharedFunction() in share.ts lines 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:

  1. 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.

  2. Nx Logic Unchanged: The core logic for processing shared configurations in Nx hasn't been fundamentally altered.

  3. Version Correlation: The timing aligns with the @module-federation/enhanced upgrade.

Recommendation:

This should be reported as either:

  1. A breaking change in @module-federation/enhanced v0.9.0 that wasn't properly documented
  2. 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;
}

claude[bot] avatar Jun 02 '25 15:06 claude[bot]

@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

Coly010 avatar Jun 02 '25 16:06 Coly010

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.

h3adache avatar Jun 02 '25 18:06 h3adache

@Coly010 this breaks nx usages of shared libs. Can you please suggest a better approach?

h3adache avatar Jun 05 '25 16:06 h3adache

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 }

h3adache avatar Jun 12 '25 14:06 h3adache