How React-Doctor Detects Dead Code Using Knip: Internal Implementation
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 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:
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
knipconfiguration file (detected viahasKnipConfig), 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
createOptionsfunction instantiates a Knip session withoptions.parsedConfigcontaining resolved workspace settings. - Pattern Sanitization: The
sanitizeKnipConfigPatternsutility (located inpackages/react-doctor/src/utils/sanitize-knip-config-patterns.ts) strips empty string patterns that would otherwise triggerpicomatcherrors during glob resolution. - Entry File Merging: When users provide custom
entryFilesvia React-Doctor configuration, these paths merge intooptions.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 (lines 95-111):
- Error Extraction: When Knip throws,
extractFailedPluginNameparses the error message to identify the offending plugin. - Plugin Disabling:
tryDisableFailedPluginmarks that specific plugin as disabled in the parsed configuration object. - Retry Logic: The system retries execution up to
KNIP_TOTAL_ATTEMPTS(defined as 3 inconstants.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
collectUnusedFilePathsutility (inpackages/react-doctor/src/utils/collect-unused-file-paths.ts) extracts absolute paths from Knip'sfilesissue records. - Other Issues:
collectIssueRecordsprocessesexports,types, andduplicatesusing theKNIP_ISSUE_TYPE_DESCRIPTORSmap (lines 15-62) to assign consistent messages, categories, and severity levels. - Diagnostic Creation: Each issue becomes a
Diagnosticwithplugin: "knip"andcategory: "Dead Code", containing file paths and specific issue metadata for unified reporting.
Integration with the Scanning Pipeline
In 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:
# 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:
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:
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
runKnipfunction inpackages/react-doctor/src/utils/run-knip.tsorchestrates 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"andcategory: "Dead Code"for unified reporting alongside lint results insrc/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 using the entryFiles array. These paths merge into Knip's entry configuration during execution (lines 28-34 of 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →