# How React-Doctor Detects Dead Code Using Knip: Internal Implementation

> Discover how React-Doctor internally leverages Knip for dead code detection. Learn about its resilience layer, configuration sanitization, and automatic retries for robust unused code identification.

- Repository: [Million Software, Inc./react-doctor](https://github.com/millionco/react-doctor)
- Tags: internals
- Published: 2026-05-12

---

**React-Doctor wraps Knip in a resilience layer that sanitizes configurations, retries automatically on plugin failures, and transforms raw Knip results into standardized Diagnostic objects to identify unused files, exports, and types.**

React-Doctor integrates Knip to provide comprehensive dead code detection for React codebases. The implementation resides in the [`packages/react-doctor/src/utils/run-knip.ts`](https://github.com/millionco/react-doctor/blob/main/packages/react-doctor/src/utils/run-knip.ts) module and orchestrates sophisticated monorepo handling, configuration sanitization, and robust error recovery. Understanding these internals reveals how the tool reliably identifies unused symbols across complex project structures.

## The Entry Point: runKnip Function

The dead code detection flow begins with the `runKnip` async function exported from [`packages/react-doctor/src/utils/run-knip.ts`](https://github.com/millionco/react-doctor/blob/main/packages/react-doctor/src/utils/run-knip.ts):

```typescript
export const runKnip = async (rootDirectory: string, entryFiles?: string[]): Promise<Diagnostic[]> => { … }

```

This function serves as the primary interface between React-Doctor and Knip, accepting a root directory and optional entry file overrides. It returns an array of `Diagnostic` objects categorized as dead code, enabling seamless integration with React-Doctor's unified reporting system.

## Monorepo Handling and Configuration Discovery

Before executing Knip, the system attempts to locate the monorepo root using `findMonorepoRoot`. The logic in `runKnipForProject` (lines 66-80) handles workspace-specific configurations through a dual-path strategy:

- **Workspace-specific configuration**: If a workspace contains its own `knip` configuration file (detected via `hasKnipConfig`), Knip executes directly against that folder for isolated analysis.
- **Monorepo fallback**: If no local configuration exists, the system falls back to the monorepo root. If this workspace run fails, it retries on the individual project folder to ensure comprehensive coverage.

This approach ensures accurate dependency graph analysis across both isolated workspaces and shared monorepo structures.

## Dependency Verification and Pre-flight Guards

React-Doctor prevents unnecessary analysis by verifying that `node_modules` exists through the `hasNodeModules` check (lines 88-94). If the dependency directory is absent, the dead code detection step skips entirely rather than failing. This guard ensures Knip receives valid resolution contexts before attempting static analysis, avoiding cryptic resolution errors in uninitialized projects.

## Configuration Sanitization and Entry Point Injection

The implementation prepares the Knip session through several defensive normalization steps:

- **Options Creation**: The `createOptions` function instantiates a Knip session with `options.parsedConfig` containing resolved workspace settings.
- **Pattern Sanitization**: The `sanitizeKnipConfigPatterns` utility (located in [`packages/react-doctor/src/utils/sanitize-knip-config-patterns.ts`](https://github.com/millionco/react-doctor/blob/main/packages/react-doctor/src/utils/sanitize-knip-config-patterns.ts)) strips empty string patterns that would otherwise trigger `picomatch` errors during glob resolution.
- **Entry File Merging**: When users provide custom `entryFiles` via React-Doctor configuration, these paths merge into `options.parsedConfig.entry` (lines 28-34). This allows custom scan entry points beyond Knip's auto-detection, supporting unconventional project structures.

## Robustness: Handling Plugin Failures with Automatic Retries

Knip plugins may crash when encountering malformed configuration files. React-Doctor implements a resilient retry mechanism in [`run-knip.ts`](https://github.com/millionco/react-doctor/blob/main/run-knip.ts) (lines 95-111):

1. **Error Extraction**: When Knip throws, `extractFailedPluginName` parses the error message to identify the offending plugin.
2. **Plugin Disabling**: `tryDisableFailedPlugin` marks that specific plugin as disabled in the parsed configuration object.
3. **Retry Logic**: The system retries execution up to `KNIP_TOTAL_ATTEMPTS` (defined as 3 in [`constants.ts`](https://github.com/millionco/react-doctor/blob/main/constants.ts), line 5), progressively eliminating incompatible plugins while preserving analysis functionality for the remainder of the codebase.

This retry loop ensures that a single misconfigured plugin cannot halt the entire dead code detection process.

## Silencing Console Noise

Knip and its ecosystem plugins may write directly to `console.*` methods. The `silenced` helper (lines 69-88) temporarily replaces `console.log`, `console.warn`, and `console.error` with no-op functions during execution. This ensures clean CLI output while React-Doctor's spinner displays `"Detecting dead code…"`, preventing plugin noise from corrupting the terminal interface.

## Transforming Knip Results into Diagnostics

After a successful Knip run produces `KnipResults`, React-Doctor converts raw issue records into standardized `Diagnostic` objects:

- **Unused Files**: The `collectUnusedFilePaths` utility (in [`packages/react-doctor/src/utils/collect-unused-file-paths.ts`](https://github.com/millionco/react-doctor/blob/main/packages/react-doctor/src/utils/collect-unused-file-paths.ts)) extracts absolute paths from Knip's `files` issue records.
- **Other Issues**: `collectIssueRecords` processes `exports`, `types`, and `duplicates` using the `KNIP_ISSUE_TYPE_DESCRIPTORS` map (lines 15-62) to assign consistent messages, categories, and severity levels.
- **Diagnostic Creation**: Each issue becomes a `Diagnostic` with `plugin: "knip"` and `category: "Dead Code"`, containing file paths and specific issue metadata for unified reporting.

## Integration with the Scanning Pipeline

In [`packages/react-doctor/src/scan.ts`](https://github.com/millionco/react-doctor/blob/main/packages/react-doctor/src/scan.ts) (lines 80-88), the dead code step executes conditionally when users enable `--dead-code` (enabled by default) and the run is not in diff mode. The `runKnip` promise merges with lint diagnostics via `combineDiagnostics`, feeding unified results to the scoring backend or CLI display. This integration allows dead code detection to run alongside other React-Doctor analyses in a single pass.

## Usage Examples

### CLI Activation

Enable or disable dead code detection via command-line flags:

```bash

# Default behavior includes dead code detection

npx react-doctor@latest /path/to/project

# Explicit control

npx react-doctor@latest /path/to/project --dead-code     # force enable

npx react-doctor@latest /path/to/project --no-dead-code  # disable

```

### Programmatic Integration

Import `runKnip` directly for custom tooling:

```typescript
import { runKnip } from "react-doctor";

async function listDeadCode(root: string) {
  // Optional: pass custom entry files
  const diagnostics = await runKnip(root, ["src/index.ts"]);
  
  return diagnostics.map(d => ({
    file: d.filePath,
    message: d.message,
    severity: d.severity,
  }));
}

listDeadCode("/my/project").then(console.log);

```

### Custom Entry Files Configuration

Define additional entry points in [`react-doctor.config.js`](https://github.com/millionco/react-doctor/blob/main/react-doctor.config.js):

```javascript
module.exports = {
  deadCode: true,
  entryFiles: ["src/main.tsx", "src/setupTests.ts"],
};

```

React-Doctor merges these into Knip's entry configuration during the `runKnip` execution (lines 28-34).

## Summary

- **React-Doctor** delegates dead code analysis to **Knip**, wrapped in a resilience layer that handles monorepos, missing dependencies, and plugin failures.
- The `runKnip` function in [`packages/react-doctor/src/utils/run-knip.ts`](https://github.com/millionco/react-doctor/blob/main/packages/react-doctor/src/utils/run-knip.ts) orchestrates configuration sanitization, entry-file injection, and result transformation.
- **Automatic retry logic** disables failing plugins up to three attempts using `KNIP_TOTAL_ATTEMPTS`, ensuring analysis completes even with configuration errors.
- **Console silencing** and dependency guards prevent noise and crashes in uninitialized projects.
- Results convert to standardized **Diagnostic** objects with `plugin: "knip"` and `category: "Dead Code"` for unified reporting alongside lint results in [`src/scan.ts`](https://github.com/millionco/react-doctor/blob/main/src/scan.ts).

## Frequently Asked Questions

### What is Knip and why does react-doctor use it?

Knip is a static analysis tool that identifies unused files, exports, types, and duplicate symbols across JavaScript and TypeScript codebases. React-Doctor integrates Knip to provide comprehensive dead code detection without reimplementing complex dependency graph analysis, leveraging Knip's existing plugin ecosystem and configuration format.

### How does react-doctor handle Knip plugin failures?

When a Knip plugin crashes due to invalid configuration, React-Doctor extracts the plugin name from the error using `extractFailedPluginName`, disables it in the configuration via `tryDisableFailedPlugin`, and retries the analysis. This process repeats up to `KNIP_TOTAL_ATTEMPTS` (3 times by default), progressively eliminating problematic plugins while continuing to analyze the codebase with the remaining functional plugins.

### Can I customize which files are considered entry points for dead code detection?

Yes. You can specify custom entry files in your [`react-doctor.config.js`](https://github.com/millionco/react-doctor/blob/main/react-doctor.config.js) using the `entryFiles` array. These paths merge into Knip's entry configuration during execution (lines 28-34 of [`run-knip.ts`](https://github.com/millionco/react-doctor/blob/main/run-knip.ts)), allowing you to define additional starting points for the dependency graph traversal beyond Knip's automatic detection.

### What happens if node_modules is missing when running dead code detection?

React-Doctor checks for the presence of `node_modules` before invoking Knip using the `hasNodeModules` guard (lines 88-94). If dependencies are not installed, the dead code detection step skips entirely rather than failing, preventing resolution errors and allowing the remaining diagnostic checks to continue.