npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cognirail/eslint-config

v2.1.3

Published

Opinionated ESLint flat config + Prettier for TypeScript — AI coding guardrails

Readme

@cognirail/eslint-config

Opinionated ESLint flat config + Prettier for TypeScript projects.

Designed as AI coding guardrails — strict type checks, complexity detection, modern JS enforcement, and no inline escape hatches. One package, zero config hassle.

Included Plugins

| Plugin | Purpose | | ------------------------------------- | ----------------------------------------------------------------- | | typescript-eslint strictTypeChecked | Strict TS rules + type-aware analysis | | eslint-plugin-unicorn | Modern JS idioms enforcement | | eslint-plugin-sonarjs | Code smell + bug detection | | eslint-plugin-import-x | Import organization + TS resolver | | eslint-plugin-n | Node.js best practices | | eslint-config-prettier | Disable formatting rules (let Prettier handle it) | | eslint-import-resolver-typescript | TypeScript-aware import resolution for import-x | | @cspell/eslint-plugin | Spell checking for identifiers, comments, and JSX text (optional) |

Requirements

  • Node.js ^20.19.0 || ^22.13.0 || >=24
  • ESLint >= 10.4.1
  • TypeScript >= 6.0.3 (optional — works without it when disableTypeChecked: true)
  • Prettier >= 3.8.3 (optional — only needed if using ./prettier export)

Quick Start

1. Install

pnpm add -D @cognirail/eslint-config eslint typescript prettier

2. ESLint — create eslint.config.mjs

import { defineConfig } from '@cognirail/eslint-config';

export default defineConfig();

Also supports eslint.config.ts with typescript-eslint v8+.

3. Prettier — add to package.json

{
  "prettier": "@cognirail/eslint-config/prettier"
}

4. Add lint scripts

{
  "scripts": {
    "lint": "eslint .",
    "lint:fix": "eslint --fix . && prettier --write .",
    "format:check": "prettier --check ."
  }
}

Done. Run pnpm lint to check, pnpm lint:fix to auto-fix.

Note: Your project must have a tsconfig.json for type-aware rules to work. If you don't have one, set disableTypeChecked: true in the options.

5. (Optional) Spell Checking

Requires Node.js >= 22.18.0 because @cspell/eslint-plugin v10 uses that runtime baseline.

pnpm add -D @cspell/eslint-plugin
// eslint.config.mjs
import { defineConfig } from '@cognirail/eslint-config';
import { cspell } from '@cognirail/eslint-config/cspell';

export default [...defineConfig(), ...cspell()];

The CSpell config includes a built-in word list for common tech ecosystem terms (AI/DB/tooling). To add project-specific words, pass them via the words option:

export default [...defineConfig(), ...cspell({ words: ['myapp', 'myterm'] })];

Alternatively, create a cspell.json at project root — CSpell auto-discovers it.

6. (Optional) Barrel Import Enforcement

Enforce barrel imports at architecture boundaries — prevent deep path imports like @agent/runtime/internal/executor and force consumers to use @agent/runtime.

// eslint.config.mjs
import { defineConfig } from '@cognirail/eslint-config';
import { barrel } from '@cognirail/eslint-config/barrel';

export default [
  ...defineConfig({ tsconfigRootDir: import.meta.dirname }),
  ...barrel({
    boundaries: [
      {
        publicAlias: '@agent/runtime',
        internalGlobs: ['src/agent/runtime/**'],
        consumers: ['src/agent/**', 'src/tools/**', 'test/**'],
      },
    ],
  }),
];

| Option | Type | Description | | ----------------------------------- | ---------- | -------------------------------------------------------------------------------------------- | | boundaries[].publicAlias | string | Public alias to import from. Blocks imports below this alias. | | boundaries[].internalGlobs | string[] | Files inside the implementation boundary; ignored so internals can import each other. | | boundaries[].consumers | string[] | Files where deep imports from the public alias are forbidden. | | boundaries[].allowInternalFrom | string[] | Additional globs allowed to import internals. | | boundaries[].ignores | string[] | Extra ignores for this boundary. | | layers / enforcedIn / ignores | — | Legacy API kept for existing configs; prefer boundaries for new architecture declarations. |

The rule only applies to boundary consumers; files matching internalGlobs, allowInternalFrom, or ignores are not restricted.

7. (Optional) Agent Project Preset

Use ./agent when the project contains agent runtime/tool/prompt/eval code and should enforce role-based file naming. This is the core agent preset; spell checking remains opt-in through ./cspell.

// eslint.config.mjs
import { defineAgentConfig } from '@cognirail/eslint-config/agent';

export default defineAgentConfig({
  tsconfigRootDir: import.meta.dirname,
  boundaries: [
    {
      publicAlias: '@agent/runtime',
      internalGlobs: ['src/agent/runtime/**'],
      consumers: ['src/agent/**', 'src/tools/**', 'test/**'],
    },
  ],
});

Default agent role suffixes:

| Suffix | Expected shape | | ----------- | -------------------- | | runtime | any | | executor | class or function | | planner | class or function | | tool | schema or function | | prompt | text or factory | | eval | any | | adapter | class or function | | schema | any | | memory | class or function | | type | interface/type alias | | interface | interface/type alias |

8. (Optional) Role-Based File Naming

Use ./role-naming directly for non-Nest, non-agent projects with their own role suffix table.

import { defineConfig } from '@cognirail/eslint-config';
import { roleNaming } from '@cognirail/eslint-config/role-naming';

export default [
  ...defineConfig({ tsconfigRootDir: import.meta.dirname }),
  ...roleNaming({
    suffixes: {
      service: { kind: 'class-or-function' },
      schema: { kind: 'any' },
      type: { kind: 'types' },
    },
  }),
];

9. (Optional) NestJS File Naming

Enforce the NestJS file-naming convention: role-suffix whitelist, content↔suffix binding, ambient declaration placement, and a single test-file convention. Self-contained — adds no external ESLint plugin dependency.

General form:

<kebab-base>.<role>.ts

The role suffix describes the dominant symbol's role, not merely its technical shape. One file should have one role: if a *.service.ts grows DTOs, interfaces, or exported utility types, split them into the role-specific files. Narrow private utility types that are not externally imported may stay close to the implementation.

// eslint.config.mjs
import { defineConfig } from '@cognirail/eslint-config';
import { nestNaming } from '@cognirail/eslint-config/nest-naming';

export default [
  ...defineConfig({ tsconfigRootDir: import.meta.dirname }),
  ...nestNaming({ files: ['src/**/*.ts'] }),
];

Rules:

| Rule | Catches | | ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | nest-naming/allowed-suffix | Filename suffix not in the role whitelist (e.g. *.widget.ts); non-kebab base (CreateUser.dto.ts) | | nest-naming/suffix-kind | Content doesn't match suffix: *.dto.ts written as interface/type; a class inside *.interface.ts / *.type.ts; exported interfaces/types left in the wrong role file | | nest-naming/suffix-decorator | A *.controller.ts / *.service.ts / *.module.ts / *.guard.ts class missing its @Controller / @Injectable / @Module decorator (severity = enforceDecorator, default warn) | | nest-naming/test-suffix | Wrong test convention: *.test.ts when Nest's default jest testRegex only matches *.spec.ts (silent skip) | | nest-naming/declaration-file | Ambient declare ... declarations hidden in normal implementation files, or project domain types placed in *.d.ts | | nest-naming/no-global-types-folder | Project types placed in a global types/ or src/types/ folder instead of being co-located with the owning module |

Suffixes are graded by runtime shape. class files emit JS and participate in DI/decorators/new; interface/type files disappear after compilation; any files are allowed because they may be functions, constants, or mixed framework forms.

Class suffixes with enforced decorators:

| Suffix | Role | Decorator | | ------------- | ------------------------------------------------------ | ------------------- | | controller | HTTP/RPC entrypoint; orchestration, not business logic | @Controller | | service | Injectable application/domain logic | @Injectable | | module | DI organization unit | @Module | | guard | CanActivate access control | @Injectable | | pipe | PipeTransform validation/transformation | @Injectable | | interceptor | NestInterceptor around advice | @Injectable | | filter | ExceptionFilter handling | @Catch | | resolver | GraphQL resolver | @Resolver | | gateway | WebSocket gateway | @WebSocketGateway |

Class suffixes without fixed decorator enforcement:

| Suffix | Role | | ------------ | -------------------------------------------------------------------- | | dto | Boundary data contract with runtime validation; DTOs must be classes | | entity | Persistence mapping / domain entity | | repository | Custom repository | | strategy | Passport strategy | | subscriber | TypeORM event subscriber |

Compile-time suffixes:

| Suffix | Must contain | Forbidden | | ----------- | ----------------------- | --------- | | interface | interface declaration | class | | type | type alias | class |

Content-free suffixes:

| Suffixes | Typical content | | --------------------------------------- | ---------------------------------------------------- | | decorator | Custom decorator functions / factories | | middleware | Function middleware or injectable class | | constant / constants | Exported constants | | enum | Enum declarations | | config | registerAs() functions or config objects | | schema / model | Mongoose / GraphQL mixed forms | | validator | @ValidatorConstraint class or validation functions | | util / utils / helper / helpers | Pure helper functions | | mock / fixture | Test helpers |

Content binding decisions:

  • DTOs must be classes. class-validator relies on runtime objects; interface/type DTOs compile away and can silently bypass ValidationPipe.
  • *.interface.ts and *.type.ts must not contain classes.
  • Use *.interface.ts for implements, declaration merging, extends, and ordinary object shapes.
  • Use *.type.ts for union, intersection, mapped, conditional, tuple, and utility types.
  • Do not use *.d.ts for project domain types. Reserve *.d.ts for ambient declarations such as third-party typings, declare global, and declare module.
  • Do not centralize project types in a global types/ folder; co-locate them with the owning module.
  • Base names must be kebab-case: create-user.dto.ts, not CreateUser.dto.ts.

Test suffixes:

| Type | Suffix | Reason | | --------- | -------------------------- | ---------------------------------------- | | Unit test | *.spec.ts | Matches Nest's default Jest testRegex | | E2E test | *.e2e-spec.ts | Matches Nest's e2e Jest config | | Forbidden | *.test.ts / *.tests.ts | Can be silently skipped in Nest projects |

| Option | Type | Default | Description | | ------------------ | -------------------------------------- | ----------------- | ---------------------------------------------------------------------------------------------------------------- | | files | string[] | ['src/**/*.ts'] | Globs the rules apply to. | | ignores | string[] | [] | Globs to exclude. | | suffixes | Record<string, { kind; decorator? }> | — | Merge on top of the default suffix table (add project suffixes / relax decorators). | | testSuffix | 'spec' \| 'test' \| false | 'spec' | Which single test convention to enforce; false disables. | | kebabCase | boolean | true | Require kebab-case base names. | | enforceDecorator | boolean \| 'warn' | 'warn' | true => error, false => off. Start at warn to surface noise from abstract bases before promoting to error. |

Test files (*.spec.ts, *.e2e-spec.ts) and suffix-less files (index.ts, main.ts) are exempt from allowed-suffix and suffix-kind. Identifier naming is handled separately by the base config: classes, interfaces, type aliases, enums, enum members, and type parameters use PascalCase; interfaces cannot use Hungarian I prefixes.

Options

import { defineConfig } from '@cognirail/eslint-config';

export default defineConfig({
  // Path to tsconfig.json directory (default: process.cwd())
  tsconfigRootDir: import.meta.dirname,

  // Additional ignore patterns (extends defaults, doesn't replace)
  ignores: ['**/generated/**', '**/proto/**'],

  // Additional abbreviations for unicorn/prevent-abbreviations (merged with defaults)
  abbreviations: { src: true, dest: true },

  // Additional prevent-abbreviations ignore patterns (merged with defaults)
  abbreviationIgnore: ['(^|[._-])api($|[._-])'],

  // Additional file globs where devDependency imports are allowed (merged with defaults)
  testFiles: ['**/__tests__/**', '**/e2e/**'],

  // Rule overrides (applied last). Keep these for explicit project exceptions,
  // not for lowering the guardrail baseline.
  overrides: {
    'sonarjs/cognitive-complexity': ['error', 20],
  },

  // Falls back to strict + stylistic (non-type-aware variants),
  // still provides substantial linting without a tsconfig.json.
  disableTypeChecked: false,
});

Default Ignores

These glob patterns are always ignored: dist/**, build/**, output/**, node_modules/**, coverage/**, .next/**, .nuxt/**, .output/**.

JS/CJS File Handling

JS files (*.js, *.mjs, *.cjs) automatically have type-checked rules and return-type requirements disabled. CommonJS files (*.cjs, *.cts) parse with sourceType: 'commonjs', so the config works in mixed TS/JS projects out of the box.

Inline Config Policy

Inline ESLint config comments are disabled via linterOptions.noInlineConfig, and ai-guardrails/no-inline-eslint-config reports ESLint directive comments as errors. AI-generated code should not be able to silence guardrails with eslint-disable comments. Use ignores for generated output and explicit top-level overrides for rare project-level exceptions.

Prettier Config

The bundled Prettier config uses these settings:

{
  "semi": true,
  "singleQuote": true,
  "jsxSingleQuote": true,
  "tabWidth": 2,
  "trailingComma": "all",
  "printWidth": 100,
  "arrowParens": "always",
  "bracketSpacing": true,
  "bracketSameLine": true,
  "endOfLine": "lf",
  "htmlWhitespaceSensitivity": "ignore",
  "vueIndentScriptAndStyle": true
}

AI Guardrail Rules

Key rules that catch common AI-generated code issues:

| Rule | AI Problem | Effect | | -------------------------------------------------- | ------------------------------------------------------ | ---------------------------------------------- | | @typescript-eslint/no-floating-promises | AI writes someAsync() without await | Forces promise handling | | @typescript-eslint/no-misused-promises | AI passes async to non-promise-expecting APIs | Catches async misuse | | @typescript-eslint/explicit-function-return-type | AI omits return types | Forces explicit declarations | | @typescript-eslint/no-explicit-any | AI falls back to any | Enforces proper typing | | @typescript-eslint/consistent-type-imports | AI mixes value/type imports | Forces import type {} | | @typescript-eslint/prefer-nullish-coalescing | AI writes x \|\| fallback instead of x ?? fallback | Prevents falsy-value bugs | | @typescript-eslint/prefer-optional-chain | AI writes manual if (a && a.b) chains | Enforces a?.b | | @typescript-eslint/strict-boolean-expressions | AI relies on truthy/falsy coercion | Forces explicit boolean intent | | @typescript-eslint/switch-exhaustiveness-check | AI misses union cases | Forces exhaustive discriminated-union handling | | ai-guardrails/no-inline-eslint-config | AI tries to silence lint with comments | Forces exceptions into reviewed config | | sonarjs/cognitive-complexity (<=15) | AI writes deeply nested logic | Limits function complexity | | unicorn/prefer-node-protocol | AI writes import fs from 'fs' | Forces node:fs prefix | | unicorn/no-array-for-each | AI uses .forEach() | Enforces for...of | | curly / eqeqeq | AI writes terse or coercive conditionals | Forces explicit blocks and equality | | no-console / no-debugger | AI leaves ad-hoc diagnostics in production paths | Blocks stray debugging artifacts | | prefer-const / no-var | AI uses let or var unnecessarily | Forces immutable bindings |

Notable Unicorn Customizations

  • prevent-abbreviations: Allows common short names: args, ctx, db, e2e, Env, env, err, fn, params, Prod, prod, props, ref, req, res, util, utils; ignores conventional *.e2e.* filename segments
  • no-null: Disabled — Node.js/TS ecosystem uses null pervasively
  • no-array-reduce: Disabled — reduce is idiomatic for data aggregation
  • prefer-module: Disabled — supports mixed CJS/ESM codebases
  • prefer-top-level-await: Disabled for main.ts — CommonJS entry files cannot use top-level await
  • filename-case: Allows kebab-case, camelCase, and PascalCase

Notable TypeScript Customizations

  • no-extraneous-class: Allows classes with decorators — required by NestJS (@Module), Angular (@NgModule), and similar frameworks
  • restrict-template-expressions: Allows number in template literals — safe and ubiquitous in practice
  • strict-boolean-expressions: Disallows truthy/falsy shortcuts for strings, numbers, nullable values, and objects; write the actual condition
  • switch-exhaustiveness-check: Requires discriminated-union switches to handle every case; redundant default cases do not hide missing variants
  • naming-convention: Enforces PascalCase for interfaces and type aliases; forbids Hungarian notation prefixes (IUserUser, TPropsProps)

Notable Node.js Plugin Customizations

  • no-missing-import: Disabled — eslint-plugin-import-x with TypeScript resolver already covers this
  • no-process-exit: Disabled — process.exit() is valid in CLI tools and application entry points
  • no-unpublished-import / no-unpublished-require: Disabled for test, spec, and config files — devDependencies imports are expected there

Notable Import-x Customizations

  • named: Disabled for TypeScript files — TypeScript compiler already validates named exports

Notable SonarJS Customizations

  • cognitive-complexity: Set to 15 (default is 10)
  • no-nested-functions: Disabled — closures and callbacks are core JavaScript/TypeScript patterns
  • todo-tag / fixme-tag: Set to warn — allows TODO markers during development without blocking CI
  • Cross-plugin dedup: 9 rules disabled where typescript-eslint already provides equivalent checks: no-array-delete, prefer-regexp-exec, deprecation, argument-type, different-types-comparison, no-try-promise, no-async-constructor, function-return-type, redundant-type-aliases

Environment

Targets modern Node.js runtimes (^20.19.0 || ^22.13.0 || >=24) with ecmaVersion: 'latest' and Node + ES2025 globals by default. Uses eslint-plugin-n's flat/recommended-module preset (ESM-first), with *.cjs and *.cts parsed as CommonJS.

VSCode Setup

// .vscode/settings.json
{
  "eslint.useFlatConfig": true,
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit",
  },
}

Recommended extensions: ESLint, Prettier, Error Lens.

Migration from @me-tool

@cognirail/eslint-config is the new package name for @me-tool/eslint-prettier-ts-config.

pnpm remove @me-tool/eslint-prettier-ts-config
pnpm add -D @cognirail/eslint-config

Then replace imports and Prettier references:

- import { defineConfig } from '@me-tool/eslint-prettier-ts-config';
+ import { defineConfig } from '@cognirail/eslint-config';

- "prettier": "@me-tool/eslint-prettier-ts-config/prettier"
+ "prettier": "@cognirail/eslint-config/prettier"

Migration from v1

v2 is a breaking change:

  1. Update the package

    pnpm add -D @cognirail/eslint-config@2 eslint@latest
    pnpm remove @me-tool/eslint-prettier-config  # remove old base
  2. Replace config file: Delete .eslintrc.js / .eslintrc.json, create eslint.config.mjs as shown in Quick Start.

  3. Update Prettier:

    - "prettier": "@me-tool/eslint-prettier-ts-config/.prettierrc.js"
    + "prettier": "@cognirail/eslint-config/prettier"
  4. Update lint-staged: Remove git add from commands (auto-staged since lint-staged v10).

    {
      "lint-staged": {
        "*.{ts,mts}": ["eslint --fix", "prettier --write"],
        "*.{js,mjs,cjs}": ["eslint --fix", "prettier --write"],
        "*.{json,md,yml,css}": ["prettier --write"]
      }
    }

New rules in v2

v2 adds significantly more rules than v1. Expect new lint errors on existing code, particularly from:

  • eslint-plugin-unicorn (modern JS patterns)
  • eslint-plugin-sonarjs (cognitive complexity)
  • @typescript-eslint strict type checks (no-floating-promises, explicit-function-return-type, etc.)

Run eslint --fix . to auto-fix what's possible, then address remaining issues manually.

License

ISC