Guia de setup para um ambiente de desenvolvimento Backend
com Node / TS / Vitest
Neste setup vamos utilizar algumas tecnologias para nos ajudar a manter um código bem escrito e organizado seguindo as boas práticas
Lista das dependências que vamos utilizar no setup
prettier
typescript
@types/node
eslint eslint-plugin-promise eslint-plugin-import eslint-plugin-n @typescript-eslint/eslint-plugin eslint-config-standard-with-typescript
lint-staged
husky
commitlint
vitest @vitest/coverage-c8
Vamos começar a instalar um por um e explicando cada configuração
Antes de tudo vamos iniciar um projeto com npm
npm init -y
E vamos também criar um repositório git dentro do nosso projeto
git init
Precisamos também do arquivo .gitignore para excluirmos alguns arquivos a serem enviados para o GitHub
Crie um na raiz do projeto e adicione o seguinte conteudo:
node_modules
dist
coverage
A pasta node_modules é muito grande e não precisamos enviar para o GitHub
A pasta dist é onde ficará todo nosso código javascript compilado
E a pasta coverage é onde ficará o coverage dos nossos testes e também não precisamos enviar
Instalação das dependências
prettier
Para formatação e organização do nosso código
npm install -D prettier
Vamos também utilizar um arquivo de configuração para o prettier
Crie na raiz do seu projeto um arquivo chamado .prettierrc.json e adicione o seguinte conteúdo:
{
"tabWidth": 2,
"semi": false,
"useTabs": false,
"endOfLine": "lf",
"printWidth": 90,
"singleQuote": true,
"jsxSingleQuote": true,
"bracketSpacing": true,
"bracketSameLine": true,
"trailingComma": "none"
}
E também vamos criar um arquivo .prettierignore para que ele ignore algumas pastas/arquivos
Adicione o seguinte conteúdo:
node_modules
dist
coverage
typescript
Pacote nescessário para utilização do typescript em nosso projeto
npm install -D typescript
Precisamos de um arquivo de configuração para o typescript
Crie na raiz do projeto um arquivo chamado tsconfig.json e adicione o seguinte conteúdo:
{
"compilerOptions": {
"outDir": "./dist",
"module": "CommonJS",
"rootDir": ".",
"target": "ES2020",
"esModuleInterop": true,
"allowJs": true,
"strict": true,
"strictNullChecks": true
}
}
Explicação do arquivo:
outDir: Pasta que será criada para manter todo nosso código compilado
module: Para podermos utilizar import/export mas compilar o código para o formato require
rootDir: Onde ficara nosso projeto central
target: Versão para qual o typescript será compilado
esModuleInterop: Converte import/export para require em módulos que ainda usam esse formato
allowJs: Permite incluir arquivos JS na compilação para pasta Dist
strict: Para habilitar o modo estrito do typescript para melhor qualidade do código
strictNullChecks: Habilita uma verificação mais rigorosa para valores null & undefined
Também vamos criar um outro arquivo para utilizarmos na hora de fazer o build do TS
Crie um arquivo chamado tsconfig.build.json e adicione o seguinte conteúdo:
{
"extends": "./tsconfig.json",
"compilerOptions": {
"rootDir": "./src"
},
"include": ["src"],
"exclude": ["**/*.spec.ts", "**/*.test.ts"]
}
Como esse arquivo será utilizado na hora de fazer o build da aplicação, vamos utilizar o exclude para excluir arquivos que não queremos dentro do nosso build, e também vamos utilizar o include e pegar apenas os arquivos dentro da pasta src
@types/node
npm install -D @types/node
Pacote que adiciona tipagens para o Node que nos ajuda a evitar alguns erros bobos
eslint eslint-plugin-promise eslint-plugin-import eslint-plugin-n @typescript-eslint/eslint-plugin eslint-config-standard-with-typescript
npm install -D eslint eslint-plugin-promise eslint-plugin-import eslint-plugin-n @typescript-eslint/eslint-plugin eslint-config-standard-with-typescript
Como vamos utilizar o padrão Standard Javascript precisamos incluir todos esses pacotes para que o eslint consiga configurar esse padrão para o typescript
Precisamos também de mais dois arquivos de configuração para o ESLINT
Crie na raiz do projeto o arquivo chamado .eslintrc.json e adicione o seguinte conteúdo:
{
"extends": "standard-with-typescript",
"parserOptions": {
"project": "./tsconfig.json"
},
"rules": {
"@typescript-eslint/strict-boolean-expressions": "off",
"@typescript-eslint/space-before-function-paren": "off"
}
}
Explicação do arquivo:
extends: Extensão das regras padrões do ESLINT para adicionar mais regras para o typescript
parserOptions: Opções que podemos passar para análise do parser do ESLINT
project: Para usar as configurações definidas no arquivo tsconfig.json do nosso projeto
rules: Onde podemos habilitar ou desabilitar regras do ESLINT
Também precisamos de outro arquivo chamado .eslintignore que vai sinalizar ao ESLINT quais pastas/arquivos ele deve ignorar na análise de código, crie um e adicione o seguinte conteúdo:
node_modules
dist
coverage
lint-staged
Pacote que vamos utilizar para rodar scripts apenas em arquivos modificados na área staged do Git
npm install -D lint-staged
Precisamos de um arquivo de configuração para o lint-staged
Crie na raiz do projeto um arquivo chamado .lintstagedrc.json e adicione o seguinte conteúdo:
{
"*.ts": ["eslint 'src/**/*' --fix"]
}
Fazendo isso estamos dizendo para o lint-staged rodar o comando em qualquer arquivo dentro de qualquer pasta dentro de src que tenha a extensão .ts
husky
Pacote para podermos adicionar hooks ao git fazendo com que ele rode scripts durante alguns estágios que escolhermos
npm install -D husky
Para habilitar os hooks rode o seguinte comando:
npx husky install
Vamos adicionar um hook que vai rodar um script sempre que fizermos um commit
npx husky add .husky/pre-commit "npx lint-staged"
Esse comando cria na raiz do seu projeto uma pasta chamada .husky onde ficará todos os seus hooks e os scripts que vão ser rodados sempre que alguma ação definida acontecer
Vamos utilizar o lint-staged para rodar scripts nos arquivos modificados do git
O lint-staged será o responsável por rodar os scripts que definimos dentro do arquivo .lintstagedrc.json
commitlint
Para padronizar os commits seguindo o Conventional commits
De baixo dos panos é criado um hook que verifica cada commit e valida se estamos seguindo o padrão Conventional commits
npm install -D @commitlint/config-conventional @commitlint/cli
Vamos precisar criar mais um arquivo de configuração para o commitlint
Crie um arquivo chamado .commitlintrc.json e adicione o seguinte conteúdo:
{ "extends": ["@commitlint/config-conventional"] }
Após instalar o husky e o commitlint e criado o arquivo de configuração use o seguinte comando:
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit ${1}'
Feito isso seu linter de commits deve funcionar normalmente
vitest @vitest/coverage-c8
Pacotes nescessários para utilização do Vitest
npm install -D vitest @vitest/coverage-c8
Após terminar a instalação dos pacotes vamos criar o seguinte arquivo: vitest.config.ts
E dentro desse arquivo adicione o seguinte conteúdo:
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
globals: true,
watch: true,
coverage: {
enabled: true,
include: ['src/**/*.ts'],
exclude: ['src/**/index.ts', 'src/**/*.spec.ts', 'src/**/test/**']
}
}
})
Explicação do arquivo:
Precisamos importar o defineConfig do Vitest para criarmos uma configuração que será utilizada nos testes do Vitest
test: Onde ficará as configurações voltadas para os testes
globals: Por padrão o Vitest não expõe sua API para ser utilizada sem importações assim como Jest faz, então para conseguirmos ter o mesmo comportamento do Jest e não precisar ficar importando describre, test, it etc... podemos colocar essa opção como true
watch: Para ativar o modo watch nos testes
coverage: Onde ficará as configurações do coverage para os testes
enabled: Para ativar a captura de coverage dos arquivos
include: Para incluir todos os arquivos dentro de src que tenham a extensão .ts no coverage
exclude: Para excluir alguns arquivos que não queremos no coverage
Agora vamos criar dois arquivos, um para utilizarmos com testes unitários e testes de integração
Vamos criar primeiro o arquivo para testes unitários
Crie o seguinte arquivo: vitest-unit.config.ts e adicione o seguinte conteúdo:
import { mergeConfig } from 'vite'
import { defineConfig } from 'vitest/config'
import viteConfig from './vitest.config'
export default mergeConfig(
viteConfig,
defineConfig({
test: {
include: ['src/**/*.spec.ts'],
watch: true
}
})
)
Explicação do arquivo:
Aqui precisamos importar o mergeConfig, defineConfig e nossa exportação do arquivo vitest.config.ts
Vamos utilizar o mergeConfig para fazer a junção de uma configuração prévia e mudar algumas coisas, nesse arquivo vamos utilizar o include para pegar apenas os arquivos .spec.ts que será nossa nomenclatura para utilizar em testes unitários
Agora a criação do arquivo de configuração para testes de integração
Crie um arquivo chamado vitest-integration.config.ts
E adicione o seguinte conteúdo:
import { mergeConfig } from 'vite'
import { defineConfig } from 'vitest/config'
import viteConfig from './vitest.config'
export default mergeConfig(
viteConfig,
defineConfig({
test: {
include: ['src/**/*.test.ts'],
watch: true
}
})
)
Explicação do arquivo:
Aqui a única mudança é que vamos pegar os arquivos com a extensão .test.ts
Essa será a nossa nomenclatura para testes de integração
Agora vamos criar nossos scripts no package.json
"build": "npx tsc -p ./tsconfig.build.json",
"lint:fix": "npx eslint src/**/* --fix",
"test": "npx vitest",
"test:unit": "npm test -- --config ./vitest-unit.config.ts",
"test:integration": "npm test -- --config ./vitest-integration.config.ts"
Explicação dos scripts:
build: Script para fazer o build do typescript utilizando a flag -p para utilizarmos o arquivo que criamos para build do typescript
lint:fix: Script para rodar o eslint na aplicação
test: Para utilizar o Vitest para rodar todos os testes
test:unit: Para utilizar o script test e adicionamos uma flag --config para utilizar o arquivo que criamos para rodar apenas em testes unitários
test:integration: Para utilizar o script test e também adicionamos uma flag --config para utilizar o arquivo de configuração para testes unitários
Agora vamos testar se todo nosso setup está rodando corretamente
Crie na raiz do projeto uma pasta chamada src e dentro dela crie um arquivo controller.spec.ts e adicione o seguinte conteúdo:
describe('Controller Test', () => {
test('Should pass the test', () => {
expect(1).toBe(1)
})
})
Após isso rode o comando:
npm run test:unit
Se o setup foi feito corretamente você deverá ver o resultado do seu primeiro teste
Vamos também testar se nossos commits estão padronizados
Rode os seguintes comandos:
git add .
git commit -m "Testando os commits"
E de novo, se o setup foi feito corretamente você deverá ver uma mensagem de erro dizendo que seu commit deve seguir o padrão definido no projeto.