@niko464/eslint-config
v0.1.0
Published
Shared ESLint flat-config blocks: a comment ban, an effect-hook ban and @shadcn/lint
Readme
@niko464/eslint-config
Shared ESLint flat-config blocks for Niko464's TypeScript projects. Each export is an array of config objects. Spread the ones a project needs into its own eslint.config.*.
| Block | For | What it turns on |
| -------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| base | every TypeScript project | @niko464/no-comments, and linterOptions.noInlineConfig: true, so no eslint-disable or inline rule comment has any effect |
| react | React projects | @niko464/no-effect-hooks: bans useEffect, useLayoutEffect and useInsertionEffect from react, however they are imported |
| shadcn | Tailwind v4 + shadcn/ui projects | @shadcn/lint with all six rules as errors: no-restyle, no-raw-colors, no-arbitrary-values, no-inline-styles, no-unknown-classes, require-static-classes |
@niko464/no-comments reports every //, /* */ and /** */ comment except the tool directives: the shebang, /// <reference, @ts-expect-error and prettier-ignore. It has no autofix: what a comment says belongs in a name, a type, a test, docs/ or an ADR, and only a person or an agent can move it there.
@niko464/no-effect-hooks leaves useEffectEvent, useSyncExternalStore and ref callbacks allowed. Its error names the replacements: useMountEffect, useEventListener or useInterval from @niko464/react-kit, a key, a ref callback, deriving the value during render, or Motion for animation. A case none of these covers becomes a new hook in @niko464/react-kit, through a PR.
Install
pnpm add -D @niko464/eslint-config eslint typescript-eslintESLint 9.30 or later is required, and @typescript-eslint/parser 8.40 or later (it comes with typescript-eslint).
Use
import { base, react, shadcn } from '@niko464/eslint-config';
import tseslint from 'typescript-eslint';
export default [...tseslint.configs.recommended, ...base, ...react, ...shadcn];A project without React spreads only base. A project without Tailwind or shadcn/ui leaves out shadcn.
In a monorepo
defineConfig's extends scopes a block to part of the repo:
import js from '@eslint/js';
import { base, react, shadcn } from '@niko464/eslint-config';
import { defineConfig } from 'eslint/config';
import tseslint from 'typescript-eslint';
export default defineConfig(
{ ignores: ['dist/'] },
js.configs.recommended,
tseslint.configs.recommended,
{
files: ['apps/api/**/*.ts'],
rules: { 'no-restricted-imports': ['error', { patterns: ['@prisma/client'] }] },
},
base,
{ files: ['apps/web/**/*.{ts,tsx}'], extends: [react, shadcn] },
{ files: ['apps/web/components/ui/**'], rules: { 'shadcn/no-restyle': 'off' } },
);- The bans have their own rule IDs, so a project's own
no-restricted-importsorno-restricted-syntaxsits next to them without replacing them. - With inline config off, every exception lives in
eslint.config.*as afiles-scoped block, where review sees it.
