Enforce JavaScript Code Quality with Husky and Hooks

Building a robust eco-system to enforce code quality with JavaScript

Viduni Wickramarachchi
Bits and Pieces

--

Ensuring code quality is very important for a maintainable and scalable application. But how can we enforce these quality standards?

Well, with JavaScript, you can use ESLint to define coding conventions and use Prettier for consistent code formatting. If you have these two configured, the first step is complete.

Now that we have standards in place, how do we enforce them? This is where Husky comes to play. Husky is used to enforcing standards in Git-based projects. It acts similarly to how Bit enforces standards on independent components before they are tagged with a new release version, end exported to various remote scopes (more on that, later).

What are Hooks?

When you initialize a project with Git (git init ), it automatically comes with a feature called Hooks. You can view these in[projectPath]/.git/hooks .

There are many Git Hooks. Some of them are as follows.

  • pre-commit — The hook that is used to ensure that all coding standards are enforced before a commit is made. This will run when you make the git commit command.
  • pre-push — Ensures coding rules are met before pushing to a remote repository.
  • pre-rebase — Similar to the above, this enforces the rules before a rebase is done.

All the Hooks available and their usages can be found here.

However, writing these Hooks manually and ensuring that all developers have them on their machines is a cumbersome process. This is where Husky comes into play.

What is Husky?

Husky automates the process of adding Hooks. When the project dependencies are installed, Husky will make sure that all Hooks will be installed in the developer’s machine locally for that particular project based on the configs in the package.json . This makes it very easy to manage and distribute Hooks as no manual invention is required.

With Husky, the following happens.

  • Hooks get created locally.
  • Hooks are run when the relevant Git command is called.
  • The policy that defines how someone can contribute to a project is enforced.

Setting up Husky in practice

You can install Husky using the following command.

npm install husky --save-dev

Configuring Husky is very easy. This can be added to the package.json .

"husky": {
"hooks": {
"pre-commit": "", // pre-commit command goes here
"pre-push": "", // pre-push command goes here
"...": "..."
}
}

As such, any hook that you require can be included here.

Let’s look at an example.

If you want to ensure that all the lint rules are met before committing new changes, the following can be done.

{
"scripts": {
"lint": "eslint . --ext .js",
},
"husky": {
"hooks": {
"pre-commit": "npm run lint"
}
}
}

This should be included in the package.json . This will ensure that you cannot complete a Git commit without the esLint checks being passed.

More uses of Husky in practice

So far, we have looked at the most basic use of Husky. Are there more things that we can do with this package? Let’s have a look.

  1. Husky has the ability to run any command which is combined with other packages. (E.g.: Prettier, Linters such as EsLint, check linting for the files ready to be committed with lint-staged, etc.)
  2. Husky has the ability to validate commit messages with the use commit-msg similar to pre-commit rules.
  3. We can use the pre-commit command to run all our unit tests and integration tests, which makes sure that we don’t commit any breaking changes.

However, a point to note is that Husky commands can be skipped if you use the no-verify flag with your Git command.

Supported Hooks

Husky supports all Git Hooks defined here. Server-side Hooks (pre-receive, update and post-receive) aren't supported.

Features of Husky

There are few highlighted features of Husky that I would like to mention.

  • Zero dependencies and lightweight (6KB)
  • Powered by modern new Git feature (core.hooksPath)
  • Follows npm and Yarn best practices regarding autoinstall
  • User-friendly messages
  • Optional install
  • Husky 4 supports platforms such as macOS, Linux, and Windows
  • Further, it supports Git GUIs, Custom directories, Monorepos

Bonus: Enforcing a single standard on independent components

Independent components are developed and versioned in Bit workspaces. They are each independently developed, versioned, and collaborated on. They enable easy cross-project collaboration on components with a CI that propagates from a single modified component to all its direct and indirect dependent components.

An independent ‘drop-down’ component rendered in isolation. The component’s dependency graph was generated by Bit. It is used for component isolation and to propagate the CI from one component to all its dependents.

There is no single CI for all components. Instead, each independent component uses a CI that is part of the component dev environment. That means, decoupled and separately developed components may use the same CI in different workspaces or remote scopes.

When using Bit, the CI runs before a component is tagged with a new release version.

Bit.dev’s Ripple CI that run on all dependent components, across projects

--

--