Introdução
Nesse artigo vou abordar a criação e publicação de um package escrito em Typescript para o NPM. A minha principal motivação é escrever algo que seja simples mas não deixe de levar em consideração fatores importantes, como versionamento, atualização, testes e automatização. O que será abordado:
- Criar um package em TS
- Boas práticas de teste pre release
- Publicar um package público no npm
- Gerenciar atualizações
- Noções de versionamento
- Automatizar a publicação com github actions
Pré-requisitos
Configurando o ambiente para o package
Criando o repositório
Nosso package vai se chamar math-ops e será responsável por fazer operações matemáticas básicas. Sendo assim, vamos criar um novo repositório com o nome escolhido:
Inicializando o repositório
Vamos criar um novo diretório e acessá-lo:
mkdir math-ops && cd math-ops
Em seguida vamos configurar o repostório git e enviar o primeiro commit.
echo "# math-ops" >> README.md
git init
git add README.md
git commit -m "first commit"
git branch -M main
git remote add origin git@github.com:thiagomr/math-ops.git
git push -u origin main
Configurando o package
Inicializando as configurações do NPM
npm init
Como resultado deste comando teremos a seguinte saída:
//package.json
{
"name": "@thiagomr/math-ops",
"version": "0.1.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/thiagomr/math-ops.git"
},
"author": "Thiago Moraes",
"license": "MIT",
"bugs": {
"url": "https://github.com/thiagomr/math-ops/issues"
},
"homepage": "https://github.com/thiagomr/math-ops#readme"
}
Nessa etapa é importante notar que definimos o nome do package (você deve mudar de acordo com o seu nome de usuário ou nome do package que você desejar), que será utilizado para instalar o mesmo. Também foi definida a versão 0.1.0, seguindo os padrões de Semantic Version. Este é um padrão que nos permite incrementar a versão de acordo com o tipo da atualização. Você pode se aprofundar sobre o assunto aqui. Vamos considerar que estamos criando uma release não oficial, ou seja, anterior à versão 1.0.0. É interessante também perceber que como ja temos o git configurado nesse diretório, o npm automaticamente sugere o preenchimento das configurações de url e homepage do mesmo.
Instalando as dependências
Em seguida vamos instalar as dependências que utilizaremos no projeto, que são basicamente o Typescript e o Jest (ferramenta que utilizaremos para escrever testes para nossas funcionalidades):
npm install typescript jest @types/jest ts-jest --save -D
Vamos adicionar o arquivo tsconfig.json
com as configurações que usaremos para compilar o projeto:
//tsconfig.json
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"sourceMap": true,
"outDir": "./lib",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"declaration": true
},
"include": [
"src"
],
"exclude": [
"**/*.spec.ts"
]
}
Em seguida, iremos adicionar alguns scripts para fazer o build do projeto e uma configuração bem simples para o Jest
. Nosso arquivo com as alterações ficará da seguinte forma:
//package.json
{
"name": "@thiagomr/math-ops",
"version": "0.1.0",
"description": "A package to make basic math operations",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"scripts": {
"clean": "rimraf lib",
"build": "npm run clean && tsc",
"test": "jest",
"prepublish": "npm run test && npm run build"
},
"author": "Thiago Moraes",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/thiagomr/math-ops.git"
},
"bugs": {
"url": "https://github.com/thiagomr/math-ops/issues"
},
"homepage": "https://github.com/thiagomr/math-ops#readme",
"jest": {
"preset": "ts-jest",
"testEnvironment": "node",
"coveragePathIgnorePatterns": [
"/node_modules/",
"lib"
]
},
"files": [
"lib/**/*"
],
"devDependencies": {
"@types/jest": "^26.0.23",
"jest": "^27.0.4",
"ts-jest": "^27.0.3",
"typescript": "^4.3.2"
}
}
Algumas explicações mais detalhadas:
-
"main": "lib/index.js",
Será o arquivo que vai expor a API pública do nosso pacote. -
"types": "lib/index.d.ts",
Indica a declaração de tipos do TS, gerado automáticamente de acordo com as nossas configurações de compilação feitas anteriormente. -
"jest": {...}
Configuração para que o Jest funcione usando TS e indicação de arquivos a serem ignorados. -
"files": {...}
Arquivos que desejamos incluir no nosso pacote. -
"prepublish"
Executa um script antes de publicar o package. Neste caso vamos rodar os testes unitários. -
"build"
Faz a compilação do projeto. De maneira bem resumida, seria o processo de typechecking e transpilação do código TS para JS.
Para finalizar essa etapa, vamos criar um arquivo .gitignore
:
//.gitignore
node_modules
lib
E então vamos enviar toda a configuração para o repositório:
git add .
git commit -m "add dependecies"
git push
Criando a primeira funcionalidade
Agora vamos adicionar o arquivo com a primeira funcionalidade, que irá retornar a soma entre dois números:
// src/sum.ts
const sum = (firstNumber: number, secondNumber: number): number => {
return firstNumber + secondNumber;
}
export {
sum
}
Vamos criar também um arquivo de entrada para exportar a API pública do nosso package:
// src/index.ts
export * from './sum';
Testando o package
Vamos escrever nosso primeiro teste untário para a funcionalidade de sum
:
// src/sum.spec.ts
import { sum } from './sum';
test('should return a sum of two numbers', () => {
const result = sum(3, 2);
expect(result).toEqual(5);
});
Rodando os testes:
npm test
Agora que já temos nosso primeiro teste unitário, vamos testar o uso real do package. Nós vamos utilizar o comando npm link
. Isso fará com que o npm crie uma referência local para esse projeto, podendo ser utilizado de forma direta em outros projetos. É uma forma de testar seu package no ambiente de desenvolvimento sem precisar publicá-lo várias vezes.
O primeiro comando será executado no diretório do package e o segundo em um novo diretório para testes:
# math-ops
npm link
# testdir
npm link @thiagomr/math-ops
No diretório de testes, vamos criar um arquivo que importa e utiliza a funcionalidade sum
:
// testdir/index.ts
const { sum } = require('@thiagomr/math-ops');
const result = sum(2, 3);
// sum is 5
console.log(`sum is ${result}`);
Para finalizar, vamos atualizar nosso repositório com as novas mudanças:
git add .
git commit -m "add sum feature"
git push
Publicando no NPM registry
Agora que já temos nosso pacote funcional e testado localmente, vamos para a fase de publicação. Vamos adicionar um arquivo .npmignore que vai excluir os arquivos desnecessários, enviando apenas o essencial e diminuindo o tamanho do package:
//.npmignore
src
node_modules
Iremos fazer o login no npm com a conta desejada:
npm login
Por padrão o versionamento do NPM utiliza o sistema de tags do GIT para indicar a publicação de novas versões. É importante manter o versionamento do NPM e GIT sincronizados, mas vale ressaltar que não há nenhuma regra que faça uma ligação entre as duas coisas. Existem alguns packages que facilitam o gerenciamento de ambos, mas como a intenção aqui é mostrar a funcionalidade básica e o mais pura possível, não utilizaremos nenhuma ferramenta adicional.
Vamos atualizar o repositório com a tag de versão incial e em seguida publicar no npm:
git tag v0.1.0
git push --tags
npm publish --access=public
Agora já temos nosso pacote publicado e disponível para instalação (O meu está em uma versão um pouco a frente pois fiz alguns testes para o artigo):
Adicionando novas funcionalidades
Nessa etapa vamos adicionar uma nova feature. Isso vai reforçar os conceitos aplicados, trazendo familiaridade com o processo. Vamos adicionar uma funcionalidade que retorna o resultado da multiplicação entre dois números index.ts
:
// src/times.ts
const times = (firstNumber: number, secondNumber: number): number => {
return firstNumber * secondNumber;
}
export {
times
}
//src/index.ts
export * from './sum'
export * from './times'
Seguindo o processo anterior, vamos escrever um teste unitário para a nova funcionalidade:
//src/times.spec.ts
import { times } from './times';
test('should return the multiplication of two numbers', () => {
const result = times(3, 3);
expect(result).toEqual(9);
});
Vamos atualizar o repositório e publicar a nova versão:
git add .
git commit -m "add times feature"
npm version minor
git push --tags
npm publish --access=public
Agora você poderá o ver o package atualizado no NPM registry.
Automatizando a publicação com Github Actions
Agora que já vimos como fazer todo o processo manualmente, vamos automatizar a publicação utilizando Github Actions.
Vamos criar um token no NPM aqui, para que seja possível fazer a publicação através do Github. Vamos inserir nosso token como uma variável de ambiente com o nome NPM_TOKEN
, acessando o repostório e em seguida selecionado as opçoes Settings > Secrets > New Repository Secret
.:
Em seguida vamos criar o arquivo de configuração do pipeline para que seja executado sempre que ouver uma alteração na branch main e no arquivo package.json
:
# .github/workflows/publish.yml
on:
push:
branches: [ main ]
paths:
- 'package.json'
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-node@v1
with:
node-version: 14
- run: npm install
- uses: JS-DevTools/npm-publish@v1
with:
token: ${{ secrets.NPM_TOKEN }}
Para testar o pipeline, nós criaremos a última funcionalidade ao nosso package, capaz de subtrair dois números:
const subtract = (firstNumber: number, secondNumber: number): number => {
return firstNumber - secondNumber;
}
export {
subtract
}
Vamos novamente atualizar a entrada do package:
//src/index.ts
export * from './sum'
export * from './times'
export * from './subtract'
Assim como nos passos anteriores, vamos criar um teste unitário para o mesmo:
// src/subtract.spec.ts
import { subtract } from './subtract';
test('should return the subtraction of two numbers', () => {
const result = subtract(4, 4);
expect(result).toEqual(0);
});
Agora vamos enviar nossas alterações para o repositório e atualizar a versão do nosso package:
git add .
git commit -m "add subtract feature"
npm version minor
git push --tags
Se a nossa configuração estiver correta e tudo ocorrer bem, podemos verificar o pipeline no Github executado com sucesso e a nova versão publicada no NPM:
Agora vamos adicionar um exemplo de uso do package que também estará no README.md
do repositório:
// Install
npm install @thiagomr/math-ops
import { sum } from '@thiagomr/math-ops';
//6
console.log(sum(4, 2));
Conclusão
Isso é tudo pessoal. Aqui está o link do repistório com todo o código utilizado. Espero que tenha ficado claro e que de alguma forma possa ajudar vocês a publicarem seus próprios packages. Gostaria de ouvir feedbacks, opiniões, sugestões e o que mais desejarem. Me sigam no Twitter para mais novidades. Grande abraço e até a próxima!
Top comments (0)