@leandromatos/eslint-config
v3.0.2
Published
ESLint flat configuration with TypeScript, React, import sorting, JSON, and Markdown support.
Readme
ESLint Config
Personal ESLint configuration: flat config, type-aware, and ESM, covering TypeScript, React, import sorting, JSON, and Markdown.
✨ Features
- One config, every project — a single source of truth for ESLint rules, so linting never drifts between repositories.
- Type-aware out of the box — TypeScript rules that read the type-checker through
projectService, catching what syntactic analysis cannot: floating promises, unsafe returns, deprecated APIs. - Native flat config, ESM — no
FlatCompat, no compat shims, no build step. The publishedindex.jsis the config. - Formatting is Prettier's job — the config never formats; it turns off the style rules that would fight the formatter and leaves the rest to Prettier. Pairs with @leandromatos/prettier-config.
- Absolute, sorted imports —
import-xblocks relative parent imports;simple-import-sortkeeps imports and exports ordered. - Batteries for React, JSON, and Markdown — React and Hooks rules on
.tsx, structural linting for JSON, JSONC, and Markdown.
🧭 How It Works
ESLint resolves the package to the array of flat config objects it exports, and you spread that array into your own eslint.config.mjs. Each object targets a file type: base JS and TS rules on all source, type-aware rules on .ts and .tsx, React rules on .tsx, and structural rules on JSON and Markdown. eslint-config-prettier comes last and switches off every rule that would fight the formatter.
The type-aware layer uses typescript-eslint's projectService, which finds the nearest tsconfig.json on its own. So the type-checked rules need a tsconfig.json in your project; without one, the parser has no types to read. typescript is a peer dependency for this reason, even in mostly-JavaScript projects.
Formatting is deliberately absent. The config never runs Prettier; it only disables the ESLint rules that overlap with it. You run Prettier separately — in lint-staged, your editor, or CI — to format, and ESLint to catch defects.
📦 Installation
Install ESLint, TypeScript, and the config as dev dependencies:
yarn add --dev eslint typescript @leandromatos/eslint-configeslint >= 10 and typescript >= 5 are peer dependencies, so you bring your own.
🚀 Quick Start
Create an eslint.config.mjs:
import config from '@leandromatos/eslint-config'
export default configThe type-aware rules need a tsconfig.json at your project root. Then run ESLint as usual:
yarn eslint .For formatting, pair it with @leandromatos/prettier-config and run Prettier on its own.
Editor and lint-staged setup
Because formatting lives in Prettier and not ESLint, your editor and your pre-commit hook need both tools: Prettier to format, ESLint to fix defects. Skip this and formatting stops happening on save: eslint --fix does not format, so if Prettier is not wired up, nothing does. Nothing errors; the code just silently stops being formatted.
VSCode, with the Prettier and ESLint extensions (.vscode/settings.json) — format with Prettier on save, and run ESLint's fixes as a separate action:
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
}Setting defaultFormatter explicitly also avoids a real conflict: if another formatter runs on save, it formats differently from prettier-config and the two fight over the file.
lint-staged (lint-staged.config.mjs) — Prettier writes first, then ESLint fixes:
export default {
'*.{js,jsx,ts,tsx,mjs,cjs,mts,cts}': ['prettier --write', 'eslint --fix'],
'*.{json,jsonc,json5}': ['prettier --write', 'eslint --fix'],
'*.{yml,yaml}': ['prettier --write'],
'*.md': ['prettier --write', 'eslint --fix'],
}Running Prettier first and ESLint second keeps them out of each other's way: Prettier owns layout, ESLint owns everything else.
🧩 What's Included
The config is a stack of flat config objects, applied by file type:
| Layer | Files | What it does |
| ------------ | ---------------------------------- | ----------------------------------------------------------------------------------- |
| Base | .{js,jsx,mjs,cjs,ts,tsx,mts,cts} | @eslint/js recommended plus custom rules, import-x, simple-import-sort |
| Type-checked | .{ts,tsx,mts,cts} | typescript-eslint recommendedTypeChecked with projectService |
| React | .tsx | eslint-plugin-react and eslint-plugin-react-hooks flat configs, plus jsx-a11y |
| JSON | .{json,jsonc,json5} | eslint-plugin-jsonc recommended |
| Markdown | .md | @eslint/markdown structural rules |
| Prettier | all | eslint-config-prettier disables style rules, applied last |
The exact rules each layer sets are in index.js.
⚙️ Configuration
The config is an array; override or extend by appending your own flat config objects after it. Objects later in the array win:
import config from '@leandromatos/eslint-config'
export default [
...config,
{
files: ['**/*.test.{ts,tsx}'],
rules: {
'no-console': 'off',
},
},
]🏷️ Versioning
Semver, published to npm. Peers are eslint >= 10 and typescript >= 5; an ESLint major that changes the flat config API ships as a major here too. Snapshots publish to the snapshot dist-tag as X.Y.Z-snapshot.YYYYMMDD.N; stable releases go to latest.
🤝 Contributing
This repository follows Conventional Commits. See CONTRIBUTING.md for the workflow, releases, and local setup.
📄 License
This software is free and open source, released by Leandro Matos under the MIT License. See the LICENSE file for the full terms.
