ESLint, Prettier and Jest integrated with Husky and lint-staged
ESLint, Prettier and Jest integrated with Husky and lint-staged

Automated Code Linting and Testing

React + VSCode + ESLint + Prettier + Jest + Husky + lint-staged

Harsh Verma
8 min readMay 11, 2020

--

Note — In my previous blog https://medium.com/@harshverma04111989/quick-linting-setup-for-a-react-web-application-4ff573239129 we have seen a very basic linting setup for react webapp using only ESLint. But it has some major drawbacks like -

- What if somehow VSCode user settings is not working or the autoFixOnSave setting got deleted

- It doesn’t stop the user to commit the code with some linting errors

- We can have some scripts in package.json to auto fix like“lint:fix”: “./node_modules/.bin/eslint src — fix” but that too is vulnerable as some one can forgot to use it before committing, In short not automatted , manual stuff

Think of setup which solves above drawbacks i.e. will not allow to commit the code without eslint fix ,fix some linting issue or format the code by itself while commiting. This saves lot of time of a developer and keep the code clean specially when you working in large team.

Lets try to make such stepup in this blog.We will use the same setup of my previous blog and extend it further. We will use -

ESLint — For code quality

Prettier — For advance code formatting i.e. file based

Jest — To run related test cases when a file got changed

EditorConfig — Basic code formatting for maintaining the consistency across all the IDE

Quick Overview -

Prettier is an npm library which helps to format the code in a particular way. The good point about this library is that it supports many languages and doesn’t need any configurations for different languages, unlike ESLint. This helps to maintain the code as per the Prettier way only (debatable today as well, as some people like more control on how the code should be written) by taking basic configuration that too is optional, because for that configuration as well Prettier has some default values. Read more at — https://prettier.io/

Husky is an npm library which helps to take advantage of git hooks like pre-commit, pre-push, etc with just a few lines of setup. Suppose you want to perform some checks or run any scripts before committing of code than with Husky it is very simple to do it. Read more — https://github.com/typicode/husky

lint-staged is the npm library which helps to run the set of commands only on staged files. Read more — https://github.com/okonet/lint-staged

EditorConfig is an npm library which helps to maintain the consistent coding styling among different developer using different IDE like VSCode, Sublime, Atom, etc. Now you may be thinking in your mind “Wait a minute Prettier is also doing same thing” To answer that yes both are similar but not same Prettier do more stuff as compared to EditorConfig like formatting different file in a different way like JS/CSS/HTML file whereas EditorConfig does a generic formatting like indentation style, setting charset, etc. Mainly the advantage of using EditorConfig is to reduce the prettier writing efforts by making the changes during editing mode only. Read more at — https://editorconfig.org/

Jest is a javascript testing framework. I have a separate blog related to unit testing which covers the basics of unit testing — https://medium.com/@harshverma04111989/unit-testing-of-basic-react-app-f54adff9d63

Enough talk I guess, Let’s understand by the code-

1)Setup husky

npm i -D husky

Add below configuration in package.json file

"husky”:{
“hooks”: {
“pre-commit”: “npm run lint”
}
}

This will run the script (i.e. npm run lint) before the git commit command runs and block the commit if the script exit code is non-zero(i.e. found some errors) and commits the code when the script exit code is zero(i.e. no errors)

Now, Let’s test the above setup by making some rule fail (say convert double quote to single quote in App.js file)and running commands git add -A and git commit -m “Linting test”, You will see git commit will fail as shown in below image

Image showing lint error
Showing lint error

But there is one problem with this, if we have some new files created which are not staged yet and we do not want to commit that underdeveloped new file, those files also get tested with this lint command as shown below

Image showing linting error in both staged and unstaged file
Showing lint error for both staged and unstaged file

So this means even if we fix the staged file (i.e. src/App.js) it will still not allow us to commit the file which is wrong because we are only committing the src/App.js

So here we have to find a way so that lint command should always run on staged file only. lint-staged helps here, let’s see how

2)Install lint-staged

npm install lint-staged -D

Now there are different way to setup this -

  • lint-staged object in your package.json
  • .lintstagedrc file in JSON or YML format
  • lint-staged.config.js file in JS format
  • Pass a configuration file using the — config or -c flag

Read more here https://www.npmjs.com/package/lint-staged#configuration

Let’s use 1st way, which is creating object in package.json file

// package.json file“lint-staged”: {
“src/**/*.js”: [
“eslint --fix”, // 1
“git add” // 2
]
},
// and change husky setting to run lint-staged on commit
“husky”: {
“hooks”: {
“pre-commit”: “lint-staged” // 3
}
},

// 1 “eslint --fix” — Fixes the fixable ESLint issues, Now you may be thinking why don’t we use npm run lint (i.e. “lint”:”./node_modules/.bin/eslint src”) or npm run lint:fix (i.e. “lint:fix”: “./node_modules/.bin/eslint src — fix”) script instead, because if you see in these script we are giving the file or folder name (i.e. src), So it will do linting on un staged file as well which is not we want

// 2 If suppose the script 1 fixed some linting issue then that file again need to be staged.

// 3 This will run the lint-staged script before git commit command

and after running git commit command, we will get below

ESLint fix runs with lint-staged library on staged file
ESLint fix runs with lint-staged library on staged file

3)Now lets setup “Prettier” to format our code

Install Prettier

npm i prettier -D

Prettier has the default configurations which you can get from here https://prettier.io/docs/en/options.html. Let’s create our own configuration. Again, there are different ways to create it, you can get from here https://prettier.io/docs/en/configuration.html

Let’s use .prettierrc json file way

{
printWidth: 80,
tabWidth: 4,
trailingComma: “all”,
singleQuote: true
}

With the above settings, you are already seeing that this will contradict with our ESLint rule i.e.

tabWidth: 4                       (Prettier Rule) 
Vs
“indent”: [“error”, 2] (Eslint Rule)
singleQuote: true (Prettier Rule)
Vs
“quotes”: [“error”, “double”] (Eslint Rule)

To solve this, the recommended approach is to install eslint-config-prettier and eslint-plugin-prettier with the below changes in .eslintrc file.

“extends”: [“eslint:recommended”, “plugin:react/recommended”,“plugin:prettier/recommended”],

Read more at — https://prettier.io/docs/en/integrating-with-linters.html

4)Adding Jest as well

While code commiting If you are sure that your unit testing is also passed then it gives us more confidence. Let’s see how

Let’s create some test cases(say App.test.js) by installing enzyme and enzyme-adapter-react-16 You can refer my blog on basic unit testing — https://medium.com/@harshverma04111989/unit-testing-of-basic-react-app-f54adff9d63

// Updated App.js by adding <b>Sample tag</b>// App.test.js file
import React from ‘react’;
import App from ‘./App’;
import Enzyme, { shallow } from ‘enzyme’;
import Adapter from ‘enzyme-adapter-react-16’;
Enzyme.configure({ adapter: new Adapter() });const enzymeWrapper = shallow(<App />);describe(‘<App />’, () => {
test(‘should render successfully’, () => {
expect(enzymeWrapper).toHaveLength(1);
});
test(‘should have b tag with value as Sample tag’, () => {
expect(enzymeWrapper.find(‘b’).text()).toBe(‘Sample tag’);
});
});

After committing this code, Update the package.json by adding jest as well inside the lint-staged as below

“scripts”: {
...
“test:staged”: “cross-env CI=true react-scripts test --findRelatedTests”,
...
},
“lint-staged”: {
“src/**/*.js”: [
“eslint --fix”,
“prettier --write”,
“npm run test:staged”,
“git add”
]
},

Script test:staged — By default the react-scripts run the test cases in watch mode but this will hang such type of CI(Continous Integration) setup as jest expects different option in wath mode. So to off this mode, we need to set the environment variable CI as true, here we used cross-env so that this will work in all environment(like windows,linux etc.).

Jest also provides a very good option findRelatedTests which informs jest to run all those test cases which get impacted by the changed file. This means, In our current scenario even if we do not change the App.test.js file and made some changes on App.js only, then also jest run all the test cases in App.test.js. Let’s see that in action -

Let’s change the <b> tag in App.js as

<b>Sample tag !!!</b>

run git add -A and git commit -m “with jest integrated”. It will get run as below

Integrated jest also in lint-staged
Integrated with jest as well

5)Adding “.editorconfig” file

Editor config is just a small plugin to maintain the basic formatting of files across the different IDE’s. These formatting of files taken care by EditorConfig during the edit mode itself resulting in less work for Prettier during file committing process. Let’s see how

Create .editorconfig file as below

# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
# Indentation override for all JS under src directory
[src/**.js]
indent_style = space
indent_size = 3

root this informs EditorConfig that this is the root file and use the defined configuration

[*] Means for all the files in all the sub-folders of this project

end_of_line Different OS has different ways of repesenting the end of file. In Unix/Linux it is LF(i.e. line feed), in Windows it is CRLF(i.e. Carriage Return Line Feed )

trim_trailing_whitespace Remove extra spaces in the line if true

insert_final_newline Insert blank line at the end of the file if true

[src/**.js] Means for all files under folder src including files in sub-folders

indent_style space or tab as per the need

indent_size whole number used when “indent_style” is “space”. For “indent_style” as “tab” it uses “tab_width”

Read more at — https://editorconfig.org/

You can see in below image, how changes reflected when we change the .editorconfig file and hit Ctrl+S in the newFile.js

Showing EditorConfig configurations
Showing EditorConfig configurations

You can get the complete code here https://github.com/harshmons/eslint-prettier-vscode-react-advance-setup. Just follow the Readme to run the code.

Happy Automated Code Linting :)

--

--