@vetwo/eslint-config
v1.0.0
Published
Shareable, strongly-typed ESLint Flat Config presets for TypeScript, Node, React, Next.js and Vitest projects.
Maintainers
Readme
@vetwo/eslint-config
Strongly-typed, composable ESLint Flat Config presets for TypeScript, Node.js, React, Next.js and Vitest — zero config to start, fully overridable when you need it.
Why
ESLint v9's Flat Config is powerful but verbose to hand-roll for every repo. This package gives you one high-quality default and a small set of composable presets, instead of a single monolithic "extends" array you can't reason about.
Features
- Zero-config default (
defineConfig()) suitable for a professional TypeScript library - Composable presets:
node(),react(),next(),vitest(),turbo(),typedoc(),library(),strict() - Fully typed public API (
FlatConfig,FlatConfigItem,ConfigInput, …) - Optional type-aware linting via
tsconfigPath(floating promises, exhaustiveness checks) — opt-in, since it requires a validtsconfig.json - Tree-shakeable, zero runtime side effects, dual ESM/CJS build
- Works with npm, pnpm, yarn, and Bun; standalone repos and monorepos (Turborepo, Nx)
Installation
# pnpm
pnpm add -D @vetwo/eslint-config eslint typescript
# npm
npm install -D @vetwo/eslint-config eslint typescript
# yarn
yarn add -D @vetwo/eslint-config eslint typescript
# bun
bun add -D @vetwo/eslint-config eslint typescriptReact/Next/Vitest presets need their respective plugins as well, since they're optional peers:
pnpm add -D eslint-plugin-react eslint-plugin-react-hooks # react()/next()
pnpm add -D @next/eslint-plugin-next # next()
pnpm add -D eslint-plugin-vitest # vitest()Quick start
eslint.config.mjs (or .js if "type": "module" is set):
import { defineConfig } from "@vetwo/eslint-config";
export default defineConfig();That's it — TypeScript, JavaScript, import hygiene, unused imports, promise safety, and a curated Unicorn subset are all wired up.
Composing presets
import { defineConfig, node, react, vitest } from "@vetwo/eslint-config";
export default defineConfig(node(), react(), vitest());Falsy values are dropped, so conditional composition reads naturally:
export default defineConfig(
node(),
process.env.WITH_REACT && react()
);Type-aware linting
Rules that need type information (no-floating-promises, switch-exhaustiveness-check, prefer-nullish-coalescing, …) are opt-in via tsconfigPath, since enabling them without a valid project throws at lint time:
import { defineConfig, typescript } from "@vetwo/eslint-config";
export default defineConfig(typescript({ tsconfigPath: "./tsconfig.json" }));Overriding rules
Any Flat Config entry — including a bare { rules: {...} } object — can be appended after a preset. Later entries win via ESLint's own cascade:
export default defineConfig(node(), {
rules: { "no-console": "off" }
});Every preset also accepts overrides and files directly:
export default defineConfig(node({ overrides: { "n/no-process-exit": "off" } }));Presets
| Preset | Purpose |
| --- | --- |
| recommended() | Default baseline (included automatically by defineConfig()) |
| strict() | recommended() + stricter TypeScript rules |
| library() | Forbids console/debugger, disallows reaching outside src/ |
| node() | Node.js runtime correctness (eslint-plugin-n) |
| react() | React + Rules of Hooks |
| next() | react() + Next.js core-web-vitals rules |
| vitest() | Vitest rules scoped to test file globs |
| turbo() | Turborepo-friendly adjustments |
| typedoc() | Avoids false positives on TSDoc comment blocks |
| ignore(patterns) | A single global-ignores entry |
Monorepo usage (Turborepo / Nx)
Each package/app gets its own eslint.config.mjs composing only what it needs:
// apps/web/eslint.config.mjs
import { defineConfig, next, turbo } from "@vetwo/eslint-config";
export default defineConfig(next(), turbo());// packages/utils/eslint.config.mjs
import { defineConfig, node, library } from "@vetwo/eslint-config";
export default defineConfig(node(), library());API
See API.md for the full reference, or run pnpm typedoc to generate browsable docs from the TSDoc comments in src/.
Migrating from .eslintrc
Flat Config has no extends chain — presets are plain arrays you compose explicitly with defineConfig(...) rather than string names in an extends array. There's no plugin-name-to-package mapping to remember either, since presets import and register their own plugins.
Contributing
See CONTRIBUTING.md.
License
MIT — see LICENSE.
