Tim Hordern

I ♥ building amazing products and teams. QA + Product + DevOps + Fast = Awesome.

React Linting With ESLint, Jest and Grunt

On one of the projects I’m working on right now, we’re building a front-end with a heavy usage of React components. We’re doing our testing with Jest, which is a wrapper on Jasmine.

Now, as mindful programmers, we like to write nice clean code. A linter isn’t the complete answer to writing clean code but it does help a lot. It can also help to trap simple errors, particularly if you don’t have deep experience in the framework ¯\_(ツ)_/¯.

Sweet, let’s add a linter then! With a bit of investigation, I’ve found that ESLint has the right plugins for React and JSX as well as some other tools that we’re using. I’m using Grunt for our build pipeline. There’s a Grunt plugin for ESLint, so let’s add that to our package.json by using npm install:

Shell
1
$ npm install grunt-eslint --save-dev

Then in our Gruntfile, we add the ESLint task:

Gruntfile.js
1
2
3
4
5
6
7
8
9
10
11
12
module.exports = function (grunt) {
  grunt.initConfig({
    eslint: {
      target: [
        'Gruntfile.js',
        'app/*.js',
        '__tests__/**/*.js'
      ]
    }
  });
  grunt.registerTask('lint', ['eslint']);
};

Whilst there is a command-line option for ESLint to initialize some configuration (--init), I like keeping specific configuration out in a separate file. ESLint will automatically search for a configuration file if you don’t specific options in your grunt configuration (you can optionally specific a different filename for the config file but the default is .eslintrc). Let’s add an .eslintrc to our app. The .eslintrc file can be YAML or JSON but I found the YAML version more readable.

.eslintrc
1
2
3
4
5
---
  extends: "eslint:recommended"
  env:
    browser: true
    node: true

The extends call implements the recommended rules and gets us up and linting.

Great! Now we can run grunt lint and we’ll get the STDOUT output of our linting. If you’re writing React code though, you’ll see a whole bunch of errors about React and the HTML inside the JSX blocks (or files if you’ve separated your JSX out).

We’ll need to add the React plugin to ESLint (it’s plugins all the way down):

Shell
1
$ npm install eslint-plugin-jasmine --save-dev

Now for our .eslintrc we can add the react-specific rules, as well as let ESLint to expect that it’s operating in a React-Jest-Jasmine environment. We also set some ECMA features because we’re using JSX.

.eslintrc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
---
  extends: "eslint:recommended"
  rules:
    react/display-name: 1
    react/jsx-boolean-value: 1
    react/jsx-curly-spacing: 1
    react/jsx-max-props-per-line: 1
    react/jsx-no-duplicate-props: 1
    react/jsx-no-undef: 1
    react/jsx-quotes: 1
    react/jsx-sort-prop-types: 1
    react/jsx-sort-props: 1
    react/jsx-uses-react: 1
    react/jsx-uses-vars: 1
    react/no-danger: 1
    react/no-did-mount-set-state: 1
    react/no-did-update-set-state: 1
    react/no-multi-comp: 1
    react/no-unknown-property: 1
    react/prop-types: 1
    react/react-in-jsx-scope: 1
    react/require-extension: 1
    react/self-closing-comp: 1
    react/sort-comp: 1
    react/wrap-multilines: 1
  env:
    browser: true
    node: true
    jasmine: true
    jest: true
  ecmaFeatures:
    jsx: true
  plugins:
    - "react"

Now, you might still have some errors thrown when you grunt lint if you’re using Browserify or one of the other require helpers like Webpack or requirejs. We can get around this by letting ESLint know that some things are global. We’re also using React-Router, so we should let ESLint know about that as well.

.eslintrc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
---
  extends: "eslint:recommended"
  rules:
    react/display-name: 1
    react/jsx-boolean-value: 1
    react/jsx-curly-spacing: 1
    react/jsx-max-props-per-line: 1
    react/jsx-no-duplicate-props: 1
    react/jsx-no-undef: 1
    react/jsx-quotes: 1
    react/jsx-sort-prop-types: 1
    react/jsx-sort-props: 1
    react/jsx-uses-react: 1
    react/jsx-uses-vars: 1
    react/no-danger: 1
    react/no-did-mount-set-state: 1
    react/no-did-update-set-state: 1
    react/no-multi-comp: 1
    react/no-unknown-property: 1
    react/prop-types: 1
    react/react-in-jsx-scope: 1
    react/require-extension: 1
    react/self-closing-comp: 1
    react/sort-comp: 1
    react/wrap-multilines: 1
  env:
    browser: true
    node: true
    jasmine: true
    jest: true
  ecmaFeatures:
    jsx: true
  globals:
    React: true
    TestUtils: true
    routerHelper: true
  plugins:
    - "react"

I’m also using the BDD and Jasmine plugins for ESLint to improve some of the wording and operation of our test suite. There’s no shortage of ESLint plugins for you to add, which is the beauty of the plugin architecture of ESLint.

If you’re having trouble resolving your linting errors, a great resource is JSLint Error Explanations. Despite the name, JSLint Error Explanations actually explains ESLint errors as well with live examples that you can play around with to see how to fix the error.

Happy linting!

Comments