Como manter assets canônicos em um monorepo React Native
10 min de leituraEscrito por: Jonathan Reis em
Ícone de app, screenshot de loja e favicon de site costumam nascer do mesmo trabalho de produto, mas acabam espalhados em cópias sem origem clara. Um acervo canônico deixa explícitos o original, os exports e as entregas técnicas.

Quase todo projeto mobile começa com uma convenção inocente: imagens ficam em assets dentro do app. Isso funciona até o mesmo ícone precisar alimentar build iOS, ícone adaptativo Android, favicon, ficha de loja, landing page e QR code. Nessa hora o repositório passa a ter vários arquivos que parecem igualmente importantes. Nenhum nome responde à pergunta que interessa: qual arquivo deve mudar primeiro?
O problema não é só nome de pasta. É ownership. A arte editável, o export enviado para uma loja e o PNG que o Metro empacota têm responsabilidades diferentes. Quando todos parecem equivalentes, alguém atualiza um e esquece os outros.
O padrão deste artigo mantém um acervo canônico de assets na raiz do repositório. Apps, pacotes e sites recebem entregas técnicas geradas. Ferramentas de build continuam usando caminhos locais, mas ninguém precisa procurar nelas pelo original.
Separe fonte, export e entrega
Um asset pode estar em três estados.
- Fonte é o arquivo que uma pessoa pode editar: um SVG de marca, captura bruta, master de áudio ou composição de campanha.
- Export é um derivado reutilizável: PNG de loja, crop social ou logo seguro para QR code.
- Entrega existe porque um consumidor exige formato ou caminho específico. Configuração Expo,
require()do Metro epublicde site estático são consumidores comuns.
Essa distinção importa porque entregas normalmente precisam ficar perto do código. Isso não faz delas donas da arte.
assets/
puzzle-app/
identity/
mark/source/mark.svg
mark/generated/
captures/source/
creatives/store/source/
creatives/store/exports/
apps/puzzle-app/assets/ # entregas Expo geradas
sites/puzzle.example/public/ # entregas públicas geradas
packages/audio-assets/assets/ # entregas Metro geradas
A raiz não substitui os assets de runtime do app. Ela é o lugar onde alguém vai para encontrar o original, entender uma licença ou criar um export novo.
Use um catálogo, não só diretórios arrumados
Pastas ajudam quando você já sabe o produto e o tipo de asset. Elas não dizem onde a imagem é usada, se é licenciada ou qual script a regenera. Coloque essas informações em um catálogo pequeno, ao lado do acervo.
id: puzzle-app-mark
kind: identity
owner: puzzle-app
canonical: identity/mark/source/mark.svg
license: proprietary
source: identity/mark/source/mark.svg
deliveries:
- apps/puzzle-app/assets/icon.png
- apps/puzzle-app/assets/adaptive-foreground.png
- sites/puzzle.example/public/favicon.svg
consumers: [expo, site, store]
generatedBy: scripts/assets.mjs
O catálogo não precisa virar banco de dados. A função é mais simples: deixar revisáveis a fonte, as entregas e a responsabilidade por cada família de asset. Um índice global lista owners e tags; catálogos por produto mantêm os detalhes.
Isso reduz risco com material de terceiros. Uma miniatura vinda de app legado ou um ícone de conjunto aberto deve ficar sob um owner de terceiros, com o aviso de licença junto. A cópia runtime continua no app, mas deixa de ser o único lugar onde a atribuição existe.
Gere ícones específicos de plataforma em vez de copiá-los
Ícones tornam esse modelo concreto. Um único símbolo vetorial pode alimentar várias saídas, mas elas não devem ser o mesmo arquivo.
- iOS precisa de raster completo e opaco.
- Ícone adaptativo Android precisa de foreground transparente, com a arte dentro da área segura.
- Favicon web pode ser raster menor ou SVG.
- Ficha de loja normalmente pede PNG quadrado sem alpha.
- Sobreposição em QR code precisa de tamanho conservador e alto contraste para não atrapalhar a leitura.
O símbolo editável continua sendo uma fonte. O gerador passa a ser responsável pelos contratos de cada plataforma.
mark.svg
-> ícone iOS opaco
-> foreground Android transparente
-> favicon web
-> ícone de loja
-> export para centro de QR
O Android é onde o fluxo de cópia costuma quebrar. Colocar o ícone iOS inteiro no slot de foreground pode causar corte pelas máscaras do launcher. Gere um foreground transparente separado e valide-o. O guia sobre ícones Expo para iOS e Android detalha essa separação de área segura.
Preserve geradores existentes e adicione um orquestrador
Não reescreva geradores que já funcionam só porque os arquivos mudaram de lugar. Um monorepo pode já ter scripts que rasterizam SVGs, compõem material de loja ou produzem imagens Open Graph. Preserve os scripts focados e adicione um comando de topo apenas para as entregas de assets que ele realmente possui.
{
"scripts": {
"assets:generate": "node scripts/assets.mjs generate",
"assets:check": "node scripts/assets.mjs check"
}
}
O orquestrador deve coordenar, não concentrar toda a lógica:
- Roda os geradores existentes de ícone e loja.
- Copia entregas finais para caminhos exigidos por Expo, Metro ou site.
- Copia masters binários que não precisam de transcodificação, como WAVs aprovados.
- Escreve ou preserva README curto em diretórios exclusivamente gerados.
- Salva manifesto de hashes das fontes e entregas.
Evite apontar Metro ou Expo diretamente para o acervo na raiz por symlink. Isso pode funcionar localmente e falhar em build nativo, build na nuvem ou etapa de empacotamento com outra visão do sistema de arquivos. Entregas locais são menos elegantes no papel, mas previsíveis para ferramentas de plataforma.
Imagens Open Graph são outro pipeline. Elas são conteúdo público gerado a partir de metadados de sites e posts, não entrega de uma família de assets de produto. Mantenha esse comando separado para que uma mudança em WAV ou ícone de app não regenere toda imagem social do repositório.
Faça a verificação sem escrever arquivos
O comando de geração pode escrever. O de verificação não deveria.
Uma primeira implementação tentadora de assets:check é gerar tudo de novo e falhar se o diretório de trabalho mudar. Ela encontra saída desatualizada, mas também altera o repositório durante a checagem. Isso esconde a origem de um diff e deixa CI menos previsível.
Uma alternativa melhor é um manifesto criado por assets:generate. Ele guarda hashes dos diretórios canônicos e das entregas. assets:check valida campos obrigatórios do catálogo, confirma paths declarados e compara os hashes atuais com o manifesto.
assets:generate -> escreve entregas + generated-manifest.json
assets:check -> valida catálogos + compara hashes
O resultado fica objetivo: fonte ou entrega alterada falha até alguém regenerar o conjunto de propósito. A checagem também mostra quando uma entrega foi colocada manualmente fora do catálogo, desde que a pasta esteja no manifesto.
Documente diretórios gerados onde a dúvida aparece
Cópias geradas ainda precisam de explicação. Coloque um README curto em todo diretório que contenha apenas outputs.
# Generated Runtime Assets
Do not edit files in this directory manually.
Canonical source: `assets/<owner>/...`
Generate: `pnpm assets:generate`
Verify: `pnpm assets:check`
Não coloque esse README dentro de public de site estático. O framework pode publicá-lo. Use um ASSETS.md na raiz do site e explique quais arquivos públicos são gerados.
A repetição é intencional. Quem está olhando um ícone Expo não precisa lembrar da arquitetura inteira. Precisa saber qual comando correto executar exatamente no ponto onde a confusão apareceu.
Remova a fonte de verdade antiga na mesma mudança
Migração incompleta mantém o SVG antigo e o novo SVG editável. A próxima troca de ícone vai usar o primeiro arquivo que alguém encontrar.
Depois de gerar e validar uma entrega, remova diretórios de fonte antigos, kits de loja vencidos, explorações temporárias, inventários duplicados e documentação que declarava caminhos antigos como canônicos. Preserve apenas as entregas runtime que ferramentas de plataforma realmente consomem.
Para masters de vídeo, arquivos de design ou gravações grandes, não jogue todo binário no Git por reflexo. Decida se o arquivo fica no Git, Git LFS ou armazenamento externo antes da primeira migração. Se a fonte ficar fora do repositório, registre localização e versão no catálogo.
Checklist de migração
- Inventarie os arquivos e descubra a fonte editável real de cada família.
- Mova fontes, capturas, criativos, licenças e avisos de terceiros para o acervo raiz.
- Adicione catálogos por owner e um índice global.
- Adapte geradores existentes para ler fontes canônicas.
- Gere entregas locais para todos os consumidores runtime e públicos.
- Adicione verificação sem escrita e rode-a no CI.
- Documente diretórios gerados fora de pastas estáticas publicáveis.
- Apague cópias editáveis antigas e atualize docs que as chamavam de canônicas.
O ganho não é ter menos arquivos. Apps nativos e sites realmente precisam de outputs específicos por formato. O ganho é que toda saída tem uma origem conhecida, toda fonte tem owner e a troca de símbolo ou captura começa em um lugar previsível.