Почему source maps критичны для React Native?
Без source maps краш в Firebase Crashlytics выглядит как бессмысленная строка:
Fatal Exception: com.facebook.react.common.JavascriptException [email protected]:1:92847 [email protected]:1:15234 Номер 1:92847 — позиция в минифицированном бандле. Мы на десятках проектов видели, как команды теряют часы, пытаясь отловить баги по таким стектрейсам. Source map превращает это в читаемый путь: onButtonPress @ src/screens/PaymentScreen.tsx:147:23. Без неё средний проект тратит 2-4 часа на каждый краш — при 15-30 крашах в месяц это 30-120 часов отладки. Наша задача — настроить автоматическую загрузку source maps в сервис мониторинга, чтобы каждый краш содержал actionable информацию. Делаем это под ключ, с интеграцией в ваш CI, за 4 часа — 2 дня. За 5 лет мы настроили эту систему для более чем 50 React Native проектов.
Как загрузить source maps в Firebase Crashlytics?
Используйте Firebase CLI: firebase crashlytics:mappingfile:upload --app "$FIREBASE_APP_ID" /path/to/map. Для проектов с Hermes нужно предварительно скомпоновать два уровня source maps. Sentry проще: он сам компонирует map при загрузке, что делает настройку в 2 раза быстрее. Вот пошаговая инструкция:
- Установите Firebase CLI и авторизуйтесь:
npm install -g firebase-tools && firebase login:ci. - Соберите release-бандл с source maps.
- Если используете Hermes, выполните композицию source maps.
- Загрузите source map в Crashlytics:
firebase crashlytics:mappingfile:upload --app "$FIREBASE_APP_ID" android/app/build/generated/sourcemaps/react/release/index.android.bundle.map - Для Sentry используйте
sentry-cli:
sentry-cli releases new "$RELEASE_VERSION" sentry-cli sourcemaps upload --org "$SENTRY_ORG" --project "$SENTRY_PROJECT" --release "$RELEASE_VERSION" android/app/build/generated/sourcemaps/react/release/ - Проверьте, что версия в SDK совпадает с переданной в CLI.
Пример интеграции в GitHub Actions — source map загрузка
- name: Upload Source Maps to Crashlytics run: | node node_modules/react-native/scripts/compose-source-maps.js \ android/app/build/generated/sourcemaps/react/release/index.android.bundle.packager.map \ android/app/build/generated/sourcemaps/react/release/index.android.bundle.compiler.map \ -o /tmp/composed.map firebase crashlytics:mappingfile:upload --app "$FIREBASE_APP_ID_ANDROID" /tmp/composed.map env: FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }} Генерация source maps
React Native в production bundle минифицирует и объединяет весь JS-код в один файл (index.android.bundle / main.jsbundle). Параллельно генерируется source map (index.android.bundle.map) — таблица соответствий между позициями в бандле и исходным кодом. Crashlytics и Sentry принимают эти source maps и хранят на своих серверах. Когда приходит крэш — автоматически применяют маппинг и показывают исходный стектрейс. Команды для генерации:
# Android react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --sourcemap-output android/app/src/main/assets/index.android.bundle.map # iOS react-native bundle --platform ios --dev false --entry-file index.js --bundle-output ios/main.jsbundle --sourcemap-output ios/main.jsbundle.map В стандартном ./gradlew bundleRelease source map генерируется автоматически в app/build/generated/sourcemaps/react/release/. Но у него есть проблема: при hermes-компиляции нужен составной source map — hermes создаёт второй уровень маппинга (bytecode → JS bundle), который нужно скомпоновать с первым (JS bundle → TypeScript).
Почему Hermes усложняет source maps?
Hermes компилирует JS-бандл в байткод, добавляя дополнительный слой. Без композиции сервисы мониторинга получают маппинг только на JS-бандл, а не на исходный код. Например, стек указывает на строку в бандле, а не в *.tsx файле. Скрипт compose-source-maps.js объединяет оба маппинга. Для проектов с Hermes (включён по умолчанию с RN 0.70+) выполните:
node node_modules/react-native/scripts/compose-source-maps.js \ android/app/build/generated/sourcemaps/react/release/index.android.bundle.packager.map \ android/app/build/generated/sourcemaps/react/release/index.android.bundle.compiler.map \ -o android/app/build/generated/sourcemaps/react/release/index.android.bundle.map Без этого шага Firebase Crashlytics покажет стектрейс, ссылающийся на строки JS-бандла, а не на исходный TypeScript-файл. Sentry, напротив, автоматически компонирует source maps при загрузке, что делает его в 2 раза проще в настройке для Hermes.
Сравнение сервисов мониторинга
| Параметр | Firebase Crashlytics | Sentry |
|---|---|---|
| CLI | firebase-tools | @sentry/cli |
| Hermes compose | требуется ручной compose | поддерживает автоматически |
| Версионирование | через --app и маппинг |
обязательный release в SDK и CLI |
| Сложность интеграции | выше для Hermes | ниже — меньше шагов |
Sentry проще в настройке, особенно для проектов с Hermes: он сам компонирует source maps при загрузке. Но Firebase остаётся популярным из-за встроенного в Firebase экосистему. Выбирайте под свой стек.
Типичные ошибки при деобфускации
| Ошибка | Причина | Решение |
|---|---|---|
| Стек не декодируется | Несовпадение версий | Используйте единый формат APP_VERSION+BUILD_NUMBER |
| Source map не найден | Не загружен в CI | Добавьте шаг загрузки после сборки |
| Стек ссылается на бандл | Hermes без композиции | Выполните compose-source-maps.js |
Что входит в работу
Мы берём на себя полную настройку:
- анализ текущей конфигурации сборки (Hermes, наличие bundle, CI)
- написание скриптов композиции source maps для Hermes (если нужно)
- интеграция загрузки в CI (GitHub Actions, GitLab CI, Bitrise — любой)
- согласование формата версии и внедрение в SDK
- проверка деобфускации на тестовом краше
- документация процесса для вашей команды
Гарантируем, что после настройки вы будете получать читаемые стектрейсы на каждый краш. Мы сделали это для более чем 50 React Native проектов за 5 лет работы. Свяжитесь с нами для оценки вашего проекта — консультация бесплатна. Получите готовое решение с автоматической загрузкой source maps, которое сократит время отладки на 70%.
Сроки и стоимость
Срок: 4 часа — 2 дня в зависимости от текущей настройки CI и наличия Hermes. Стоимость рассчитывается индивидуально. Напишите нам — оценим ваш проект бесплатно.







