Metro

$ Metro Bundler İç Yapısı

MetroBundlerTooling

Resolve → transform → serialize boru hattı: monorepo resolution, Babel plugin sırası, Hermes bytecode, cache ve release/debug farkları.

EG

Emre Gürbüz

29 Temmuz 2026 · 4 dk okuma

“Metro yavaş” veya “hot reload çalışmıyor” şikayetlerinin çoğu, bundler’ın siyah kutu kalmasından kaynaklanır. Metro, React Native’in mobil için optimize edilmiş bundler’ıdır — Webpack veya Vite değil. Her npx react-native start arkasında resolution, transformation ve serialization çalışır.

Senior hız: config kopyala-yapıştır değil; hangi dosyanın hangi aşamada seçildiğini ve cache’in ne zaman yalan söylediğini bilmektir.

Pipeline: üç ana stage

1) Resolution: doğru dosyayı bulmak

import './x' veya from '@app/ui' dediğinizde Metro gerçek yolu çözer. metro.config.js içindeki resolver, extraNodeModules, nodeModulesPaths, package.json exports burada devreye girer.

Monorepo’da yanlış resolution sessizce kök node_modules’taki eski React’i seçebilir — duplicate React’in klasik kaynağı. İki React kopyası “Cannot read property useState of null” gibi belirsiz hatalar üretir.

disableHierarchicalLookup: true yukarı doğru rastgele gezmeyi keser; bazı legacy paketleri kırabilir. why duplicate react kontrolünü CI’a ekleyin.

2) Transformation: Babel

Babel TS/JSX → JS dönüşümünü yapar. Plugin sırası kritiktir: Reanimated plugin genelde en sonda olmalıdır; aksi halde worklet’ler transform edilmeden kalır ve release’de animasyon kırılır.

Transformer’da ağır iş — her kaydetmede pahalı AST analizi — dev server’ı öldürür. Analiz CI quality gate’ine aittir.

3) Serialization: bundle ve source map

Modül grafiği tek bundle haline gelir. Source map üretilir. Release’de Hermes hermesc bytecode’a (.hbc) çevirebilir.

Monorepo: watchFolders ve nodeModulesPaths

watchFolders olmadan Metro bazı dosya değişikliklerini algılamaz. “Bazen hot reload olmuyor” şikayetinin büyük kısmı buradan gelir.

Monorepo için tipik metro.config.js:

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

const workspaceRoot = path.resolve(__dirname, '../..');
const projectRoot = __dirname;

const config = {
  watchFolders: [workspaceRoot],
  resolver: {
    nodeModulesPaths: [
      path.resolve(projectRoot, 'node_modules'),
      path.resolve(workspaceRoot, 'node_modules'),
    ],
    disableHierarchicalLookup: true,
  },
};

module.exports = mergeConfig(getDefaultConfig(projectRoot), config);

Geniş watchFolders = daha yavaş startup; dar = hot reload kaçırır. Doğru denge, gerçekten import edilen paket root’larını kapsamaktır. macOS’ta Watchman kurulu olsun.

Cache: dost ve düşman

Metro agresif cache kullanır. Belirtiler:

  • Dependency bump sonrası eski kod çalışıyor
  • Babel plugin eklediniz, transform değişmedi
  • CI cache hit yüksek ama clean build kırılıyor

Cache sıfırlama:

npx react-native start --reset-cache
rm -rf /tmp/metro-*

CI cache key’e dahil edin: lockfile hash + babel.config + metro.config + RN version + NODE_ENV. Haftalık clean builder job “CI’da geçti, lokalde kırıldı” senaryolarını erken yakalar.

Dev vs release

DevRelease
MinifyGenelde kapalıAçık
Inline requiresOpsiyonelSık açık (TTI için)
Source mapHızlı iterasyonCrash tooling için zorunlu
HermesDebug VM.hbc bytecode

“Dev’de hızlı, release’de yavaş açılıyor” çoğu zaman inlineRequires, bundle boyutu ve I/O farkındandır. Dev config’i release’e kopyalamayın.

inlineRequires: true cold start iyileştirir; ilk erişimde micro-delay tradeoff’u vardır.

Custom resolver

Platform-spesifik dosya seçimi (.ios.ts, .android.ts) için custom resolveRequest kullanıyorsanız minimal tutun. Hata üretirse Metro sessizce yanlış fallback’e düşebilir.

Asset pipeline ve OTA

require('./logo.png') → packager asset registry. OTA kullanıyorsanız asset’ler bundle ile atomik deploy edilmeli. Büyük PNG’ler Metro’dan geçer ama decode maliyeti UI thread’e yansır — pre-process adımı ekleyin.

Sık tuzaklar

  • İki React kopyası — npm ls react / yarn why react
  • Package exports uyumsuzluğu
  • Symlink + Watchman eksikliği
  • Transformer’da ağır iş
  • Hermes bytecode adımı OTA ile uyumsuz format

Production checklist

  • Monorepo watchFolders / nodeModulesPaths doğrulanmış
  • Duplicate React kontrolü CI’da
  • Reanimated Babel plugin sırası dokümante
  • Release source map CI artifact
  • --reset-cache runbook’ta
  • Hermes bytecode OTA ile aynı format
  • Dev ve release config farkları bilinçli
  • CI cache key’ine config hash dahil
  • Haftalık clean build job aktif

Özet

Metro’yu “start komutu” olmaktan çıkarın. Resolve doğru paket seçer, transform doğru dili üretir, serialize motorun yiyeceği bundle’ı paketler. “Metro yavaş” ticket’ında önce hangi stage’in darboğaz olduğunu ölçün — tahminle config değiştirmeyin.


Diğer yazılar