Photo by Joseph Barrientos on Unsplash
Format and fix code: VS Code config and git pre-commit hook with husky and lint-staged
Table of contents
In a typical web code repository, we use linters like eslint to highlight warnings or syntax errors and use tools like prettier to format the code.
There are a few ways to trigger the action:
VS Code
At editor level, e.g. VS Code, we can use command palette to run it or add keyboard shortcuts. We can also format on save. E.g. for VS Code settings.json
We can do
{
"editor.codeActionsOnSave": ["source.formatDocument", "source.fixAll.eslint"]
}
Another way is to enforce this at pre-commit or pre-push git lifecycles. This will ensure that committed code are all complied to a same code format. And at this repo level, we can set up the standard across the team collaborating on this.
Husky
We can use husky to config arbitrary shell cmds to be run pre-commit.
pnpx husky-init && pnpm install
Then we can edit shell cmds in .husky/pre-commit
E.g. we can create custom linting scripts in package.json
like
{
// ...
"scripts": {
"lint": "eslint . --ext .ts,.tsx,.js,.jsx",
"lintfix": "eslint . --ext .ts,.tsx,.js,.jsx --fix",
"prepare": "husky && husky install"
}
}
Then add npm run lintfix
to .husky/pre-commit
Lint-staged
We may not want to format the whole code repo on each commit which is not performant
Alternatively we can only format staged files and format incrementally with caching
pnpx mrm lint-staged
Then add to package.json
{
"lint-staged": {
"*.{json,js,jsx,ts,tsx,css,scss,md}": "prettier . --config .prettierrc --write",
"*.{ts,tsx,js,jsx}": "eslint --cache --max-warnings 0"
}
}
Now modify .husky/pre-commit
to
npx lint-staged
Now on each commit it will only run eslint and prettier for staged files.
One more follow up on error with lint-staged
The above setup results in a error when I have generated files that shouldn't be linted but picked up by lint-staged:
/path/to/file/name.min.js
0:0 warning File ignored because of a matching ignore pattern. Use "--no-ignore" to override
The solution is introduced in https://stackoverflow.com/questions/37927772/how-to-silence-warnings-about-ignored-files-in-eslint
Create a new file lint-staged.config.js
under root
const { ESLint } = require('eslint');
const removeIgnoredFiles = async (files) => {
const eslint = new ESLint();
const ignoredFiles = await Promise.all(
files.map((file) => eslint.isPathIgnored(file))
);
const filteredFiles = files.filter((_, i) => !ignoredFiles[i]);
return filteredFiles.join(' ');
};
module.exports = {
'*.{json,js,jsx,ts,tsx,css,scss,md}': () =>
'prettier . --config .prettierrc --write',
'*.{js,ts,jsx,tsx}': async (files) => {
const filesToLint = await removeIgnoredFiles(files);
return [`eslint --max-warnings=0 ${filesToLint}`];
},
};
This will ignore the files
If we adopt this then we can remove the lines about lint-staged
in package.json