Skip to content

Coderrob/eslint-config-zero-tolerance

Repository files navigation

ESLint Plugin Zero Tolerance

@coderrob/eslint-plugin-zero-tolerance

69 opinionated ESLint rules for TypeScript teams that refuse to compromise on code quality.

npm version License Coverage ESLint TypeScript


Zero tolerance means every rule earns its place. No warnings you learn to ignore. No exceptions you forget about. Every violation is a conversation about quality — and quality always wins.

Why Zero Tolerance?

Most linting setups start strict and erode over time. A scattered eslint-disable here, an any cast there, and before long the rules exist in name only.

This plugin takes the opposite approach:

  • No eslint-disable comments — fix the root cause, don't silence the symptom.
  • No any smuggling — type assertions and non-null assertions are flagged.
  • No magic values — every number and string earns a name.
  • No leaky tests — persistent mocks, imprecise matchers, and timer abuse are caught.
  • No complexity hiding — functions stay short, parameters stay few, imports stay clean.

The result is a codebase where the rules are the culture and the culture is visible in every file.


Documentation

Hosted docs https://coderrob.github.io/eslint-config-zero-tolerance/
Rules reference docs/rules/index.md
Configuration guide docs/configuration.md

Packages

This monorepo publishes two packages:

Package Description
@coderrob/eslint-plugin-zero-tolerance The ESLint plugin — 69 custom rules
@coderrob/eslint-config-zero-tolerance Pre-built recommended and strict config presets

Requirements

  • ESLint 8.57.0+, 9.x, or 10.x
  • TypeScript-ESLint 8.x
  • TypeScript 5.x

Installation

npm install --save-dev @coderrob/eslint-plugin-zero-tolerance @typescript-eslint/parser

Usage

ESLint 9+ (Flat Config)

Using the recommended preset:

// eslint.config.js
import zeroTolerance from '@coderrob/eslint-plugin-zero-tolerance';

export default [
  zeroTolerance.configs.recommended,
  // your other configs...
];

Using the strict preset:

// eslint.config.js
import zeroTolerance from '@coderrob/eslint-plugin-zero-tolerance';

export default [
  zeroTolerance.configs.strict,
  // your other configs...
];

Alternative: Import presets directly from the config package:

// eslint.config.js
import recommended from '@coderrob/eslint-config-zero-tolerance/recommended';
// or
import strict from '@coderrob/eslint-config-zero-tolerance/strict';

export default [
  recommended, // or strict
  // your other configs...
];

Custom configuration:

// eslint.config.js
import zeroTolerance from '@coderrob/eslint-plugin-zero-tolerance';

export default [
  {
    plugins: {
      'zero-tolerance': zeroTolerance,
    },
    rules: {
      'zero-tolerance/require-interface-prefix': 'error',
      'zero-tolerance/no-throw-literal': 'error',
      'zero-tolerance/max-function-lines': ['warn', { max: 40 }],
      // ... other rules
    },
  },
];

ESLint 8.x (Legacy Config)

Using .eslintrc.js:

module.exports = {
  plugins: ['zero-tolerance'],
  extends: ['plugin:zero-tolerance/legacy-recommended'],
  // or for strict mode:
  // extends: ['plugin:zero-tolerance/legacy-strict'],
};

Rules

The plugin ships 69 rules across 8 categories. The grouped catalog below is exhaustive and links every rule to its dedicated documentation page.

Preset legend:

  • Both = enabled by both recommended and strict
  • Strict only = enabled only by strict
  • Opt-in = not enabled by either preset
Category Rules Focus
Naming Conventions 1 Interface naming standards
Documentation 5 JSDoc, BDD specs, optional chaining, readonly props
Testing 8 Test descriptions, mocks, timers, fetch, interfaces
Type Safety 11 Assertions, unions, imports, exported types
Code Quality 15 Function size, magic values, immutability, sorting
Error Handling 3 Throw safety, empty catches, Result patterns
Imports 8 Barrels, re-exports, dynamic imports, node protocol
Bug Prevention 18 Identical code, control flow, async safety

Naming Conventions

Rule Type Preset Description
require-interface-prefix suggestion Both Enforce that interface names start with "I"

Documentation

Rule Type Preset Description
require-bdd-spec suggestion Opt-in Enforce that every TypeScript source file has a valid sibling .ts.bdd.json BDD spec file
require-jsdoc-anonymous-functions suggestion Strict only Require JSDoc comments on anonymous function-like constructs except in test files and known test callbacks
require-jsdoc-functions suggestion Both Require JSDoc comments on all functions and require @param/@returns/@throws tags when applicable (except in test files)
require-optional-chaining suggestion Both Require optional chaining instead of repeated logical guard access
require-readonly-props suggestion Both Require readonly typing for JSX component props

Testing

Rule Type Preset Description
require-test-description-style suggestion Both Enforce that test descriptions start with "should"
no-jest-have-been-called suggestion Both Prohibit toBeCalled, toHaveBeenCalled, toBeCalledWith, toHaveBeenCalledWith, toHaveBeenLastCalledWith, and toLastCalledWith; use toHaveBeenCalledTimes with an explicit call count and toHaveBeenNthCalledWith with an explicit nth-call index and arguments instead
no-mock-implementation suggestion Both Prohibit persistent mock implementations; use the Once variants to avoid test bleeds
no-set-timeout-in-tests suggestion Both Disallow setTimeout usage in test files
no-set-interval-in-tests suggestion Both Disallow setInterval usage in test files
no-fetch-in-tests suggestion Opt-in Disallow fetch usage in test files
no-restricted-imports-in-tests suggestion Opt-in Disallow restricted dependency imports in test files
no-test-interface-declaration suggestion Both Disallow interface declarations in test files; import production types instead

Type Safety

Rule Type Preset Description
no-type-assertion suggestion Both Prevent use of TypeScript "as" type assertions
no-non-null-assertion problem Both Disallow non-null assertions using the "!" postfix operator
no-explicit-any problem Both Disallow explicit any; model unknown values precisely and narrow them explicitly
no-indexed-access-types problem Both Disallow TypeScript indexed access types
no-literal-unions suggestion Both Ban literal unions in favor of enums
no-literal-property-unions suggestion Both Require property literal unions to use named domain types
no-inline-type-import problem Both Disallow TypeScript inline type imports using import("...")
no-return-type problem Both Disallow TypeScript ReturnType utility usage
require-union-type-alias suggestion Both Require inline union types with multiple type references to be extracted into named type aliases
no-destructured-parameter-type-literal suggestion Both Disallow inline object type literals on destructured parameters; require a named type instead
require-exported-object-type suggestion Both Require exported object constants to declare an explicit type annotation

Code Quality

Rule Type Preset Description
max-function-lines suggestion Both Enforce a maximum number of lines per function body
max-params suggestion Both Enforce a maximum number of function parameters
no-array-mutation suggestion Both Disallow mutating array methods; prefer immutable alternatives such as spread, slice, and toSorted
no-date-now suggestion Both Disallow Date.now() and new Date(); prefer injected clocks for deterministic behavior
no-magic-numbers suggestion Both Disallow magic numbers; use named constants instead of raw numeric literals
no-magic-strings suggestion Both Disallow magic strings in comparisons and switch cases; use named constants instead
no-map-set-mutation suggestion Both Disallow direct Map and Set mutation methods; rebuild collections instead of mutating them in place
no-object-mutation suggestion Both Disallow direct object-property mutation; prefer creating new objects with immutable update patterns
sort-imports suggestion Both Require import declarations to be grouped (side-effect -> builtin -> external -> parent -> peer -> index) and sorted alphabetically within each group
sort-functions suggestion Both Require top-level functions to be sorted alphabetically
prefer-nullish-coalescing suggestion Both Prefer nullish coalescing instead of a nullish guard ternary
prefer-object-spread suggestion Both Enforce object spread syntax instead of Object.assign with an empty object literal as the first argument
prefer-readonly-parameters suggestion Both Prefer readonly typing for object and array-like parameters to prevent accidental mutation of inputs
prefer-string-raw suggestion Both Prefer String.raw for string literals containing escaped backslashes (Sonar S7780)
prefer-structured-clone suggestion Both Prefer structuredClone(...) over JSON.parse(JSON.stringify(...)) when creating a deep clone

Error Handling

Rule Type Preset Description
no-empty-catch problem Both Disallow empty catch blocks that silently swallow errors
no-throw-literal problem Both Disallow throwing literals, objects, or templates; always throw a new Error instance
prefer-result-return suggestion Strict only Prefer Result-style return values instead of throw statements to make error flows explicit and composable

Imports

Rule Type Preset Description
require-clean-barrel suggestion Both Require barrel files (index.*) to contain only module re-export declarations
require-barrel-relative-exports suggestion Both Require barrel re-export declarations to use current-directory descendant paths that start with './'
no-dynamic-import problem Both Ban await import() and require() except in test files
no-export-alias suggestion Both Prevent use of alias in export statements
no-barrel-parent-imports suggestion Both Disallow parent-directory imports (.. and ../*) inside barrel files (index.*) across import declarations, import expressions, require calls, and import-equals declarations
no-parent-internal-access suggestion Opt-in Disallow parent-relative access into protected internal directories such as src
no-re-export suggestion Both Disallow direct or indirect re-export statements from parent or ancestor modules; barrel files (index.*) are exempt from this restriction
require-node-protocol suggestion Both Require Node.js built-in module imports to use the node: protocol prefix

Bug Prevention

Rule Type Preset Description
no-identical-expressions problem Both Disallow identical expressions on both sides of a binary or logical operator (Sonar S1764)
no-identical-branches suggestion Both Disallow identical if/else and conditional-expression branches; consolidate duplicate conditional fragments
no-boolean-return-trap suggestion Both Disallow ambiguous boolean-return APIs; prefer predicate naming or richer result types for clearer call sites
no-redundant-boolean suggestion Both Disallow redundant comparisons to boolean literals (Sonar S1125)
no-for-in problem Both Disallow for..in loops; use Object.keys/values/entries to avoid prototype-chain iteration
no-labels problem Both Disallow labels because they make control flow harder to reason about
no-with problem Both Disallow with statements because they make scope resolution unpredictable
no-await-in-loop problem Both Disallow await expressions inside loops; use Promise.all() for parallel execution
no-floating-promises problem Both Disallow floating promises; explicitly handle with await, void, or rejection handlers
no-math-random problem Both Disallow Math.random(); inject randomness explicitly or use a dedicated random source
no-eslint-disable suggestion Both Prevent use of eslint-disable comments
no-parameter-reassign suggestion Both Disallow assignments and updates to function parameters; use a new local variable instead
no-process-env-outside-config problem Both Disallow process.env reads outside configuration modules; import typed config instead
no-flag-argument suggestion Both Disallow boolean flag arguments in function declarations; prefer explicit methods or command objects
prefer-guard-clauses suggestion Both Prefer guard clauses by disallowing else blocks when the if branch already terminates control flow
prefer-shortcut-return suggestion Both Prefer shortcut boolean returns by replacing if/return true-false patterns with direct return expressions
no-query-side-effects suggestion Both Disallow side effects in query-style functions (get*/is*/has*/can*/should*); separate query from modifier
require-exhaustive-switch suggestion Both Require exhaustive switch statements over finite union, enum, and boolean discriminants

Development

This repository itself is a pnpm workspace and dogfoods its own rules through eslint.config.mjs. Every source file in the plugin must pass the same rules it enforces on consumers.

Setup

pnpm install

Building

pnpm build

Testing

pnpm test

Coverage gates are enforced per-file at 95% minimum across statements, branches, functions, and lines. The test command also refreshes the coverage badge automatically.

README Sync

pnpm readme:sync
pnpm validate:readme

The root README.md rule catalog is generated from deterministic metadata in scripts/metadata/readme-rule-catalog.json, canonical rule source metadata, preset metadata from packages/plugin/src/rules/support/rule-map.ts, and rule docs page existence checks.

Type Checking

pnpm --filter @coderrob/eslint-plugin-zero-tolerance exec tsc -p tsconfig.json --noEmit
pnpm --filter @coderrob/eslint-config-zero-tolerance exec tsc -p tsconfig.json --noEmit

Dependency Graph

pnpm deps:graph
pnpm deps:circular

Publishing

This monorepo provides automated scripts to handle versioned releases.

Quick release (single command):

pnpm release:prepare --release patch --commit --tag --publish

This will:

  • bump the root, plugin, and config package versions
  • replace workspace:* with a versioned peer dependency in packages/config
  • run pnpm build and pnpm test
  • create a release commit and annotated git tag
  • publish both packages to npm

If you want to restore workspace:* after publishing for local development, run:

pnpm release:restore-workspace

Or include --restore-workspace and commit that restoration separately.

Manual/stepwise release flow:

# 1. Build all packages
pnpm build

# 2. Run tests to ensure everything works
pnpm test

# 3. Prepare packages for publishing (converts workspace:* to versioned dependency)
pnpm release:prepare

# 4. Publish the plugin package
cd packages/plugin
npm publish

# 5. Publish the config package
cd ../config
npm publish

# 6. Restore workspace:* for local development
cd ../..
pnpm release:restore-workspace

Additional release:prepare options:

# Bump versions and prepare manifests without publishing
pnpm release:prepare --release minor

# Skip build/test if already run in CI or a previous step
pnpm release:prepare --release 1.2.0 --skip-build --skip-test --commit --tag --publish

# Dry run the full flow
pnpm release:prepare --release patch --commit --tag --publish --dry-run

License

Apache 2.0 Copyright Robert Lindley

About

Zero-tolerance ESLint plugin and config for enforcing strict code quality standards.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors