# How to Use the `diagnose()` API Programmatically for Custom Analysis in React Doctor

> Unlock custom analysis with react-doctor's diagnose() API. Programmatically discover React projects, run oxlint and knip runners in parallel, and get a health score.

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

---

**The `diagnose()` function exported from `react-doctor` provides a fully-typed, programmatic interface that mirrors the CLI scan command, enabling custom analysis workflows by discovering React projects, running oxlint and knip runners in parallel, and returning a computed health score.**

You can embed the `millionco/react-doctor` toolchain directly into Node.js scripts, CI pipelines, or custom dashboards by importing the `diagnose()` API from [`src/index.ts`](https://github.com/millionco/react-doctor/blob/main/src/index.ts). This method handles project discovery, configuration loading, and diagnostic aggregation automatically, returning a structured result that includes lint errors, dead-code findings, and an overall project health rating.

## Importing and Calling `diagnose()`

Import the public API entry point and invoke it with an absolute path to your React project root.

```typescript
import { diagnose, clearCaches, AmbiguousProjectError, NoReactDependencyError } from 'react-doctor';

async function runAnalysis() {
  try {
    const result = await diagnose('/path/to/your/react/app', {
      lint: true,          // Enable oxlint (default: true)
      deadCode: false,     // Disable knip dead-code analysis
      includePaths: ['src/**/*.tsx'], // Optional custom globs
    });

    console.log('Diagnostics:', result.diagnostics);
    console.log('Score:', result.score?.score, result.score?.label);
    console.log('Project:', result.project);
  } catch (err) {
    if (err instanceof AmbiguousProjectError) {
      console.error('Multiple React projects found:', err.candidates);
    } else if (err instanceof NoReactDependencyError) {
      console.error('No React dependency detected.');
    }
  }
}

```

The function accepts a target directory string and an optional **DiagnoseOptions** object, returning a Promise that resolves to a **DiagnoseResult** containing diagnostics, score metadata, and project information.

## Step-by-Step Execution Flow

According to the source code in [`src/index.ts`](https://github.com/millionco/react-doctor/blob/main/src/index.ts), the `diagnose()` function executes an eight-stage analysis pipeline:

1. **Resolve Target Directory** – `resolveDiagnoseTarget` (via [`src/utils/resolve-diagnose-target.ts`](https://github.com/millionco/react-doctor/blob/main/src/utils/resolve-diagnose-target.ts)) normalizes the input path, handling monorepo wrappers and respecting the optional `rootDir` redirect from [`react-doctor.config.json`](https://github.com/millionco/react-doctor/blob/main/react-doctor.config.json).

2. **Load Configuration** – `loadConfigWithSource` (via [`src/utils/load-config.ts`](https://github.com/millionco/react-doctor/blob/main/src/utils/load-config.ts)) reads the configuration file (or applies defaults) and merges it with any runtime overrides.

3. **Discover Project** – `discoverProject` (via [`src/utils/discover-project.ts`](https://github.com/millionco/react-doctor/blob/main/src/utils/discover-project.ts)) traverses up the filesystem to locate the nearest [`package.json`](https://github.com/millionco/react-doctor/blob/main/package.json) declaring a React dependency, extracting version and framework details. This stage throws **ProjectNotFoundError**, **NoReactDependencyError**, or **AmbiguousProjectError** if discovery fails.

4. **Determine Include Paths** – `computeJsxIncludePaths` and `resolveLintIncludePaths` calculate which JSX files oxlint should analyze based on the project structure and user configuration.

5. **Run Analysis Engines** – `runOxlint` ([`src/utils/run-oxlint.ts`](https://github.com/millionco/react-doctor/blob/main/src/utils/run-oxlint.ts)) and `runKnip` ([`src/utils/run-knip.ts`](https://github.com/millionco/react-doctor/blob/main/src/utils/run-knip.ts)) execute in parallel using `Promise.allSettled()`, ensuring a failure in one runner does not abort the entire scan.

6. **Merge and Filter** – `mergeAndFilterDiagnostics` ([`src/utils/merge-and-filter-diagnostics.ts`](https://github.com/millionco/react-doctor/blob/main/src/utils/merge-and-filter-diagnostics.ts)) consolidates results, applies user-level suppressions, handles inline disables, and de-duplicates findings.

7. **Calculate Health Score** – `calculateScore` ([`src/utils/calculate-score.ts`](https://github.com/millionco/react-doctor/blob/main/src/utils/calculate-score.ts)) aggregates rule severities into a 0-100 numeric health score with a descriptive label.

8. **Return Result** – The function returns a **DiagnoseResult** object containing the full diagnostics array, **ScoreResult**, **ProjectInfo**, and elapsed time.

## Handling Errors and Edge Cases

The discovery phase in [`src/utils/discover-project.ts`](https://github.com/millionco/react-doctor/blob/main/src/utils/discover-project.ts) validates the target environment strictly. You must catch specific error classes to handle Edge cases gracefully:

- **AmbiguousProjectError** – Thrown when multiple sub-projects contain React dependencies, requiring you to specify a more precise sub-directory from the provided `candidates` array.
- **NoReactDependencyError** – Indicates the target directory lacks a [`package.json`](https://github.com/millionco/react-doctor/blob/main/package.json) with React listed in dependencies or devDependencies.
- **ProjectNotFoundError** – Raised when no [`package.json`](https://github.com/millionco/react-doctor/blob/main/package.json) exists in the target or parent directories.

Always wrap calls in `try/catch` blocks when analyzing unknown or dynamically supplied paths.

## Configuring Analysis Options

The **DiagnoseOptions** interface (defined in [`src/types.ts`](https://github.com/millionco/react-doctor/blob/main/src/types.ts)) allows granular control over the analysis behavior:

- `lint` – Boolean flag to enable or disable oxlint execution (default: `true`).
- `deadCode` – Boolean flag to enable or disable knip dead-code detection (default: `true`).
- `includePaths` – Array of glob patterns to restrict which files are analyzed.
- `respectInlineDisables` – Boolean to toggle honoring inline `eslint-disable` style comments.

```typescript
const result = await diagnose('/monorepo/apps/web', {
  lint: true,
  deadCode: true,
  includePaths: ['app/**/*.tsx', 'components/**/*.tsx'],
  respectInlineDisables: false,
});

```

## Managing Caches in Long-Running Processes

The `react-doctor` module maintains internal caches for parsed [`package.json`](https://github.com/millionco/react-doctor/blob/main/package.json) files, configuration objects, and ignore patterns to improve performance. When implementing watch modes or running sequential analyses in persistent processes, you must invalidate these caches using the **clearCaches()** function before each new scan:

```typescript
async function watchMode() {
  while (true) {
    clearCaches(); // Invalidate memoized reads
    const result = await diagnose('/project', { lint: true });
    await new Promise(r => setTimeout(r, 5000));
  }
}

```

Failure to call `clearCaches()` will result in stale diagnostics if underlying files change between runs.

## Exporting JSON Reports for Downstream Tooling

To generate the same JSON output format used by the CLI, import the **toJsonReport** utility and pass the `DiagnoseResult`:

```typescript
import { diagnose } from 'react-doctor';
import { writeFileSync } from 'node:fs';
import { join } from 'node:path';

async function exportReport() {
  const result = await diagnose('/my/monorepo/apps/web', { lint: true, deadCode: true });
  
  const { toJsonReport } = await import('react-doctor');
  const json = toJsonReport(result, { version: '1.0.0', mode: 'full' });
  
  writeFileSync(join(process.cwd(), 'react-doctor-report.json'), JSON.stringify(json, null, 2));
}

```

This produces a standardized report compatible with CI artifacts and external monitoring systems.

## Summary

- **Import** `diagnose` from [`src/index.ts`](https://github.com/millionco/react-doctor/blob/main/src/index.ts) to access the programmatic API in `millionco/react-doctor`.
- **Pass** an absolute path and optional **DiagnoseOptions** to control linting, dead-code analysis, and file inclusion.
- **Handle** specific errors (**AmbiguousProjectError**, **NoReactDependencyError**) to manage monorepos and invalid targets.
- **Call** `clearCaches()` between runs in long-lived processes to ensure fresh results.
- **Access** the full diagnostic array, health score, and project metadata via the returned **DiagnoseResult** object.
- **Generate** CLI-compatible JSON reports using the `toJsonReport` helper for integration with external dashboards.

## Frequently Asked Questions

### What is the difference between the CLI and the programmatic `diagnose()` API?

The CLI is a thin wrapper around the same `diagnose()` function implemented in [`src/index.ts`](https://github.com/millionco/react-doctor/blob/main/src/index.ts). While the CLI handles argument parsing and stdout formatting, the programmatic API returns raw typed objects (**DiagnoseResult**, **ProjectInfo**) that you can process, filter, or transform in custom Node.js scripts without spawning child processes.

### How do I analyze a specific package in a monorepo?

Pass the absolute path to the specific package subdirectory as the first argument to `diagnose()`. If multiple packages contain React dependencies and you pass a parent directory, the function throws **AmbiguousProjectError**. Catch this error and inspect the `candidates` property to select the correct subdirectory, or resolve the target explicitly using `resolveDiagnoseTarget` before calling `diagnose()`.

### Can I run only the linter without dead-code detection?

Yes. Set the `deadCode` option to `false` in your **DiagnoseOptions** object. This skips the `runKnip` execution phase while still running `runOxlint` (assuming `lint` remains `true`), reducing analysis time when you only need linting results.

### How is the health score calculated?

The **calculateScore** function in [`src/utils/calculate-score.ts`](https://github.com/millionco/react-doctor/blob/main/src/utils/calculate-score.ts) aggregates the severity of all diagnostics returned by oxlint and knip. It applies a weighted algorithm to error and warning counts, producing a 0-100 integer score where 100 indicates a perfectly healthy project with no detected issues. The score is returned as part of the **ScoreResult** object within the **DiagnoseResult**.