Criando monorepo com biblioteca reutilizavel, usando PNPM (React + TypeScript)

Criando monorepo com biblioteca reutilizavel, usando PNPM (React + TypeScript)

Introdução:

Neste artigo aborda a criação de um monorepo para desenvolvimento de projetos, com destaque para a configuração de um workspace e a publicação de um pacote no GitHub Package Registry. Demonstramos também como utilizar esse pacote em um novo projeto externo ao monorepo, fornecendo uma visão abrangente do desenvolvimento modular e colaborativo.

Passo 1: Configurando o Ambiente Inicial

Vamos começar criando o diretório do projeto e inicializando nosso workspace monorepo usando pnpm. Primeiro, crie uma nova pasta para o projeto no local de sua preferência. No exemplo abaixo, criaremos a pasta no diretório home:

mkdir ~/monorepo-project

Essa pasta servirá como o workspace do nosso monorepo. Em seguida, navegue até essa pasta e inicialize um novo projeto Node.js com pnpm:

cd ~/monorepo-project
pnpm init

Após inicializar o projeto, vamos configurar o controle de versão com Git e criar um arquivo .gitignore para ignorar a pasta node_modules:

git init
echo -e “node_modules” > .gitignore

O comando git init inicializa um novo repositório Git no diretório atual, e o comando echo -e “node_modules” > .gitignore cria um arquivo .gitignore com a entrada node_modules, que informa ao Git para ignorar essa pasta, evitando que ela seja incluída nos commits.

Finalmente, vamos configurar o projeto para usar módulos ES6 alterando o tipo de módulo no arquivo package.json:

npm pkg set type=”module”

Isso adiciona a seguinte linha ao seu package.json:

{
….
“type”: “module”
}

A configuração “type”: “module” permite que você use a sintaxe de módulos ES6 (import/export) no seu projeto Node.js.

Passo 2: Estruturando o Projeto

Agora que temos o ambiente inicial configurado, vamos estruturar nosso monorepo criando as pastas necessárias e inicializando um projeto de biblioteca de componentes.

Primeiro, crie duas pastas chamadas packages e apps:

mkdir packages apps

A pasta packages será usada para armazenar pacotes reutilizáveis, como nossa biblioteca de componentes, e a pasta apps conterá nossos aplicativos.

Criando o Projeto de Biblioteca de Componentes (UI Kit)

Dentro da pasta packages, vamos criar um projeto de biblioteca de componentes chamado uikit usando o Vite. Para isso, execute os seguintes comandos:

cd packages
pnpm create vite

Durante a criação do projeto com o Vite, você será solicitado a fornecer algumas informações, como o nome do projeto e o template a ser usado. Siga as instruções e escolha as opções apropriadas para o seu projeto de biblioteca de componentes.

Depois de concluir a criação do projeto, sua estrutura de diretórios deverá se parecer com isto:

monorepo-project/
├── packages/
│ └── uikit/
├── apps/
├── package.json
├── .gitignore
├── pnpm-lock.yaml

Passo 3: Criando o Componente de UI

Agora que temos nosso projeto de biblioteca de componentes configurado, vamos criar um simples componente de botão dentro do uikit.

Primeiro, navegue até a pasta uikit:

cd uikit
pnpm install

Em seguida, dentro da pasta src do projeto uikit, crie a estrutura de diretórios components/Button:

mkdir -p src/components/Button

Dentro dessa pasta Button, crie um arquivo chamado index.tsx e adicione o seguinte código:

// src/components/Button/index.tsx
export default function ButtonTeste() {
return <div style={{ backgroundColor: ‘green’, borderRadius: ’25px’, padding: ’10px’ }}>Button</div>
}

Este código define um componente React simples chamado ButtonTeste que renderiza um botão estilizado com uma cor de fundo verde, bordas arredondadas e padding.

Após criar o componente, sua estrutura de diretórios deve se parecer com isto:

cssCopiar código
monorepo-project/
├── packages/
│ └── uikit/
│ └── src/
│ └── components/
│ └── Button/
│ └── index.tsx
├── apps/
├── package.json
├── .gitignore
├── pnpm-lock.yaml

Passo 4: Configurando o Vite para Modo Biblioteca

Para que nosso projeto uikit funcione como uma biblioteca, precisamos ajustar a configuração do Vite. Por padrão, o Vite busca um arquivo index.html como ponto de entrada no modo de aplicação. No entanto, queremos que ele procure main.ts para exportar nossos componentes.

Instalando o Plugin DTS

Primeiro, vamos instalar o plugin DTS, que gera arquivos de declaração (*.d.ts) a partir de arquivos .ts(x) quando o Vite está configurado no modo biblioteca. Execute o seguinte comando dentro da pasta uikit:

pnpm add -D vite-plugin-dts

Configurando o Vite

Se o arquivo vite.config.ts não existir, crie-o na raiz do projeto uikit e adicione o seguinte código:

// vite.config.ts
import { defineConfig } from ‘vite’;
import { resolve } from ‘path’;
import dts from ‘vite-plugin-dts’;

export default defineConfig({
build: {
lib: {
entry: resolve(__dirname, ‘src/main.ts’),
formats: [‘es’, ‘cjs’],
fileName: (format) => `main.${format}.js`,
},
rollupOptions: {
external: [‘react’, ‘react-dom’],
output: {
globals: {
react: ‘React’,
‘react-dom’: ‘ReactDOM’,
},
},
},
},
resolve: {
alias: {
src: resolve(__dirname, ‘src/’),
},
},
plugins: [
dts({
tsconfigPath: ‘./tsconfig.json’, // Caminho para o arquivo tsconfig.json
outDir: ‘dist’, // Diretório de saída dos arquivos de definição de tipos
}),
],
});

Se der erro na importação do resolve do pacote ‘path’ basta dar o seguinte comando dentro da pasta uikit: pnpm install –save-dev @types/node

Criando o Arquivo main.ts

Agora, crie o arquivo main.ts dentro da pasta src que será o responsável exportar nossos componentes:

touch src/main.ts

Adicione o seguinte conteúdo ao arquivo main.ts para exportar o componente ButtonTeste:

// src/main.ts
import ButtonTeste from ‘./components/Button’;
export { ButtonTeste }

Após essas configurações, sua estrutura de diretórios deve se parecer com isto:

monorepo-project/
├── packages/
│ └── uikit/
│ ├── src/
│ │ ├── components/
│ │ │ └── Button/
│ │ │ └── index.tsx
│ │ └── main.ts
│ └── vite.config.ts
├── apps/
├── package.json
├── .gitignore
├── pnpm-lock.yaml

Passo 5: Configurando o package.json para Suporte a CommonJS e ES Modules

Para garantir que nossa biblioteca uikit suporte tanto CommonJS quanto ES Modules, precisamos atualizar o package.json com as entradas apropriadas para os pontos de entrada do módulo e as declarações de tipos.

Atualizando o package.json

Abra o arquivo package.json do projeto uikit e adicione as seguintes configurações:

{

“main”: “./dist/main.es.js”,
“module”: “./dist/main.es.js”,
“types”: “./dist/main.d.ts”,
“exports”: {
“.”: {
“import”: “./dist/main.es.js”,
“require”: “./dist/main.es.js”,
“types”: “./dist/main.d.ts”
}
},
“files”: [
“dist”
],

}

Detalhes do arquivo package.json:

“main”: “./dist/main.cjs.js”: Especifica o ponto de entrada para consumidores CommonJS.

“module”: “./dist/main.js”: Especifica o ponto de entrada para consumidores ES Modules.

“types”: “./dist/main.d.ts”: Especifica o arquivo de declarações de tipos TypeScript.

“exports”: Define como os diferentes módulos podem ser importados.

“import”: Usado por ES Modules.

“require”: Usado por CommonJS.

“types”: Define o caminho para os arquivos de declarações de tipos.

“files”: Especifica quais arquivos devem ser incluídos no pacote publicado. Neste caso, apenas a pasta dist será incluída.

“scripts”: Adiciona um script de build para compilar a biblioteca usando Vite.

Passo 6: Criando o Projeto Web App e Configurando o Workspace

Agora vamos criar o projeto web-app, que fará uso da nossa biblioteca de componentes uikit, dentro do mesmo repositório.

Criando o Projeto Web App

Navegue até a pasta apps dentro da raiz do projeto monorepo-project e crie o projeto web-app usando o Vite:

# dentro de monorepo-project/apps execute:
pnpm create vite

entre dentro da pasta web-app execute pnpm install para instalar as dependências.

Configurando o Workspace

Após criar o projeto web-app, precisamos configurar um workspace para gerenciar todos os projetos dentro do mesmo repositório.

Volte para a pasta raiz do monorepo-project e crie um arquivo chamado pnpm-workspace.yaml:

cd ..
touch pnpm-workspace.yaml

Dentro desse arquivo, insira o seguinte conteúdo:

packages:
– ‘apps/*’
– ‘packages/*’

Essa configuração informa ao pnpm para reconhecer os diretórios apps e packages como parte do workspace, permitindo que eles sejam gerenciados em conjunto.
Após realizar essas etapas, sua estrutura de diretórios deve se parecer com isto:

monorepo-project/
├── packages/
│ └── uikit/
│ ├── src/
│ │ ├── components/
│ │ │ └── Button/
│ │ │ └── index.tsx
│ │ └── main.ts
│ ├── tsup.config.ts
│ └── package.json
├── apps/
│ └── web-app/
│ ├── src/
│ ├── package.json
├── package.json
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── .gitignore

Com isso, temos o projeto web-app criado e o workspace configurado para gerenciar todos os projetos dentro do mesmo repositório.

Passo 7: Instalando e Configurando o tsup

O tsup é uma ferramenta rápida para transpilar arquivos TypeScript em JavaScript. Ele simplifica o processo de configuração e execução, facilitando a compilação de código TypeScript para uso em projetos JavaScript.

Instalando o tsup

Para instalar o tsup, execute o seguinte comando dentro da pasta uikit:

pnpm add tsup -D

Criando o Arquivo de Configuração tsup.config.ts

Agora, crie o arquivo tsup.config.ts na raiz do projeto uikit e adicione o seguinte código:

// tsup.config.ts
import { defineConfig } from ‘tsup’;

export default defineConfig({
entry: [‘src/main.ts’],
format: [‘cjs’, ‘esm’],
dts: true,
sourcemap: true,
clean: true,
outDir: ‘dist’,
external: [‘react’, ‘react-dom’],
});

detalhes o tsup.config.ts

entry: Especifica o arquivo de entrada ou uma lista de arquivos de entrada para transpilação.

format: Define os formatos de saída desejados, neste caso, CommonJS (cjs) e ES Module (esm).

dts: Habilita a geração de arquivos de definição de tipos TypeScript.

sourcemap: Habilita a geração de sourcemaps para depuração.

clean: Limpa o diretório de saída antes de compilar.

outDir: Especifica o diretório de saída dos arquivos transpilados.

external: Lista de módulos que serão considerados externos e não incluídos no pacote.

Após essas configurações, sua estrutura de diretórios deve se parecer com isto:

cssCopiar código
monorepo-project/
├── packages/
│ └── uikit/
│ ├── src/
│ │ ├── components/
│ │ │ └── Button/
│ │ │ └── index.tsx
│ │ └── main.ts
│ ├── tsup.config.ts
│ └── package.json
├── apps/
├── package.json
├── .gitignore
├── pnpm-lock.yaml

Passo 8: Configurando o Web App para Usar o UI Kit

Agora vamos configurar o projeto web-app para utilizar o uikit que criamos anteriormente.

Adicionando o UI Kit como Dependência

Abra o arquivo package.json do projeto web-app e adicione o uikit como uma dependência:

{
“name”: “web-app”,
“version”: “1.0.0”,
“dependencies”: {
“uikit”: “workspace:*”
}
}

Execute o comando pnpm install na pasta web-app para que as dependências sejam instaladas e o uikit seja reconhecido pelo projeto.

Atualizando o App.tsx

Atualize o arquivo App.tsx do projeto web-app para importar e utilizar o componente ButtonTeste do uikit:

// web-app/src/App.tsx
import { ButtonTeste } from “uikit”;

function App() {
return (
<>
<ButtonTeste />
</>
);
}

export default App;

Compilando o UI Kit

Antes de iniciar o servidor de desenvolvimento, certifique-se de compilar o uikit. Navegue até a pasta uikit e execute o comando pnpm build.

Iniciando o Servidor de Desenvolvimento

Por fim, execute o comando pnpm run dev dentro da pasta web-app para iniciar o servidor de desenvolvimento e visualizar o botão na tela.

cd monorepo-project/apps/web-app
pnpm run dev

Isso iniciará o servidor de desenvolvimento e você poderá acessar o projeto web-app em seu navegador para visualizar o botão do UI Kit.

Passo 9: Publicando o UI Kit no GitHub Package Registry

Agora vamos publicar nosso pacote uikit no GitHub Package Registry.

Configurando o Arquivo .npmrc

Crie um arquivo chamado .npmrc na raiz do projeto uikit e adicione o seguinte conteúdo, substituindo seuNomeDeUsuario pelo seu nome de usuário do GitHub e SeuToken pelo seu token de acesso (certifique-se de que seu token tenha permissões para criar pacotes no GitHub):

@seuNomeDeUsuario:registry=https://npm.pkg.github.com/
//npm.pkg.github.com/:_authToken=SeuToken

Criando o Arquivo .npmignore

Também na raiz do projeto uikit, crie um arquivo chamado .npmignore com o seguinte conteúdo:

# Ignore everything
*

# Mas não ignore a pasta dist
!dist

# Você também pode querer incluir o package.json e outros arquivos importantes
!package.json
!README.md

Modificando o package.json

Abra o arquivo package.json do projeto uikit e modifique o nome do pacote e o atributo private. Seu package.json deve ficar semelhante a isso:

{
“name”: “@seuNomeDeUsuario/nomeDoPacote”,
“private”: false,

}

Substitua seuNomeDeUsuario pelo seu nome de usuário do GitHub e nomeDoPacote pelor exemplo o meu projeto fica da seguinte forma:

{
“name”: “@romulospl/uikit”,
“private”: false,

}

Publicando o Pacote

Agora estamos prontos para publicar nosso pacote. Execute o seguinte comando na raiz do projeto uikit:
npm publish –registry=https://npm.pkg.github.com

Isso irá publicar o pacote uikit no GitHub Package Registry.

Após publicar o pacote uikit no GitHub Package Registry, é importante verificar se a publicação foi realizada com sucesso.

Acessando o GitHub Package Registry

Acesse o seguinte link em seu navegador, substituindo seuNomeDeUsuario pelo seu nome de usuário do GitHub:

https://github.com/seuNomeDeUsuario?tab=packages

Verificando o Pacote Publicado

Na página dos pacotes do seu perfil do GitHub, você deve ser capaz de ver o pacote uikit listado. Isso confirma que o pacote foi publicado com sucesso no GitHub Package Registry.
Verifique se todas as informações estão corretas e se o pacote está disponível para uso.

Passo 10: Criando o Projeto use-component e Utilizando o UI Kit

Agora vamos criar um novo projeto chamado use-component fora do monorepo-project e utilizá-lo para demonstrar o uso do nosso pacote uikit do GitHub Package Registry.

Criando o Projeto use-component

Na pasta de sua preferência (por exemplo, na sua pasta pessoal), execute o seguinte comando para criar o projeto use-component usando Vite:

pnpm create vite use-component

Em seguida, entre na pasta do projeto e execute pnpm install para instalar as dependências:

cd use-component
pnpm install

Instalando o Pacote uikit do GitHub Package Registry

Acesse o GitHub e vá para a página dos seus pacotes, substituindo seuNomeDeUsuario pelo seu nome de usuário do GitHub:
https://github.com/seuNomeDeUsuario?tab=packages

Clique no pacote uikit para acessar sua página. Lá você encontrará o comando para instalar o pacote.

Copie o comando, substituindo @romulospl/uikit pelo nome do seu pacote e 0.0.0 pela versão desejada.

Em seguida, crie um arquivo chamado .npmrc na raiz do projeto use-component e adicione a seguinte linha, substituindo seuNomeDeUsuario pelo seu nome de usuário do GitHub:

@seuNomeDeUsuario:registry = https://npm.pkg.github.com

Depois disso, cole o comando que você copiou anteriormente para instalar o pacote uikit, substituindo as informações necessárias.

pnpm install @seuNomeDeUsuario/uikit@0.0.0

Utilizando o Componente do UI Kit

Agora que o pacote uikit está instalado, vamos utilizá-lo no arquivo App.tsx dentro da pasta src do projeto use-component.

Modifique o arquivo App.tsx para importar e utilizar o componente ButtonTeste do pacote uikit:

// use-component/src/App.tsx
import ‘./App.css’;
import { ButtonTeste } from ‘@seuNomeDeUsuario/uikit’;

function App() {
return (
<>
<ButtonTeste />
</>
);
}

export default App;

Executando a Aplicação

Agora execute a aplicação com o comando pnpm run dev e você verá o botão do UI Kit em funcionamento na tela.

Se você verificar a pasta node_modules dentro do projeto use-component, verá uma pasta @seuNomeDeUsuario (por exemplo, @romulospl). Dentro dessa pasta, há uma pasta uikit, contendo apenas a pasta dist do projeto e o arquivo package.json com as configurações de entrada e exportação.