Create a pre-commit hook
A pre-commit hook preserves the quality of your code by running lint, format, and/or test scripts before commits. It prevents commits if it finds a problem in the code, or, in other words, if one of the previous scripts fails. A popular tool that is used for setting up pre-commit hooks is husky
.
husky
, is often used along with another tool the lint-staged
. As the name suggests, lint-staged
improves the performance of your pre-commit hook by allowing you to run scripts only against the staged files. For example, you don’t have to run the lint
script for the entire project but only for the files that changed.
Install dependencies
Let’s see how you can create a pre-commit hook with husky
and lint-staged
. First, install the dependencies:
yarn add -D husky lint-staged
Configure husky
Then, configure husky
to run the lint-staged
script before you commit the code:
{
"name": "pre-commit-hook",
"private": true,
"version": "1.0.0",
"main": "src/index.js",
"license": "MIT",
"scripts": {},
"husky": {
"hooks": {
"pre-commit": ["lint-staged"]
}
}
}
By the way, you can create more Git hooks with husky
; you are not restricted to pre-commit hooks.
Linting
You now have to configure lint-staged
. The following script runs ESLint against the staged files and fails—it won’t commit the code—if it finds a lint error:
{
"name": "pre-commit-hook",
"scripts": {
"lint": "eslint --ext js,jsx src"
},
"husky": {
"hooks": {
"pre-commit": ["lint-staged"]
}
},
"lint-staged": {
"*.{js,jsx}": ["eslint"]
}
}
Notice that you only run the eslint
command in the lint-staged
script and not the lint
script I highlighted above. If you run the latter, it would run ESLint against the entire project multiple times—for each staged file— and would slow down the pre-commit hook without a reason. Needless to say that I’ve made that mistake in the past.
Formatting
In addition to linting, a pre-commit hook can also format the code. A popular formatting tool is Prettier. Depending on how you configured Prettier with ESLint, the format command may differ. In the following example, let’s assume that you configured ESLint to run Prettier (this is the best way), and as a result, you can run both tools with the eslint --fix
command:
{
"name": "pre-commit-hook",
"husky": {
"hooks": {
"pre-commit": ["lint-staged"]
}
},
"lint-staged": {
"*.{js,jsx}": ["eslint --fix", "git add"]
}
}
prettier-eslint
, you run prettier-eslint --write
; if you use only Prettier, you run prettier --write
.After you format the code, you run git add
to stage any JavaScript files that changed after the formatting.
git add
to stage the files because lint-staged
adds them automatically in the commit to prevent race conditions. In the next examples, I will omit it.But because we can run ESLint only against our JavaScript files, the rest of our files—CSS, JSON, e.t.c.—remain unformatted. Let’s fix that by creating another file rule that runs prettier to format the remaining files:
{
"name": "pre-commit-hook",
"husky": {
"hooks": {
"pre-commit": ["lint-staged"]
}
},
"lint-staged": {
"*.{js,jsx}": ["eslint --fix"],
"*.{md,mdx,json,css,scss,yaml,yml}": ["prettier --write"]
}
}
See the lint-staged documentation for more examples.
Optional testing
Finally, you can run a test script— in this case with Jest—in your lint-staged
script:
{
"name": "pre-commit-hook",
"husky": {
"hooks": {
"pre-commit": ["lint-staged"]
}
},
"lint-staged": {
"*.{js,jsx}": [
"eslint --fix",
"jest --findRelatedTests --bail"
],
"*.{md,mdx,json,css,scss,yaml,yml}": ["prettier --write"]
}
}
The --findRelatedTests
flag instructs Jest to check if the staged files have tests and run only those tests. Additionally, with the --bail
flag, Jest will stop testing if a single test fails.
Keep in mind, that most people prefer to run only lint and format scripts in their pre-commit hooks and leave the test scripts for Continuous Integration. But for simple projects, it’s ok to run test scripts too.
Links
Other things to read
Popular
- Reveal animations on scroll with react-spring
- Gatsby background image example
- Extremely fast loading with Gatsby and self-hosted fonts