yarn-3-monorepo-com-zero-install

A demanda

  1. Criando um MonoRepo com Yarn Workspaces.

A primeira vez que utilizei Worskspaces do Yarn foi por conta do @ohager5, que criou o nosso projeto BVotal, no hackathon que participamos:

No bVotal havia 2 services e 2 fronts (um dashboard, um para o usuário).
Com o uso de workspaces, ficaram todos no mesmo repositório, e com um pouco de gitflow, trabalhamos ao mesmo tempo, e com muito mais agilidade do que se fosse necessário ficar pullando changes de cada proj o tempo todo, conforme novos commits iam sendo adicionados em cada service, ou front.

Foi uma forma legal de trabalharmos no mesmo repositório e também compartilhando as dependências.

Resultado:

  • Exemplo deste post sobre Yarn Workspaces: no Github

Passo 1. Yarn

  • Estou usando Node 18

Vamos garantir que estamos com a versão mais recente do yarn instalada.

Se não tiver yarn, instale:

1
$ npm install --global yarn

Se tiver, atualize:

1
2
$ corepack enable
$ yarn set version stable

Passo 2. Criando o Workspace

Escolha uma pasta pro projeto.
Vamos criar como um workspace, onde teremos subprojetos que compartilham as dependências instaladas.

Também é interessante que tudo que precisamos para o projeto ficará no mesmo Repositorio GIT. POR EXEMPLO:

1
2
3
4
5
6
7
8
9
├── README.md
├── package.json
├── packages
| ├── Service1
| ├── Service2
| ├── AlgumaLib
| ├── Front01
| ├── Front02
└── yarn.lock

  • Inicie um novo projeto Yarn 2

    terminal
    1
    $ yarn init -2
  • Edite o package.json para incluir a propriedade workspaces. O meu ficou assim:

    package.json
    1
    2
    3
    4
    5
    6
    7
    {
    "name": "yarn-2-rcdevlabs-blog",
    "packageManager": "yarn@3.2.2",
    "workspaces": [
    "packages/*"
    ]
    }
  • Basicamente estamos dizendo: “Olha, senhor Yarn…. Esse projetão tem vários wokspaces, e eles ficam na pasta packages.”

  • crie o diretório packages

    terminal
    1
    2
    $ mkdir packages
    $ touch packages/.gitkeep

Aqui podemos realizar o primeiro commit do projeto.
A partir de agora, criaremos nossas aplicações (services, bff, frontend) na pasta packages
(o .gitkeep foi adicionado apenas para seu commit ficar bonitinho)

1
2
$ git add .
$ git commit -m "chore(base): basic project-repo structure"

Pode notar que não teremos pasta node_modules no projeto!

quick note sobre zero-installs

Atenção: Veja que a pasta .yarn tem uma pá de coisa e está sendo enviada ao GIT, mas tá tudo bem.
Isso é uma feature do Yarn PnP - Plug n Play - chamada “Zero-Install”.

A idéia é que dê pra rodar o projeto logo após clonar, sem precisar baixar as dependências. Ousado.

Pode notar que não teremos pasta node_modules no projeto!
Leia mais sobre o Zero-Installs Aqui


Passo 3: Crie novos componentes do seu projeto

  • Como exemplo, vamos criar uma novo service usando NestJs
    terminal
    1
    $ npx @nestjs/cli new packages/hello-service --skip-git

Lembrando de adicionar o --skip-git, pois nosso repositório GIT já existe, e fica fora do BFF.

  • adicione a sessão scripts ao package.json, e vamos criar um atalho para rodar o modo desenvolvimento do NestJs.

    package.json
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    {
    "name": "yarn-2-rcdevlabs-blog",
    "packageManager": "yarn@3.2.2",
    "scripts": {
    "hsvc:dev": "yarn workspace hello-service start:dev"
    },
    "workspaces": [
    "packages/*"
    ]
    }
  • Vamos ver se está rodando.

  • Vamos mandar instalar as dependencias
    • passando o parametro workspace [name] antes do comando.
  • e depois rodar o script que criamos.
    terminal
    1
    2
    $ yarn workspace hello-service install
    $ yarn hsvc:dev

http://localhost:3000](http://localhost:3000)
conferir o “Hello Word” do NestJs.

O segundo commit traz o service

terminal
1
2
$ git add packages
$ git commit -m "feat(hello): new nestjs project as hello service package"

Ops
Para funcionar com o intelisense, precisamos instalar algumas dependencias no nosso projeto yarn. Os comandos abaixo instalam, como devDependencies, no projeto-raíz:

terminal
1
2
3
4
$ yarn add -D typescript
$ yarn add -D prettier
$ yarn add -D ts-node
$ yarn dlx @yarnpkg/sdks vscode

Depois, vamos criar/editar uma configuração para o nosso Workspace

.vscode/settings.json
1
2
3
4
5
6
7
{
"typescript.tsdk": ".yarn/sdks/typescript/lib",
"search.exclude": {
"**/.yarn": true,
"**/.pnp.*": true
}
}

após essas alterações, reload a janela: ctrl/command shift p : reload window [enter].
Se pedir para instalar o plugin ZipFs, aceitar

  • Se algum arquivo estiver com erro nos imports, pode ser queo Vscode esteja interpretando o arquivo com a versão errada do TS. Ajuste assim:

    vscode
    1
    2
    3
    4
    ctrl + shift + p
    >TypeScript: Select TypeScript Version

    --> escolha `Workspace Version`
  • Uma vez que não haja mais erro, podemos commitar - mas só o que não estiver na pasta packages

    terminal
    1
    2
    $ git add ':!packages' # adiciona tudo que não está na pasta packages
    $ git commit -m "chore(yarn): add ts, prettier and ts-node"

Passo 4: Plug n Play Zero Install MESMO?

  • Clone seu repositório em outra pasta, e rode diretamente (sem rodar install)
    terminal
    1
    2
    $ yarn install
    $ yarn hsvc:dev

Bom, a gente até rodou yarn install, mas veja que nada foi baixado. Apenas a dependência @nestjs/core foi buildada, a partir da própria .yarn/cache.

E Vale a Pena?

Olha, pra comparar eu usei o npm install no mesmo projeto, e olha só o tamanho da pasta node_modules

1
2
$ du -hs node_modules/
339M node_modules/

Levando em conta que o projeto zero-installs foi upado comprimido ao git com 70mb com todas as dependências, e apenas buildou o @nestjs/core localmente… O que vc achou?

Diz aí nos comentários.


Aviso: Considerações de segurança.

Conforme avisa o site do Yarn, se o projeto receber PRs de pessoas externas ao time, vale rodar um passo adicionar durante a Pipeline: $ yarn install --check-cache. Com isso, yarn baixa novamente as dependências pra conferir o checksum, emitindo um alerta caso algo suspeito.

Conclusão

Bom, essa é a estrutura do MonoRepo usando Yarn Workspaces.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
├── README.md
├── package.json
├── packages
| ├── Service1
| ├── Service2
| ├── AlgumaLib
| ├── Front01
| ├── Front02
└── yarn.lock

# e mais os *dotfiles/folders*
└── .editorconfig
└── .gitignore
└── .pnp.cjs
└── .pnp.loader.mjs
└── .yarn
└── .yarnrc.yml

Utilizando a funcionalidade de workspaces do Yarn conseguimos ter todos os diferentes pacotes do nosso projeto unificados em um único repositório de projeto.

Existem algumas outras características ao trabalhar com workspaces, mas cobrí-las foge do objetivo desta série.

Outra feature bacaníssima do modo “Plug and Play” de pensar é a chamada Zero-Installs, onde nossas dependências são curadas e adicionadas ao GIT, não sendo necessario rodar Install após clonar o projeto.

Segundo Yarn, não ocupa muito espaço, pois são salvos apenas binários das dependências, e não todo o projeto, como quando usamos a boa e velha node_modules.

Eventualmente pode ser necessário reinstalar uma dependência, com ou sem comittar ao GIT - por ex, se trabalhando em outro Sistema Operacional -, e há opções para isso.

Image by Freepik

Image by Freepik