Um Guia: Como Criar e Publicar um package NPM em TypeScript
Introdução
Já te aconteceu copiar e colar os mesmos pedaços de código entre diferentes projetos? Este era um problema constante para mim, por isso comecei a desenvolver packages em TypeScript que me permitissem reutilizar partes úteis de código. Este guia vai mostrar-te, passo a passo, como criar um package utilizando TypeScript e publicá-lo no Node Package Manager (NPM).
Porque usar TypeScript?
Usar TypeScript vai proporcionar-te uma melhor experiência de desenvolvimento, sendo o teu melhor amigo durante o processo. Ele vai “gritar” contigo sempre que cometeres um erro. No início, pode parecer que o strong typing diminui a produtividade e que não compensa utilizá-la. Mas, acredita em mim, o TypeScript tem algumas vantagens muito impactantes:
Optional Static Typingya – Podes adicionar types a variáveis, funções, propriedades, etc. Isto ajuda o compilador a detetar e avisar sobre possíveis erros no código antes mesmo do pacote ser executado. Os types são ótimos quando se usa libraries, pois mostram aos desenvolvedores exatamente que tipo de dados são esperados.
Intellisense – Uma das maiores vantagens do TypeScript é a sua capacidade de code completion e IntelliSense, fornecendo sugestões ativas à medida que vais escrevendo código.
Código mais robusto e fácil de manter.
Na minha opinião, o TypeScript deve ser o teu melhor amigo ao construir packages!
Vamos lá cozinhar!
O primeiro passo é criar a pasta do teu package, escolhendo um nome criativo.Cria um repositório Git. A seguir, vamos criar um repositório Git remoto para o teu pacote. Embora a criação de repositórios Git não seja o foco deste artigo, o GitHub fornece instruções claras e simples para te ajudar a criar um repositório. Basta seguires os passos apresentados no GitHub durante a criação do repositório, e, quando estiveres pronto, volta aqui para continuar!Inicia o teu packageDepois de criares o repositório, precisas de criar um package.json. Este é um ficheiro JSON que reside na pasta principal do projeto. O package.json contém informações importantes sobre o projeto, como o nome e a descrição do mesmo, metadados funcionais como o número da versão do pacote, scripts a executar no CLI e uma lista de dependências necessárias para o projeto.Após isso, precisamos de criar um ficheiro .gitignore na raiz do projeto. Não queremos que código desnecessário entre no repositório ✋. Por agora, só precisamos de ignorar a pasta node_modules.Excelente trabalho! Assim deverá estar o teu projeto no Visual Studio Code e no repositório Git. A partir deste ponto, irei continuar a adicionar ficheiros diretamente do Visual Studio Code.Vamos adicionar o Typescript como uma DevDependency Vamos instalar uma versão mais estável do Typescript, que é compatível com múltiplos pacotes que serão usados durante este guia.Usando a flag --save-dev, estamos a dizer ao NPM para instalar o Typescript como uma devDependency. Isto significa que o Typescript será instalado apenas quando executares o npm install, mas não quando o utilizador final instalar o pacote.O Typescript é necessário para desenvolver o package, mas não é necessário quando o package é utilizado.Para compilar o Typescript, precisamos de criar um ficheiro tsconfig.json na raiz do projeto. Este ficheiro corresponde à configuração do compilador Typescript (tsc).Existem muitas opções de configuração no campo tsconfig.json, e é importante estar ciente do que elas fazem.
target: a linguagem usada para a saída compilada. Compilar para es6 tornará o nosso pacote compatível com os navegadores mais recentes.
module: o gestor de módulos usado na saída compilada.
declaration: Deve estar definido como true ao construir um package. Isto permite que o Typescript exporte também os ficheiros de definição de types juntamente com o código JavaScript compilado, permitindo que o package seja usado tanto com Typescript quanto com JavaScript.
outDir: A saída compilada será gravada nesta pasta.
include: Caminho para os arquivos de origem. Neste caso, a pasta src.
exclude: O que queremos excluir da compilação pelo tsc (compilador Typescript).
Vamos codificar! Agora, com a configuração do Typescript pronta, estamos prontos para codificar uma função simples que recebe parâmetros e os multiplica, retornando o resultado da operação. Para isso, vamos criar uma pasta src na raiz do projeto e adicionar um arquivo index.ts:Depois adiciona um script de build ao arquivo package.json: Agora, executa o comando de build no console: Isto irá compilar o teu código Typescript e criar uma nova pasta chamada lib na raiz do projeto, contendo o código compilado em JavaScript e as definições de tipo. É necessário adicionar a pasta lib ao teu arquivo .gitignore. Não é recomendado que arquivos gerados automaticamente sejam enviados ao repositório remoto do Git, pois isso pode causar conflitos desnecessários.Formatação e Linting Um bom pacote deve incluir regras para linting e formatação. Este processo é importante quando várias pessoas estão a trabalhar/contribuir para o mesmo projeto, garantindo que todos estejam alinhados em relação à sintaxe e ao estilo do código. Tal como fizemos com o Typescript, estas ferramentas são usadas apenas para o desenvolvimento do pacote e, por isso, devem ser adicionadas como devDependencies. Vamos começar por adicionar o ESLint ao nosso pacote:
eslint: Biblioteca principal do ESLint.
@typescript-eslint/parser: Um parser que permite ao ESLint compreender código TypeScript.
@typescript-eslint/eslint-plugin: Plugin com um conjunto de regras recomendadas para TypeScript.
Assim como acontece com as definições do compilador TypeScript, pode-se usar a linha de comando para gerar um ficheiro de configuração ou criá-lo manualmente no VSCode. Em ambos os casos, é necessário um ficheiro de configuração para o ESLint. Cria um ficheiro .eslintrc de raiz: Podes usar a seguinte configuração inicial e depois explorar a lista completa de regras para ajustar as definições do ESLint às tuas necessidades:
parser: indica ao ESLint para processar o código através de um parser ao analisá-lo.
plugins: define os plugins que está a usar.
extends: diz ao ESLint que configuração deve herdar. A ordem é importante.
env: indica em que ambientes o seu código será executado.
Agora vamos adicionar um script de lint ao package.json. Adicionar a flag --ext especificará quais extensões o lint deve considerar. Por padrão, é .js, mas também usaremos .ts. Não é necessário fazer linting em alguns ficheiros, como a pasta lib. É possível evitar o linting de ficheiros e pastas desnecessários ao criar um ficheiro .eslintignore.Agora o ESLint está pronto! Recomendo a integração do ESLint com o editor de código que preferires usar. No VSCode, vai até as extensões e instala a extensão do ESLint. Para verificar o teu código usando o ESLint, podes executar manualmente o script na linha de comandos:Agora vamos configurar o PrettierÉ comum o uso do ESLint e do Prettier ao mesmo tempo, por isso, vamos adicionar o Prettier ao nosso projeto:O Prettier não necessita de um ficheiro de configuração, pode simplesmente ser executado e utilizado imediatamente. Caso queiras definir a tua própria configuração, será necessário criar um ficheiro .prettierrc na raiz do teu projeto.Se estiveres curioso para saber mais, deixo aqui uma lista completa de opções de formatação e o Prettier Playground.Vamos adicionar o comando Prettier aos nossos scripts. Vamos também configurar o suporte a todos os ficheiros que terminem em .ts, .js, e .json, e ignorar os mesmos ficheiros e diretórios que estão no .gitignore (ou criar um ficheiro .prettierignore). Agora, basta executar o comando npm run format para formatar e corrigir todo o código.Conflitos entre ESLint e Prettier É possível que o Prettier e o ESLint gerem conflitos devido a regras comuns que se sobrepõem. A melhor solução para isso é usar o eslint-config-prettier, que desativa todas as regras do ESLint irrelevantes para formatação de código, já que o Prettier é eficaz nessa tarefa.Para fazer isto funcionar, precisas de ir ao ficheiro .eslintrc e adicionar o Prettier no final da tua lista de extends, para desativar qualquer outra regra anterior de outros plugins. Com isso, a secção de formatação e linting está concluída! Excelente trabalho!Configuração de testes com Jest Na minha opinião, todo o pacote deve incluir testes unitários! Vamos adicionar o Jest para nos ajudar com isso. Como estamos a usar o Typescript, também precisamos adicionar o ts-jest e @types/jest.Cria um ficheiro jestconfig.json na raiz:Agora, vamos atualizar o antigo script de testes no nosso ficheiro package.json:O teu ficheiro package.json deverá ficar algo assim: Agora, vamos escrever um teste básico! Na pasta src, cria uma nova pasta chamada tests, e dentro dessa pasta, adiciona um ficheiro com o nome que preferires, mas deve terminar com .test.ts. Por exemplo, multiplier.test.ts. Neste teste simples, vamos passar os números 2 e 3 como parâmetros para a nossa função Multiplier e esperar que o resultado seja 6. Agora, basta executá-lo.Funciona! Bom trabalho! O teste passou com sucesso, como podes ver! Isso significa que a nossa função está a multiplicar corretamente. Scripts mágicos do package.json Existem muitos scripts mágicos disponíveis para uso no ecossistema do Node Package Manager. É bom automatizar o nosso pacote tanto quanto possível. Nesta secção, vamos analisar alguns destes scripts no npm: prepare, prepublishOnly, preversion, version e postversion.
prepare: O script prepare é executado quando as dependências do Git estão a ser instaladas. Este script corre após o prepublish e antes do prepublishOnly. Perfeito para construir o código.
prepublishOnly: Este comando serve o mesmo propósito que prepublish e prepare, mas é executado apenas durante o comando npm publish.
preversion: Este comando será executado antes de aumentar a versão de um novo package. Perfeito para verificar o código com linters.
version: Este comando será executado depois de uma nova versão ser incrementada. Se o teu package tem um repositório Git, como no nosso caso, um commit e uma nova tag de versão serão feitos sempre que a versão for aumentada. Este comando será executado ANTES de o commit ser feito.
postversion: Será executado depois de o commit ter sido feito. Um local perfeito para fazer o push do commit, bem como da tag.
O meu package.json após a implementação dos novos scripts:Antes de publicarQuando adicionámos o .gitignore ao nosso projeto com o objetivo de não passar os ficheiros de build para o repositório Git, desta vez o oposto acontece para o package publicado. Não queremos que o código-fonte seja publicado com o package, apenas os ficheiros de build.Isso pode ser resolvido adicionando a propriedade files no package.json:Agora, apenas a pasta lib será incluída no package publicado! Detalhes finais no package.json Finalmente, é hora de preparar o nosso package.json antes de publicar o package:Estes toques finais no package.json incluem adicionar uma boa descrição, palavras-chave e um autor. Isto é importante, pois informará ao NPM de onde ele pode importar os módulos.Compromete-te e envia o código Chegou a hora de enviar todo o teu trabalho para o repositório remoto!Publica o teu pacote no NPM! Para poderes publicar o teu package, é necessário criar uma conta no NPM. Se ainda não tens uma conta, podes criar uma em: https://www.npmjs.com/signupExecuta o comando npm login para fazer login na tua conta do NPM. Depois, tudo o que precisas de fazer é publicar o teu package com o comando:Se tudo correu bem, agora podes ver o teu package em https://www.npmjs.com/.Temos um package! Excelente trabalho!Aumentar para uma nova versão Vamos aumentar a versão do nosso package utilizando os nossos scripts: Isto criará uma nova tag no Git e enviá-la-á para o repositório remoto. Agora, basta publicar novamente: Com isto, tens uma nova versão do teu pacote! Parabéns! Chegaste ao fim. Esperamos que agora saibas como começar a construir o teu incrível package!