Когда Expo SDK 54 встречает SDK 55: как несовместимость пакетов уронила сборку Android
Разработчик обнаружил, что установка отдельных Expo-пакетов «последней версии» привела к тихой несовместимости с основным SDK. TypeScript ничего не заметил, dev-сервер работал — а нативная сборка Android падала.
TypeScript не поймал ошибку, Expo-сервер запускался без нареканий — но нативная сборка Android рухнула из-за того, что в проект попали пакеты от другого SDK. Проблема, которую легко не заметить и ещё проще предотвратить.
Что произошло
Представьте рабочий проект на Expo SDK 54. Всё стабильно, код компилируется, разработка идёт полным ходом. Но однажды приходит время собрать релиз через EAS Build — и Android-сборка падает на этапе работы с нативным кодом.
Причина? Часть Expo-пакетов в package.json оказалась от SDK 55. Не потому что кто-то сознательно обновлял SDK — а потому что при установке каждого пакета отдельно менеджер пакетов подтянул «последнюю версию», которая предназначалась для следующей мажорной версии Expo.
Вот как выглядела проблемная часть зависимостей:
{
"dependencies": {
"expo": "~54.0.33",
"expo-apple-authentication": "~55.0.13",
"expo-dev-client": "^55.0.27",
"expo-image-picker": "^55.0.18",
"expo-linking": "^55.0.12",
"expo-notifications": "^55.0.19",
"expo-splash-screen": "^55.0.18"
}
}
Ядро expo — на версии 54. Шесть окружающих пакетов — на версии 55. Выглядит как мелочь, но для нативной сборки это фатальная несовместимость.
Почему «на глаз» это сложно отловить
Здесь кроется одна из неочевидных ловушек Expo. Номера пакетов не совпадают с номером SDK. К примеру, в Expo SDK 54 пакет expo-notifications имеет версию 0.32, а expo-splash-screen — 31. Пакеты SDK 55, которые попали в проект, имели версии 55.x — и это выглядело правдоподобно для того, кто не проверяет совместимость по документации.
Угадать, какой пакет к какому SDK относится, сходу практически невозможно. А беглый взгляд на package.json не выдаёт никаких предупреждений.
Почему молчал TypeScript
Это второй подвох. Пакеты от SDK 55 по-прежнему экспортируют JavaScript-интерфейсы и TypeScript-типы, которые устраивают компилятор. Код с точки зрения типизации — корректен. Запуск tsc проходит чисто. Dev-сервер тоже не жалуется: он не занимается сборкой нативных модулей.
Проблема проявляется только на этапе, когда EAS Build начинает компоновать нативные конфигурации, конфигурационные плагины и модули для Android и iOS. Тут несовместимость версий бьёт сразу — и сборка падает.
Иными словами, TypeScript проверяет поверхность API, а не совместимость нативного слоя. Полагаться только на tsc при работе с Expo — недостаточно.
Как это исправить
Автор статьи нашёл решение с помощью двух команд Expo CLI, которые, по сути, должны быть в арсенале каждого разработчика на React Native с Expo.
Шаг 1: Проверить совместимость
npx expo install --check
Эта команда сравнивает установленные пакеты с теми версиями, которые ожидает текущий SDK. Она сразу покажет расхождения. Можно получить результат и в формате JSON для автоматизации:
npx expo install --check --json
Это куда надёжнее, чем вручную сверять версии в package.json с таблицами из документации.
Шаг 2: Исправить версии
Expo CLI умеет автоматически подставить правильные версии:
npx expo install --fix
npx expo-doctor
Автор предпочёл отсмотреть каждый пакет вручную и скорректировал package.json сам. Вот что изменилось:
- "expo-apple-authentication": "~55.0.13"
+ "expo-apple-authentication": "~8.0.8"
- "expo-dev-client": "^55.0.27"
+ "expo-dev-client": "~6.0.21"
- "expo-image-picker": "^55.0.18"
+ "expo-image-picker": "~17.0.11"
- "expo-linking": "^55.0.12"
+ "expo-linking": "~8.0.12"
- "expo-notifications": "^55.0.19"
+ "expo-notifications": "~0.32.17"
- "expo-splash-screen": "~55.0.18"
+ "expo-splash-screen": "~31.0.13"
Разница впечатляющая. Пакеты «версии 55» оказались пакетами с версиями 0.32, 6, 8, 17 и 31 — и это нормально для SDK 54.
Шаг 3: Для монорепозиториев — нюанс
В монорепо команды нужно запускать с фильтрацией по конкретному пакету, иначе Expo CLI проанализирует корневой package.json, а не тот, что используется мобильным приложением:
pnpm install
pnpm --filter @squadnote/mobile exec expo install --check
pnpm --filter @squadnote/mobile exec expo-doctor
Как не попасть в эту ловушку снова
Автор перешёл на простое правило: добавлять Expo-пакеты только через expo install, а не через npm install или yarn add:
npx expo install expo-notifications
Эта команда сама выберет версию, совместимую с установленным SDK. После каждого обновления SDK — перед первой сборкой через EAS — запускать проверки:
npx expo install --check
npx expo-doctor
Что стоит вынести из этой истории
Ситуация банальна по механике, но коварна по последствиям. Вот три ключевых вывода:
-
Версии Expo-пакетов не интуитивны. Номер SDK и номер пакета — это разные вещи. Не пытайтесь определить совместимость визуально.
-
TypeScript не проверяет нативную совместимость. Чистый
tsc— не гарантия того, что проект соберётся. Expo SDK живёт на стыке JavaScript и нативного кода, и проблемы могут быть только на одной стороне. -
Инструменты проверки есть — пользуйтесь ими.
expo install --checkиexpo-doctor— это буквально одна команда, которая может сэкономить часы отладки на этапе CI/CD.
Случай этот — хороший пример того, как современная экосистема мобильной разработки порождает новые классы ошибок. Не ошибки в коде, не баги в бизнес-логике — а несовместимость на уровне сборочного конвейера, которую невозможно поймать привычными инструментами. Ирония в том, что чем удобнее становятся инструменты автоматизации вроде EAS Build, тем выше цена невнимательности к версионированию.
Ссылки: