Skip to content

A batteries-included linter and formater for maximum DX and minimum friction. Supports JS, TS, Vue, JSX, and more.

License

Notifications You must be signed in to change notification settings

maninak/eslint-config

Repository files navigation

@maninak/eslint-config

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 on git 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

Usage

Install

npm i -D @maninak/eslint-config

Create config file

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. 🙆‍♂️

Migration

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

Automated

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

Combining flat and legacy config formats

// 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. Use ignores (flat config) or excludedFiles (legacy config).

Suggested recipes

It is strongly suggested that you apply all recipes.

Package.json script

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

Auto-lint changed files on git commit

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

VS Code Support

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",
  ],
}

Line-break consistency between Linux/Mac and Windows

Add the following to your .gitattributes:

* text=auto eol=lf

Cross-editor Support

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

Stricter TypeScript checks

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
  },
}

Configuration

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'],
    },
    // ...
  }
})

Optional Extra Rules

The config also provides some optional plugins/rules for extended usages.

perfectionist (sorting)

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" */

Plugins Renaming

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 }

Versioning Policy

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.

Changes Considered 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

Changes Considered as Non-breaking Changes

  • Enable/disable rules and plugins (that might become stricter)
  • Rules options changes
  • Version bumps of dependencies

FAQ

I found a rule that shows a red squiggle but should have yellow instead (or any other problem/idea)

Awesome! Please open an Issue with a reproduction code snippet or consider opening a PR to patch it.

I prefer XYZ...

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.

Check Also

  • maninak/ts-xor - Compose custom types containing mutually exclusive keys

🫶 Follow me on X.

License

MIT License © 2019-PRESENT Kostis Maninakis

About

A batteries-included linter and formater for maximum DX and minimum friction. Supports JS, TS, Vue, JSX, and more.

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project