[PR #413] [MERGED] fix: dedup RSC client references to prevent module duplication in dev #554

Closed
opened 2026-05-06 13:08:44 +02:00 by BreizhHardware · 0 comments

📋 Pull Request Information

Original PR: https://github.com/cloudflare/vinext/pull/413
Author: @Divkix
Created: 3/10/2026
Status: Merged
Merged: 3/10/2026
Merged by: @james-elicx

Base: mainHead: fix/client-reference-dedup


📝 Commits (3)

  • 7b82067 fix: dedup RSC client references to prevent module duplication in dev
  • 439d2ec fix: make dedup test layout a server component to exercise RSC proxy path
  • 641653f docs: address PR review feedback for client-reference-dedup plugin

📊 Changes

11 files changed (+355 additions, -0 deletions)

View changed files

📝 packages/vinext/src/index.ts (+3 -0)
packages/vinext/src/plugins/client-reference-dedup.ts (+103 -0)
📝 pnpm-lock.yaml (+8 -0)
📝 tests/app-router.test.ts (+15 -0)
tests/client-reference-dedup.test.ts (+183 -0)
tests/fixtures/app-basic/__test_packages__/fake-context-lib/index.js (+1 -0)
tests/fixtures/app-basic/__test_packages__/fake-context-lib/internal/context.js (+14 -0)
tests/fixtures/app-basic/__test_packages__/fake-context-lib/package.json (+9 -0)
tests/fixtures/app-basic/app/context-dedup-test/layout.tsx (+5 -0)
tests/fixtures/app-basic/app/context-dedup-test/page.tsx (+13 -0)
📝 tests/fixtures/app-basic/package.json (+1 -0)

📄 Description

Summary

Fixes #409

When the RSC plugin creates client-in-server-package-proxy modules for "use client" submodules within packages (e.g., @mantine/core), it re-exports from absolute file paths. These bypass Vite's pre-bundling in the client environment, causing the browser to load two separate module instances — one from the raw ESM path and one from .vite/deps/. This breaks React context providers/consumers since createContext() runs twice.

  • Add vinext:client-reference-dedup plugin (enforce: "pre", dev-only) that intercepts absolute node_modules path imports originating from proxy virtual modules in the client environment and redirects them through bare specifier imports, which Vite's import analysis routes through pre-bundling automatically
  • Respects optimizeDeps.exclude from resolved config so user overrides are honored
  • No RSC plugin modifications needed — works transparently at the Vite plugin level

Test plan

  • Unit tests for extractPackageName (regular, scoped, nested node_modules, edge cases)
  • Unit tests for plugin resolveId/load hooks (client env gating, proxy importer gating, exclude list)
  • Integration test with fake-context-lib fixture package (internal "use client" submodule with createContext — verifies provider value is readable by consumer)
  • pnpm run typecheck passes
  • pnpm run lint passes
  • pnpm run fmt:check passes

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/cloudflare/vinext/pull/413 **Author:** [@Divkix](https://github.com/Divkix) **Created:** 3/10/2026 **Status:** ✅ Merged **Merged:** 3/10/2026 **Merged by:** [@james-elicx](https://github.com/james-elicx) **Base:** `main` ← **Head:** `fix/client-reference-dedup` --- ### 📝 Commits (3) - [`7b82067`](https://github.com/cloudflare/vinext/commit/7b82067d0a2daee77e8762d1bb68fbad67aad1bb) fix: dedup RSC client references to prevent module duplication in dev - [`439d2ec`](https://github.com/cloudflare/vinext/commit/439d2ec6d315554d508d0766ac66daa55960b73f) fix: make dedup test layout a server component to exercise RSC proxy path - [`641653f`](https://github.com/cloudflare/vinext/commit/641653f9aeddcef308f54cc54746ea0186edc361) docs: address PR review feedback for client-reference-dedup plugin ### 📊 Changes **11 files changed** (+355 additions, -0 deletions) <details> <summary>View changed files</summary> 📝 `packages/vinext/src/index.ts` (+3 -0) ➕ `packages/vinext/src/plugins/client-reference-dedup.ts` (+103 -0) 📝 `pnpm-lock.yaml` (+8 -0) 📝 `tests/app-router.test.ts` (+15 -0) ➕ `tests/client-reference-dedup.test.ts` (+183 -0) ➕ `tests/fixtures/app-basic/__test_packages__/fake-context-lib/index.js` (+1 -0) ➕ `tests/fixtures/app-basic/__test_packages__/fake-context-lib/internal/context.js` (+14 -0) ➕ `tests/fixtures/app-basic/__test_packages__/fake-context-lib/package.json` (+9 -0) ➕ `tests/fixtures/app-basic/app/context-dedup-test/layout.tsx` (+5 -0) ➕ `tests/fixtures/app-basic/app/context-dedup-test/page.tsx` (+13 -0) 📝 `tests/fixtures/app-basic/package.json` (+1 -0) </details> ### 📄 Description ## Summary Fixes #409 When the RSC plugin creates `client-in-server-package-proxy` modules for `"use client"` submodules within packages (e.g., `@mantine/core`), it re-exports from absolute file paths. These bypass Vite's pre-bundling in the client environment, causing the browser to load two separate module instances — one from the raw ESM path and one from `.vite/deps/`. This breaks React context providers/consumers since `createContext()` runs twice. - Add `vinext:client-reference-dedup` plugin (`enforce: "pre"`, dev-only) that intercepts absolute `node_modules` path imports originating from proxy virtual modules in the client environment and redirects them through bare specifier imports, which Vite's import analysis routes through pre-bundling automatically - Respects `optimizeDeps.exclude` from resolved config so user overrides are honored - No RSC plugin modifications needed — works transparently at the Vite plugin level ## Test plan - [x] Unit tests for `extractPackageName` (regular, scoped, nested node_modules, edge cases) - [x] Unit tests for plugin `resolveId`/`load` hooks (client env gating, proxy importer gating, exclude list) - [x] Integration test with `fake-context-lib` fixture package (internal `"use client"` submodule with `createContext` — verifies provider value is readable by consumer) - [x] `pnpm run typecheck` passes - [x] `pnpm run lint` passes - [x] `pnpm run fmt:check` passes --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
BreizhHardware 2026-05-06 13:08:44 +02:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/vinext#554
No description provided.