Aplicando Padrões de Código com Pré-Commit Hook usando Husky

Eduardo Rabelo
4 min readApr 26, 2021

--

Neste artigo, aprenderemos como configurar o Husky para evitar git commits ruins e impor padrões de código em seu projeto.

Introdução

Na maioria dos projetos em que já trabalhei de forma colaborativa, alguém assume o papel de campeão de limpeza de código. Geralmente é o líder da equipe e, muitas vezes, sua função envolve revisar os PRs e garantir que o amor e o cuidado sejam colocados na qualidade do código.

A qualidade inclui as convenções e padrões de código escolhidas, além da formatação do código.

Hoje, é uma boa prática em projetos JavaScript utilizar ESLint para definir as convenções de código do projeto. Por exemplo, como sua equipe se sente em relação ao uso de for loops? E o ponto-e-vírgula - são obrigatórios? Etc.

Essas são convenções.

A outra peça do quebra-cabeça é a formatação. Essa é a aparência visual do código. Quando há mais de um desenvolvedor trabalhando em um projeto, garantir que o código pareça consistente é algo a ser abordado.

Prettier é a ferramenta correta para isso.

No artigo anterior, aprendemos como combinar ESLint e Prettier, mas não aprendemos como realmente aplicar as convenções e a formatação em um projeto da vida real com vários desenvolvedores.

Neste artigo, aprenderemos como usar o Husky para fazer isso em um projeto baseado em Git.

Husky

Husky é um pacote npm que “torna os hooks Git fáceis”.

Quando você inicializa o Git (a ferramenta de controle de versão com a qual você provavelmente está familiarizado) em um projeto, ele vem automaticamente com um recurso chamado hooks.

Se você for até a raiz de um projeto inicializado com Git e digitar:

ls .git/hooks

Você verá uma lista de ganchos de exemplo, como pre-push, pre-rebase, pre-commit e assim por diante. Esta é uma maneira de escrevermos algum código de plugin para executar alguma lógica antes de realizar a ação do Git.

Se quisermos garantir que seu código foi devidamente lintado e formatado, antes de alguém criar um commit usando o comando git commit, poderíamos escrever um hook Git de pre-commit.

Escrever isso manualmente provavelmente não seria divertido. Também seria um desafio distribuir e garantir que os hooks fossem instalados nas máquinas de outros desenvolvedores.

Esses são alguns dos desafios que o Husky pretende resolver.

Com o Husky, podemos garantir que, para um novo desenvolvedor trabalhando em nossa base de código (usando pelo menos o Node versão 10):

  • Hooks são criados localmente
  • Hooks são executados quando o comando Git é chamado
  • Aplicar uma regra que define como alguém pode contribuir para o projeto

Vamos configurá-lo.

Instalando Husky

Para instalar o Husky, execute:

npm install husky --save-dev

Configurando Husky

Para configurar o Husky, na raiz do nosso projeto no package.json, adicione a seguinte chave husky:

{
"husky": {
"hooks": {
"pre-commit": "", // seu comando vai aqui
"pre-push": "", // seu comando vai aqui
"...": "..."
}
}
}

Quando executamos o comando git commit ou git push, o respectivo hook executará o script que fornecemos em nosso package.json.

Fluxo de trabalho de exemplo

Seguindo os exemplos dos artigos anteriores, se configurarmos o ESLint e Prettier, sugiro utilizar dois scripts:

{
"scripts": {
"prettier-format": "prettier --config .prettierrc 'src/**/*.ts' --write",
"lint": "eslint . --ext .ts",
...
},
"husky": {
"hooks": {
"pre-commit": "npm run prettier-format && npm run lint"
}
}
}

Inclua esses scripts no objeto scripts em seu package.json. E execute prettier-format e depois lint com um hook pre-commit.

Isso garantirá que você não possa completar um commit sem que seu código seja formatado de acordo com as convenções do seu time.

Exemplo — Bloqueando um commit

Eu gosto de usar o pacote no-loops como exemplo. Esta convenção não permite aos desenvolvedores usar for loops, ao invés disso, sugere que usamos funções de utilidade do Array como forEach, map e afins.

Adicionando o plug-in e sua regra ao .eslintrc:

{
"root": true,
"parser": "@typescript-eslint/parser",
"plugins": [
"@typescript-eslint",
"no-loops",
"prettier"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"prettier"
],
"rules": {
"no-loops/no-loops": 2, // 2 singifica "retornar um errro"
"no-console": 1,
"prettier/prettier": 2
}
}

E vamos colocar um for loop no código-fonte:

console.log('Hello world!');for (let i = 0; i < 12; i++) {
console.log(i);
}

E ao tentar commitar, veremos um código de saída diferente de zero, o que, como sabemos, significa que ocorreu um erro:

simple-typescript-starter git:(prettier) ✗ git commit -m "Test commit"
husky > pre-commit (node v10.10.0)
> typescript-starter@1.0.0 prettier-format simple-typescript-starter
> prettier --config .prettierrc 'src/**/*.ts' --write
src/index.ts 191ms> typescript-starter@1.0.0 lint /simple-typescript-starter
> eslint . --ext .ts
/simple-typescript-starter/src/index.ts
1:1 warning Unexpected console statement no-console
3:1 error loops are not allowed no-loops/no-loops
4:3 warning Unexpected console statement no-console
✖ 3 problems (1 error, 2 warnings)

E aí está!

Outras considerações

Se você perceber que o lint está demorando muito, verifique este pacote, lint-staged. Ele executa o linter, mas apenas nos arquivos que estão em git stage (arquivos que você está pronto para commitar). Isso me foi sugerido por @glambertmtl. Obrigado!

Créditos

--

--