Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"name": "@react-doctor/core",
"version": "0.3.0",
Expand Down Expand Up @@ -28,11 +28,18 @@
"effect": "4.0.0-beta.70",
"eslint-plugin-react-hooks": "^7.1.1",
"jiti": "^2.7.0",
"oxlint": "^1.66.0",
"oxlint-plugin-react-doctor": "workspace:*",
"picomatch": "^4.0.4",
"typescript": "^6.0.3"
},
"peerDependencies": {
"oxlint": ">=1.0.0"
},
"peerDependenciesMeta": {
"oxlint": {
"optional": true
}
},
"devDependencies": {
"@effect/vitest": "4.0.0-beta.70",
"@types/node": "^25.6.0",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,4 @@ export * from "./utils/warn-config-issue.js";
export * from "./runners/oxlint/capabilities.js";
export * from "./runners/oxlint/config.js";
export * from "./runners/oxlint/plugin-resolution.js";
export { OxlintNotInstalledError } from "./runners/oxlint/resolve-paths.js";
21 changes: 20 additions & 1 deletion packages/core/src/runners/oxlint/resolve-paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,27 @@ import * as path from "node:path";

const esmRequire = createRequire(import.meta.url);

export class OxlintNotInstalledError extends Error {
constructor() {
super(
`oxlint is not installed. Install it as a dependency:\n\n` +
` npm install -D oxlint\n` +
` # or: pnpm add -D oxlint\n` +
` # or: yarn add -D oxlint\n\n` +
`In pnpm monorepos using vite-plus/vitest, install oxlint at the workspace root ` +
`to avoid duplicate module instances. See: https://github.com/millionco/react-doctor/issues`,
);
this.name = "OxlintNotInstalledError";
}
}

export const resolveOxlintBinary = (): string => {
const oxlintMainPath = esmRequire.resolve("oxlint");
let oxlintMainPath: string;
try {
oxlintMainPath = esmRequire.resolve("oxlint");
} catch {
throw new OxlintNotInstalledError();
}
const oxlintPackageDirectory = path.resolve(path.dirname(oxlintMainPath), "..");
return path.join(oxlintPackageDirectory, "bin", "oxlint");
};
Expand Down
48 changes: 48 additions & 0 deletions packages/react-doctor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,54 @@ Prefer JSON? Use `doctor.config.json`:
}
```

## Troubleshooting

### pnpm monorepos with vite-plus/vitest

Installing `react-doctor` as a workspace devDependency in pnpm monorepos using vite-plus (or custom vitest aliases) can cause Vitest to fail with:

```
Error: Vitest failed to find the current suite.
```

This happens because `react-doctor` depends on `oxlint`, which can introduce a second physical install of vitest with a different peer-dependency fingerprint. pnpm creates multiple module instances when peer contexts differ, causing Vitest's hook registry to split.

**Workarounds:**

1. **Use `npx`/`pnpm dlx` instead of installing** (recommended):

```bash
pnpm dlx react-doctor@latest
```

This avoids polluting the dependency graph entirely.

2. **Install `oxlint` at the workspace root separately**:

```bash
pnpm add -Dw oxlint
```

Then add `react-doctor` to a specific package instead of the workspace root.

3. **Use pnpm overrides** to force a single vitest instance:

```yaml
# pnpm-workspace.yaml or package.json
pnpm:
overrides:
vitest: "catalog:viteplus"
peerDependencyRules:
allowedVersions:
vitest: "*"
```

For programmatic use without the dependency graph, you can import types only:

```ts
import type { ReactDoctorConfig } from "react-doctor/api";
```

## Telemetry

The CLI reports crashes, basic run traces, and anonymous usage counters to [Sentry](https://sentry.io/) to help us fix bugs and prioritize work.
Expand Down
9 changes: 8 additions & 1 deletion packages/react-doctor/package.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"name": "react-doctor",
"version": "0.4.0",
Expand Down Expand Up @@ -66,14 +66,21 @@
"eslint-plugin-react-hooks": "^7.1.1",
"jiti": "^2.7.0",
"magicast": "^0.5.3",
"oxlint": "^1.66.0",
"oxlint-plugin-react-doctor": "workspace:*",
"prompts": "^2.4.2",
"typescript": ">=5.0.4 <7",
"vscode-languageserver": "^9.0.1",
"vscode-languageserver-textdocument": "^1.0.12",
"vscode-uri": "^3.1.0"
},
"peerDependencies": {
"oxlint": ">=1.0.0"
},
"peerDependenciesMeta": {
"oxlint": {
"optional": true
}
},
"devDependencies": {
"@react-doctor/api": "workspace:*",
"@react-doctor/core": "workspace:*",
Expand Down
Loading