core icon indicating copy to clipboard operation
core copied to clipboard

feat(enhanced): support shared module layers

Open ScriptedAlchemy opened this issue 1 year ago • 10 comments

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

  1. Schema Validation Enhancements

    • Added TypeScript-based validation for ConsumeSharedPlugin schema
    • Enhanced ProvideSharedPlugin schema 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
      }
      
  2. 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
  3. 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

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

ScriptedAlchemy avatar Nov 25 '24 23:11 ScriptedAlchemy

🦋 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

changeset-bot[bot] avatar Nov 25 '24 23:11 changeset-bot[bot]

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

QR Code

Use your smartphone camera to open QR code link.

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

netlify[bot] avatar Nov 25 '24 23:11 netlify[bot]

breadcrumb for myself - i'm hitting this same issue in next using a shared module library

sebbean avatar Nov 27 '24 00:11 sebbean

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.

ScriptedAlchemy avatar Nov 27 '24 01:11 ScriptedAlchemy

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

ScriptedAlchemy avatar Dec 03 '24 03:12 ScriptedAlchemy

@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

ScriptedAlchemy avatar Jan 21 '25 20:01 ScriptedAlchemy

@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 avatar Jan 22 '25 08:01 sokra

@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

ScriptedAlchemy avatar Feb 17 '25 22:02 ScriptedAlchemy

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

image image

ScriptedAlchemy avatar Feb 28 '25 05:02 ScriptedAlchemy

need to update changeset , current can not be consumed...

https://github.com/module-federation/core/actions/runs/14100100380/job/39494613362

2heal1 avatar Mar 27 '25 09:03 2heal1

Sure thing, doing it now!

ScriptedAlchemy avatar Mar 28 '25 03:03 ScriptedAlchemy