Skip to content

fix(nextjs-mf): use matchRemoteWithNameAndExpose for beforeRequest cache-busting#4661

Open
nag-clearstreet wants to merge 2 commits intomodule-federation:mainfrom
nag-clearstreet:fix/before-request-alias-lookup
Open

fix(nextjs-mf): use matchRemoteWithNameAndExpose for beforeRequest cache-busting#4661
nag-clearstreet wants to merge 2 commits intomodule-federation:mainfrom
nag-clearstreet:fix/before-request-alias-lookup

Conversation

@nag-clearstreet
Copy link
Copy Markdown

@nag-clearstreet nag-clearstreet commented Apr 14, 2026

Summary

The beforeRequest hook in next-internal-plugin appends a cache-busting ?t= parameter to remote entry URLs. However, it uses id.split('/').shift() to extract the remote name, which has two bugs:

  1. Alias mismatch: args.id uses the remote's alias, but the lookup only checks remote.name. When alias differs from name, find() returns undefined and cache-busting is silently skipped.

  2. Scoped name/alias breakage: For scoped identifiers like @scope/remote/widget, split('/').shift() yields @scope — not @scope/remote. This means any remote with a scoped name or alias never gets cache-busted.

This causes stale mf-manifest.json to be served from browser/CDN cache after deployments for affected remotes.

Change

Replace the broken split('/').shift() + find() approach with the canonical matchRemoteWithNameAndExpose from @module-federation/runtime-core. This function uses startsWith + boundary checking to correctly handle scoped names, scoped aliases, and all edge cases — and is already the standard approach used elsewhere in the runtime (e.g. getRemoteModuleAndOptions).

+import { matchRemoteWithNameAndExpose } from '@module-federation/runtime-core';

 beforeRequest: function (args: any) {
   const options = args.options;
   const id = args.id;
-  const remoteName = id.split('/').shift();
-  const remote = options.remotes.find(function (remote: any) {
-    return remote.name === remoteName;
-  });
-  if (!remote) return args;
+  const match = matchRemoteWithNameAndExpose(options.remotes, id);
+  if (!match) return args;
+  const remote = match.remote;
   if (remote.entry && remote.entry.includes('?t=')) {
     return args;
   }
   remote.entry = remote.entry + '?t=' + Date.now();
   return args;
 },

Also adds @module-federation/runtime-core as a direct dependency in nextjs-mf/package.json (already a transitive dep via @module-federation/runtime).

Tests

8 test cases for the beforeRequest hook:

  • Bare id with no expose path (app1)
  • Name match with expose (app1/button)
  • Alias match (my-alias/button → name my-remote-provider)
  • Scoped alias (@scope/my-remote/widget)
  • Scoped name without alias (@federation/app1/button)
  • Idempotency — no double-append when ?t= already present
  • No-match — unknown remote left unchanged
  • Multi-remote — only the matched remote is modified

Reproduction

  1. Register a remote with a scoped alias different from its name:
    registerRemotes([
      { name: 'my-provider', alias: '@scope/my-remote', entry: 'https://cdn.example.com/mf-manifest.json' }
    ]);
  2. Load via alias: loadRemote('@scope/my-remote/some-module')
  3. Observe mf-manifest.json request has no ?t= parameter
  4. With the fix, ?t= is correctly appended

Fixes #4659

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 14, 2026

⚠️ No Changeset found

Latest commit: f561060

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

@netlify
Copy link
Copy Markdown

netlify Bot commented Apr 14, 2026

Deploy Preview for module-federation-docs ready!

Name Link
🔨 Latest commit f561060
🔍 Latest deploy log https://app.netlify.com/projects/module-federation-docs/deploys/69dffcf2df1e370008b40771
😎 Deploy Preview https://deploy-preview-4661--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.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8b750f7347

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/nextjs-mf/package.json
@nag-clearstreet nag-clearstreet force-pushed the fix/before-request-alias-lookup branch from 8b750f7 to 7bbcb0b Compare April 14, 2026 17:34
@2heal1
Copy link
Copy Markdown
Member

2heal1 commented Apr 15, 2026

can you help add related test cases?

@nag-clearstreet nag-clearstreet force-pushed the fix/before-request-alias-lookup branch from 7bbcb0b to eca846a Compare April 15, 2026 06:31
@nag-clearstreet
Copy link
Copy Markdown
Author

nag-clearstreet commented Apr 15, 2026

@2heal1
Added 7 test cases for the beforeRequest hook covering:

  • Name match (simple, unscoped)
  • Alias match (alias differs from name)
  • Scoped alias (e.g. @scope/my-remote/widget)
  • Scoped name without alias (e.g. @federation/app1/button)
  • No double-append when ?t= already present
  • No match (unknown remote)
  • Multiple remotes (only matching one is modified)

Also added diagnostics: false to the ts-jest config since runtimePlugin.ts uses webpack globals and untyped hook args that fail strict type-checking.

…okup

Replace the broken id.split('/').shift() approach in the beforeRequest
hook with the canonical matchRemoteWithNameAndExpose from runtime-core.

The split approach has two bugs:
1. args.id uses the remote's alias, but the lookup only checks
   remote.name — when alias differs from name, cache-busting is skipped.
2. For scoped identifiers like @scope/remote/widget, split('/').shift()
   yields @scope, never matching the full alias @scope/remote.

matchRemoteWithNameAndExpose uses startsWith + boundary checking to
correctly handle scoped names, scoped aliases, and all edge cases — and
is already the standard approach used elsewhere in the runtime (e.g.
getRemoteModuleAndOptions).

Fixes module-federation#4659
@nag-clearstreet nag-clearstreet force-pushed the fix/before-request-alias-lookup branch from eca846a to 4d158f1 Compare April 15, 2026 06:45
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4d158f1400

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/nextjs-mf/src/plugins/container/runtimePlugin.ts
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.

beforeRequest cache-busting skipped when remote alias differs from name

2 participants