Em minha vida anterior, escrevi em C ++ e depois em C # e usei ambos para compartilhar código compilando-o em um”arquivo de biblioteca”, que eu e minha equipe podíamos copiar para outros projetos. Compartilhar bibliotecas de código como essa é importante para que não estejamos reescrevendo ou duplicando código comum repetidamente para novos projetos.

Parece que deveria ser tão simples fazer isso no TypeScript. Nós apenas compilamos para JavaScript (que podemos considerar como nossa nova versão do “arquivo de biblioteca”) e, em seguida, copiamos o código JavaScript para outros projetos.

Na prática, porém, não é tão simples. Por definição, aplicativos complexos-como os modernos que estamos construindo no TypeScript atualmente-são compostos de várias partes interativas.

Você já codificou no TypeScript e viu uma mensagem de erro como esta?

Arquivo’X’não está em’rootDir”Y’. Espera-se que’rootDir’contenha todos os arquivos de origem. ts (6059)

Isso significa que você está tentando importar código de fora do seu projeto atual. Provavelmente, você está apenas tentando compartilhar o código de um projeto para outro!

Mas, como realmente compartilhamos as bibliotecas de código entre os projetos?

Uma maneira é criar um código separado bibliotecas que fatoram nosso aplicativo por funcionalidade, permitindo-nos reutilizar nosso código mais comumente usado e útil entre os projetos. Veja uma representação disso na Figura 1.

Figura 1: Compartilhando bibliotecas de código entre projetos. A imagem da pasta é de OpenClipArt

Na verdade, não existe uma maneira elegante de fazer isso até o TypeScript v3.0-mas chegaremos a isso em breve.

Esta postagem do blog demonstra os resultados mais recentes da minha pesquisa para encontrar uma maneira similarmente leve de compartilhar bibliotecas de código no TypeScript. Nesta postagem, corrigiremos esse problema antes de passar para métodos mais avançados de compartilhamento de código TypeScript, como compartilhamento entre microsserviços e imagens Docker ou compartilhamento entre o back-end e o front-end.

Podemos conseguir muitos de valor a partir disso, mesmo dentro de um único aplicativo. Quando a natureza da estrutura do aplicativo deve ser separada em componentes (por exemplo, microsserviços, back-end/front-end, micro-front-ends etc.), podemos ver facilmente a necessidade de compartilhar funções e estruturas de dados comuns em um único aplicativo.

Se você deseja DRY seu código entre os componentes do aplicativo, isso é o que você deve saber.

Revisando os métodos padrão

Antes de abordarmos o código de exemplo, vamos revisar brevemente as maneiras padrão de compartilhar código na comunidade JavaScript.

O máximo método comum é publicar nosso código em o registro npm , para que possa ser instalado como uma dependência em qualquer outro projeto onde gostaríamos de usá-lo.

Uma alternativa é publicar nosso código em um repositório Git (não precisa ser GitHub, mas geralmente é) e, em seguida, use npm para instalá-lo diretamente de lá.

Lembre-se de que qualquer método, publicar no registro npm ou publicar no GitHub, pode ser feito publicamente ou em particular. Publicar publicamente é o padrão, é claro, mas você também pode publicar de forma privada ao trabalhar em uma base de código que não seja de código aberto.

Essas abordagens de publicação de JavaScript mais comuns são relevantes porque também são a maneira mais comum de publicar o código TypeScript. (Se você precisar de uma atualização rápida sobre isso, dê uma olhada neste guia de publicação .)

É raro que normalmente publiquemos código TypeScript diretamente em vez de compilá-lo em JavaScript e publicá-lo. Para muitos projetos, porém, publicar uma biblioteca de código compartilhada parece um exagero.

Por um lado, é um grande impedimento para o desenvolvimento rápido e, segundo, parece um desperdício de esforço publicar uma biblioteca que eu’Usarei apenas para alguns microsserviços ou quando quiser reutilizar um modelo de dados em qualquer um dos lados de uma API REST. Na verdade, geralmente não vale a pena publicar, a menos que você queira compartilhar seu modelo de dados com o mundo.

Limitações dos métodos padrão

Durante a codificação de rotina, faço alterações frequentes e iterativas em todo o codebase. (Quando você tiver que trabalhar dessa maneira para pacotes npm, certifique-se de usar npm-link .) Algumas dessas mudanças acabarão sendo desnecessárias ou ruins (por exemplo, elas quebram alguma coisa), mas precisamos de liberdade para experimentar nosso código e depois sair dele quando não estiver funcionando. É fundamental que possamos experimentar e testar nosso trabalho sem precisar confirmá-lo e publicá-lo primeiro.

Normalmente me pergunto as seguintes perguntas antes de publicar minhas bibliotecas de código no npm:

A biblioteca foi projetada para uso autônomo em qualquer outro projeto? Eu quero compartilhar com outras pessoas? O código desta biblioteca é ou deveria ser open source?

Ainda assim, existem alguns outros casos em que publicar em npm pode ser um obstáculo para o desenvolvimento eficiente-e é aí que precisamos encontrar métodos alternativos de compartilhamento de nosso código.

Explorando métodos alternativos para compartilhar código TypeScript

Assim, acima, estabelecemos as seguintes diretrizes para desenvolver um método alternativo de compartilhamento de código:

Em projetos complexos, gostaríamos de fazer uso de bibliotecas de código compartilhadas para que possamos reutilizar o código entre componentes do aplicativo Publicar código em um repositório Git ou npm pode ser desnecessário e até mesmo um impedimento para a velocidade de nosso fluxo de trabalho, dependendo da situação.

Com isso em mente, vamos passar para o evento principal: olhar para uma alternativa meios de compartilhar bibliotecas de código TypeScript entre nossos projetos.

Primeiros passos

Nesta postagem do blog, veremos o código de vários exemplos.

O primeiro exemplo é um ponto de partida básico, mas necessário. Você precisa instalar o Node.js para executá-lo.

O segundo exemplo é mais avançado e mostra como compartilhar uma biblioteca de código em um microsserviço baseado em Docker. Você precisa instalar o Docker para executá-lo.

O terceiro e último exemplo mostra como compartilhar código entre um microsserviço baseado em Docker e um front-end construído com React, TypeScript e Parcel. Você precisa do Docker e do Node.js instalados para construir e executar totalmente este exemplo, mas se quiser construir apenas o front-end, você só precisa do Node.js.

O código está disponível no meu GitHub , e você pode clonar o código para si mesmo se quiser acompanhar:

git clone [email protected]: ashleydavis/sharing-typescript-code-libraries.git

Ou baixe o arquivo zip e descompacte-o em seu computador local.

Os métodos que apresento abaixo permitem uma estrutura flexível de projeto. Você pode usar essas técnicas com um mono-repo ou um meta-repo quando usando a meta-ferramenta . Você não precisa usar nenhum repo se, por algum motivo estranho, não usar o controle de versão-em vez disso, você pode usar essas técnicas com uma estrutura de diretório ad hoc em seu computador local.

Estes os exemplos seguem um layout específico, mas isso não é muito importante. Você pode organizar os arquivos e pastas de acordo com seu gosto e necessidades.

Finalmente, os métodos apresentados aqui também são escalonáveis. Você pode adicionar mais bibliotecas de código compartilhado a cada exemplo e você pode adicionar mais projetos principais (por exemplo, um projeto para cada microsserviço).

Exemplo 1: Uma cartilha para referências de projeto TypeScript

Por favor sinta-se à vontade para pular esta seção se você já sabe como usar as referências do projeto TypeScript.

referências de projetos do TypeScript são realmente muito novas! Eles só estão disponíveis desde o TypeScript v3.0, lançado em 2018. De acordo com a documentação, referências de projeto permitem que você estruture seus programas TypeScript em partes menores, o que ajuda a melhorar os tempos de compilação, impor a separação lógica entre os componentes e organizar seu código de maneiras novas e melhores.

Vamos começar com o exemplo mais básico: um Node.js “Hello, World!” programa que mostra como incluir uma biblioteca de código TypeScript compartilhada em um projeto Node.js. Isso é bastante simples, mas é um bloco de construção necessário para os exemplos mais avançados.

Este exemplo usa apenas uma única biblioteca compartilhada, mas você pode adicionar mais, adicionando referências ao seu arquivo tsconfig.json. Cada um dos projetos referenciados deve ser um projeto TypeScript válido e ter seu próprio arquivo tsconfig.json.

A Figura 2, abaixo, explica a estrutura do projeto de exemplo. Sinta-se à vontade para explorá-lo por si mesmo no GitHub ou em seu computador local se você clonar o projeto .

Figura 2: Usando uma biblioteca TypeScript compartilhada em um projeto Node.js

O código para a biblioteca compartilhada é apresentado na Listagem 1 a seguir. Ele imprime “Hello, World!” para o console.

Listagem 1: Código da biblioteca compartilhada

export function showMessage (): void {console.log (“Hello world! \ n”) ; }

O código do projeto principal importa a função showMessage da biblioteca compartilhada e a chama, conforme mostrado abaixo na Listagem 2.

Listagem 2: O projeto principal usa o código do compartilhado biblioteca

import {showMessage} de”../../libs/my-library”; Mostrar mensagem();

Como eu disse antes, este exemplo não poderia ser muito mais simples.

A parte interessante é como as referências do projeto são configuradas para o projeto principal em seu arquivo tsconfig.json. Um extrato é mostrado abaixo na Listagem 3.

Listagem 3: Extrato do tsconfig.json do projeto principal

“referências”: [{“caminho”:”../libs/my-library”}]

Na Listagem 3, definimos uma matriz de referências para outros projetos TypeScript. Isso conecta nosso projeto principal às suas bibliotecas compartilhadas.

Então, como construímos o projeto de exemplo Node.js?

Primeiro, abra uma janela de terminal e navegue até o diretório do repo principal, em seguida, no exemplo Node.js:

cd sharing-typescript-code-libraries/nodejs-example

Agora, navegue até o projeto principal:

cd my-project

Podemos construir o projeto agora.

Para um projeto TypeScript comum, invocaríamos o compilador TypeScript, como este:

npx tsc

Mas, agora que estamos usando referências de projeto TypeScript, devemos adicionar o argumento–build até o fim:

npx tsc–build

O argumento–build faz com que o compilador TypeScript leia o campo de referências de tsconfig.json. Em seguida, ele constrói cada projeto referenciado antes de construir o projeto principal.

É isso. No nível mais básico, não há mais nada a fazer.

Isso economiza muito tempo! Isso significa que não precisamos invocar separadamente npx tsc para cada biblioteca de código compartilhada e, mais importante, significa que nunca nos esqueceremos de construir uma biblioteca de código depois de alterar seu código-o que nos poupará muito tempo nos perguntando por que nosso a mudança no código não está chegando ao projeto principal.

Como parte da minha convenção pessoal, eu envolvo isso em um script npm chamado build. Visite package.json arquivo se quiser ver sua aparência. Isso significa que posso chamar o npm run build para qualquer projeto e ele será automaticamente convertido em npx tsc–build para projetos TypeScript com bibliotecas compartilhadas. Acho que é um toque legal porque significa que não preciso me lembrar de adicionar–build a cada vez.

A coisa boa sobre esse método de compartilhamento é que ele é altamente extensível. Podemos escalar isso para muitas outras bibliotecas de código compartilhadas e tudo o que precisamos fazer é compilar o projeto principal.

Exemplo 2: Compartilhando bibliotecas de código entre microsserviços e imagens Docker

Vamos considerar um exemplo mais avançado. Imagine que estamos criando um aplicativo de microsserviços em que cada microsserviço é implantado a partir de uma imagem Docker. Compilamos o código que gostaríamos de compartilhar entre os microsserviços e precisamos colocá-lo em cada imagem do Docker.

Embora seja mais avançado, o código de exemplo aqui é essencialmente o mesmo do anterior exemplo, exceto que desta vez iremos compilar nosso projeto principal e biblioteca compartilhada dentro do processo de construção do Docker. Em seguida, agruparemos e copiaremos o código compilado em nossa imagem Docker de produção.

A Figura 3 explica a estrutura deste projeto.

Figura 3: Compartilhamento de código entre microsserviços

O parte importante deste projeto de exemplo é o Dockerfile que usamos para construir a imagem do Docker .

A Figura 4 é uma versão anotada do Dockerfile destacando as partes mais importantes.

Figura 4: Dockerfile anotado que pode compartilhar bibliotecas de código TypeScript

Observe como copiamos todo o projeto raiz aqui. Isso copia o código para nossas bibliotecas compartilhadas e o projeto principal para o estágio de construção de nossa imagem Docker.

Em seguida, invocamos npm run build para compilar TypeScript para JavaScript e agrupar os resultados. Em seguida, usamos a ferramenta instalação recursiva para instalar dependências somente de produção para a biblioteca compartilhada e o projeto principal.

Em última análise, para gerar uma imagem Docker de produção, devemos copiar o pacote de código compilado do estágio de construção para a imagem final. A imagem final deve omitir o código-fonte do TypeScript e as dependências de desenvolvimento, que simplesmente não são necessárias na produção. Apenas o código JavaScript compilado e as dependências de produção são copiados.

Portanto, podemos dizer que a imagem de produção é enxuta e não inchada com os resíduos desnecessários de desenvolvimento, depuração e teste.

Uma pergunta permanece. Como exatamente agrupamos o código JavaScript compilado?

Você não perdeu isso. Na verdade, ele está oculto na compilação de execução do npm. Você pode ver como isso funciona na Listagem 4, que é um extrato do arquivo package.json para o projeto principal.

Listagem 4: Extrato do package.json

“scripts”: {“build”:”tsc–build && ts-project-bundle–out=build”,},

Nosso problema é que o código JavaScript compilado está embutido em cada um dos projetos TypeScript separados. Devemos separar o código compilado, deixando para trás o código-fonte TypeScript original que não precisamos na produção.

Temos algumas opções para uma solução. Poderíamos facilmente copiar o código em nossa imagem Docker de produção adicionando um monte de comandos de cópia ao nosso Dockerfile, mas então precisaríamos de novos comandos de cópia para todas as novas bibliotecas que adicionamos, o que não é muito escalonável. Além disso, é melhor se pudermos ter algo que copie o que temos agora e qualquer coisa que possamos adicionar no futuro.

É aqui que ts-project-bundle entra em jogo, como você pode ver acima na Listagem 4. Esta é uma pequena ferramenta de linha de comando simples que criei (e compartilhada via npm , é claro, porque eu quero que todos usem).

Ele lê os arquivos tsconfig.json para o projeto principal primeiro, depois para cada projeto referenciado. Em seguida, ele copia o código compilado para o diretório de saída de sua escolha. É assim que o código JavaScript compilado é colocado no diretório de compilação e está pronto para ser copiado na imagem Docker de produção final.

Incrivelmente, esse tipo de extração de código e agrupamento não está incluído no compilador TypeScript-e espero que eles adicionem isso no futuro e tornem ts-project-bundle redundante.

Agora, queremos construir a imagem Docker.

Navegue até o diretório do principal projeto e, em seguida, invoque o comando de compilação do Docker como este:

docker build..-f./Dockerfile-t hello-world

Observe como estamos usando o diretório pai como o contexto de compilação. Isso ocorre porque o estágio de compilação do Docker precisa de acesso ao diretório pai, que inclui o código do projeto principal e das bibliotecas compartilhadas.

O Dockerfile está no mesmo diretório do projeto principal para mantê-lo relevante e conectado a esse microsserviço. Outros microsserviços também devem ter seus próprios Dockerfiles, e você pode querer compartilhar um Dockerfile modelado-mas isso é uma postagem de blog diferente.

Como o Dockerfile está em um diretório diferente do contexto de construção, temos que especificar manualmente e é por isso que usamos o argumento-f acima.

Depois de construir a imagem Docker, você pode executar um contêiner como este:

docker run hello-world

Este método de compartilhamento o código é escalonável. Mais uma vez, o exemplo aqui é relativamente simples, mas podemos facilmente adicionar mais bibliotecas de código e microsserviços a ele. Para recapitular:

estamos usando referências de projeto TypeScript para garantir que a construção de qualquer projeto principal também crie suas bibliotecas compartilhadas. Estamos usando ts-project-bundle para agrupar o código de um microsserviço, incluindo todas e quaisquer bibliotecas das quais ele dependa Estamos usando a instalação recursiva para instalar dependências npm para cada microsserviço e todas as suas bibliotecas compartilhadas.

Se você está se perguntando como é o código do pacote, consulte a figura 5 abaixo. Você também pode querer construir o código para si mesmo e inspecioná-lo. Você nem precisará do Docker instalado para fazer isso-basta navegar até o projeto principal, invocar npm run build e vasculhar o subdiretório de build gerado para ver o código compilado e empacotado.

Figura 5: Saída da compilação processo

Exemplo 3: Compartilhando bibliotecas de código entre back-end e front-end

Este exemplo também tem um microsserviço Docker, mas funciona da mesma maneira que no exemplo anterior, então não vou explicar isso novamente.

Agora vamos nos concentrar em compartilhar a biblioteca de código para o front-end. A IU de exemplo é criada com TypeScript e React e é fornecida com Parcel.

A Figura 6 explica a estrutura deste projeto.

Figura 6: Compartilhamento de código entre back-end e front-end

Este exemplo tem os diretórios backend, frontend e libs. Novamente, o layout do projeto é extensível: você pode adicionar mais bibliotecas ao diretório libs e mais microsserviços ao diretório backend.

A estrutura também é flexível: você pode colocar tudo em um mono-repo ou você pode separá-lo totalmente como um meta-repo com repositórios de código individuais para cada microsserviço, cada biblioteca e o front-end.

A Listagem 5 mostra o código HTML para nosso exemplo de front-end .

Listagem 5: Arquivo HTML simples para o frontend

Um frontend React simples usando Parcel