bluebird-nestjs
v0.1.22
Published
Diagnose and improve NestJS project health
Maintainers
Readme
Bluebird
Static analysis CLI for NestJS projects
Stability
Bluebird is production-ready for NestJS static analysis. The project maintains:
- 944+ tests with comprehensive coverage across 23 test files
- 95%+ line coverage (statements, branches, functions)
- Semantic versioning for predictable upgrades
- CI/CD integration with SARIF output for code scanning
- Active maintenance with regular security updates
The API surface (diagnose, runEslint, analyseFiles, formatters) is stable. Breaking changes will only occur in major version bumps with migration guides.
Bluebird is a static analysis CLI for NestJS projects. It parses TypeScript source files, runs 38 purpose-built rules across architecture, security, performance, correctness, and API design, and produces a health score useful both locally and in CI.
Why Bluebird?
NestJS projects can accumulate technical debt silently. Bluebird catches issues that generic linters miss:
- Security issues — Hardcoded secrets, SQL injection, missing validation pipes
- Architectural problems — Circular dependencies, god controllers/services, DI bypasses
- Performance blockers — Sync filesystem operations, blocking crypto, N+1 queries
- Common mistakes — Missing decorators, lifecycle hook interfaces, duplicate routes
- API design flaws — Missing Swagger docs, entities exposed directly, inconsistent HTTP status
Unlike generic linters, Bluebird understands NestJS patterns, decorators, and module structure.
Installation
From npm (recommended)
# Install globally
npm install -g bluebird-nestjs
bluebird --help
# Or add to your project
npm install --save-dev bluebird-nestjs
npx bluebirdFrom source
# Clone and install
git clone https://github.com/endpointclosing/bluebird.git
cd bluebird
pnpm install
pnpm build
# Link the CLI globally (run from packages/bluebird directory)
cd packages/bluebird
npm link
# Now you can use bluebird globally from any directory
cd /path/to/your-nestjs-project
bluebird --helpTroubleshooting Installation
If you encounter issues:
"husky: command not found" - This is safe to ignore. The prepare script handles this gracefully.
"bluebird: command not found" after
npm link:# Make sure you're in the packages/bluebird directory cd /path/to/bluebird/packages/bluebird npm link # Verify the link was created npm list -g bluebird-nestjsPermission errors on macOS/Linux:
sudo npm linkStill not working? Check that the build completed:
ls packages/bluebird/dist/cli.js # Should exist
Getting Started in 60 Seconds
# 1. Install globally
npm install -g bluebird-nestjs
# 2. Run analysis on your NestJS project
cd /path/to/your-nestjs-project
bluebird
# 3. Generate a config file (optional)
bluebird init
# 4. Create a baseline for existing issues (optional)
bluebird --baselineThat's it! Bluebird auto-detects your NestJS setup and runs 38 purpose-built rules.
Quick Start
# Run against a NestJS project
bluebird
# Watch mode — re-run on file changes
bluebird --watch
# Fast mode — run passes in parallel
bluebird --fast
# Diff mode — only check changed files
bluebird --diff main
# Output formats for different use cases
bluebird --format text # Terminal (default)
bluebird --format json # CI/CD integration
bluebird --format sarif # GitHub Code Scanning
bluebird --format html # Shareable dashboard
# Include heuristic (opt-in) rules
bluebird --include-heuristic
# Get detailed info about a specific rule
bluebird explain no-hardcoded-secrets
# List all available rules
bluebird explain --listExample Output
✔ Project detected NestJS 10.3.0 · express · 235 files · swagger, config
✔ Lint analysis 0.4s (30 diagnostics)
✔ Graph analysis 0.1s (0 diagnostics)
✔ Dead code analysis 1.2s (15 diagnostics)
__
__( o> Bluebird
(___/ NestJS Health Report
┌──────────────────────────────────────────────┐
│ Score: 89/100 ██████████████████░░ Great │
└──────────────────────────────────────────────┘
2 errors · 43 warnings
By Category:
Security ⚠ 5
Correctness ⚠ 12
Architecture ✖ 2 ⚠ 3
API Design ⚠ 8
Dead Code ⚠ 15
Top Issues:
✖ bluebird/no-hardcoded-dependency (2) Inject via constructor
⚠ bluebird/missing-swagger-decorators (8) Add @ApiOperation/@ApiResponse
⚠ bluebird/no-console-log (7) Use NestJS Logger service
⚠ knip/exports (10) Unused export
⚠ bluebird/no-process-env-direct (5) Use ConfigService.get()
Run with --verbose to see all 45 diagnosticsLogic Flow
flowchart LR
A["CLI (`bluebird` command)"] --> B["Parse Flags (`verbose`, `diff`, `format`, pass toggles)"]
B --> C["Scan Orchestrator (`scan.ts`)"]
C --> D["Load Config + Merge with CLI Options"]
D --> E["Discover Project (`discover-project.ts`)"]
E --> F["Build `ProjectInfo` (adapter, ORM, features, strict mode, tests)"]
F --> G["Resolve Enabled Rules (`getEnabledRules`)"]
G --> H["Run File-Level Checkers (`fileCheckers`)"]
G --> I["Run Graph-Level Checkers (`graphCheckers`)"]
G --> J["Run Dead-Code Pass (`knip`)"]
H --> K["Collect Diagnostics"]
I --> K
J --> K
K --> L["Apply Ignore/Waiver Filtering"]
L --> L2["Apply Baseline Filtering"]
L2 --> M["Calculate Score (`calculate-score.ts`)"]
M --> N["Format Output (`text` | `json` | `sarif` | `html`)"]
N --> O["Print CLI Summary + Exit Code"]CLI Options
| Flag | Description |
|:---|:---|
| -v, --verbose | Show all diagnostics |
| -q, --quiet | Suppress output, only set exit code |
| -p, --project <path> | Path to the NestJS project (default: cwd) |
| -w, --watch | Watch mode: re-run analysis on file changes |
| -s, --score | Output only the numeric health score |
| --diff <branch> | Only check files changed from branch |
| --format <fmt> | Output format: text, json, sarif, html (default: text) |
| --fail-on <threshold> | Exit-code threshold: error, warning, none (default: error) |
| --fail-on-score <score> | Exit with code 1 when score is below this value (0–100) |
| --no-lint | Skip the lint (file-level) analysis pass |
| --no-dead-code | Skip the dead code analysis pass |
| --no-graph-analysis | Skip the graph (cross-file) analysis pass |
| --include-heuristic | Include heuristic-confidence rules |
| --baseline | Generate a baseline snapshot of current diagnostics |
| --update-baseline | Update the baseline snapshot after fixes |
| --fast | Run analysis passes in parallel for faster execution |
| -o, --open | Open HTML report in browser (use with --format html) |
Initialize Configuration
Generate a bluebird.config.json config file interactively:
bluebird initThe init wizard prompts for:
- Rules to ignore globally
- File patterns to exclude
- Whether to enable heuristic rules
Non-Interactive Mode
For CI/CD or scripted setups, use the --yes flag:
# Accept all defaults
bluebird init --yes
# Customize options
bluebird init --yes --heuristic --skip-graph
# Ignore specific rules
bluebird init --yes --ignore-rules "bluebird/no-god-service,bluebird/no-console-log"
# Ignore file patterns
bluebird init --yes --ignore-files "src/legacy/**,src/generated/**"Explain Rules
Get detailed information about any rule:
# Explain a specific rule
bluebird explain no-hardcoded-secrets
# List all available rules
bluebird explain --list
# Filter rules by category
bluebird explain --category securityThe explain command shows:
- Rule description and severity
- How to fix the issue
- How to ignore or waive the rule
Baseline
Baseline lets you adopt Bluebird on an existing codebase without drowning in legacy diagnostics. It captures a snapshot of current issues so subsequent runs only report new violations.
# Create a baseline from the current state
bluebird --baseline
# After fixing some issues, update the baseline
bluebird --update-baselineThe baseline is stored in .bluebird-baseline.json and can be committed to version control. Diagnostics matching the baseline (same rule, file, and line) are excluded from the output and score. New violations introduced after the baseline are always surfaced.
Baseline Workflow
A typical workflow for adopting Bluebird on an existing codebase:
Create initial baseline to snapshot existing issues:
bluebird --baseline git add .bluebird-baseline.json git commit -m "chore: add bluebird baseline"Run in CI — only new violations will fail the build:
bluebird --fail-on errorFix issues incrementally and update baseline:
# After fixing some issues bluebird --update-baseline git add .bluebird-baseline.json git commit -m "chore: update bluebird baseline after fixes"Prevent new debt — CI catches any new violations not in the baseline
Watch Mode
Watch mode monitors your source files and automatically re-runs analysis when changes are detected. Perfect for development workflows.
# Start watch mode
bluebird --watch
# Or use the short flag
bluebird -w
# Combine with other options
bluebird --watch --include-heuristicWatch mode features:
- Debounced re-runs — Waits 500ms after the last change before re-running
- File change detection — Monitors
.tsand.tsxfiles - Automatic ignore — Skips
node_modules,dist,build,.git, andcoverage - Graceful shutdown — Press
Ctrl+Cto exit
HTML Reports
Generate standalone HTML dashboards for sharing analysis results with your team.
# Generate and open HTML report in browser (recommended)
bluebird --format html --open
# Or save to a specific file
bluebird --format html > report.htmlThe HTML report includes:
- Score gauge — Circular progress indicator with color-coded score
- Project details — NestJS version, adapter, ORM, file count, features
- Category chart — Horizontal bar chart showing issues by category
- Top issues table — Most frequent rules with fix suggestions
- All diagnostics — Collapsible sections with full file paths and messages
- Dark theme — Modern UI that looks great when shared
- Self-contained — Single HTML file with inline CSS, no external dependencies
CI/CD Integration
Bluebird integrates easily into CI/CD pipelines. Use --format sarif for GitHub Code Scanning, --format json for custom integrations, or --format html for artifact reports.
GitHub Actions
name: Bluebird Analysis
on:
pull_request:
branches: [main]
push:
branches: [main]
jobs:
bluebird:
runs-on: ubuntu-latest
permissions:
security-events: write # Required for SARIF upload
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- name: Install Bluebird
run: npm install -g bluebird-nestjs
- name: Run Bluebird
run: bluebird --format sarif > bluebird.sarif
continue-on-error: true
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: bluebird.sarifGitLab CI
bluebird:
stage: test
image: node:20-alpine
script:
- npm ci
- npm install -g bluebird-nestjs
- bluebird --format json > bluebird.json
- bluebird --fail-on error
artifacts:
reports:
codequality: bluebird.json
paths:
- bluebird.json
when: alwaysGeneric SARIF Workflow
For other CI systems that support SARIF:
# Generate SARIF report
bluebird --format sarif > bluebird.sarif
# Fail on errors (exit code 1 if errors found)
bluebird --fail-on error
# Fail if score drops below threshold
bluebird --fail-on-score 80Diff Mode for PRs
Run analysis only on changed files for faster PR checks:
# Compare against main branch
bluebird --diff main
# Compare against specific commit
bluebird --diff HEAD~1Rules
Bluebird ships 38 rules split into two confidence tiers:
- Deterministic (25 rules) — statically provable, always enabled
- Heuristic (13 rules) — context-dependent, opt-in via
--include-heuristic
Rules run in one of three analysis passes:
| Pass | Scope | Description |
|:---|:---|:---|
| eslint | Single file | AST-based checks on each .ts file |
| graph | Cross-file | Whole-project analysis (module graph, route map) |
| knip | Cross-file | Dead-code detection (unused files, exports, types, duplicates) |
Architecture (4 rules)
| Rule | Severity | Confidence | Description |
|:---|:---|:---|:---|
| no-circular-dependency | error | deterministic | Circular module imports detected |
| no-god-controller | warning | deterministic | Controller exceeds route threshold |
| no-god-service | warning | deterministic | Service exceeds line threshold |
| no-hardcoded-dependency | error | deterministic | Direct instantiation instead of DI |
Security (5 deterministic + 4 heuristic)
| Rule | Severity | Confidence | Description |
|:---|:---|:---|:---|
| missing-class-validator | warning | deterministic | DTO property without validation decorators |
| missing-csrf-protection | warning | heuristic | No CSRF middleware configured |
| missing-global-guard | warning | heuristic | No global auth guard |
| missing-helmet | warning | heuristic | No helmet middleware |
| missing-rate-limiting | warning | heuristic | No throttling configured |
| missing-validation-pipe | warning | deterministic | No global ValidationPipe configured |
| no-any-in-dto | warning | deterministic | DTO property typed as any |
| no-hardcoded-secrets | error | deterministic | Hardcoded credentials in source |
| no-raw-sql | error | deterministic | SQL template without parameterization |
Correctness (8 deterministic + 2 heuristic)
| Rule | Severity | Confidence | Description |
|:---|:---|:---|:---|
| lifecycle-hook-interface | warning | deterministic | Lifecycle method without interface |
| missing-config-validation | warning | heuristic | ConfigModule without validation schema |
| missing-exception-filter | warning | heuristic | No global exception filter |
| missing-injectable | error | deterministic | Provider class missing @Injectable() |
| missing-parse-pipe | warning | deterministic | Route param without parsing pipe |
| no-console-log | warning | deterministic | Direct console usage instead of Logger |
| no-constructor-side-effects | warning | deterministic | Side effects in constructor |
| no-duplicate-route | error | deterministic | Duplicate HTTP method + path |
| no-nested-controller-decorator | error | deterministic | @Controller() on non-top-level class |
| no-process-env-direct | warning | deterministic | Direct process.env instead of ConfigService |
API Design (3 deterministic + 2 heuristic)
| Rule | Severity | Confidence | Description |
|:---|:---|:---|:---|
| missing-swagger-decorators | warning | deterministic | Missing @ApiOperation/@ApiResponse |
| no-entity-as-response | warning | deterministic | ORM entity returned directly |
| no-generic-exception | warning | deterministic | Throwing Error instead of HttpException |
| no-inconsistent-http-status | warning | heuristic | HTTP status doesn't match method |
| prefer-pagination | warning | heuristic | List endpoint without pagination |
Performance (2 deterministic + 2 heuristic)
| Rule | Severity | Confidence | Description |
|:---|:---|:---|:---|
| missing-caching | warning | heuristic | No caching strategy detected |
| no-blocking-crypto | warning | deterministic | Blocking crypto operations |
| no-n-plus-one | warning | heuristic | Potential N+1 query pattern |
| no-sync-fs-operations | warning | deterministic | Sync fs operations block event loop |
Database (2 heuristic)
| Rule | Severity | Confidence | Description |
|:---|:---|:---|:---|
| missing-indexes | warning | heuristic | Query patterns without indexes |
| missing-migration | warning | heuristic | Schema changes without migrations |
Testing (1 heuristic)
| Rule | Severity | Confidence | Description |
|:---|:---|:---|:---|
| low-test-coverage | warning | heuristic | Missing spec files for providers |
GraphQL (1 deterministic, feature-gated)
| Rule | Severity | Confidence | Description |
|:---|:---|:---|:---|
| missing-resolver-decorator | warning | deterministic | Resolver method without @Query/@Mutation |
Microservices (1 deterministic, feature-gated)
| Rule | Severity | Confidence | Description |
|:---|:---|:---|:---|
| missing-message-pattern | warning | deterministic | Handler without @MessagePattern/@EventPattern |
WebSockets (1 deterministic, feature-gated)
| Rule | Severity | Confidence | Description |
|:---|:---|:---|:---|
| missing-websocket-decorator | warning | deterministic | Gateway method without @SubscribeMessage |
Note: Feature-gated rules only run when Bluebird detects the corresponding feature in your project (e.g.,
@nestjs/graphql,@nestjs/microservices,@nestjs/websockets).
Rule Examples
// ❌ Bad - Hardcoded secret
@Injectable()
export class AuthService {
private readonly jwtSecret = 'super-secret-key-123';
}
// ✅ Good - Use environment variables
@Injectable()
export class AuthService {
constructor(private config: ConfigService) {}
private get jwtSecret() {
return this.config.get<string>('JWT_SECRET');
}
}// ❌ Bad - Missing @Injectable()
export class UserRepository {
findAll() { /* ... */ }
}
// ✅ Good - Has @Injectable()
@Injectable()
export class UserRepository {
findAll() { /* ... */ }
}// ❌ Bad - Direct instantiation bypasses DI
@Injectable()
export class OrderService {
private logger = new Logger(); // Creates tight coupling
process() {
this.logger.log('Processing...');
}
}
// ✅ Good - Inject dependencies
@Injectable()
export class OrderService {
constructor(private readonly logger: Logger) {}
process() {
this.logger.log('Processing...');
}
}// ❌ Bad - No input validation
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
// ✅ Good - Global ValidationPipe enabled
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
}));
await app.listen(3000);
}// ❌ Bad - SQL injection risk
async findByEmail(email: string) {
return this.db.query(`SELECT * FROM users WHERE email = '${email}'`);
}
// ✅ Good - Parameterized query
async findByEmail(email: string) {
return this.db.query('SELECT * FROM users WHERE email = $1', [email]);
}// ❌ Bad - Missing interface
@Injectable()
export class StartupService {
onModuleInit() {
console.log('Starting...');
}
}
// ✅ Good - Implements interface
@Injectable()
export class StartupService implements OnModuleInit {
onModuleInit() {
console.log('Starting...');
}
}Scoring
Each diagnostic carries a severity-based penalty. The health score starts at 100 and decreases per unique rule violation (capped at 10 instances per rule):
| Severity | Base penalty | Per-instance penalty | |:---|:---|:---| | error | 1.5 | 0.15 | | warning | 0.75 | 0.08 |
Score Interpretation
| Range | Label | Meaning | |:---|:---|:---| | 90–100 | Excellent | Production-ready, minimal issues | | 75–89 | Great | Good health, minor improvements needed | | 50–74 | Needs work | Significant issues requiring attention | | 0–49 | Critical | Major problems blocking production readiness |
What Each Range Means
90–100 (Excellent): Your codebase follows NestJS best practices. No security vulnerabilities, clean architecture, proper DI patterns. Ready for production deployment.
75–89 (Great): Generally healthy with some minor issues. Might have a few warnings about missing Swagger decorators, console.log usage, or optional optimizations. Safe for production but could benefit from cleanup.
50–74 (Needs Work): Has notable issues that should be addressed. May include security warnings, architectural concerns (god controllers/services), or missing validation. Prioritize fixing errors before deploying.
0–49 (Critical): Serious problems detected. Likely includes hardcoded secrets, SQL injection risks, circular dependencies, or severe architectural issues. Address these before deploying to production.
Improving Your Score
- Fix errors first — they have higher penalties and often indicate security or correctness issues
- Use baseline to track progress on existing codebases
- Enable heuristic rules (
--include-heuristic) for additional insights - Use waivers sparingly for intentional deviations with documented reasons
Configuration
Bluebird loads configuration from the first source found:
bluebird.config.jsonin the project root- A
"bluebird"key insidepackage.json
If neither exists, all defaults apply. CLI flags always take precedence over config file values.
{
"ignore": {
"rules": ["bluebird/no-god-service"],
"files": ["src/legacy/**"]
},
"lint": true,
"deadCode": true,
"graphAnalysis": true,
"includeHeuristic": false,
"diff": "main",
"waivers": [
{
"rule": "bluebird/no-inconsistent-http-status",
"file": "src/controllers/legacy.controller.ts",
"reason": "Legacy API intentionally returns 200 from DELETE"
}
]
}| Key | Type | Default | Description |
|:---|:---|:---|:---|
| ignore.rules | string[] | [] | Rule IDs to suppress globally |
| ignore.files | string[] | [] | Glob patterns for files to skip |
| lint | boolean | true | Enable the ESLint (file-level) pass |
| deadCode | boolean | true | Enable the dead-code (knip) pass |
| graphAnalysis | boolean | true | Enable the graph (cross-file) pass |
| includeHeuristic | boolean | false | Include heuristic-confidence rules |
| diff | string | — | Only check files changed from this branch |
| waivers | Waiver[] | [] | Per-file rule exemptions with required reason |
JSON Schema
For IDE autocomplete and validation, reference the JSON schema in your config:
{
"$schema": "https://raw.githubusercontent.com/endpointclosing/bluebird/main/packages/bluebird/bluebird.schema.json",
"ignore": {
"rules": ["bluebird/no-god-service"]
}
}The schema provides:
- Autocomplete for all configuration options
- Validation of rule IDs and waiver structure
- Documentation on hover in VS Code and other editors
Inline Disable Comments
Suppress diagnostics directly in source code using comments:
// Disable a specific rule for the next line
// bluebird-disable-next-line no-hardcoded-secrets
const API_KEY = 'test-key-for-demo';
// Disable all rules for a block
// bluebird-disable
const legacyCode = doSomethingUnsafe();
// bluebird-enable
// Disable specific rules for a block
// bluebird-disable no-raw-sql, no-sync-fs-operations
await db.query(rawQuery);
fs.readFileSync(configPath);
// bluebird-enableSupported comment formats:
bluebird-disable-next-line [rule1, rule2, ...]— suppress on the following linebluebird-disable [rule1, rule2, ...]— start suppression blockbluebird-enable— end suppression block
Project Detection
Bluebird auto-detects project characteristics from package.json and source files:
- NestJS version and HTTP adapter (Express / Fastify)
- ORM (TypeORM, Prisma, Mongoose, Sequelize, MikroORM, Drizzle)
- Features: GraphQL, WebSockets, Microservices, CQRS, Swagger, Bull, Config, Throttler, Cache
- TypeScript strictness and test presence
Rules that depend on specific features (e.g. missing-swagger-decorators requires @nestjs/swagger) are automatically disabled when the feature is not detected.
strictTypeScript detection resolves tsconfig.json with extends support and reports the merged strict setting.
Programmatic API
import { diagnose } from "bluebird-nestjs";
const result = await diagnose({ format: "json", includeHeuristic: true });
// result.diagnostics — array of Diagnostic objects
// result.score — { score: number, label: string }
// result.project — detected ProjectInfoESLint Runner
The file-level analysis pass can be invoked directly via runEslint (filesystem-based) or analyseFiles (in-memory, no disk I/O). Both return a LintResult containing diagnostics and runner warnings:
import { runEslint, analyseFiles } from "bluebird-nestjs";
import type { LintResult } from "bluebird-nestjs";
// Filesystem — discovers .ts/.mts/.cts files, skips node_modules/dist/declarations
const { diagnostics, warnings } = await runEslint({ cwd: "/path/to/project", project });
// In-memory — pass a Map<relativePath, sourceText>
const files = new Map([["src/app.ts", sourceCode]]);
const { diagnostics, warnings } = analyseFiles(files, project);
// Warnings surface I/O failures and parse errors instead of silently ignoring them
for (const w of warnings) {
console.warn(`[${w.type}] ${w.filePath}: ${w.message}`);
}Graph Runner
The cross-file analysis pass can be invoked directly via runGraphAnalysis (filesystem-based) or analyseGraph (in-memory, no disk I/O). Both return a GraphAnalysisResult containing diagnostics and runner warnings:
import { runGraphAnalysis, analyseGraph } from "bluebird-nestjs";
import type { GraphAnalysisResult } from "bluebird-nestjs";
// Filesystem — discovers .ts/.mts/.cts files, skips node_modules/dist/declarations
const { diagnostics, warnings } = await runGraphAnalysis({ cwd: "/path/to/project", project });
// In-memory — pass a Map<relativePath, sourceText>
const files = new Map([["src/app.module.ts", moduleSource]]);
const { diagnostics, warnings } = analyseGraph(files, project);Graph-level checkers receive the complete set of parsed source files, enabling cross-file analysis such as circular module dependency detection (no-circular-dependency) and duplicate route detection across controllers (no-duplicate-route).
Dead Code Runner
The dead-code analysis pass can be invoked directly via runKnip. It wraps knip to detect unused files, exports, types, and duplicate exports:
import { runKnip } from "bluebird-nestjs";
import type { KnipResult } from "bluebird-nestjs";
const { diagnostics, warnings } = await runKnip({ cwd: "/path/to/project" });
// Each diagnostic has category: 'dead-code' and a rule like knip/files, knip/exports, etc.
for (const d of diagnostics) {
console.log(`[${d.rule}] ${d.filePath}: ${d.message}`);
}The runner automatically detects monorepo setups (pnpm workspaces, Lerna, Nx, Rush) and scopes analysis to the correct workspace. It skips analysis gracefully when node_modules is not present and retries on plugin config-loading errors.
NestJS-Aware Entry Points
Bluebird automatically configures Knip with NestJS-aware entry patterns to reduce false positives:
- NestJS bootstrap:
main.ts,src/main.ts(application entry point, never imported) - OpenTelemetry instrumentation:
instrumentation.ts(imported by main.ts before NestJS starts) - TypeORM CLI files:
data-source.ts,ormconfig.ts(referenced by CLI, not imported) - Migration runners:
run-migrations.ts,run-migration.ts(standalone entry points) - Seeder scripts:
seed.ts,seeder.ts(standalone entry points) - TypeORM entities:
**/entity/*.ts,**/entities/*.ts(loaded via glob patterns at runtime) - TypeORM migrations:
**/migration/*.ts,**/migrations/*.ts(loaded via glob patterns) - TypeORM subscribers:
**/subscriber/*.ts,**/subscribers/*.ts(loaded via glob patterns) - Integration/E2E tests:
test/integration/**/*.ts,test/e2e/**/*.ts(test entry points)
These patterns prevent false "unused file" warnings for files that are consumed by TypeORM's runtime glob-based loading, referenced in package.json scripts, or serve as application/test entry points rather than being statically imported.
The conversion layer is also exported for direct use:
import { convertKnipIssues, findMonorepoRoot } from "bluebird-nestjs";Output Formatters
Four output formatters are exported for programmatic use. Each accepts a ScanResult and returns a formatted string:
import { formatText, formatJson, formatSarif, formatHtml } from "bluebird-nestjs";
import type { JsonOutput } from "bluebird-nestjs";
const result = await diagnose();
// Terminal-friendly output with ANSI colors, score gauge, and grouped diagnostics
const text = formatText(result, /* verbose */ false);
// Structured JSON with score, counts, project metadata, and full diagnostics
const json = formatJson(result);
const parsed: JsonOutput = JSON.parse(json);
// SARIF 2.1.0 for CI integrations (GitHub Code Scanning, Azure DevOps, etc.)
const sarif = formatSarif(result);
// Standalone HTML dashboard with charts and collapsible diagnostics
const html = formatHtml(result);The text formatter shows a category summary, top issues, and optional detailed diagnostics (with --verbose). The json formatter includes top-level errorCount/warningCount/baselinedCount fields for easy consumption. The sarif formatter produces a spec-compliant SARIF 2.1.0 log with deduplicated rule entries, physical locations, and score metadata in run properties. The html formatter generates a self-contained HTML dashboard with dark theme, score gauge, category charts, and collapsible diagnostic details.
Exported Types
import type {
Diagnostic,
ScanResult,
ProjectInfo,
RuleMeta,
RuleCategory,
Severity,
RuleConfidence,
BluebirdConfig,
Waiver,
RunEslintOptions,
RunnerWarning,
LintResult,
RunGraphAnalysisOptions,
GraphAnalysisResult,
JsonOutput,
} from "bluebird-nestjs";Registry Helpers
import {
getAllRules,
getEnabledRules,
getRuleById,
getRulesByCategory,
getRulesByConfidence,
} from "bluebird-nestjs";
// Get all 38 rules
const allRules = getAllRules();
// Get rules enabled for a specific project
const enabledRules = getEnabledRules(project, /* includeHeuristic */ true);
// Look up a specific rule
const rule = getRuleById("no-hardcoded-secrets");
// Filter by category or confidence
const securityRules = getRulesByCategory("security");
const deterministicRules = getRulesByConfidence("deterministic");Troubleshooting
"Cannot find module" errors
Ensure you've installed dependencies in your project:
npm installRules not detecting my NestJS features
Bluebird auto-detects features from package.json. Ensure your NestJS-related dependencies are listed:
{
"dependencies": {
"@nestjs/swagger": "^7.0.0",
"@nestjs/graphql": "^12.0.0"
}
}Score seems too low
Use --verbose to see all violations and identify patterns:
bluebird --verboseCommon causes of low scores:
- Missing
@Injectable()decorators on services - Direct
process.envaccess instead of ConfigService - Missing validation decorators on DTOs
- Console.log instead of NestJS Logger
Baseline not filtering old violations
Ensure the baseline file exists and matches the current file paths:
# Regenerate baseline if file paths changed
bluebird --baselineFeature-gated rules not running
Feature-gated rules (GraphQL, Microservices, WebSockets) only run when the corresponding package is detected. Check that your package.json includes the relevant @nestjs/* package.
Too many false positives
Consider using waivers for intentional deviations:
{
"waivers": [
{
"rule": "bluebird/no-console-log",
"file": "src/main.ts",
"reason": "Console logging needed during bootstrap"
}
]
}Config files flagged as "unused"
Bluebird automatically ignores common configuration files (eslint.config.js, jest.config.ts, etc.). If you have custom config files being flagged, add them to ignore.files:
{
"ignore": {
"files": ["my-custom-config.js"]
}
}TypeORM entities/migrations flagged as "unused"
Bluebird automatically recognizes TypeORM's glob-based loading patterns (**/entity/*.ts, **/migrations/*.ts, etc.) and marks these as entry points. If you have entities in non-standard directories, add them to your knip config or use waivers:
{
"ignore": {
"files": ["src/custom-entities/**"]
}
}data-source.ts or run-migrations.ts flagged as "unused"
These files are CLI entry points referenced in package.json scripts, not imported by other code. Bluebird v0.1.12+ automatically recognizes these patterns. If you're on an older version, upgrade:
npm install -g bluebird-nestjs@latestAnalysis is slow on large projects
Try these optimizations:
# Run passes in parallel for ~8% faster execution
bluebird --fast
# Skip dead-code analysis (fastest improvement)
bluebird --no-dead-code
# Only check changed files
bluebird --diff main
# Skip graph analysis for single-file checks only
bluebird --no-graph-analysisHow do I understand a specific rule?
Use the explain command:
bluebird explain no-hardcoded-secretsThis shows the rule's purpose, severity, and how to fix violations.
How do I adopt Bluebird on an existing project?
Use the baseline workflow to avoid being overwhelmed by legacy issues:
# 1. Create baseline of existing issues
bluebird --baseline
git add .bluebird-baseline.json
git commit -m "chore: add bluebird baseline"
# 2. Run in CI - only new issues will fail
bluebird --fail-on error
# 3. Fix issues over time and update baseline
bluebird --update-baselineHow do I run Bluebird in CI without prompts?
Use the --yes flag with init, and standard flags for analysis:
# Non-interactive config generation
bluebird init --yes
# Quiet mode for CI (exit code only)
bluebird --quiet
# JSON output for parsing
bluebird --format jsonESLint Plugin
Bluebird includes an ESLint plugin for real-time feedback in your IDE. This surfaces Bluebird rules as you type, without running the full CLI.
Installation
The plugin is bundled with Bluebird:
npm install --save-dev bluebird-nestjsConfiguration
Add the plugin to your eslint.config.js (flat config):
import bluebird from 'bluebird-nestjs/eslint-plugin';
export default [
// Your existing config...
{
plugins: {
bluebird,
},
rules: {
// Enable all Bluebird rules as warnings
...Object.fromEntries(
Object.keys(bluebird.rules).map(rule => [`bluebird/${rule}`, 'warn'])
),
// Or enable specific rules
'bluebird/no-hardcoded-secrets': 'error',
'bluebird/missing-injectable': 'error',
'bluebird/no-console-log': 'warn',
},
},
];Available Rules
All file-level Bluebird rules are available in the ESLint plugin. Graph-level rules (like no-circular-dependency and no-duplicate-route) require cross-file analysis and are only available via the CLI.
IDE Integration
Once configured, Bluebird rules will appear in:
- VS Code with the ESLint extension
- WebStorm/IntelliJ with built-in ESLint support
- Neovim with nvim-lspconfig or ALE
- Any editor with ESLint language server support
Docker
Bluebird provides a Docker image for containerized analysis:
# Build the image
docker build -t bluebird .
# Run analysis on current directory
docker run --rm -v $(pwd):/workspace bluebird
# JSON output
docker run --rm -v $(pwd):/workspace bluebird --format json
# Diff mode
docker run --rm -v $(pwd):/workspace bluebird --diff main
# With configuration file
docker run --rm -v $(pwd):/workspace bluebird --verboseThe container runs as a non-root user for security and mounts your project at /workspace.
Repository Structure
This is a pnpm monorepo. The main package is packages/bluebird.
packages/bluebird/
├── src/
│ ├── cli.ts # Commander-based CLI entrypoint
│ ├── scan.ts # Terminal UI orchestration (spinner, summary)
│ ├── watch.ts # Watch mode implementation
│ ├── index.ts # Public API surface (diagnose, exports)
│ ├── types.ts # Shared type contracts
│ ├── constants.ts # Scoring thresholds and penalties
│ ├── rules/
│ │ ├── index.ts # Rule registry (38 rules, frozen metadata)
│ │ ├── checkers.ts # Checker maps (36 file + 2 graph)
│ │ ├── ast-helpers.ts # TypeScript AST utilities
│ │ ├── architecture.ts # DI bypass, god controller/service
│ │ ├── security.ts # Secrets, validation, DTO typing, SQL injection
│ │ ├── correctness.ts # Injectable, lifecycle hooks, side effects
│ │ ├── api-design.ts # Swagger, entity exposure, HTTP status
│ │ ├── performance.ts # Sync fs/crypto blocking, caching, N+1
│ │ ├── database.ts # Missing indexes, unsafe synchronize
│ │ ├── testing.ts # Test coverage heuristics
│ │ └── graph-rules.ts # Circular deps, duplicate routes
│ └── utils/
│ ├── run-eslint.ts # File-level analysis runner (ESLint pass)
│ ├── run-graph-analysis.ts # Cross-file analysis runner (graph pass)
│ ├── run-knip.ts # Dead-code analysis runner (knip pass)
│ ├── discover-project.ts # Auto-detect NestJS project metadata
│ ├── calculate-score.ts # Hybrid health score computation
│ ├── load-config.ts # Config file loading and validation
│ ├── init-config.ts # Interactive config generation (bluebird init)
│ ├── filter-diagnostics.ts # Ignore/waiver/glob filtering
│ ├── baseline.ts # Baseline snapshot load/save/apply
│ ├── parse-disable-comments.ts # Inline disable comment parsing
│ ├── combine-diagnostics.ts
│ ├── orchestrate.ts # Analysis orchestration with progress callbacks
│ ├── format-text.ts # Text output formatter (terminal)
│ ├── format-json.ts # JSON output formatter
│ ├── format-sarif.ts # SARIF 2.1.0 output formatter
│ └── format-html.ts # HTML dashboard formatter
└── tests/ # 944 tests across 23 files
├── rules-checkers.test.ts # Rule checker unit tests
├── run-eslint.test.ts # ESLint runner tests
├── run-graph-analysis.test.ts # Graph runner tests
├── run-knip.test.ts # Dead-code runner tests
├── scan.test.ts # Scan orchestrator tests
├── diagnose.test.ts # Integration tests
└── ...Development
# Install dependencies
pnpm install
# Run tests
pnpm --filter bluebird run test
# Watch mode
pnpm --filter bluebird run test:watch
# Type check
pnpm --filter bluebird run typecheck
# Lint
pnpm --filter bluebird run lint
# Build
pnpm --filter bluebird run buildContributing
We welcome contributions! Please see our development setup above and ensure:
- All tests pass (
pnpm --filter bluebird run test) - Code is formatted (
pnpm --filter bluebird run lint) - Types check (
pnpm --filter bluebird run typecheck)
Adding a New Rule
- Add the checker function to the appropriate file in
src/rules/ - Add the rule metadata to
src/rules/index.ts - Register the checker in
src/rules/checkers.ts - Add tests in
tests/rules-checkers.test.ts - Update the rule counts in this README
Releasing to npm
Releases are automated via GitHub Actions when a version tag is pushed:
# 1. Update version in packages/bluebird/package.json
cd packages/bluebird
npm version patch # or minor, major
# 2. Push the tag to trigger release
git push origin main --tagsThe workflow will:
- Build and test
- Publish to npm as
bluebird-nestjs
Prerequisites:
NPM_TOKENsecret must be configured in GitHub repository settings- npm account must have publish access to the
bluebird-nestjspackage
License
UNLICENSED
