DEV Community

Cover image for Como publicar seu primeiro package Typescript e automatizar com Github Actions
Thiago Moraes
Thiago Moraes

Posted on

Como publicar seu primeiro package Typescript e automatizar com Github Actions

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

  • Criar uma conta no Github
  • Criar uma conta no NPM

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:

Screenshot from 2021-06-13 19-07-03

Inicializando o repositório

Vamos criar um novo diretório e acessá-lo:

mkdir math-ops && cd math-ops
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Configurando o package

Inicializando as configurações do NPM

npm init
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"
  ]
}

Enter fullscreen mode Exit fullscreen mode

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"
  }
}


Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

E então vamos enviar toda a configuração para o repositório:

git add .
git commit -m "add dependecies"
git push
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

Vamos criar também um arquivo de entrada para exportar a API pública do nosso package:

// src/index.ts

export * from './sum';
Enter fullscreen mode Exit fullscreen mode

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);
});

Enter fullscreen mode Exit fullscreen mode

Rodando os testes:

npm test
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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}`);
Enter fullscreen mode Exit fullscreen mode

Para finalizar, vamos atualizar nosso repositório com as novas mudanças:

git add .
git commit -m "add sum feature"
git push
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Iremos fazer o login no npm com a conta desejada:

npm login
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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):

Screenshot from 2021-06-14 20-51-47

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
}
Enter fullscreen mode Exit fullscreen mode
//src/index.ts

export * from './sum'
export * from './times'
Enter fullscreen mode Exit fullscreen mode

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);
});

Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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.:

Screenshot from 2021-06-14 20-58-56

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 }}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

Vamos novamente atualizar a entrada do package:

//src/index.ts

export * from './sum'
export * from './times'
export * from './subtract'
Enter fullscreen mode Exit fullscreen mode

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);
});

Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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:

Screenshot from 2021-06-14 21-04-51

Screenshot from 2021-06-14 21-05-12

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
Enter fullscreen mode Exit fullscreen mode
import { sum } from '@thiagomr/math-ops';

//6
console.log(sum(4, 2));

Enter fullscreen mode Exit fullscreen mode

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)