Getting started
All eleven ESLint plugins share one shape. Requirements: ESLint 9 or newer, flat config only
(eslint.config.js), Node 22 or newer. TypeScript sources are parsed with @typescript-eslint/parser,
which you almost certainly have already.
Where to start
Section titled “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) |
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) |
Each preset is its own config entry with the same files and parser. They do not merge, and the
plugin namespaces (noctcore-<plugin>) never collide:
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 },];The steps below take one plugin at a time and explain each piece.
1. Install the plugin you need
Section titled “1. Install the plugin you need”npm install --save-dev @noctcore/eslint-plugin-react# bun add -D / pnpm add -D work the same way2. Enable its recommended preset
Section titled “2. Enable its recommended preset”Every plugin exports configs.recommended, a complete flat-config block that registers the plugin
under its namespace (noctcore-<plugin>) and sets each rule’s severity.
import react from '@noctcore/eslint-plugin-react';import asyncSafety from '@noctcore/eslint-plugin-async-safety';
export default [ // ...your existing config react.configs.recommended, asyncSafety.configs.recommended,];The preset sets no files and no parser
Section titled “The preset sets no files and no parser”configs.recommended registers the plugin and sets rule severities, nothing else. It has no files
pattern, so it applies to whatever files the rest of your config lints, and no parser, so on its own
ESLint cannot read TypeScript. For .ts and .tsx files, give the preset both:
import tsParser from '@typescript-eslint/parser';import react from '@noctcore/eslint-plugin-react';
export default [ { ...react.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.
3. Turn on the rules that need your project’s knowledge
Section titled “3. Turn on the rules that need your project’s knowledge”Some rules cannot do anything useful until they know something about your codebase: your workspace
scope, your tenant-scoped models, your translation catalogs. Those ship off or are left out of the
preset, because a rule that reports nothing reads as coverage you do not have. Each package page’s rule
table shows which ones, and each rule page documents its options.
export default [ monorepo.configs.recommended, { rules: { 'noctcore-monorepo/no-deep-package-imports': ['error', { scopes: ['@acme'] }], }, },];4. One rule needs type information
Section titled “4. One rule needs type information”noctcore-prisma/mutation-entry-must-reach-audit follows calls across files, so it needs a
type-checked program. Linting it without one is an error, not a silent pass. One more rule,
noctcore-contracts/translation-key-exists, runs without type information and uses it when present
(to resolve a namespace from an identifier’s type). Every other rule is syntactic. The Types
column of each package’s rule table, and the all-rules index, say which is
which.
export default [ { languageOptions: { parserOptions: { projectService: true } }, },];Severity policy
Section titled “Severity policy”Every rule in every preset is error or off. A warning is a rule nobody obeys: if a rule matters it
fails the build, and if it does not fit your codebase you switch it off. The
no-warn-severity lint-meta rule exists to
hold a repo to the same policy. Turning a preset on in a codebase that already has hundreds of hits
is its own page: adopting in an existing codebase.
Where the error links go
Section titled “Where the error links go”Every rule’s meta.docs.url points at its page on this site, so the link your editor or CI shows next to
a report lands on the rendered rule doc.