perf: guard isDeferred() behind experiments.deferImport in ConcatenatedModule#21096
Conversation
🦋 Changeset detectedLatest commit: 93041a9 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
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 |
|
👋 Hi there! This PR was automatically flagged and closed by our quality checks. If you believe this was a mistake, please review our contributing guidelines |
|
Hi, I believe this was flagged in error. This is a genuine performance fix — not a spam or low-quality contribution. The PR adds a missing The CLA is signed and a changeset is included. Happy to address any feedback from the maintainers. |
|
Please stop SPAM, if you think it was by mistake, just a wait an answer |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #21096 +/- ##
==========================================
+ Coverage 91.67% 91.81% +0.14%
==========================================
Files 581 581
Lines 61163 61168 +5
Branches 16691 16696 +5
==========================================
+ Hits 56069 56161 +92
+ Misses 5094 5007 -87
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Merging this PR will not alter performance
Comparing Footnotes
|
|
This PR is packaged and the instant preview is available (657d57b). Install it locally:
npm i -D webpack@https://pkg.pr.new/webpack@657d57b
yarn add -D webpack@https://pkg.pr.new/webpack@657d57b
pnpm add -D webpack@https://pkg.pr.new/webpack@657d57b |
Summary
In large builds with many external modules (for example, Module Federation remotes and CDN externals), ConcatenatedModule unconditionally calls moduleGraph.isDeferred() for every type: "external" entry during code generation and hash generation.
moduleGraph.isDeferred() walks all incoming connections of a module. When experiments.deferImport is disabled (the default), the result is guaranteed to be false, making these traversals unnecessary. In large applications this can result in a significant amount of redundant work.
ModuleConcatenationPlugin already avoids this overhead by guarding its isDeferred() check behind deferEnabled:
// ModuleConcatenationPlugin.js
if (deferEnabled && moduleGraph.isDeferred(module)) { ... }
The same guard was missing in ConcatenatedModule._getModulesWithInfo() and ConcatenatedModule.updateHash().
This change adds the missing guard, avoiding unnecessary graph traversals when deferImport is disabled while preserving the existing behavior when it is enabled.
In a real-world Next.js application using Module Federation with approximately 10 remotes, build profiling attributed roughly 11 minutes of build time to these redundant traversals.
Before CPU Profile

After CPU Profile

What kind of change does this PR introduce?
perfThis change does not alter functionality. It only skips work that is provably unnecessary when experiments.deferImport is disabled.
Did you add tests for your changes?
No additional tests were added.
The behavior when experiments.deferImport: true remains unchanged. This change only replaces a computed false result with an explicit false when deferImport is disabled.
Does this PR introduce a breaking change?
No runtime behavior changes are introduced.
If relevant, what needs to be documented once your changes are merged or what have you already documented?
No documentation changes are required.
Use of AI
partial