No-sweat, lint and format everything!
Design goal: maximize DX, minimize friction, auto-fix as much as possible! 🪄
- Yellow squiggles for most benign rules triggered while you're in the middle of writing new, unfinished code, leaving the red squiggles for the important issues needing your attention
- Lints your code with ESLint and formats it with Prettier
- Supports JS, TS, Vue, JSX, JSON, YAML, Markdown, TailwindCSS, Node.js, Vitest, Jest, and more
- Infers eslintignore list from your
.gitignore
by default - Spaces, single quotes, no semi, dangling commas, sorted imports
- Auto-fix on
CTRL + S
and ongit commit
- Auto-add missing imports on save (and remove unused ones)
- Reasonable defaults, best practices, simple setup, single dep install
- Code style principle: Minimal for reading, stable for diff, consistent, safe, strict
- Based on
@antfu/eslint-config
npm i -D @maninak/eslint-config
ESM
If you're in a repo using "type": "module"
then create an eslint.config.js
with the following contents:
import maninak from '@maninak/eslint-config'
export default maninak({
typescript: { tsconfigPath: 'tsconfig.json' },
})
CJS
Note
Not supported yet. 🙆♂️
If you are still using ESLint's legacy config format it is strongly suggested that you migrate to the their new flat config.
Show more
The base package team provides an experimental CLI tool to help with the migration.
Commit any unsaved changes and then run:
npx @antfu/eslint-config@latest
// eslint.config.js
import maninak from '@maninak/eslint-config'
import { FlatCompat } from '@eslint/eslintrc'
module.exports = maninak(
{
typescript: { tsconfigPath: 'tsconfig.json' },
},
// Legacy config example
...new FlatCompat().config({
extends: [
'eslint:recommended',
// Other extends...
],
overrides: [
{
files: ['*.vue'],
extends: ['plugin:vue-scoped-css/vue3-recommended'],
parser: 'vue-eslint-parser',
parserOptions: { parser: '@typescript-eslint/parser' },
rules: { 'vue-scoped-css/no-deprecated-v-enter-v-leave-class': 'error' },
},
]
}),
// Other flat ESLint configs...
)
[!IMPORTANT]
.eslintignore
no longer works in the new flat ESLint config. Useignores
(flat config) orexcludedFiles
(legacy config).
It is strongly suggested that you apply all recipes.
To lint all files on command, ideal also to run in your CI, merge this into to your package.json
:
{
"scripts": {
"lint": "eslint . --max-warnings 0 --no-warn-ignored --cache --cache-location node_modules/.cache/eslint",
}
}
Tip
To lint and auto-fix all files in your repo run:
npm run lint -- --fix
To automatically lint and auto-fix (only) all staged files before every commit, add the following to your package.json
:
{
"lint-staged": {
"*": "eslint --fix --max-warnings 0 --no-warn-ignored --cache --cache-location node_modules/.cache/eslint"
},
"simple-git-hooks": {
"pre-commit": "npx lint-staged"
},
}
Important
Make sure to follow these steps if you are migrating from husky
To get in-editor squiggles, auto-fix, auto-import and more follow the next steps.
Install VS Code ESLint extension.
Add the following settings to your .vscode/settings.json
:
{
/* eslint-disable jsonc/sort-keys */
"git.inputValidationSubjectLength": 72,
"eslint.experimental.useFlatConfig": true,
// Disable other linters/formatters, use eslint instead
"prettier.enable": false,
"editor.formatOnSave": false,
"tailwindCSS.validate": false,
// Auto fix eslint issues on save
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.organizeImports": "never"
},
// Enable eslint for all supported languages
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"vue",
"html",
"markdown",
"json",
"jsonc",
"yaml",
],
}
Add the following to your .gitattributes
:
* text=auto eol=lf
Add the following to your .editorconfig
:
# editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
Consider adding the following to your tsconfig.json
and fixing any issues that pop up (or comment out hard-to-fix options):
{
"compilerOptions": {
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"moduleDetection": "force",
"noFallthroughCasesInSwitch": true,
"noPropertyAccessFromIndexSignature": true,
"noUncheckedIndexedAccess": true,
"skipLibCheck": true,
"strict": true
},
}
Certain rules only get enabled in specific files, for example, ts/*
rules only get enabled in .ts
files and vue/*
rules only in .vue
files. If you want to override the rules, you need to specify the file extension:
// eslint.config.js
import maninak from '@maninak/eslint-config'
export default maninak(
{ vue: true, typescript: true },
{
// Without `files` specified, these are general rules for all files
rules: {
'style/semi': ['error', 'never'],
},
}
{
// Remember to specify the file glob as done here, otherwise thise vue rule will try to run on non-vue files too
files: ['**/*.vue'],
rules: {
'vue/operator-linebreak': ['error', 'before'],
},
},
)
There's also an overrides
property in the first param for ease of use:
// eslint.config.js
import maninak from '@maninak/eslint-config'
export default maninak({
overrides: {
vue: {
'vue/operator-linebreak': ['error', 'before'],
},
typescript: {
'ts/consistent-type-definitions': ['error', 'interface'],
},
// ...
}
})
The config also provides some optional plugins/rules for extended usages.
The plugin eslint-plugin-perfectionist
allows you to sorted object keys, imports, etc, with auto-fix. It's already installed for you but no rules are enabled by default.
It's recommended to opt-in on each file individually using configuration comments.
/* eslint perfectionist/sort-objects: "error" */
const objectWantedToSort = {
a: 2,
b: 1,
c: 3,
}
/* eslint perfectionist/sort-objects: "off" */
Since flat ESLint config requires us to explicitly provide the plugin names (instead of mandatory convention from npm package name), we renamed some plugins to make the overall scope more consistent and easier to write.
New Prefix | Original Prefix | Source Plugin |
---|---|---|
import/* |
i/* |
eslint-plugin-i |
node/* |
n/* |
eslint-plugin-n |
yaml/* |
yml/* |
eslint-plugin-yml |
ts/* |
@typescript-eslint/* |
@typescript-eslint/eslint-plugin |
test/* |
vitest/* |
eslint-plugin-vitest |
test/* |
no-only-tests/* |
eslint-plugin-no-only-tests |
When you want to override rules or disable them inline, you need to update to the new prefix:
-// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
+// eslint-disable-next-line ts/consistent-type-definitions
type foo = { bar: 2 }
This project follows Semantic Versioning for releases. However, since this is just a config and involved with opinions and many moving parts, we don't treat rules changes as breaking changes.
- Node.js version requirement changes
- Huge refactors that might break the config
- Plugins made major changes that might break the config
- Changes that might affect most of the codebases
- Enable/disable rules and plugins (that might become stricter)
- Rules options changes
- Version bumps of dependencies
Awesome! Please open an Issue with a reproduction code snippet or consider opening a PR to patch it.
Sure, you can config and override rules locally in your project to fit your needs. If that still does not work for you, you can always fork this repo and maintain your own.
- maninak/ts-xor - Compose custom types containing mutually exclusive keys
🫶 Follow me on X.
MIT License © 2019-PRESENT Kostis Maninakis