Монорепозиторий с JavaScript: организация кода для Node.js проектов

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Монорепозиторий с JavaScript: организация кода для масштабируемых проектов

С ростом сложности JavaScript-проектов (особенно на Node.js) перед разработчиками встаёт вопрос: как организовать код, чтобы он оставался поддерживаемым, переиспользуемым и легко масштабируемым? Традиционный подход — мультирепозиторий (polyrepo), когда каждый сервис или библиотека живут в своём Git-репозитории. Однако для многих команд удобнее и эффективнее оказывается монорепозиторий (monorepo) — единый репозиторий, содержащий несколько проектов (пакетов).

В этой статье мы разберём, что такое монорепозиторий, какие инструменты для JavaScript/Node.js существуют, и как правильно организовать структуру кода.

Что такое монорепозиторий и зачем он нужен?

Монорепозиторий — это стратегия управления кодом, при которой все компоненты системы (фронтенд, бэкенд, общие библиотеки, конфигурации) хранятся в одном репозитории. В контексте JavaScript это часто означает набор пакетов (packages), каждый из которых может быть опубликован отдельно или использован как внутренняя зависимость.

Преимущества монорепозитория

  • Единая версия зависимостей: Все пакеты используют один node_modules (благодаря hoisting), что уменьшает дублирование и конфликты версий.
  • Простой рефакторинг: Изменения в общей библиотеке сразу видны во всех потребителях в рамках одного PR.
  • Сквозная атомарность: Один коммит может содержать изменения в нескольких пакетах, что упрощает откат и CI/CD.
  • Переиспользование кода: Общие утилиты, типы и конфигурации легко шарить между проектами.
  • Единые инструменты: Один линтер, один тест-раннер, один форматтер для всего репозитория.

Недостатки (и как их нивелировать)

  • Рост размера репозитория: Git может замедлиться. Решение — использовать git sparse-checkout или глубинные клоны.
  • Сложность настройки CI: Нужно умное кэширование и запуск только изменённых пакетов (помогают инструменты вроде Nx или Turborepo).
  • Повышенные требования к дисциплине: Чёткие правила оформления коммитов и версионирования обязательны.

Инструменты для организации монорепозитория в JavaScript

Выбор инструмента зависит от размера команды и сложности проекта. Рассмотрим три самых популярных варианта.

1. Yarn / npm Workspaces (базовый уровень)

Это встроенная возможность менеджеров пакетов. Позволяет управлять несколькими пакетами в одном репозитории, используя единый node_modules.

Пример структуры:

my-monorepo/
├── package.json          # корневой package.json
├── packages/
│   ├── utils/            # общая библиотека
│   │   └── package.json
│   ├── server/           # бэкенд приложение
│   │   └── package.json
│   └── client/           # фронтенд (если нужно)
│       └── package.json
└── node_modules/

Корневой package.json:

{
  "name": "my-monorepo",
  "private": true,
  "workspaces": [
    "packages/*"
  ]
}

Пакет packages/server/package.json:

{
  "name": "@myorg/server",
  "version": "1.0.0",
  "dependencies": {
    "@myorg/utils": "*"   // ссылается на локальный пакет
  }
}

Плюсы: простота, не требует дополнительных зависимостей. Минусы: нет умного кэширования и параллельного запуска задач.

2. Lerna (классика для монорепозиториев)

Lerna уже много лет является стандартом. Он работает поверх Yarn/npm Workspaces и добавляет возможности для публикации пакетов и выполнения скриптов.

Установка:

npm install --global lerna

Инициализация:

lerna init

Пример команды для запуска тестов во всех пакетах:

lerna run test --since master

Lerna умеет автоматически определять, какие пакеты изменились, и запускать задачи только для них. Однако его развитие замедлилось, и многие переходят на более современные инструменты.

3. Nx и Turborepo (современный стандарт)

Оба инструмента решают главную проблему монорепозиториев — скорость. Они используют граф зависимостей и распределённое кэширование, чтобы не перезапускать то, что уже было сделано.

Пример с Turborepo:

Установка:

npm install turbo --save-dev

Корневой turbo.json:

{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**"]
    },
    "test": {
      "dependsOn": ["build"],
      "outputs": []
    }
  }
}

Теперь при запуске turbo run build Turborepo построит граф зависимостей и запустит сборку пакетов параллельно, кэшируя результаты.

Nx предлагает похожий функционал, но имеет более мощную интеграцию с генераторами кода и плагинами для Angular, React, NestJS и т.д.

Практический пример: создание простого монорепозитория с двумя пакетами

Создадим структуру с общей библиотекой @myorg/utils и приложением @myorg/app, которое её использует.

Шаг 1: Инициализация

mkdir my-monorepo && cd my-monorepo
npm init -y
npm install --save-dev typescript @types/node ts-node

Корневой package.json:

{
  "name": "my-monorepo",
  "private": true,
  "workspaces": ["packages/*"]
}

Блоги

Рекомендаций по книгам