Thursday, 5 January, 2017 UTC


Summary

In this article, we will extend our application setup with linting.
If you have not read my article on setting up a JavaScript application with React, I highly recommend reading it first.

Setting Up ESLint

ESLint helps us catch mistakes by enforcing consistent standards and best practices. As our code is guarded by linting rules, we are guaranteed to produce consistent code, increasing maintainability.
As we will write React code, we will set up ESLint together with eslint-plugin-react. This plugin is responsible for linting React code. First, let’s install ESLint via npm:
npm install eslint eslint-plugin-react eslint-watch --save-dev
On top of the eslint plugin, we installed React support using eslint-plugin-react. We also installed eslint-watch, a tool for live reloading changes after save. We will set up the eslint-watch npm script in the next section of this article.
The ESLint configuration is in the .eslintrc file of the root folder of your application. The file .eslintrc is a JSON file responsible for configuring your individual linting rules. When it comes to React, the following keys and values are recommended so that ESLint works with React smoothly:
{
    "plugins": [
        "react"
    ],
    "parserOptions": {
        "ecmaVersion": 6,
        "sourceType": "module",
        "ecmaFeatures": {
            "jsx": true
        }
    },
    "env": {
        "es6":     true,
        "browser": true,
        "node":    true,
        "mocha":   true
    },   
    "extends": [
        "eslint:recommended", 
        "plugin:react/recommended"
    ],    
    "rules": {
    }
}
The plugins key is responsible for enabling the React plugin.
In newer versions of ESLint, we have to wrap the ecmaFeatures key in parserOptions. In version 1, ecmaFeatures was a top level key. Regardless of the version, this is the section where you can enable ESLint.
Talking about parser options, check out the documentation on default settings. It is important to use "sourceType": "module" in case you are planning to use ES6 modules. Otherwise, each import would result in a linting error.
Recommended settings come with a set of predefined rules. You can include them in the extends field. In this example, I defined eslint:recommended and plugin:react/recommended.
Whenever a recommended rule appears too strict, or a rule we need is missing, we can override it in the "rules" section of .eslintrc.
The "env" key is responsible for telling ESLint that it should accept some global variables. For instance, if you disallow using globals in general, you will have to take care of it, xit, expect etc when writing your automated tests.

Linting in the console

We have already installed the eslint-watch npm module. It’s time to use it.
Add "lint" and "lint:watch" to your package.json:
"scripts": {
    "build": "webpack -d && cp src/index.html dist/index.html && webpack-dev-server --hot --inline --colors --progress --content-base src/",
    "build-prod": "webpack -p && cp src/index.html dist/index.html",
    "lint": "esw webpack.config.* src tools --color",
    "lint:watch": "npm run lint -- --watch"
  },
You can now run npm run lint:watch in order to watch linting. Once you save a file, the linter displays all the errors and warnings.
If you want to run the linter once, use npm run lint.

Convenient linting in the console

You need to run
  • npm run build to build your application and launch the development environment,
  • npm run lint:watch to run the linter.
Later, you may run an automated testing tool as well.
You may want to execute just one npm script in order to launch your application and your linter. We will install the npm-run-all package to handle executing multiple scripts:
npm install npm-run-all --save-dev
After the installation, define a fifth script in your package.json:
"scripts": {
    "build": "webpack -d && cp src/index.html dist/index.html && webpack-dev-server --hot --inline --colors --progress --content-base src/",
    "build-prod": "webpack -p && cp src/index.html dist/index.html",
    "lint": "esw webpack.config.* src tools --color",
    "lint:watch": "npm run lint -- --watch",
    "start": "npm-run-all --parallel build lint:watch"
  },
When executing npm start, both scripts are executed in parallel. You will get all the compilation errors and linting errors in one window. Whenever you save a file, all the compilation and linting errors will be printed in the terminal.

Linting in Sublime Text 3

You can also configure the linter in Sublime Text 3. Open the Package Control, and install the following two packages:
  • Sublime Linter,
  • Sublime Linter ESLint.
Red and yellow dots appear next to the line numbering, indicating warnings and errors respectively. Error messages appear at the bottom of your editor.
If you are using Atom, VS Code, or WebStorm, configuration is different. Check out the ESLint integrations guide for more information.

Sample rule configuration

If you prefer configuring your own rules, you have to know what your options are. In this section, we will set up custom linting rules. These rules reflect my own opinion on how coding works the best for me.
"rules": {
        // first argument: 0 - silent, 1 - warning, 2 - error
        "strict":             [ 2, "safe" ],
        "no-debugger":        2,
        "brace-style": [
            2,
            "1tbs",
            { "allowSingleLine": true }
        ],        
        "no-trailing-spaces": 2,
        "keyword-spacing":    2,
        "space-before-function-paren": [
            2,
            "never"
        ],
        "spaced-comment":     [2, "always" ],
        "vars-on-top":        2,
        "no-undef":           2,
        "no-undefined":       2,
        "comma-dangle":       [ 2, "never" ],
        "quotes":             [ 2, "single" ],
        "semi":               [ 2, "always" ],        
        "guard-for-in":       2,
        "no-eval":            2,
        "no-with":            2,
        "valid-typeof":       2,
        "no-unused-vars":     2,
        "no-continue":        1,
        "no-extra-semi":      1,
        "no-unreachable":     1,
        "no-unused-expressions": 1,
        "no-magic-numbers":   1,
        "max-len":            [1, 80, 4],
        "react/prefer-es6-class": 1,
    }
As you can see from the comment, the first argument of the rule setting describes the enforcement level of the rule:
  • 0 is silent,
  • 1 displays a warning,
  • 2 displays an error.
You may ask the question why we need a 0. There are two valid reasons:
  1. You may want to define a rule for future use, but currently, you prefer muting it to focus on other linting errors;
  2. You may want to override a stronger value from another linting configuration by using the "extends" option, and muting some rules you don’t need.
Let’s see some clarification why I chose these settings. Warning: you will read my opinion, not the ultimate truth.
You can look up the documentation of any ESLint rules by pasting the name of the rule in the search bar of eslint.org.
  • "strict" requires the usage of "use strict"; in global scope. Strict mode comes with benefits similarly to linting your code, by forcing you to stick to best practices.
  • "no-debugger": have you ever had nightmares of your teammates committing debugger; statements in your application and giving you a surprise? Have you ever committed a debugger; statement yourself to an open source repo just to enjoy a facepalm moment? If any of these have happened to you, you will likely turn this option on. Regarding debuggers on production, your build process should eliminate all debuggers, so you should not rely on linting for this purpose. Oh, and never debug on production.
  • "brace-style": I use the one true brace style for personal projects, and "allman" in a corporate project. I don’t believe in brace-style wars, and I am fine with any of these two brace styles. The "allowSingleLine" key allows us to write compact blocks that fit in an 80 character long line.
  • "no-trailing-spaces" is for the convenience of developers. Imagine you wanted to reformate the code by pressing delete after your statement. Instead of joining the current line with the following line, you would just delete a trailing space.
  • "keyword-spacing" and "space-before-function-paren": use these two settings to differentiate between statements and functions. Keeping the space in statements and loops like if (...) and for (...), and allowing no spaces between function and (...) makes sense.
  • "spaced-comment": minor detail for the sake of readability of comments. Keep a space between /* or // and the comment text itself.
  • "vars-on-top" requires you to define your variables at the very top of your scope. This helps you think about the state space of a problem, helps you organized your thoughts, and penalizes you if you are a fan of creating very long functions.
  • "no-undef" and "no-undefined": this is not a typo. The former disallows the usage of globals unless they are mentioned in the global key of .eslintrc or in a comment containing a global declaration. The key "no-undefined" disallows the usage of undefined. When we assign a variable a value, it’s defined. Once we indicate that the value should not be there anymore, instead of using undefined, I prefer using null. Using null indicates the absence of a value. There are some other inconvenient edge cases which sane developers will never use: it is possible to override undefined as a variable locally.
{
  let undefined = 2;

  console.log( undefined, typeof undefined );
}
> 2 "number"
This does not work in the global scope, as undefined is a property of the global object, and the value of this property is not changeable.
{
  var undefined = 2;

  console.log( undefined, typeof undefined );
}
> undefined "undefined"
  • "comma-dangle" disallows trailing commas in object literals. I don’t believe in this simplification, and I am convinced that most developers handle copy-pasting or extending configuration without trailing commas well enough. If you are interested in the advantages that come with using trailing commas, read this post.
  • "quotes": yes, I use single quotes. Some people become emotional about favoring one way over the other. Post the question “Single or Double Quotes?” on HackerNews, and watch the incoming messages.
  • "semi" enforces semicolons. Highly recommended.
  • "guard-for-in", "no-eval", and "no-with" are losing relevance, as these are quite old language constructs. Most of the time, we use the for...of loop on iterables anyway. With and eval are in the bad parts of JavaScript according to common knowledge.
  • "valid-typeof" double checks your typos. When using the typeof operator, you compare its value against a string. If you make a typo in the string value, you get an error that’s hard to detect.
  • "no-unused-vars" check if your code contains variables that are never used. This rule detects typos as well as badly written code. Less disciplined developers tend to leave unused code in their code-base. This is a perfect way to send them automatic reminders.
The following rules are only warnings, as there are cases when it makes sense to violate them:
  • "no-continue": another Crockford-rule. According to my opinion, continue in a loop is sometimes a good idea, but most of the time, it makes your code harder to read.
  • "no-extra-semi": I am not a big fan of characters that don’t serve a semantic purpose
  • "no-unreachable", no-unused-expressions": an obvious rule. Warn developers if their code is unreachable or their expressions are not used, as this phenomenon is rarely intentional.
  • "no-magic-numbers" there is nothing worse than using integers for constant values, and referencing these integers in your code. One change in the constant values results in maintenance hell. You don’t even know how to search for all the code segments that use these constant. Would you search for all occurrences of the value 1? This rule is highly inconvenient, as sometimes, magic numbers are values that are only used in one place, and declaring a constant for these values doesn’t make your code more readable.
  • "max-len": Some people prefer 120 character long lines, as our resolution is large enough to add some horizontal space even if you have a two column layout in your text editor. I will stick to 80 characters, as I am also recording tutorial videos, and very long lines are harder to read in a tutorial video.
  • "react/prefer-es6-class" This is one example rule for React. We will use the ES6 syntax.
For a complete list of React/JSX linting rules, check out the npm documentation.

Editor configuration

Your editor can also help you get rid of some linting errors. I use the following configuration in Sublime Text:
{
    "rulers": [80, 100],
    "tab_size": 4,
    "translate_tabs_to_spaces": true
}
Access and modify the user settings on your computer in Preferences > Settings. Your configuration is a JSON file. If you like the above keys, add them to your configuration.
Using the "translate_tabs_to_spaces" key neutralizes the default linting rule "no-mixed-spaces-and-tabs".

Exercise

Check out the code of the React Chat Example tutorial.
You can download or fork the correct branch of my GitHub repository here.
Set up the linter based on this article, and correct linting errors to get a feel for using ESLint. Try out using ESLint both in the terminal and in your favorite IDE or text editor.