How to Add Pre/post-Commit Hooks to Your Node.js Project
Published: 2024-04-11 18:51:24

As the project grows larger it's critical for the whole team to follow certain code quality and security standards. One way to ensure that is by using "git hooks".

> Git hooks are scripts that run automatically every time a particular event occurs in a Git repository.

Numerous git hook managers are available for Node.js. Husky, pre-commit, and Lefthook are a few examples of such tools. We opted for Lefthook for this tutorial due to its capability to execute scripts concurrently and its simplicity.

So let's start!

Before each commit(pre-commit) we need to ensure the following.

- **No credentials leak**: To ensure no secrets are accidentally exposed in the source.
- **No lint errors**: The committed code does not contain any lint errors ([eslint](https://eslint.org/)).
- **No formatting inconsistencies**: The committed code should follow the organization's code formatting standards([prettier](https://prettier.io/) or [pretty-quick](https://www.npmjs.com/package/pretty-quick)).
- No type errors: There shouldn't be any typescript errors.

And on each pre-push we need to ensure the following
- No vulnerable packages: `npm audit` or `pnpm audit --audit-level high`
- Standardized branch-names: we can use[**validate-branch-name**](https://www.npmjs.com/package/validate-branch-name)

Apart from these we also need to enforce standards for all our commit messages([**commit-lint**](https://github.com/conventional-changelog/commitlint)).

### GitLeaks
install gitleaks in your machine [gitleaks](https://github.com/gitleaks/gitleaks)

add the following changes to your _package.json_
```js
{
   "scripts":{
+      "gitleaks": "gitleaks detect -v"
    }

}
```
### Eslint
install and configure eslint with
```js
npm init @eslint/config
```

add the following changes to your _package.json_
```js
{
   "scripts":{
+      "lint": "eslint --ignore-path .gitignore \"{src,tests}/**/*.+(ts|js|tsx)\"",
    }

}
```
### Typecheck

```js
{
   "scripts":{
+      "typecheck": "tsc --noEmit",
    }

}
```

### Prettier for code formatting
```js
npm install --save-dev --save-exact prettier
```
Package.json
```js
{
   "scripts":{
+      "format": "prettier --ignore-path .prettierignore --write \"**/*.+(js|ts|json|tsx|mdx)\" --log-level silent",
    }

}
```

### Branch name validations
```js
npm i validate-branch-name -D
```
Package.json
```js
{
   "validate-branch-name": {
		"pattern": "^(feat|fix|hotfix|release|test|experimental)/.+$",
		"errorMsg": "Branch name validation failed"
	},

}
```
### Commit lint for standardized commit messages
```js
npm i @commitlint/cli -D
npm i @commitlint/config-conventional -D
```
create the file _commitlint.config.js_ at the root
```js
module.exports = {
	extends: ['@commitlint/config-conventional'], // => @commitlint/config-conventional
};
```
create the file _.lefthook/commit-msg/commit_
```bash
echo $(head -n1 $1) | npx commitlint --color
```
### Lefthook as githooks manager
```
// install lefthook
npm install lefthook --save-dev
```
```yml
# create a lefthook.yml file in the root
pre-commit:
  parallel: true
  commands:
    lint:
      glob: '*.{js,ts,jsx,tsx}' # glob filter for list of files
      run: npm run lint
    format:
      run: npm run format 
    types:
      glob: '*.{js,ts, jsx, tsx}'
      run: npm run typecheck
    gitLeaks:
      run: npm run gitleaks
pre-push:
  parallel: true
  commands:
    branchName:
      run: npx validate-branch-name
    packages-audit:
      tags: frontend security
      run: npm audit
commit-msg:
  parallel: true
  scripts:
    "commitlint.sh":
      runner: bash
```
package.json
```js
{
   "scripts":{
+     "prepare": "lefthook install",
    }
}
```
Run `lefthook install` and try making your first commit.