esbuild · SWC · optionalDependencies · Удалённый Mac CI · 2026

2026: сборка фронтенда на удалённом Mac —
нативные модули esbuild и SWC, optionalDependencies и матрица решений для CI

24.04.2026 Фронтенд / DevOps 10 мин чтения

Для кого: фулстек и фронтенд, кто гоняет Vite, Next.js, Rspack или кастомные пайплайны на удалённом Mac в CI. Разрыв «у меня собралось» vs «на агенте упало» чаще всего в нативных биндингах esbuild, платформенных пакетах @swc/core, optionalDependencies и несовпадении архитектуры Node / libc++ на macOS. Дальше — таблица симптомов, матрица локально vs CI, команды, ключи кэша, приёмка параллельных джобов и FAQ. Ещё по теме: esbuild и SWC в monorepo, Rspack и воркеры, pnpm и Turborepo, Node и npm на remote Mac. Тарифы, аренда.

01 Таблица соответствия симптомов и причин

Нативный слой не проявляется в TypeScript-ошибках: он ломается при первом вызове трансформера или бандлера. Используйте таблицу как быстрый триаж перед долгим дебагом сети или кода приложения.

Симптом Вероятная причина Первый шаг проверки
dlopen / «wrong architecture» для .node Node запущен под одной архитектурой (например x64 в Rosetta), а модуль собран под arm64, или наоборот node -p "process.arch + ' ' + process.platform" и сравнить с путём внутри node_modules
Cannot find module для подпакета @swc/core-darwin-* optional-платформа не установилась: сбой скрипта postinstall, обрезанный кэш, перенос node_modules с Linux Переустановка на целевом Mac; проверить логи npm_config_platform / npm_config_arch
Сборка падает только в параллельном matrix, локально стабильна Разные раннеры (arm vs intel pool), гонка за диском или кэшем Turbo/pnpm store Зафиксировать pool и добавить в ключ кэша runner.arch + версию Node
«Symbol not found», ссылки на libc++ Бинарник собран под более новый SDK, чем на агенте, или смешение toolchain Выровнять версию Xcode CLT на удалённом Mac с документированным эталоном

02 Матрица архитектуры: локально и CI

Паритет — это не «одинаковый package.json», а совпадение набора: бинарная архитектура Node, политика Rosetta, версия macOS для загрузчиком и набор нативных зависимостей, выбранных через optionalDependencies.

Измерение Локальный Mac разработчика Удалённый Mac CI Риск при расхождении
process.arch arm64 «из коробки» на Apple Silicon Может быть x64-раннер или смешанный пул Неверный вариант esbuild/SWC из optional-пакета
Источник node_modules Долгоживущий каталог, частичные обновления Часто чистая установка из кэша слоёв Скрытые артефакты старой платформы в кэше
libc++ / SDK Xcode пользователя Образ провайдера CI ABI-мismatch на редких нативных цепочках
Конкуренция за CPU 1–2 параллельных задачи Несколько шардов + линтер + тесты Таймауты postinstall, «битые» полуустановки

Зафиксируйте в README эталон окружения и короткий assert в preinstall — агент падает сразу с диагностикой, а не на 15-й минуте сборки.

03 Чек-лист команд пересборки

Выполняйте на том же раннере, где падает пайплайн. Не копируйте node_modules между хостами с разной ОС.

  1. Диагностика среды:
node -p "process.version + ' ' + process.arch + ' ' + process.platform"
which node
xcodebuild -version 2>/dev/null | head -n 1
  1. Полная переустановка зависимостей (npm):
rm -rf node_modules
npm cache verify
npm ci
  1. Точечная пересборка нативных модулей без полного rm (если менялся только toolchain):
npm rebuild esbuild
npm rebuild @swc/core --verbose
  1. pnpm (монорепо):
pnpm store prune
rm -rf node_modules **/node_modules
pnpm install --frozen-lockfile
  1. Проверка, какой бинарник реально грузится:
node -e "console.log(require('esbuild').execPath)"
find node_modules -name '*.node' -path '*esbuild*' | head

04 Ключи кэша и очистка node_modules

Кэш CI должен включать всё, что влияет на выбор optional-пакета: версия Node, архитектура, хэш lock-файла, идентификатор образа macOS. Иначе вы восстановите «чужой» node_modules и получите дрейф, который локально невоспроизводим.

  • GitHub Actions пример ключа: node-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ runner.os }}-${{ runner.arch }}
  • Turbo remote cache: отдельный префикс для веток с экспериментальным Node 22 vs LTS 20, чтобы не смешивать артефакты.
  • Очистка: при смене пула раннеров инвалидируйте кэш установки целиком один раз; дальше держите тёплый кэш по стабильному ключу.
  • Антипаттерн: таскать tarball node_modules с ноутбука на сервер через rsync — ломает optional-маршрутизацию.

Для монорепозиториев согласуйте политику с гайдом по pnpm и зеркалу: одна точка правды для store снижает число «призрачных» платформенных пакетов.

05 Параллельные джобы CI — приёмка

  1. Один тип раннера на workflow: не смешивайте в одном job-кэше arm и x64 даже при одинаковой версии Node.
  2. Лимит параллелизма установки: при восьми одновременных npm ci на общем диске включите очередь или разнесите по volume, чтобы postinstall не умирал по таймауту.
  3. После restore кэша: node -e "require('esbuild')" и импорт @swc/core в одном шаге job.
  4. Метрики: фиксируйте медиану времени install и cold build; всплеск на 40%+ после смены образа — сигнал проверить нативный слой, а не только прикладной diff.
  5. Документация: приложите к runbook ссылку на эту матрицу и эталонную команду диагностики — сокращает эскалации на DevOps.

06 FAQ

Достаточно ли зафиксировать только версию Node в engines?

Недостаточно: добавьте явное требование архитектуры в документации CI и, по возможности, assert в скрипте, иначе менеджер пакетов формально удовлетворён, а загрузчик — нет.

Помогает ли npm rebuild всегда?

Только если исходники нативного модуля присутствуют и toolchain совместим. Для предсобранных бинарников надёжнее чистая установка на правильной платформе.

Связан ли libc++ с esbuild?

Напрямую редко, но косвенно — при смеси плагинов на C++ или кастомных нативных аддонов в том же процессе Node. Выровняйте Xcode CLT на удалённом Mac с эталоном команды.

Арендуйте удалённый Mac с той же архитектурой CPU и образом macOS, что в боевом CI: меньше сюрпризов optionalDependencies, быстрее воспроизведение. Тарифы, аренда, блог с чек-листами по фронтенду на Mac.

Удалённый Mac · Паритет сборки

Выберите узел с архитектурой как в CI

Выделенный Apple Silicon с фиксированным образом macOS снимает расхождения esbuild, @swc/core и optionalDependencies. Арендуйте удалённый Mac под ту же связку Node + архитектура, что в пайплайне: тарифы, покупка и аренда без обязательного входа, помощь по доступу, главная с планами по регионам.

arm64 / паритет optionalDeps CI и SSH
Mac под ваш CI — тот же чип