Pipeline overview
How nestjs-doctor works from CLI invocation to final output.
Flow
Directory map
| Directory | Responsibility |
|---|---|
src/cli/ | CLI flags, entry point, output formatters, and the scan worker entry |
src/engine/ | Config loading, project detection, file collection, AST parsing, module graph, provider resolution, rule execution, diagnostic filtering, scoring |
src/engine/rules/definitions/ | All 52 rules organized by category (security/, correctness/, architecture/, performance/, schema/) |
src/report/ | HTML report generation (interactive dashboard, module graph, schema ER diagram) |
src/common/ | Shared type definitions (Diagnostic, Config, Result, errors) |
Stages
The pipeline has 10 stages. In a monorepo, stages 1-9 run once per sub-project.
| # | Stage | Source | Scope |
|---|---|---|---|
| 1 | Config loading | src/engine/config/loader.ts | Root, then per project |
| 2 | Project detection | src/engine/project-detector.ts | Per project |
| 3 | File collection | src/engine/file-collector.ts | Per project |
| 4 | AST parsing | src/engine/graph/ast-parser.ts | Per project |
| 5 | Module graph building | src/engine/graph/module-graph.ts | Per project |
| 6 | Provider resolution | src/engine/graph/type-resolver.ts | Per project |
| 7 | Rule execution | src/engine/rule-runner.ts | Per project |
| 8 | Diagnostic filtering | src/engine/filter-diagnostics.ts | Per project |
| 9 | Scoring | src/engine/scorer/index.ts | Per project + combined |
| 10 | Output | src/cli/formatters/ | Once |
Orchestrator
The autoScan() and scanMonorepo() functions in src/engine/scanner.ts wire these stages together. The CLI calls one of these, and the Node.js API (diagnose(), diagnoseMonorepo()) wraps them with path validation.
Parallel execution
Stages that do not depend on each other run concurrently:
- Single-project mode: config loading and custom rule resolution run first. Project detection and file collection then run together via
Promise.all. The remaining stages run in order, from AST parsing through scoring. - Monorepo mode: file collection for every sub-project runs in parallel via
Promise.all. The sub-projects themselves are then built one at a time. Each context is dropped before the next one is built, so peak memory stays close to a single project.
Where the scan runs
An interactive terminal run executes the engine middle in a worker thread, so the spinner and progress bar keep painting while rules run. CI, every machine-readable format, and --score scans run in process. If the worker cannot start or fails mid-scan, the scan reruns in process and a warning goes to stderr.