Menu Search
Jump to the content X X
Smashing Conf San Francisco

We use ad-blockers as well, you know. We gotta keep those servers running though. Did you know that we publish useful books and run friendly conferences — crafted for pros like yourself? E.g. upcoming SmashingConf San Francisco, dedicated to smart front-end techniques and design patterns.

ESLint: The Next-Generation JavaScript Linter

It was the summer of 2013 and I was working on a project for my employer, Box. I had just finished wiring up JSDoc1 as a nightly build using a plugin to detect T32 patterns in our code and document them automatically. It occurred to me that these patterns might be easy to get wrong, and I started looking for a way to automatically detect incorrect patterns. I immediately turned to JSHint3 because we were already using it and I thought it could support plugins. Unfortunately, it could not.

Still, I couldn’t get the idea of a linter with pluggable runtime rules out of my head. I had just spent a bunch of time learning about Esprima4 and abstract syntax trees (ASTs), and I thought to myself, “It can’t be all that hard to create a pluggable JavaScript linter using an AST.” It was from those initial thoughts that ESLint5 was born.

Note: The “ES” in “ESLint” stands for “ECMAScript”, the name for the core of the JavaScript language. This term has become more popular thanks to ECMAScript 6.

Meet ESLint347, a tool that allows you to automatically detect incorrect patterns in JavaScript.

Legacy Problems Link

I had made a couple small contributions to JSHint over the years, and had also co-created CSS Lint8, so I had a decent amount of experience both writing and modifying linters. There were some things about JSHint that bothered me, and we tried to address them in CSS Lint. Even so, I felt that CSS Lint wasn’t anywhere near where I’d want a modern linter to be. Across JSHint and CSS Lint, I saw some problems and decided that if I were to create a new linter then it must solve as many of these problems as possible.

A lot of the problems are artifacts of legacy: this is simply the way things have always been done. JSHint, in particular, was suffering from some of the legacy of JSLint (from which it was forked). But since I was starting from scratch, I had an opportunity to look at these problems with fresh eyes and no constraints around their solutions. The problems I was most interested in solving were:

  1. Single runtime
    Both JSHint and CSS Lint run in both Rhino and Node.js; something I initially saw as a benefit in the past quickly became a significant cost. The amount of time spent trying to abstract away the underlying JavaScript engine, as well as maintaining compatibility between the engines, is a huge source of pain and a hole into which many hours disappear on a regular basis. Not only was getting the runtime working correctly in both engines difficult, it was also difficult getting the tests to run in both engines.
  2. Disabling Rules
    One aspect of JSHint that always bothered me was how you had to figure out which rules were off and on by default. While you can turn them off, the rules have strange names and some of them have no names at all, just codes (W030, for example). This was a problem we addressed in CSS Lint by making it obvious which rules were enabled and giving rules human-readable names.
  3. Documentation
    JSHint has always been fairly sparse when it comes to documentation. JSLint had almost no documentation, so the JSHint documentation was an improvement. Still, figuring out what W030 meant was really hard. We went further with CSS Lint rules documentation9, and people seemed to appreciate the extra examples. I felt strongly that this was the direction any new linter would have to go.
  4. Configuring Rules
    Another issue I had with JSHint was how some rules had to be set to true to enable, while others had to be set to false to enable. This wasn’t really JSHint’s fault, as that strange behavior was inherited from its predecessor, JSLint. Still, even after years of using JSHint, I always had to look up which rules needed to be configured in which way.
  5. Rule Error Levels
    JSHint, like JSLint before it, forces all rules to have the same severity: error. In my experience, you often want to phase in the use of certain rules, allowing them to be set as warnings that don’t break the build and then later strictly enforcing them. CSS Lint allowed you to configure warnings and errors separately, and that ended up working very well, so I wanted ESLint to have the same capability.
  6. Write Your Own Rules
    I saw both JSHint and CSS Lint struggle with the problem of not being able to keep up with the demand for rules. There were endless debates about whether a rule was general enough to be included, and if it wasn’t, then the user was stuck. I didn’t want ESLint to be the single source of rules. I didn’t want to have those same debates, and the only way that would happen was if everyone could write their own rules. So I resolved that ESLint shouldn’t just be a tool, it should be the center of an ecosystem that allowed other developers to extend it easily.

With all of this in mind, and with the help of over 200 contributors in the past two years, ESLint has become the solid, flexible JavaScript linter I always hoped it could be.

Getting Started Link

The hardest part about incorporating a new linter in your project is getting it set up for the first time. From installation to initial configuration, it can take a significant amount of time just to get those first linting results to show up and be useful. With ESLint, the team has worked hard to make getting started as fast as possible.

You can install ESLint from npm10 by typing:

$ npm install -g eslint

This installs ESLint globally, which is useful for demonstration purposes. Many projects install ESLint locally (just remove the -g) so that it can interact with their build process.

Most linters require you to manually go through and set up configuration options before linting for the first time. This can involve digging through documentation to try to figure out which rules you want to apply. While you may want to do that eventually, ESLint can guide you through the basics of setting up your initial configuration. Switch to a directory with files you want to lint and type:

$ eslint --init

You’ll be prompted to answer some questions about the style of JavaScript you write that lets ESLint set up a proper configuration file to get started.

$ eslint --init
? What style of indentation do you use? Tabs
? What quotes do you use for strings? Double
? What line endings do you use? Unix
? Do you require semicolons? Yes
? Are you using ECMAScript 6 features? No
? Where will your code run? Browser
? Do you use JSX? No
? What format do you want your config file to be in? css
Successfully created .eslintrc file in c:\Users\Nicholas\projects\personal\tmp

Notice that you are asked if you’re using ECMAScript 6 and JSX; out of the box, ESLint supports both through language options11. In fact, ESLint was the first linter to fully support ECMAScript 6 and JSX, which has made it quite popular among those who are using React12 and webpack13.

The eslint --init process sets up an ESLint configuration file, .eslintrc, in the current directory. ESLint uses this file to determine which rules to apply when evaluating your code. Configuration files can be in JSON format or css, and we find most users prefer css.

After that, you can start linting files by passing in one or more filenames or directories:

$ eslint test.js src/

Configuration Files Link

Configuration files14 are what make ESLint so flexible. Inside your .eslintrc file, you can specify multiple settings, including:

  • Rules you want to run on files
  • Global variables that should be present in files
  • Environments in which files run
  • A base configuration to inherit
  • Plugins to load
  • Alternate parsers to use

To better understand configuration files, it’s helpful to look at an example. Here’s an example file generated from eslint --init:

    - 2
    - tab
    - 1
    - double
    - 2
    - unix
    - 2
    - always
  browser: true
extends: 'eslint:recommended'

The first section in this file is rules, which specifies rule settings15. The names indent, quotes, linebreak-style and semi all correspond to ESLint rules. Each rule is configured with an array, the first item of which is the rule severity. The rule severity is one of three values:

  • 0: disable the rule completely
  • 1: enable the rule as a warning
  • 2: enable the rule as an error

The difference between warnings and errors is that warnings will not affect the exit code of ESLint. For instance, if you have ten warnings and no errors, the exit code is still 0. Rules configured as errors will cause the exit code to be 1 if that error is present. In this way, you can enable new rules without blocking a build process by setting them as warnings. You can change the rules to be errors later on when you’re ready.

Each rule may also have several options associated with it. In the previous example, the indent rule has tab specified as an option. That tells the rule that these files should use tabs for indentation instead of spaces. Other rules have their own options, and every rule’s options are spelled out on its own documentation page16.

You can specify environments17 using the env key. Environments provide predefined global variables and, in some cases, slightly alter how the parser works. The two most popular environments are browser and node.

Perhaps the most powerful aspect of configuration files is the extends key, which allows you to inherit settings18 from one or more other configuration files. The eslint:recommended configuration is built into ESLint and contains the rules that the team recommends to avoid common errors (you can see which rules are recommended on the documentation page19). You can also inherit from a shareable configs20, which is a configuration file defined as an npm package so that it can easily be shared among projects.

Understanding the Output Link

The default formatter for ESLint output, designed by Sindre Sorhus21, is another great example of how ESLint works hard to be useful to users. Here’s some example output:

$ eslint test.js

  1:11  error    Expected linebreaks to be 'LF' but found 'CRLF'  linebreak-style
  2:1   error    Unexpected console statement                     no-console
  3:9   warning  Strings must use doublequote                     quotes

✖ 3 problems (2 errors, 1 warning)

The results for each file are separated out with a header (in this case, test.js) and then each error and warning is listed beneath with four pieces of information:

  1. The line number and column number that triggered the rule
  2. The rule severity (error or warning)
  3. The message
  4. The rule that generated the message

We’ve found that all of this information is key to helping developers understand what to fix. In JSLint and JSHint, it’s hard to know how to eliminate a message (which gave rise to the JSLint Errors22 site). With ESLint, the rule to configure is right there in the output.

ESLint also ships with other formatters23 designed to make integration with other tools easy. And because ESLint is all about extensibility, you can also create and distribute your own formatters24.

Plugins Link

As mentioned previously, one of ESLint’s original goals was to enable developers to write their own custom rules and plug them in at runtime. ESLint accomplishes this through plugins25. An ESLint plugin can contain any number of custom rules that can then be distributed and used.

For instance, eslint-plugin-react26 is a popular ESLint plugin that has additional rules specifically targeting the React library. To use eslint-plugin-react, you must first install it via npm:

$ npm install eslint-plugin-react --save-dev

Then, in your configuration file, you indicate that eslint-plugin-react should be loaded by using the plugins array. After that, you can configure individual rules inside the plugin just like you would any other ESLint rule:

  - react
  react/display-name: 2
    - 2
    - tab
    - 1
    - double
    - 2
    - unix
    - 2
    - always
  browser: true
  jsx: true
extends: 'eslint:recommended'

You can safely omit the eslint-plugin- prefix when using a plugin name in the configuration file, so just react is enough to identify the plugin. The rule react/display-name is set to be an error. The react/ prefix lets ESLint know that this rule is from a plugin rather than the core.

There are over 80 ESLint plugins27 published to npm, and many that teams use internally at their own companies. Anyone can create their own custom rules28 and the ESLint Yeoman Generator29 to guide you through the process.

Custom Parsers Link

Another way you can customize ESLint is by specifying custom parsers30. By default, ESLint uses the Espree31 parser (a fork of Esprima) that provides ECMAScript 6 and JSX support natively. However, ESLint can use any parser that generates an ESTree-compatible AST. It’s this capability that led ESLint to be the first linter to support Babel32 through the use of babel-eslint33.

The babel-eslint parser is an adapter that makes Babel output an AST format that ESLint can understand. As a result, using babel-eslint means that ESLint can understand and work with almost every experimental syntax that Babel supports (there are, of course, some compatibility issues when dealing with experimental features). To use babel-eslint, first install it:

$ npm install babel-eslint --save-dev

Then specify the parser key in your configuration file:

parser: babel-eslint
  react/display-name: 2
    - 2
    - tab
    - 1
    - double
    - 2
    - unix
    - 2
    - always
  browser: true
  jsx: true
extends: 'eslint:recommended'

When ESLint runs using this configuration file, it will swap in babel-eslint for Espree when it parses your code.

Decoupling the linter from the parser is one of the significant innovations in ESLint that has allowed us to move quickly to support a wide variety of use cases.

Linting Improvements Link

Linters have traditionally worked in the same way: figure out a list of files to lint, lint each file, then report the results. The ESLint team, however, is always looking for ways to make the linting experience more effective and efficient. Recently, the team added a couple of new features that really emphasize just how powerful ESLint is:

  • The --fix command line option tells ESLint to attempt to automatically fix as many problems as possible. Fixes are only applied when it is safe to do so, and you’ll see any problems that were left unfixed. So now instead of going back into your files to insert a missing semicolon or properly indent some code, ESLint can do it for you. This is especially useful when you first introduce ESLint into a project as it means you don’t have to manually fix every file.
  • The --cache command line options tells ESLint to keep track of files that had no problems so that future runs will only lint files that have changed. If you’re repeatedly running ESLint over a large codebase, this can save you a lot of time

Conclusion Link

ESLint347 is a JavaScript linter that has learned from our collective past of JavaScript development. Our development paradigms have moved away from walled-garden, one-size-fits-all approaches into an era of small components and composability. The ESLint team knows that JavaScript development in 2015 is a lot different from when JSLint was first released, and that no single team can ever properly account for all the different variations and desires of developers all around the world.

That’s why ESLint is committed to not only being a great linter out of the box, but also to being the center of a great and growing ecosystem of plugins, shareable configs and parsers.

(ml, og)

Footnotes Link

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  32. 32
  33. 33
  34. 34

↑ Back to top Tweet itShare on Facebook

Nicholas C. Zakas is a principal architect at Box, as well as an author and speaker. He worked at Yahoo! for almost five years, where he was front-end tech lead for the Yahoo! homepage and a contributor to the YUI library. He is the author of Maintainable JavaScript (O’Reilly, 2012), Professional JavaScript for Web Developers (Wrox, 2012), High Performance JavaScript (O’Reilly, 2010), and Professional Ajax (Wrox, 2007). Nicholas is a strong advocate for development best practices including progressive enhancement, accessibility, performance, scalability, and maintainability. He blogs regularly at and can be found on Twitter via @slicknet.

  1. 1

    I recently started using ESLint. It helps me a lot to keep my code consistent and errorfree. The available amount of rules is huge and it takes some time to set it up for own needs, but when the set up is ready, workflow becomes amazing! I combined it with my editor of choice Sublime Text via plugin. So happy that you put that work together Nicholas!

  2. 2

    This looks amazing. I have started to use something similar in my projects. I will most definitely look at this.

  3. 4

    It occurred to me that these patterns might be easy to get wrong, and I started looking for a way to automatically detect incorrect patterns

    So how did you end up detecting bad patterns? That lead-in is pretty disappointing when it turns out the article is just your own JS Hinting that doesn’t appear to solve for the fairly important need that you outlined.

  4. 6
  5. 7

    Thanks Nicholas. This is by far the best linter I’ve worked with. I hope more linters follow suit.

  6. 8

    Recently switched over to this from JSHint. I liked JSHint; it’s great. I *love* ESLint. It appears to be more powerful, more up to date and more pluggable.

  7. 9

    Kevin Lozandier

    October 4, 2015 11:30 pm

    Great read, Nicholas; I totally didn’t know the default format of .eslintrc is a YAML file instead of JSON.

  8. 10

    OIeg Shalygin

    October 5, 2015 1:46 am

    Basically my go-to JS linter these days. I’ve been following the development on GitHub for some time and its good to see more widespread use.

    If anyone is interested check out the github for ESLint.


  9. 11

    This sounds like there are some really good improvements over existing linters and I’ll be trying it out.

    One thing that I can see from the above examples is that the config files use numbers for the rule codes. I’d like to see some “word” alternatives to the numbers to make the config files more readable. It may also lessen any confusion over the exit code for the program.

    E.g. something like:
    0 -> “off”
    1 -> “warn”
    2 -> “error”

  10. 12

    Any work happening that would allow client side data models (i.e. Angular $scope) to be validated against server side data models? (i.e. Mongoose schema definitions)

  11. 13

    My website is on a remote server, I have copied all the files from their remote host server. How do I now configure MAMP to see this as a local copy of wordpress so I can edit it and try out different themes. . Please help. I am pulling my hair out!.


↑ Back to top