How react-doctor --staged Mode Works: Internal Pre-Commit Scanning Architecture

react-doctor's --staged flag executes diagnostics exclusively on Git staged files by creating a temporary snapshot of the index, scanning it in isolation, and remapping diagnostic paths back to the original repository locations.

The millionco/react-doctor repository provides a specialized pre-commit scanning workflow that evaluates only what has been added to Git's staging area, ensuring that linting results match the exact state of the upcoming commit. Unlike standard scans that analyze the working directory, this mode materializes a sandboxed environment to eliminate interference from unstaged modifications. Understanding the internal architecture reveals how the tool orchestrates Git subprocesses, temporary file systems, and path remapping to deliver deterministic results.

Internal Execution Pipeline

The --staged implementation follows a seven-step workflow that bridges Git internals with the scanner's diagnostic engine.

Step 1: CLI Flag Validation

In packages/react-doctor/src/cli.ts (lines 40-53), the command-line interface first validates that --staged is not combined with mutually exclusive flags such as --diff. This prevents conflicting scan modes and ensures the subsequent logic operates on a well-defined subset of files.

Step 2: Discover Staged Source Files

The getStagedSourceFiles() function in packages/react-doctor/src/utils/get-staged-files.ts (lines 36-38) executes git diff --cached --diff-filter=ACMR to list paths currently in the index, filtering for source files using the SOURCE_FILE_PATTERN regex (\.(tsx?|jsx?)$). The --diff-filter=ACMR flag specifically excludes deleted files (status D) because they no longer exist in the index and cannot be linted.

Step 3: Materialize the Temporary Snapshot

For each staged file, the materializeStagedFiles() function (lines 52-78 in get-staged-files.ts) runs git show :<path> to extract the exact blob content from Git's index, bypassing the working tree. These files are written to a fresh temporary directory, which also receives copies of project configuration files like tsconfig.json, package.json, and react-doctor.config.json to ensure the scanner can resolve TypeScript and plugin inheritance without accessing the real repository's .git directory.

Step 4: Execute the Scan on the Snapshot

Back in cli.ts (lines 76-105), the scan() function receives includePaths limited to the staged file list and a configOverride pointing to the temporary directory as the new scan root. This isolates the analysis from any unstaged changes lingering in the working tree.

Step 5: Remap Diagnostic Paths

After scanning, diagnostic objects contain absolute paths pointing to the temporary directory. The remapping logic in cli.ts (lines 15-22) rewrites these paths back to the original repository locations by substituting snapshot.tempDirectory with resolvedDirectory, ensuring that error locations align with the developer's actual file structure.

Step 6: Emit Remapped Output

The output pipelines—human-readable logs via utils/highlighter.ts, JSON reports via utils/build-json-report.ts, and GitHub Actions annotations—receive the remapped diagnostics. JSON reports include the field mode: "staged" to indicate the scan context.

Step 7: Cleanup

Finally, the cleanup() callback stored in the snapshot object deletes the temporary directory via a finally block in the staged execution logic, guaranteeing no ephemeral files persist on disk regardless of scan success or failure.

Why react-doctor Uses a Temporary Snapshot

The materialization strategy addresses three critical requirements for reliable pre-commit scanning:

  • Exact Index State: By reading staged blobs via git show rather than working tree files, the scan reflects precisely what will be committed, eliminating false negatives caused by unstaged fixes or false positives from unstaged bugs.
  • Sandboxed Isolation: Tools like OXLint and ESLint expect a coherent file system layout. Writing staged files to a dedicated temporary directory prevents these tools from resolving relative imports against unstaged or untracked files that would skew the analysis.
  • Configuration Availability: Copying tsconfig.json and other config files into the snapshot ensures TypeScript resolution and plugin loading function correctly without requiring complex path mapping back to the original repository.

Error Handling and Edge Cases

The implementation includes specific safeguards for common edge cases:

  • Empty Staged Sets: If no staged files match the SOURCE_FILE_PATTERN, the command returns early with an empty JSON report or the message "No staged source files found."
  • Memory Limits: All Git subprocesses are constrained by GIT_SHOW_MAX_BUFFER_BYTES (10 MiB) defined in constants.ts, preventing out-of-memory errors when processing enormous diff outputs.
  • Deleted Files: The --diff-filter=ACMR flag excludes deleted files from the scan list, as they cannot be materialized from the index.

Pre-Commit Hook Configuration Examples

Husky Integration

Configure a pre-commit hook to block commits containing error-level diagnostics:


# .husky/pre-commit

#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npx react-doctor --staged --fail-on error

When --fail-on error detects an error-level diagnostic, the process exits with code 1, aborting the commit.

Manual Terminal Usage

Run targeted scans during development:


# Scan only staged files

react-doctor --staged

# Generate compact JSON for CI pipelines

react-doctor --staged --json --json-compact

Debugging the Snapshot

To inspect the temporary snapshot manually for debugging:


# Check what files exist in the temp directory

TMP=$(mktemp -d)

# The internal implementation writes to a similar temp path

# Use lsof or process monitoring to identify the active temp directory

# during a react-doctor run

Note: The CLI does not expose a --dry-run flag; this example illustrates the internal mechanism where staged content is written to a transient location.

Summary

  • react-doctor --staged mode creates an isolated temporary snapshot of Git's index to scan exactly what will be committed.
  • The workflow spans seven steps: flag validation, staged file discovery, snapshot materialization via git show, sandboxed scanning, path remapping, output generation, and cleanup.
  • Key implementation files include packages/react-doctor/src/cli.ts for orchestration and packages/react-doctor/src/utils/get-staged-files.ts for Git interaction.
  • The snapshot approach guarantees deterministic results by eliminating interference from unstaged working tree changes.
  • Safety limits like GIT_SHOW_MAX_BUFFER_BYTES and filters like --diff-filter=ACMR prevent crashes on large diffs or deleted files.

Frequently Asked Questions

Can I combine --staged with --diff in the same command?

No, these flags are mutually exclusive. The CLI validation in packages/react-doctor/src/cli.ts (lines 40-53) explicitly prevents combining --staged with --diff because they define conflicting file selection strategies—staged files versus differential changes.

What happens if no staged files match the source pattern?

If getStagedSourceFiles() returns an empty list after filtering by SOURCE_FILE_PATTERN, the command exits early with an empty JSON array (in JSON mode) or the message "No staged source files found." No temporary directory is created and no scan occurs.

How does react-doctor handle very large staged files?

The tool sets a hard buffer limit of 10 MiB (GIT_SHOW_MAX_BUFFER_BYTES in constants.ts) when executing git show to materialize file contents. If a staged file exceeds this size, the subprocess will truncate or error, preventing out-of-memory crashes during the pre-commit phase.

Does --staged scan unstaged modifications in tracked files?

No. The --staged flag exclusively analyzes content from Git's index using git show :<path>, which retrieves the staged blob. Any unstaged modifications in the working tree are ignored, ensuring the scan represents only the changes intended for commit.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →