Skip to content

Fix chunk-merge bug in ReactFlightWebpackPlugin (v19.2.1)#21

Open
AbanoubGhadban wants to merge 1 commit intoupgrade-to-react-v19.2.1from
fix/chunk-merge-bug-v19.2.1
Open

Fix chunk-merge bug in ReactFlightWebpackPlugin (v19.2.1)#21
AbanoubGhadban wants to merge 1 commit intoupgrade-to-react-v19.2.1from
fix/chunk-merge-bug-v19.2.1

Conversation

@AbanoubGhadban
Copy link
Collaborator

Summary

  • Fix recordModule() in ReactFlightWebpackPlugin to merge chunk arrays instead of overwriting when the same module appears in multiple webpack chunk groups
  • This fixes TypeError: r[e] is not a function errors during RSC hydration on slow networks (e.g., 3G + CPU throttling)
  • Built from React fork rsc-patches/v19.2.1 with the fix applied

Root Cause

When webpack splits modules across multiple chunks (via chunk groups), the plugin's recordModule() was called multiple times for the same module. Each call overwrote the previous entry with only the current chunk group's chunks, losing earlier chunk references. The RSC flight client then failed to preload all necessary chunks, causing __webpack_require__ to fail when a module factory hadn't been registered yet.

The Fix

Changed from:

filePathToModuleMetadata[href] = { id, chunks, name: '*' };

To merge logic that preserves chunks from all chunk groups:

if (filePathToModuleMetadata[href]) {
  // Merge chunks from additional chunk groups
  const existing = filePathToModuleMetadata[href];
  const existingChunkIds = new Set();
  for (let i = 0; i < existing.chunks.length; i += 2) {
    existingChunkIds.add(existing.chunks[i]);
  }
  for (let j = 0; j < chunks.length; j += 2) {
    if (!existingChunkIds.has(chunks[j])) {
      existing.chunks.push(chunks[j], chunks[j + 1]);
    }
  }
} else {
  filePathToModuleMetadata[href] = { id, chunks: chunks.slice(), name: '*' };
}

Test plan

  • Built from fixed React fork branch
  • Verify with localhub-demo-search under Slow 3G + 6x CPU throttling
  • Confirm zero TypeError errors after 3+ hard reloads

Resolves #19

🤖 Generated with Claude Code

When a 'use client' module appears in multiple webpack chunk groups,
recordModule() was overwriting the manifest entry instead of merging
chunk arrays. This caused missing chunk preloads during RSC hydration,
leading to __webpack_require__ failures under slow networks.

Built from React fork rsc-patches/v19.2.1 with the fix applied.

Resolves #19

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Feb 24, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/chunk-merge-bug-v19.2.1

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link

greptile-apps bot commented Feb 24, 2026

Greptile Summary

Fixes a critical bug where React Server Components would fail to hydrate on slow networks with TypeError: r[e] is not a function. The root cause was that when a client module appeared in multiple webpack chunk groups, the plugin was overwriting the module's chunk metadata instead of merging it, causing the RSC flight client to miss necessary chunk preloads.

Key changes:

  • Modified recordModule() to detect when a module has already been recorded
  • Added merge logic that combines chunk arrays using a Set to avoid duplicates
  • Changed from direct assignment to chunks.slice() to prevent unintended mutations

Trade-offs:

  • Variable shadowing (id and module reused for different purposes) reduces code readability, though the fix is functionally correct

Confidence Score: 4/5

  • Safe to merge - fixes a critical production bug with correct merge logic
  • The fix correctly addresses the root cause by merging chunk arrays instead of overwriting them. The logic uses Set-based deduplication and properly handles the paired chunk structure (id, file). Minor style issues with variable shadowing don't affect correctness.
  • No files require special attention - the single-file change is straightforward

Important Files Changed

Filename Overview
src/react-server-dom-webpack/cjs/react-server-dom-webpack-plugin.js Implements chunk array merging to fix RSC hydration errors when modules appear in multiple webpack chunk groups

Last reviewed commit: 14ff4dd

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

1 file reviewed, 4 comments

Edit Code Review Agent Settings | Greptile

void 0 !== module)
)
if (filePathToModuleMetadata[module]) {
id = filePathToModuleMetadata[module];
Copy link

Choose a reason for hiding this comment

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

Variable shadowing: reusing id parameter name to store metadata object reduces readability

Suggested change
id = filePathToModuleMetadata[module];
const existing = filePathToModuleMetadata[module];

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

)
if (filePathToModuleMetadata[module]) {
id = filePathToModuleMetadata[module];
module = new Set();
Copy link

Choose a reason for hiding this comment

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

Variable shadowing: reusing module parameter name to store Set object reduces readability

Suggested change
module = new Set();
const existingChunkIds = new Set();

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +251 to +252
for (var i = 0; i < id.chunks.length; i += 2)
module.add(id.chunks[i]);
Copy link

Choose a reason for hiding this comment

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

Use descriptive variable name in the merge loop

Suggested change
for (var i = 0; i < id.chunks.length; i += 2)
module.add(id.chunks[i]);
for (var existingIdx = 0; existingIdx < existing.chunks.length; existingIdx += 2)
existingChunkIds.add(existing.chunks[existingIdx]);

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +253 to +255
for (i = 0; i < chunks.length; i += 2)
module.has(chunks[i]) ||
id.chunks.push(chunks[i], chunks[i + 1]);
Copy link

Choose a reason for hiding this comment

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

Use descriptive variable name and clearer condition formatting

Suggested change
for (i = 0; i < chunks.length; i += 2)
module.has(chunks[i]) ||
id.chunks.push(chunks[i], chunks[i + 1]);
for (var newIdx = 0; newIdx < chunks.length; newIdx += 2) {
if (!existingChunkIds.has(chunks[newIdx])) {
existing.chunks.push(chunks[newIdx], chunks[newIdx + 1]);
}
}

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant