Skip to content

fix(build): make vinext shim chunk rule reachable for installed copies - #2795

Open
Jbithell wants to merge 1 commit into
cloudflare:mainfrom
Jbithell:claude/client-manual-chunks-shims-97v23p
Open

fix(build): make vinext shim chunk rule reachable for installed copies#2795
Jbithell wants to merge 1 commit into
cloudflare:mainfrom
Jbithell:claude/client-manual-chunks-shims-97v23p

Conversation

@Jbithell

@Jbithell Jbithell commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Closes #2794

Problem

createClientManualChunks in src/build/client-build-config.ts returns early
for every module id containing node_modules. For an installed copy of vinext,
its own client shims live at <app>/node_modules/vinext/dist/shims/, so they
hit that early return — getPackageName() yields "vinext", which matches no
framework package — and the shimsDir branch below is never reached. The
"vinext" chunk therefore never forms for any app installing vinext from npm;
it only forms from a linked checkout.

Left to graph-based splitting, the shims scatter: in a real production build
(1.0.0-beta.4, Workers target, App Router), shims/navigation-context-state.js
landed inside the browser entry chunk while shims/slot.js,
shims/error-boundary.js, and shims/navigation.js became separate
client-reference chunks, each in a static import cycle with the entry:

index-*.js -> slot-*.js           -> index-*.js
index-*.js -> error-boundary-*.js -> index-*.js
index-*.js -> navigation-*.js     -> index-*.js

Chunks are ES modules, so the chunk closing the cycle evaluates before the
entry's body has run. slot.js calls getBfcacheIdMapContext() at module
scope; that helper reads .createContext off the entry chunk's hoisted
var F = __toESM(requireReact(), 1), which is still undefined:

Uncaught TypeError: Cannot read properties of undefined (reading 'createContext')

This throws during entry evaluation, so hydration never happens — the site
server-renders and is completely inert. The build succeeds and dev is
unaffected (it doesn't chunk the same way), so nothing in the toolchain
catches it.

Fix

Check the shims prefix before the node_modules branch. shimsDir is an
absolute path into vinext's own dist/shims/, so the check is unambiguous in
both installed and linked layouts, and no react/react-dom/scheduler id can
start with it — the framework and react-dom-server groupings are
unaffected. The react-dom branch now reuses the hoisted slashedId instead
of computing its own.

No change to shimsDir construction is needed: it comes from pathslash, whose
resolve already guarantees forward slashes on Windows.

Verification

  • New unit tests: an installed-layout shim id (.../node_modules/vinext/dist/shims/slot.js)
    now maps to "vinext" (fails on main), route-owned shims are still
    excluded under preserveRouteBoundaries, and framework/vendor/app ids are
    unchanged — verified against the shipped implementation across 17 id shapes,
    including pnpm layouts and ?query suffixes.
  • Rebuilt a real affected app with the fix's grouping: the vinext-* chunk
    forms, the three cycles disappear, and the entry evaluates cleanly.

Notes

Copilot AI review requested due to automatic review settings August 2, 2026 15:46
@pkg-pr-new

pkg-pr-new Bot commented Aug 2, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@vinext/cloudflare@2795
npm i https://pkg.pr.new/create-vinext-app@2795
npm i https://pkg.pr.new/@vinext/types@2795
npm i https://pkg.pr.new/vinext@2795

commit: 82378a5

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes client production chunk grouping so vinext’s own compiled shims still get grouped into the "vinext" chunk when vinext is installed from npm (i.e., shims live under <app>/node_modules/vinext/dist/shims/). This prevents Rolldown’s graph-based splitting from scattering shims into separate chunks that can form static import cycles with the browser entry chunk and break hydration at runtime.

Changes:

  • Make the "vinext" shims grouping check run before the node_modules early-return logic in createClientManualChunks.
  • Reuse the hoisted slashedId for the react-dom subpath classification.
  • Add unit tests covering the “installed layout” (.../node_modules/vinext/dist/shims/...) and ensuring existing route-boundary exclusions and framework grouping remain intact.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
packages/vinext/src/build/client-build-config.ts Moves shims detection ahead of the node_modules branch so installed-copy shims reliably map to the "vinext" chunk; reuses slashedId in the react-dom path logic.
tests/build-optimization.test.ts Adds focused regression tests for installed-layout shim IDs, plus assertions that preserve-route-boundary and framework/vendor behavior is unchanged.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Performance benchmarks

Compared 82378a5 against base ced0881 using alternating same-runner rounds. Next.js was unchanged and skipped.

0 improved · 0 regressed · 6 within ±1.5%

Scenario Framework Baseline Current Change
Client bundle size (gzip) vinext 134.6 KB 134.6 KB ⚫ +0.0%
Client entry size (gzip) vinext 122.1 KB 122.1 KB ⚫ +0.0%
Dev server cold start vinext 3.01 s 2.99 s ⚫ -0.9%
Production build time vinext 3.33 s 3.30 s ⚫ -0.8%
RSC entry closure size (gzip) vinext 112.4 KB 112.4 KB ⚫ -0.0%
Server bundle size (gzip) vinext 189.9 KB 189.9 KB ⚫ -0.0%

View detailed results and traces

🟢 improvement · 🔴 regression · ⚫ change below 1.5% · paired base/head

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.

Client build: the "vinext" shim chunk rule is unreachable for installed copies, producing an entry↔shim chunk cycle that kills hydration

3 participants