Create a reusable ESLint config

Define your style guide using the popular linter

Why do I need a custom ESLint config?

A natural tendency after coding for a while is gravitating towards specific habits and conventions. If you participate in collaborative projects, there is a high chance that a specific style is enforced via code reviews or automated checks. Either implicitly or explicitly, you most probably already adhere to a style guide.

There are two main aspects of a style guide: coding practices and formatting. In the JavaScript ecosystem, two popular tools to define and check those are ESLint (linter) and Prettier (formatter).

Why do I need a linter?

There is a variety of syntax issues that will not come up with regular syntax highlighting, but a linter can catch: misspelling a variable, defining the same const twice etc. Those errors are easy to make and they will cause runtime errors. Essentially, a linter serves as static tests for your project (see Kent C. Dodds Testing Trophy). Plus, you can define all sorts of preferable/unwanted practices linter rules, and make sure they are enforced across your project.

Why do I need a formatter?

A formatter takes care of maintaining a consistent style and ensuring that your code is elegant. Its deterministic approach is especially beneficial in collaborative projects, resulting in less time deciding on the exact style and a more predictable code structure.

Usage in projects

After you have decided on a specific style, there comes the chore of ensuring it is used correctly in your projects. Moreover, preferences change, which means updating multiple projects accordingly.

One approach would be following the same steps in each and every project to result in the same linter/formatter configuration: install packages, enable/disable rules etc. Luckily, there is a better way! πŸŽ‰ You can define your style guide as a shareable ESLint config that can be installed and configured for each project in a few simple steps. We will see how in the following section.

Creating an ESLint config

Let's demonstrate a simple example of defining our style guide as a shareable ESLint config. It is based on my personal configs which are available in GitHub:

kael89/eslint-config-kael89: JavaScript style guide
kael89/eslint-config-kael89-ts: TypeScript style guide

Step 1: Package structure scaffolding

  1. Create a new npm package. By convention, it should start with `eslint-config-`:

    mkdir eslint-config-example
    cd eslint-config-example
    npm init -y
  2. Create an index.js file under the project's root. We will define our rules here.

  3. Create a .gitignore file with the following contents:

    node_modules
  4. (Optional) It is often a good idea to do some housekeeping in your package:

    • Add a README.md file
    • Add a LICENSE file
    • Update the default package.json fields (keywords, author, license etc)

Step 2: Pick a base config (optional)

This step is optional but I find it very convenient. Instead of having to define an extensive set of rules, you can pick an existing config and adjust it to your needs.

In this example, we will use the popular Airbnb Style Guide as our base. The following command installs the airbnb config package as well as its plugin dependencies:

npm install eslint-plugin-import eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-jsx-a11y

Step 3: Install additional plugins

There is a multitude of great ESLint plugins out there. In this example, we'll add eslint-plugin-import, which enables linting of import statements:

npm install eslint-plugin-import

Step 4: Install Prettier for ESlint (optional)

Again, this is an optional but recommended step. Prettier is a highly opinionated code formatter. This seems to be its basic strength: it works out of the box and results in an arguably elegant formatting.

npm install eslint-config-prettier eslint-plugin-prettier

Step 5: Specify peer dependencies

Our package includes all the required packages as dependencies, except for the most basic:

  • eslint
  • prettier (if you followed Step 4)

We will specify those as peer dependencies instead. The reason is that they are not strictly required by the plugins, but rather the other way around:, eslint and prettier are "host' packages which will load our config/plugins. I suggest reading this npm blog post for a more detailed explanation.

This is also an effective way to specify the supported host package versions. An example:

{
"peerDependencies": {
"eslint": "^6 || ^7.2.0",
"prettier": ">= 1.13"
}
}

Step 6: Configure ESLint

And now time for the real action! Everything comes together in the index.js file, where we will specify our selected configs, rules and plugins.

Each config file will be uniquely suited to your needs and taste. For our example:

module.exports = {
// eslint-configs
extends: ['airbnb', 'plugin:prettier/recommended'],
// eslint-plugins
plugins: ['simple-import-sort'],
// enabling/disabling/changing level of rules
rules: {
'class-methods-use-this': 'off',
'import/order': 'warn',
'import/prefer-default-export': 'off',
'no-plusplus': 'off',
'no-restricted-globals': 'off',
'prettier/prettier': [
'error',
// configure Prettier for ESLint
{
arrowParens: 'avoid',
printWidth: 100,
singleQuote: true,
trailingComma: 'all',
},
],
radix: 'off',
},
};

We have plenty of options here: luckily, the ESLint documentation is very helpful. A few key points:

  • A config/plugin will not be loaded by ESLint unless if you specify it here!
  • The precedence of items in extends and plugins follows the order they are listed. This is important in case some items conflict each other
  • A rule can be either specified as
    • a string/number indicating its level: 'off' | 'warn' | 'error', 0 | 1 | 2
    • an array of [level, options] where options is used to configure the rule. You can look at each rule's documentation for the available options, for example: import/order options

Using our ESLint configuration

Publishing our package

Our style guide should be now ready for consumption! Before we add it in our superCoolProject, there is one last step: we need to publish it. We have two main options:

  • npm: publish your package, then simply install it running

    npm install {{packageName}}
  • a git repository: GitHub, GitLab etc. Publish your package, then

    npm install git+{{gitUrl}}

    # Example:
    npm install git+https://github.com/kael89/eslint-config-kael89.git

Don't forget to update your package's version with each change, which will allow you to pick up any updates in your projects.

Installing our package

  1. Install the peer dependencies as devDependencies in our project:

    npm install -D eslint prettier
  2. Install our config. For npm packages:

    npm install {{packageName}}

    For git repositories:

    npm install git+{{gitUrl}}

    # Example:
    npm install git+https://github.com/organisation/packageName.git
  3. Configure ESLint to use our package. There are many ways to do so, the simplest probably is adding an eslintConfig field in our project's package.json file:

    {
    "extends": "{{configName}}"
    }

    πŸ€“Tip: We can omit the eslint-config- prefix in our package's name. Eg if our package is called eslint-config-example:

    {
    "extends": "example"
    }

Next steps

Rules breaking between version updates is not at all uncommon. If your config is used by multiple users, it may be a good idea to add some tests to ensure there is no regression when you update it.

You may prefer to publish a personal style guide just on your GitHub account, and not in the npm registry. We will describe how to maintain a versioned node package in GitHub in a future article.

Until then, rock the code with your tailor-made ESLint config! πŸ€˜πŸ€“