Criando monorepo com biblioteca reutilizavel, usando PNPM (React + TypeScript)
Published: 2024-05-30 20:25:33

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


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u84oa615uicm7dfrf7u7.png)

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
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1fav50gxfoe4bpf0fjo4.png)

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.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nbdqmazfr0c2j0d1b889.png)

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](https://github.com/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.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xdgt87pgl16rn54vbfyd.png)

[Link para o repositório monorepo-project](https://github.com/romulospl/monorepo-project)