Skip to content

Repository files navigation

@noctcore/eslint-plugins

A family of focused, general-purpose ESLint plugins that encode architecture and correctness conventions generic linters can't see: cross-file boundaries, IO contracts, and "this compiles but bites in production" patterns.

CI Docs MIT license

Flat-config only · ESLint 9+ · zero-config presets · independently versioned.

Documentation: noctcore.github.io/eslint-plugins
What each plugin is for, when it is a bad fit, and every rule with its options and examples.


Packages

Eleven ESLint plugins, plus @noctcore/lint-meta-rules for whole-repo checks and @noctcore/eslint-utils, the shared building block they are made with. Each plugin is versioned and installed on its own.

Package npm What it enforces
@noctcore/eslint-plugin-code-quality npm Guard clauses, comment and test hygiene, deterministic time, no stray process.exit
@noctcore/eslint-plugin-async-safety npm Fetch timeouts, AbortSignal forwarding, async races and shared mutable state
@noctcore/eslint-plugin-contracts npm IO boundaries (checked fetch, parsed boundary data), error cause and taxonomy, zod schema and wire naming, env access, decimal money, translation keys
@noctcore/eslint-plugin-security npm Shell injection, path traversal, SSRF, open redirect, unsanitized HTML (XSS), timing-unsafe comparison, server actions that bypass their action client
@noctcore/eslint-plugin-observability npm Structured logging: context objects over interpolation, no sensitive fields in logs, no lost error detail, declared PII in audit payloads
@noctcore/eslint-plugin-react npm React architecture and correctness (prop drilling, state colocation, memoized context, effect safety, guarded web storage)
@noctcore/eslint-plugin-rsc npm React Server Components / App Router correctness (navigation errors that must not be swallowed)
@noctcore/eslint-plugin-llm npm LLM output treated as untrusted input before it reaches a sink
@noctcore/eslint-plugin-prisma npm Prisma tenancy, soft-delete and transaction guardrails (tenant-scope escape hatches, raw SQL, request-body writes, single-writer models, audit placement)
@noctcore/eslint-plugin-architecture npm Module and folder shape (folder-per-component, barrels, feature boundaries, import depth, colocated tests)
@noctcore/eslint-plugin-monorepo npm Workspace package boundaries (barrel-only and exports-map-aware imports); needs your workspace scope
@noctcore/eslint-utils npm Shared rule-creator + AST helpers (internal building block)
@noctcore/lint-meta-rules npm Whole-repo structure-lock rules for @noctcore/harness

Install

Requirements: ESLint 9 or newer, flat config (eslint.config.js) only. Install the plugins you want and @typescript-eslint/parser:

npm i -D @noctcore/eslint-plugin-code-quality @typescript-eslint/parser   # or bun add -D / pnpm add -D

Every plugin's configs.recommended registers the plugin and sets rule severities, nothing else. It sets no files and no parser, so on its own ESLint cannot read TypeScript. Give it both:

// eslint.config.js
import tsParser from '@typescript-eslint/parser';
import codeQuality from '@noctcore/eslint-plugin-code-quality';

export default [
  {
    ...codeQuality.configs.recommended,
    files: ['**/*.{ts,tsx}'],
    languageOptions: { parser: tsParser },
  },
];

If your config already sets the parser for those files (through typescript-eslint's configs, for example), spreading the preset alongside it is enough.

Where to start

Start with the three plugins that fit almost any TypeScript codebase, then add the ones for the stack you use:

Add When
code-quality, async-safety, contracts Always: the starter set
security You run server code (shell, HTTP, redirects, HTML rendering)
react You write React components
rsc You use the Next.js App Router / React Server Components
prisma You use Prisma (several rules need your tenant models; see its README)
llm You call an LLM SDK
observability You log through a structured logger
architecture, monorepo You want folder and package boundaries enforced (monorepo needs your scope)

One config that spreads several presets. Each preset is its own entry with the same files and parser; they do not merge, and the plugin namespaces (noctcore-<plugin>) never collide:

// eslint.config.js
import tsParser from '@typescript-eslint/parser';
import asyncSafety from '@noctcore/eslint-plugin-async-safety';
import codeQuality from '@noctcore/eslint-plugin-code-quality';
import contracts from '@noctcore/eslint-plugin-contracts';
import react from '@noctcore/eslint-plugin-react';
import security from '@noctcore/eslint-plugin-security';

const typescript = { files: ['**/*.{ts,tsx}'], languageOptions: { parser: tsParser } };

export default [
  // The starter set.
  { ...codeQuality.configs.recommended, ...typescript },
  { ...asyncSafety.configs.recommended, ...typescript },
  { ...contracts.configs.recommended, ...typescript },
  // Add the ones for your stack.
  { ...security.configs.recommended, ...typescript },
  { ...react.configs.recommended, ...typescript },
];

Every preset rule is error, never warn. In a codebase that already has hundreds of hits, read adopting in an existing codebase first: it shows how to switch the flooding rules off and turn them back on one directory at a time. Rules that need a per-project fact (a scope, a model list, an action-client name) are opt-in; each package README lists them under "Opt-in rules", with the config that turns them on.

Reporting a problem

A rule that reports correct code, or misses code it documents as bad, is the report that matters most for plugins that run at error. The issue forms ask for the rule id, the code, the options and the versions, which is what makes one actionable. There is a form for rule proposals too; it asks what the rule must not flag, because that is the question that decides whether it can ship.

Develop

bun install
bun run build       # first, always: typecheck resolves @noctcore/eslint-utils through its built d.ts
bun run typecheck
bun run test        # every package, on ESLint 10 and then on ESLint 9
bun run docs:dev    # the docs site (site/), at http://localhost:4321/eslint-plugins/

CONTRIBUTING.md has the rest: where a rule lives, the severity policy, the executable rule docs, the generated README tables, and what a changeset means here. Read it before adding a rule.

Releasing

Changesets, two-phase: add a changeset (bun run changeset), merge, the bot opens a "Version Packages" PR, and merging that publishes the touched packages to npm with provenance.

License

MIT

About

General-purpose ESLint plugins for architecture and correctness conventions that generic linters can't see.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages