Skip to content

Commit ffed994

Browse files
zrosenbauerclaude
andauthored
fix(packages/ui,packages/cli,packages/zpress): resolve duplicate React instances in dev server (#90)
* fix(packages/ui,packages/cli,packages/zpress): resolve duplicate React instances in dev server pnpm's strict isolation caused consumer repos to get two React copies — one from the consumer's peer dep and one privately installed for @zpress/cli. Rspress's rspack resolved theme components to a different React instance, triggering "Invalid hook call" errors and preventing the dev server from loading. - Add react/react-dom resolve aliases in Rspress builder config - Move react from dependencies to peerDependencies in @zpress/cli - Align React peer version in @zpress/kit to ^19.2.5 Co-Authored-By: Claude <noreply@anthropic.com> * fix(packages/ui): alias react to package directory, not entry file Rspack uses prefix matching for aliases, so aliasing react to its entry file (index.js) would break subpath imports like react/jsx-runtime. Resolve to the package directory instead via react/package.json dirname. Co-Authored-By: Claude <noreply@anthropic.com> * fix(packages/cli): restore react as direct dependency for ink ecosystem ink-gradient and other ink transitive deps import react directly. With pnpm's strict isolation, moving react to peerDependencies breaks these transitive imports on Vercel. Keep react in dependencies for runtime resolution; the webpack alias in @zpress/ui handles browser deduplication independently. Co-Authored-By: Claude <noreply@anthropic.com> * fix(repo,packages/ui): resolve pnpm strict isolation failures for ink-gradient and react alias - Add packageExtensions for ink-gradient to declare react as a peer dep (ink-gradient imports react without declaring it, breaking pnpm strict mode) - Resolve react alias from @zpress/ui's own context (import.meta.url) instead of project root, which may not have react as a direct dependency Co-Authored-By: Claude <noreply@anthropic.com> * fix(examples/simple): update config to current sections schema Replace removed keys (link, from, prefix, titleFrom) with their current equivalents (path, include). Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent c02525c commit ffed994

File tree

6 files changed

+48
-55
lines changed

6 files changed

+48
-55
lines changed

.changeset/fix-duplicate-react.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
'@zpress/ui': patch
3+
'@zpress/cli': patch
4+
'@zpress/kit': patch
5+
---
6+
7+
fix(packages/ui,packages/cli,packages/zpress): resolve duplicate React instances in consumer repos
8+
9+
Added `react` and `react-dom` resolve aliases to the Rspress builder config so rspack always uses the consumer's single React copy. Moved `react` from direct dependencies to peer dependencies in `@zpress/cli` to prevent pnpm from installing a private copy. Aligned React peer version range in `@zpress/kit` to `^19.2.5`.

examples/simple/zpress.config.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,21 @@ export default defineConfig({
77
sections: [
88
{
99
title: 'Getting Started',
10-
link: '/getting-started',
11-
from: 'docs/getting-started.md',
10+
path: '/getting-started',
11+
include: 'docs/getting-started.md',
1212
icon: 'pixelarticons:speed-fast',
1313
},
1414
{
1515
title: 'API Reference',
16-
link: '/api-reference',
17-
from: 'docs/api-reference.md',
16+
path: '/api-reference',
17+
include: 'docs/api-reference.md',
1818
icon: 'pixelarticons:book-open',
1919
},
2020
{
2121
title: 'Guides',
22-
prefix: '/guides',
23-
from: 'docs/guides/*.md',
22+
path: '/guides',
23+
include: 'docs/guides/*.md',
2424
icon: 'pixelarticons:article',
25-
titleFrom: 'frontmatter',
2625
sort: 'alpha',
2726
},
2827
],

package.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,13 @@
6464
],
6565
"overrides": {
6666
"@rsbuild/core": "2.0.0-beta.9"
67+
},
68+
"packageExtensions": {
69+
"ink-gradient": {
70+
"peerDependencies": {
71+
"react": "*"
72+
}
73+
}
6774
}
6875
}
6976
}

packages/ui/src/config.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { execSync } from 'node:child_process'
22
import { existsSync, readFileSync } from 'node:fs'
3+
import { createRequire } from 'node:module'
34
import path from 'node:path'
45

56
import type { UserConfig } from '@rspress/core'
@@ -75,6 +76,15 @@ export function createRspressConfig(options: CreateRspressConfigOptions): UserCo
7576
const isVscode = vscode === true
7677
const headScriptBody = buildHeadScriptBody({ colorMode, themeName, vscode: isVscode })
7778

79+
// Force a single React instance across all compiled theme components.
80+
// Without this alias, Rspress's rspack may resolve react from the
81+
// @zpress/ui dist/theme directory (deep inside pnpm's .pnpm store),
82+
// producing a second copy that triggers "Invalid hook call" errors.
83+
// Resolve from this package's context (react is a peer dep of @zpress/ui).
84+
const selfRequire = createRequire(import.meta.url)
85+
const reactAlias = path.dirname(selfRequire.resolve('react/package.json'))
86+
const reactDomAlias = path.dirname(selfRequire.resolve('react-dom/package.json'))
87+
7888
return {
7989
root: paths.contentDir,
8090
outDir: paths.distDir,
@@ -130,6 +140,10 @@ export function createRspressConfig(options: CreateRspressConfigOptions): UserCo
130140
},
131141
resolve: {
132142
alias: {
143+
// Deduplicate React — pnpm isolation can cause rspack to resolve
144+
// different physical copies from theme components vs Rspress internals.
145+
react: reactAlias,
146+
'react-dom': reactDomAlias,
133147
// Allow generated MDX files in .zpress/content/ to import
134148
// zpress React components used in landing pages.
135149
'@zpress/ui/theme': path.resolve(import.meta.dirname, 'theme', 'index.tsx'),

packages/zpress/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@
5353
},
5454
"peerDependencies": {
5555
"@rspress/core": "catalog:",
56-
"react": "^19.2.4",
57-
"react-dom": "^19.2.4"
56+
"react": "^19.2.5",
57+
"react-dom": "^19.2.5"
5858
},
5959
"engines": {
6060
"node": ">=24.0.0"

pnpm-lock.yaml

Lines changed: 10 additions & 46 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)