core
core copied to clipboard
feat(enhanced): support shared module layers
Description
This pull request enhances the module sharing functionality in the enhanced package by implementing comprehensive layer support and improving schema validation. The changes focus on the compiler mechanics for module layers, with runtime updates planned for a future PR.
https://github.com/module-federation/core/issues/3153
Key Changes
-
Schema Validation Enhancements
- Added TypeScript-based validation for
ConsumeSharedPluginschema - Enhanced
ProvideSharedPluginschema with layer support - Added new properties for layer configuration:
layer: { description: 'Layer for the shared module.', type: 'string', minLength: 1 }, issuerLayer: { description: 'Layer in which the issuer should be.', type: 'string', minLength: 1 }
- Added TypeScript-based validation for
-
Module Resolution Improvements
- Enhanced request key creation with layer support
- Updated module resolution logic to handle layer-specific requests
- Improved handling of relative, absolute, and module requests
- Added support for composite keys in module resolution
-
Share Configuration Updates
- Added layer support to
normalizeConsumeShareOptions - Enhanced share scope management with layer awareness
- Updated module matching logic to consider layer context
- Improved type definitions for shared module configurations
- Added layer support to
Example Configuration
new SharePlugin({
shareScope: ['rsc','default'],
shared: {
react: {
singleton: true,
},
'explicit-layer-react': {
request: 'react/index2',
import: 'react/index2',
shareKey: 'react',
singleton: true,
issuerLayer: 'differing-layer',
layer: 'explicit-layer',
}
}
})
Technical Implementation Details
- [
utils.ts]: Enhanced module resolution with layer support - [
ConsumeSharedPlugin.check.ts]: Added comprehensive schema validation - [
ProviderSharedPlugin.ts]: Updated schema with layer properties - Added support for composite keys in module resolution
- Improved error handling for module resolution failures
Note
This PR focuses on the compiler mechanics for module layers. Runtime module aspects will be updated in a subsequent PR.
Types of changes
- [ ] Docs change / refactoring / dependency upgrade
- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
Checklist
- [x] I have added tests to cover my changes
- [x] All new and existing tests passed
- [ ] I have updated the documentation
🦋 Changeset detected
Latest commit: 6905c82e33c63a51f042be46cd00d4652ffe42b5
The changes in this PR will be included in the next version bump.
This PR includes changesets to release 31 packages
| Name | Type |
|---|---|
| @module-federation/enhanced | Major |
| @module-federation/runtime | Major |
| @module-federation/webpack-bundler-runtime | Major |
| @module-federation/runtime-core | Major |
| @module-federation/managers | Major |
| @module-federation/sdk | Major |
| @module-federation/nextjs-mf | Patch |
| @module-federation/modern-js | Major |
| @module-federation/node | Patch |
| @module-federation/rsbuild-plugin | Major |
| @module-federation/storybook-addon | Major |
| @module-federation/modernjsapp | Patch |
| @module-federation/devtools | Major |
| @module-federation/data-prefetch | Major |
| @module-federation/dts-plugin | Major |
| @module-federation/retry-plugin | Major |
| @module-federation/runtime-tools | Major |
| @module-federation/bridge-react | Major |
| @module-federation/bridge-vue3 | Major |
| @module-federation/manifest | Major |
| @module-federation/rspack | Major |
| @module-federation/cli | Major |
| @module-federation/esbuild | Patch |
| @module-federation/utilities | Patch |
| @module-federation/bridge-react-webpack-plugin | Major |
| @module-federation/inject-external-runtime-core-plugin | Major |
| @module-federation/third-party-dts-extractor | Major |
| @module-federation/bridge-shared | Major |
| @module-federation/error-codes | Major |
| create-module-federation | Major |
| website-new | 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
Deploy Preview for module-federation-docs ready!
| Name | Link |
|---|---|
| Latest commit | 6905c82e33c63a51f042be46cd00d4652ffe42b5 |
| Latest deploy log | https://app.netlify.com/sites/module-federation-docs/deploys/67f6398820e32c000892be77 |
| Deploy Preview | https://deploy-preview-3276--module-federation-docs.netlify.app |
| Preview on mobile | Toggle QR Code...Use your smartphone camera to open QR code link. |
To edit notification comments on pull requests, go to your Netlify site configuration.
breadcrumb for myself - i'm hitting this same issue in next using a shared module library
Would be best to contact vercel since nextjs-mf is in maintenance mode and while i can provide the compiler mechanics, stuff in next probably needs to be changed to support federation specifically.
@sokra Ive split some of the changes off into another PR: https://github.com/module-federation/core/pull/3307
Specifically - dealing with filtering the requests accordingly - because the key in share is used to match the request, this causes problems when i need same package layered in etc, also if user uses require(lib-two), {lib-two: {import: lib2}}, since lib-twois what the bundler is looking for the match requests, i propose we add arequestfield that works the same as thekey` on the share object to allow us to match against all conditons like prefix share etc.
@sokra - share scope and container init only accepts 1 share scope, so this approach did not work for layers. What i have done instead is alias the share keys with the layer, like "(rsc)react" so that the layered modules can co-exist in one share scope and webpack_init_sharing("default") still work as expected, otherwise youd need to be able to apply multiple share scopes at once
@sokra - share scope and container init only accepts 1 share scope, so this approach did not work for layers. What i have done instead is alias the share keys with the layer, like "(rsc)react" so that the layered modules can co-exist in one share scope and webpack_init_sharing("default") still work as expected, otherwise youd need to be able to apply multiple share scopes at once
hmm... I'm not a fan of adding the layer as string to the request. You can use share scope for that. The runtime code supports to have multiple share scopes, each module can have a different one. It would only initialize a share scope when a module from that scope/layer is requested, which should be fine. Yes, manually calling __webpack_init_sharing__ would require multiple calls if you want to manually initialize multiple scope scopes, but that isn't something that you would normally do anyway.
@sokra but when i tried to do this, the host would not initialize the share scope of another at startup.
It seems like the remoteEntry "picks" a share scope to interface with, so if there is not another remote who has "shareScope: 'rsc'" set, then it seems not to work or pull in multiples.
I can alter the source code in the branch again to display the problem i encountered.
It seems that if there is no runtime who specified their shareScope as the one we are using, other moduels are unable to find it.
I believe the other issue i encountered was that it is not trivial to determine which share scope to use, becuase it depends on the issuer - the issuer may be using default share scope while the layered module may require a different share scope. Since I do not know much information during the resolve process.
For instance, i have to use a composite key in resolveMatchedConfigs because i cannot have 2 keys with the same name, so i need to give it some alternative key. UPDATE: so i switched it to not modifying the share key, and i do think that this was the original issue i encountered, how the resolve matched config works, i need a predictable way to locate the right layered module. making a composite key like (layer)key worked, but iill try moving it under another share scope + provide some updated way for the reslve key to be composite so that i can match the configs of the same share to a different layer
heres a reference without share key mutations: https://github.com/module-federation/core/pull/3524
@sokra in that other pr, the root issue was as follows.
the RemoteRuntimeModule constructs its map from the config, and that requires "sharescope" to be set in the build config. Infering it from the "layer" of the consuming file would be unreliable i think? So everyone just inits with default, even tho the shares are set to the right share scopes - nobody ever uses that scope when initializing the remote to it just never looks for it.
in the factorize hook of ContainerReferencePlugin i could do something like this:
),
`.${data.request.slice(key.length)}`,
//@ts-ignore
data.contextInfo.issuerLayer || config.shareScope,
however, this would not work if the issuer module was in another layer.
Like in my tests, App.js has no issuerLery, but the component i import does have a layer. This might work for rsc, but for other use cases where the parent module is a differnt layer than the exposed child it imports - it would not work. Im not sure how common this case would be.
need to update changeset , current can not be consumed...
https://github.com/module-federation/core/actions/runs/14100100380/job/39494613362
Sure thing, doing it now!