Why and how to use ESLint in your project

Npmjs.org has hundreds of thousands of packages, but they aren’t all of equal quality. It’s important to check how well managed your direct dependencies are. No single missing management practice should eliminate a package from your consideration if the features are right, but when you have a choice of packages, pick ones that are well managed — or be prepared to maintain the package yourself!

One practice that I use to evaluate projects is JavaScript linting.

Why lint your JavaScript?

Well run projects have clear, consistent coding conventions with automated enforcement. When I review a project and its code looks like a house built by a child using nothing but a hatchet and a picture of a house, it doesn’t inspire confidence that the code is functional.

Not having coding conventions is also a barrier to attracting contributions. Depending on a project that does not welcome (quality!) contributions is itself a risk.

Besides checking style, linters are also excellent tools for finding certain classes of bugs, such as those related to variable scope. Assignment to undeclared variables (these leak into the global scope, contaminating it and possibly causing very difficult to find bugs) and use of undefined variables are examples of errors that are detectable at lint time.

How to configure ESLint

ESLint is the dominant tool for linting Node.js packages, and can be configured to enforce multiple coding styles. It is possible to define your own style definitions, but here I will show how to use the StrongLoop style. There are others, but StrongLoop’s style is unremarkable (a good thing, coding style should not attract attention), and is similar to that used in many open-source Node.js projects.

  1. Install and save package dependencies: npm install --save-dev eslint eslint-config-strongloop.

  2. Set up ESLint to use the StrongLoop configuration by running echo '{"extends": "strongloop"}' > .eslintrc.json.

  3. Ensure you have a .gitignore file (so derived files do not get linted, only original source files). If you don’t have one, you can create one with minimal effort: echo node_modules/ >> .gitignore.

    Note that is also possible to use an ESLint-specific .eslintignore file, that has the same syntax as .gitignore, and likely the same contents. To avoid this maintenance burden, most projects use just a .gitignore.

  4. With this setup, configure ESLint to run automatically before your tests by changing your package.json to have a pretest script. It should look similar to this:

     {
         ...
         "scripts": {
             "pretest": "eslint --ignore-path .gitignore ."
         }
         ...
     }
    

    The exact contents of your package.json depend on your project. You have to add the pretest script to cause ESLint to run before your unit tests. When you you use npm to run the test script, it will also run the pretest and posttest scripts if they exist.

    I prefer this, because eslint usually runs much more quickly than my tests, and lint errors are easy to fix, but some people prefer the entire test suite to run before the linter, in which case, use posttest.

  5. Commit the ESLint automation support:

     git add package.json .gitignore .eslintrc.json
     git commit -m 'Add eslint automation'
    
  6. Once this is complete, run the linter:

     npm run pretest
    

Do not get discouraged if your console is awash in a sea of errors!

How to get existing code to lint clean

One reason people avoid using ESLint is that cleaning up never-before-linted code can feel like cleaning the Augean stables. I recommend doing as Hercules did: get help from tools.

  1. ESLint can automatically fix many syntactic problems automatically. This should be the first tool you use to clean up your source:

    ./node_modules/.bin/eslint --fix --ignore-path .gitignore .

  2. If you have an ESLint pretest script, you can also do:

    npm run pretest -- --fix

  3. There are certain classes of problems that ESLint will not fix, however, in which case you can do a one-time cleanup using prettier. prettier is most commonly used as an alternative to ESLint and auto-formats source before it is committed. It is also quite useful in bootstrapping ESLint. Run it like:

     npm i prettier
     ./node_modules/.bin/prettier --single-quote --trailing-comma es5 --print-width 80 --write --no-bracket-spacing **/*.js
    

    After running eslint --fix and prettier, you should have very few remaining warnings to clean up manually. While prettier isn’t as commonly used as ESLint, it can be used as a complement to ESLint if you want (prettier for auto-formatting, ESLint for format enforcement and error checking). If for some reason you don’t have the time to fix these right now, disable the ESLint rules. It is much better to have some subset of style enforced automatically than none at all. You can override some of the StrongLoop style for a specific project, and then come back and cleanup the code when you have time.

  4. Here’s an example of relaxing the max-len rule to allow run-on lines up to 120 characters wide:

     {
       "extends": "strongloop",
       "rules": {
         "max-len": [2, 120, 8]
       }
     }
    

    You may find that your code uses a consistent style, but is not StrongLoop’s style. If it is close, you can customize the StrongLoop style and publish it as your own. If your style is radically different, it could make sense to just write and publish your own reuseable configuration.

  5. Once your code lints cleanly (check with npm run pretest), commit the result:

     git commit -a -m 'Project lints clean'
    

Automate linting

There are two levels of automation: project-wide policy and your own personal setup.

In terms of project-wide policies, because ESLint is configured to run with your tests, there is nothing more to do. Unless you don’t run your tests automatically for your project, in which case it is time to start!

In terms of my own personal setup, I prefer ESLint to be run on everyone of my commits, so any problems I introduce are caught on my machine before they are caught by CI. I do this with a git pre-commit hook.

To set this up, use the example hook as a base:

cp .git/hooks/pre-commit.sample .git/hooks/pre-commit

The last lines of the file will look like this:

# If there are whitespace errors, print the offending file names and fail.
exec git diff-index --check --cached $against --

Change it to look like:

set -e
npm run pretest

# If there are whitespace errors, print the offending file names and fail.
exec git diff-index --check --cached $against --

Congratulations!

That’s it, you are now another user of eslint.