How to add git hooks using husky v5 ( Bonus — Formatting + Linting + Lint Staged)
Git Hooks
Git hooks are simple scripts that run before or after certain actions. They are useful for a variety of tasks like before committing format the code, fix the lint issues etc. Before push, run the test locally or create the build to check build compiles or not.
Husky
Husky is an npm package that “makes Git hooks easy”. You can use it to prevent bad git commit
, git push
, lint your commit messages, enforce code standards in your project and more. Husky supports all Git hooks.
Step 0: Initialize the project
For demonstration purpose, we need a starter project. Let’s use create-react-app to setup a react application. We are choosing react just to minimize the steps for getting set up to work with Husky.
npx create-react-app husky-app
cd husky-app
Step 1: Install husky and Enable git hooks
To install husky and enable git hooks, husky recommends the following(automatic) approach.
# npm
npm install husky --save-dev && npm exec husky init# yarn
yarn add husky -D && yarn husky init
NOTE: Run git init
first if the repository is not initialized.
The command above will setup husky and create a sample pre-commit
hook that you can edit. Now your folder structure would look like this.
husky-app
├── .husky
│ ├── _
│ │ └── husky.sh
│ ├── .gitignore
│ └── pre-commit
....
Step 2: Add more hooks
To add another hook use husky add
.
To lint commits before they are created we can use Husky’s commit-msg
hook.
First, add commitlint cli and config-conventional to our project.
#npm
npm install -D @commitlint/cli @commitlint/config-conventional#yarn
yarn add -D @commitlint/cli @commitlint/config-conventional
Lastly, we need to add rules to commitlint
field in package.json
//package.json"devDependencies": {
....
},
"commitlint": {
"extends": [
"@commitlint/config-conventional"
]
}
Let’s add commit-msg
hook:
# npm
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit $1'# yarn
yarn husky add .husky/commit-msg "yarn commitlint --edit $1"
Let’s add one more hook. To prevent a push from taking place if script fails we can use pre-push
hook:
#npm
npx husky add .husky/pre-push "npm run build"#yarn
yarn husky add .husky/pre-push "yarn build"
Step 3: Test the hooks
Let’s commit the changes and see how husky fires off some of our scripts.
As you can see how husky fired off pre-commit
script and how commitlint
throw an error with text that does not follow the conventional commit pattern.
Now if we try to commit with a conventional message pattern our commit should be added.
Similarly, if we push those changes to the repository, we can see that the push process runs the pre-push
hook which creates the build of our project.
NOTE: Before push remote origin should exist.
Bonus Step: Use Husky to format and lint the code
First, let’s talk about ESlint. It is a static code analyzer, which means it tells you errors and mistakes that you may make while you are developing.
Prettier is a code formatter, it’s only concerned with how your code looks to ensure consistent indentation in the entire project.
Let’s install the dev dependencies for eslint and prettier.
yarn add -D eslint prettier \
eslint-plugin-react eslint-plugin-react-hooks \
eslint-plugin-prettier eslint-config-prettier \
eslint-plugin-jsx-a11y
Making the config files
touch .eslintrc.json .prettierrc
Let’s populate our configs now.
// .prettierrc
{
"semi": true,
"tabWidth": 4,
"printWidth": 100,
"singleQuote": true,
"trailingComma": "none",
"jsxBracketSameLine": true
}// .eslintrc.json
{
"root": true,
"parserOptions": {
"ecmaVersion": 2020,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
"settings": {
"react": {
"version": "detect"
}
},
"env": {
"browser": true,
"amd": true,
"node": false
},
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"plugin:jsx-a11y/recommended",
"plugin:prettier/recommended"
],
"rules": {
"prettier/prettier": ["error", {}, { "usePrettierrc": true }],
"react/react-in-jsx-scope": "off",
"jsx-a11y/anchor-is-valid": [
"error",
{
"components": ["Link"],
"specialLink": ["hrefLeft", "hrefRight"],
"aspects": ["invalidHref", "preferButton"]
}
]
}
}
We need to add scripts to our package.json
and removeeslintConfig
from package.json
because create-react-app add it by default.
"scripts": {
"lint": "eslint --fix . --ignore-path ./.gitignore",
"format": "prettier --write './**/*.{js,jsx,css,md,json}' --config ./.prettierrc --ignore-path ./.gitignore"
},
Now simply add these scripts to the pre-commit
hook
// pre-commit#!/bin/sh
. "$(dirname "$0")/_/husky.sh"yarn format
yarn lint
Let’s test by commit the changes.
Thanks to the husky pre-commit
hook our code got formatted and exited with error code 1 due to the linting issues. I am not going to fix the linting issues in this article but hopefully you got the idea.
Lint-Staged
Running linting/formatting scripts on a whole project is slow and linting results can be irrelevant. Ultimately you only want to run the scripts against the files that will be committed. Lint-staged allows you to granularly pick the files that will go into your next commit i.e staged files.
The installation and setup are straightforward:
#npm
npm install --save-dev lint-staged#yarn
yarn add -D lint-staged
and add a list of commands with glob patterns to run these commands against:
"lint-staged": {
"./**/*.{js,jsx}": [
"yarn format",
"yarn lint"
]
}
Add lint-staged
to package.json
scripts and modify pre-commit
hook to run the lint-staged script.
"scripts" {
...
"lint-staged": "lint-staged"
}// pre-commit#!/bin/sh
. "$(dirname "$0")/_/husky.sh"yarn lint-staged
Conclusion
That’s it. Thank you for reading this far. You can find the full source code at Github. Let me know if you find some issues. I have taken a lot of help from the following articles.