Stack 30/10/2025

React Native e pnpm: Resolvendo Problemas de Módulos com Metro

Enfrentando problemas com @babel/runtime no React Native ao usar pnpm? Descubra como configurar o Metro para entender a estrutura de módulos do pnpm.
EQ
Por Equipe Midiaville
Especialistas em desenvolvimento web
30 de Outubro de 2025

Stack

O desenvolvimento de aplicações web e mobile modernas frequentemente envolve o uso de monorepos, que permitem compartilhar código entre diferentes projetos. Ferramentas como o pnpm (performant npm) têm ganhado popularidade devido à sua eficiência e capacidade de otimizar o gerenciamento de dependências em monorepos. No entanto, a integração do pnpm com o React Native, especialmente com o bundler Metro, pode apresentar desafios inesperados. Este artigo explora um problema comum enfrentado por desenvolvedores que utilizam essa combinação e oferece uma solução prática para garantir um fluxo de trabalho suave.

O Desafio: pnpm e Metro no React Native

Ao trabalhar com um monorepo que inclui um projeto React Native e utiliza o pnpm para gerenciar as dependências, é possível encontrar erros relacionados à resolução de módulos. Um erro comum é o "Unable to resolve module @babel/runtime/helpers/interopRequireDefault", mesmo que o módulo @babel/runtime esteja instalado e presente na pasta node_modules do projeto React Native.

Este problema surge devido à forma como o pnpm organiza as dependências. Diferentemente do npm ou yarn, o pnpm não instala as dependências de forma "plana" na pasta node_modules. Em vez disso, ele cria um diretório virtual sob .pnpm/ e utiliza symlinks (links simbólicos) para conectar as dependências. Essa abordagem otimiza o espaço em disco e evita problemas de dependências duplicadas, mas pode confundir o Metro, que espera uma estrutura de node_modules mais tradicional.

Entendendo a Estrutura do pnpm

Para ilustrar, considere a seguinte estrutura de diretórios:

  • mobile/node_modules/@babel/runtime

Em um projeto pnpm, este diretório não contém os arquivos reais do @babel/runtime. Em vez disso, ele é um link simbólico que aponta para:

O Node.js geralmente consegue seguir esses links sem problemas, mas o resolver do Metro, por vezes, não. Ele pode subir diretórios até encontrar um diretório node_modules, acabando por carregar módulos da raiz do workspace em vez da cópia local da aplicação mobile, causando o erro.

Soluções Tentativas e Frustrações

Antes de encontrar a solução ideal, muitos desenvolvedores tentam diversas abordagens para contornar o problema. Algumas das tentativas mais comuns incluem:

  • Desabilitar o hoisting: Configurar o hoistPattern para um array vazio no arquivo .npmrc.
  • Isolar linkers: Utilizar a configuração node-linker=isolated.
  • Hoisting "vergonhoso": Ativar a opção shamefully-hoist=true.

Embora essas abordagens possam parecer promissoras, elas frequentemente causam outros problemas. Desabilitar o hoisting pode quebrar a construção do projeto mobile ou impedir que os pacotes compartilhados sejam corretamente linkados. O hoisting "vergonhoso", embora às vezes funcione, pode levar a comportamentos inesperados e é geralmente considerado uma solução de último recurso.

A frustração surge quando, após várias tentativas, nenhuma dessas soluções resolve o problema de forma consistente e confiável.

A Solução Definitiva: Adaptando o Metro ao pnpm

A verdadeira solução reside em adaptar o Metro para entender a estrutura de diretórios do pnpm, em vez de forçar o pnpm a se comportar como o npm. Isso pode ser feito configurando o arquivo metro.config.js do projeto React Native.

O arquivo metro.config.js permite personalizar o comportamento do Metro, incluindo a forma como ele resolve os módulos. Para resolver o problema de resolução de módulos com o pnpm, adicione a seguinte configuração ao arquivo metro.config.js:


const path = require('path');
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');

/**
 * Metro configuration for pnpm monorepos
 * (do-not-stop project)
 *
 * - Lets Metro follow pnpm's symlinks
 * - Always prefers the mobile app's own node_modules
 * - Watches the monorepo root so shared packages rebuild correctly
 */
const defaultConfig = getDefaultConfig(__dirname);

const config = {
  watchFolders: [path.resolve(__dirname, '..')],
  resolver: {
    ...defaultConfig.resolver,
    unstable_enableSymlinks: true,
    unstable_enablePackageExports: true,
    extraNodeModules: new Proxy(
      {},
      {
        get: (_target, name) =>
          path.join(__dirname, 'node_modules', String(name)),
      }
    ),
  },
};

module.exports = mergeConfig(defaultConfig, config);

Essa configuração faz o seguinte:

  • watchFolders: Adiciona o diretório raiz do monorepo à lista de diretórios observados pelo Metro. Isso garante que o Metro detecte mudanças nos pacotes compartilhados.
  • unstable_enableSymlinks: true: Permite que o Metro siga os symlinks criados pelo pnpm.
  • unstable_enablePackageExports: true: Habilita o suporte a package exports, uma feature moderna do Node.js.
  • extraNodeModules: Cria um proxy que redireciona as requisições de módulos para a pasta node_modules local do projeto React Native. Isso garante que o Metro sempre prefira os módulos instalados localmente em vez dos módulos da raiz do workspace.

Com essa configuração, o Metro consegue entender a estrutura de diretórios do pnpm e resolver corretamente os módulos, eliminando o erro "@babel/runtime".

Utilizando Pacotes Compartilhados

Uma das vantagens de usar um monorepo é a capacidade de compartilhar código entre diferentes projetos. Com a configuração do Metro descrita acima, é possível importar e utilizar pacotes compartilhados no projeto React Native sem problemas.

O watchFolders inclui a raiz do repositório, permitindo que o Metro detecte alterações em pacotes compartilhados como packages/shared-auth/. Para um controle mais refinado, você pode especificar os diretórios dos pacotes compartilhados individualmente:


watchFolders: [
  path.resolve(__dirname, '..', 'packages', 'shared-auth'),
  path.resolve(__dirname, '..', 'packages', 'utils'),
],

Conclusão

A integração do pnpm com o React Native pode apresentar desafios iniciais, mas a solução reside em adaptar as ferramentas para coexistirem harmoniosamente. Ao configurar o Metro para entender a estrutura de diretórios do pnpm, é possível resolver problemas de resolução de módulos e aproveitar os benefícios de um monorepo sem comprometer a estabilidade e o desempenho do projeto React Native.

No futuro, esperamos que as ferramentas de desenvolvimento web e mobile se tornem mais inteligentes e capazes de lidar automaticamente com diferentes estruturas de diretórios e sistemas de gerenciamento de dependências. Isso simplificará o desenvolvimento de aplicações multiplataforma e permitirá que os desenvolvedores se concentrem na criação de recursos e funcionalidades inovadoras, em vez de gastar tempo resolvendo problemas de configuração.

A adoção de abordagens como a descrita neste artigo não apenas resolve problemas imediatos, mas também contribui para um ecossistema de desenvolvimento mais flexível e adaptável.

Compartilhe este artigo

Artigos Relacionados

Continue explorando nossos insights sobre desenvolvimento web e estratégias digitais

Precisa de Uma Solução Personalizada?

Nossa equipe especializada está pronta para desenvolver a solução ideal para o seu negócio.