Okay, this kept me frustrated for a whole week because I suck at configurations at many levels. But thankfully, I've now closed my 16 chrome tabs and writing this out to make sure you don't have to face the same exact problem.
Introduction
When different developers work on the same codebase, it becomes necessary to enforce some rules to keep code in check. ESLint and Prettier go hand in hand for this purpose in most of JS projects and integration support is widely available.
Finally husky is library that allows us trigger actions before committing or pushing. It provides git hooks for this purpose. I'll navigate it to in a minute.
Problem Statement
The problem that I faced here was that my project was built like a monorepo. It has frontend, backend and library folders in it. In order to use husky git hooks, they are to be placed in the directory where git is placed.
But then again, for husky to work, it needs to utilize package.json file. This issue had me rolling for days.
Solution
I'll navigate step by step from installing husky to committing the code. This might takes quite a few commands, so please bear with me.
Installing husky
In the root folder of the repo where git resides, run following commands:
npx husky install
npx husky add .husky/pre-commit "npm test"
This will create a .husky
folder in the root directory with pre-commit
file in it. This file would have a single command npm test in it.
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npm test
Let's leave it for now and move to next step.
Installing Linters
Go to your frontend project and install eslint, husky and prettier with the following commands:
npm install husky lint-staged eslint-plugin-prettier eslint-config-prettier --save-dev
npm install --save-dev --save-exact prettier
--save-dev
keeps these libraries in devDependencies
because they won't be used in production and are here for development only.
Configuring Linters:
We'll be creating few files to let our linters know how they would be working across the project.
- Create
.estlintignore
and.prettierignore
files and place the following code
build
node_modules
.github
This will inform our linters not to look into files in the above mentioned directories
- Now we'll be adding few configurations for estlint. Create a file
.eslintrc.js
with this code:
module.exports = {
env: {
browser: true,
es6: true,
},
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:prettier/recommended',
'plugin:jsx-a11y/strict',
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 2018,
sourceType: 'module',
},
plugins: ['react', 'jsx-a11y', '@typescript-eslint'],
rules: {
'react-hooks/exhaustive-deps': 'error',
'no-var': 'error',
'brace-style': 'error',
'prefer-template': 'error',
radix: 'error',
'space-before-blocks': 'error',
'import/prefer-default-export': 'off',
},
overrides: [
{
files: [
'**/*.test.js',
'**/*.test.jsx',
'**/*.test.tsx',
'**/*.spec.js',
'**/*.spec.jsx',
'**/*.spec.tsx',
],
env: {
jest: true,
},
},
],
};
- And finally the configuration for prettier. Add a file
.prettierrc.js
and put the following code:
module.exports = {
printWidth: 100,
tabWidth: 2,
singleQuote: true,
semi: true,
trailingComma: 'all',
arrowParens: "always",
overrides: [
{
files: '*.{js,jsx,tsx,ts,scss,json,html}',
options: {
tabWidth: 4,
},
},
],
};
Setting Up Package.json
We're almost there and now we'll have to add few scripts to package.json. I'll guide you about their purpose along the way.
- Add the following line in the scripts section:
"prepare": "cd .. && husky install frontend/.husky"
npm prepare
command runs before we commit our code. What essentially we're doing here is that we are moving out of frontend directory and installing husky in the front-end. - Now we need to add our linting configuration governed by
lint-staged
. Place the following code after scripts section:
"lint-staged": {
"*.{js,ts,tsx, jsx}": [
"eslint --quiet --fix"
],
"*.{json,md,html,js,jsx,ts,tsx}": [
"prettier --write"
]
},
We've written the extensions of the files eslint and prettier would be amending.
Finally, we'll be adding a script that would invoke linting. Add this line in your scripts:
"lint-front": "lint-staged"
Runningnpm run lint-front
would trigger linting our application.Let's just inform our husky to run
npm run lint-front
before commit. Go to the husky folder in the project root and replace pre-commit file with this code:
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
cd frontend
npm run lint-frontend
If everything followed correctly, making a commit would trigger linting. That's the end of it. Hope this helps someone. If you are still having issue, do mention the comments. I'd be more than happy to help.
Top comments (5)
It was so helpful. Thanks for sharing.
Awesome tutorial, just 2 small fixes:
Under package.json, it should be "lint-frontend": "lint-staged" such that it matches "npm run lint-frontend" on .husky/pre-commit.
In my case, it was also necessary to add:
module.exports = {
env: {
....
node: true
},
...
}
Could you share your repo as an example?
I ran into an error ".git can't be found" with this setup. Both lint-staging and husky docs suggest installing them at root in a monorepo. When I did this and kept my
"lint-staged": {
in the child packages, everything worked well. I expect this is related to the fact that I am linting all my packages with my pre-commit looking like this:Under the scripts section, rather than "prepare": "cd .. && husky install frontend/.husky", what works for me is "prepare": "cd && husky install .husky".
The former setup another git hook in my frontend folder, while the latter set up the git hook in the root folder.