npm package: @nytodev/eslint-plugin-baseline
Description: ESLint baseline plugin - Ignore existing errors and only report new ones (PHPStan-like)
Latest version: 1.0.1

# @nytodev/eslint-plugin-baseline

A PHPStan-like baseline for ESLint. Ignore existing errors and only report new ones.

[![npm version](https://badge.fury.io/js/@nytodev%2Feslint-plugin-baseline.svg)](https://www.npmjs.com/package/@nytodev/eslint-plugin-baseline)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

## Why?

When adopting ESLint on a legacy codebase, you might have hundreds or thousands of existing errors. Fixing them all at once is often impractical. This plugin lets you:

1. **Capture all existing errors** in a baseline file
2. **Ignore those errors** in subsequent runs
3. **Report only new errors** introduced after the baseline was created

This is the same approach used by [PHPStan](https://phpstan.org/user-guide/baseline) for PHP projects.

## Installation

```bash
npm install --save-dev @nytodev/eslint-plugin-baseline
```

## Quick Start

### 1. Generate the baseline

```bash
npx eslint-baseline --update
```

This creates `.eslintbaseline.json` containing all current errors.

### 2. Run ESLint with the baseline

```bash
npx eslint-baseline
```

Only new errors (not in the baseline) will be reported.

### 3. Commit the baseline

```bash
git add .eslintbaseline.json
git commit -m "chore: add eslint baseline"
```

## CLI Usage

```bash
# Generate or update the baseline
npx eslint-baseline --update

# Lint with baseline (default)
npx eslint-baseline

# Lint specific files/directories
npx eslint-baseline src/

# Remove fixed errors from baseline
npx eslint-baseline --prune

# Show baseline statistics
npx eslint-baseline --stats

# Baseline only specific rules
npx eslint-baseline --suppress-rule no-console --suppress-rule no-debugger --update

# Split baseline by rule (like PHPStan baseline-per-identifier)
npx eslint-baseline --update --split-by-rule

# Report unmatched baseline entries (errors that no longer exist)
npx eslint-baseline --report-unmatched

# Delete baseline
npx eslint-baseline --clean

# Allow empty baseline
npx eslint-baseline --update --allow-empty

# Pass additional arguments to ESLint
npx eslint-baseline -- --fix

# Verbose output
npx eslint-baseline --verbose
```

### CLI Options

| Option | Short | Description |
|--------|-------|-------------|
| `--update` | `-u` | Generate or update the baseline file |
| `--prune` | `-p` | Remove fixed errors from baseline |
| `--stats` | | Show detailed baseline statistics |
| `--clean` | | Delete the baseline file |
| `--suppress-rule <rule>` | | Only baseline specific rule (can be repeated) |
| `--baseline-file <path>` | `-b` | Baseline file path (default: `.eslintbaseline.json`) |
| `--split-by-rule` | `-s` | Split baseline into multiple files by rule |
| `--allow-empty` | | Allow generating an empty baseline |
| `--report-unmatched` | `-r` | Report baseline entries that no longer match |
| `--verbose` | `-v` | Verbose output with rule statistics |
| `--no-color` | | Disable colored output |
| `--help` | `-h` | Show help |
| `--version` | | Show version |
| `--` | | Pass remaining arguments to ESLint |

## Baseline Format

### Single file (default)

```json
{
  "src/legacy/module.ts": [
    {
      "ruleId": "@typescript-eslint/no-unused-vars",
      "line": 42,
      "column": 5,
      "message": "'foo' is defined but never used."
    }
  ]
}
```

### Split by rule (`--split-by-rule`)

```
.eslintbaseline/
├── _loader.json
├── @typescript-eslint-no-unused-vars.json
├── @stylistic-indent.json
└── no-console.json
```

Each file contains only errors for that specific rule, making it easier to:

- Track progress on specific rules
- Assign different rules to different team members
- See which rules have the most violations

## ESLint Plugin Integration

You can also use the plugin directly in your ESLint configuration:

### Flat config (eslint.config.js)

```javascript
import baseline from '@nytodev/eslint-plugin-baseline';

export default [
  {
    plugins: { baseline },
    processor: 'baseline/baseline',
  },
  // ... your other configs
];
```

### Legacy config (.eslintrc.js)

```javascript
module.exports = {
  plugins: ['@nytodev/baseline'],
  processor: '@nytodev/baseline/baseline',
};
```

> **Note**: When using the processor, you'll need to run ESLint with special environment variables to generate the baseline. The CLI is recommended for most use cases.

## Comparison with PHPStan Baseline

| Feature | PHPStan | eslint-plugin-baseline |
|---------|---------|------------------------|
| Generate baseline | `--generate-baseline` | `--update` |
| Baseline file | `phpstan-baseline.neon` | `.eslintbaseline.json` |
| Format | NEON/PHP | JSON |
| Allow empty | `--allow-empty-baseline` | `--allow-empty` |
| Split by identifier | Extension required | `--split-by-rule` |
| Report unmatched | `reportUnmatchedIgnoredErrors` | `--report-unmatched` |
| Matching | Regex + path + count | Hash (rule + line + message) |

## How It Works

1. **Baseline generation**: When you run `--update`, the CLI runs ESLint and captures all errors with their exact location (file, line, column) and message.

2. **Error matching**: Each error is identified by a hash of `ruleId + line + message`. This ensures that:
   - Moving code to a different line creates a "new" error
   - Changing the error message creates a "new" error
   - Adding a new error of the same type on a different line is detected

3. **Filtering**: When linting, errors that match the baseline are filtered out, and only new errors are reported.

## Workflow

### Initial setup

```bash
# Generate baseline with all current errors
npx eslint-baseline --update

# Commit baseline
git add .eslintbaseline.json
git commit -m "chore: add eslint baseline"
```

### Daily development

```bash
# Run lint with baseline (only new errors reported)
npx eslint-baseline
```

### After fixing errors

```bash
# Regenerate baseline to remove fixed errors
npx eslint-baseline --update
git add .eslintbaseline.json
git commit -m "chore: update eslint baseline"
```

### CI/CD

```yaml
# GitHub Actions
- name: Lint
  run: npx eslint-baseline

# GitLab CI
lint:
  script:
    - npx eslint-baseline
```

## API

For programmatic usage:

```javascript
const { Baseline, Reporter, createFormatter } = require('@nytodev/eslint-plugin-baseline');

// Create baseline instance
const baseline = new Baseline({
  cwd: process.cwd(),
  baselineFile: '.eslintbaseline.json',
  splitByRule: false,
});

// Load baseline
baseline.load();

// Check if error is in baseline
const isBaselined = baseline.isInBaseline(filePath, ruleId, line, message);

// Get statistics
const stats = baseline.getStats();

// Get unmatched entries
const unmatched = baseline.getUnmatched();
```

## License

MIT