Налаштування Monorepo (Lerna) для веб-проекту
Lerna—один з перших інструментів для JavaScript monorepo. Довгий час був де-факто стандартом, потім втратив популярність, а з версії 6+ розквітнув під керуванням Nrwl (创ателів Nx) з інтеграцією з Nx під капотом. Сьогодні Lerna—хороший вибір для проектів, яким потрібне управління версіями пакетів і публікація в npm, але не потрібна складна інфраструктура Nx.
Коли Lerna, а не Turborepo або Nx
Lerna має смисл, коли:
- Проект—бібліотека або набір пакетів, які публікуються в npm
- Потрібне автоматичне управління версіями (semver) і CHANGELOG
- Команда невелика, складна інфраструктура надмірна
Для закритого продукту без публікації в npm—краще Turborepo або Nx.
Ініціалізація
# Новий monorepo
npx lerna init --packages="packages/*" --independent
cd my-monorepo
# Або додати в існуючий
npm install lerna --save-dev
npx lerna init
--independent—кожен пакет версіюється незалежно. Альтернатива—fixed mode, коли всі пакети мають одну версію.
Конфігурація lerna.json
{
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
"version": "independent",
"npmClient": "pnpm",
"command": {
"publish": {
"conventionalCommits": true,
"createRelease": "github",
"registry": "https://registry.npmjs.org"
},
"version": {
"conventionalCommits": true,
"gitTagVersion": true,
"push": true
}
},
"useWorkspaces": true,
"useNx": true
}
conventionalCommits: true—Lerna визначає тип версіювання за форматом коммітів: fix: → patch, feat: → minor, BREAKING CHANGE → major.
Структура для бібліотеки компонентів
my-ui-library/
├── packages/
│ ├── button/
│ │ ├── src/
│ │ ├── package.json
│ ├── input/
│ ├── modal/
├── apps/
│ └── docs/
├── lerna.json
└── pnpm-workspace.yaml
// packages/button/package.json
{
"name": "@acme/button",
"version": "1.2.0",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.js"
}
},
"scripts": {
"build": "tsup src/index.ts --format cjs,esm --dts",
"dev": "tsup src/index.ts --watch",
"test": "vitest run"
},
"peerDependencies": {
"react": ">=17.0.0"
},
"devDependencies": {
"@acme/theme": "workspace:*"
}
}
Управління версіями
# Визначити, що змінилось (dry run)
npx lerna changed
# Оновити версії інтерактивно
npx lerna version
# Автоматично за conventional commits
npx lerna version --conventional-commits --yes
# Версія з prerelease тегом (beta)
npx lerna version --conventional-commits --conventional-prerelease --preid=beta
Процес lerna version:
- Знаходить змінені пакети
- Пропонує нові версії за semver-правилами
- Оновлює package.json і міжпакетні залежності
- Генерує CHANGELOG.md
- Створює git commit і теги
Публікація
# Опубліковувати всі неопубліковані пакети
npx lerna publish from-package
# Опубліковувати змінені (після lerna version)
npx lerna publish from-git
# До приватного реєстру
npx lerna publish from-git --registry=https://npm.pkg.github.com
# .github/workflows/release.yml
jobs:
release:
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- run: pnpm install --frozen-lockfile
- run: npx lerna run build
- run: npx lerna version --conventional-commits --yes --no-push
- run: npx lerna publish from-git --yes
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
Запуск завдань з Nx під капотом
Lerna 6+ використовує Nx для розумного запуску завдань:
# Збирати всі пакети паралельно з кешем
npx lerna run build
# Тільки змінені пакети
npx lerna run test --since=main
# Пакет і залежні
npx lerna run build --scope=@acme/modal --include-dependents
// nx.json (створюється автоматично з --useNx)
{
"extends": "nx/presets/npm.json",
"targetDefaults": {
"build": {
"dependsOn": ["^build"],
"cache": true
}
}
}
Управління залежностями між пакетами
# Додати залежність до конкретного пакету
pnpm add react --filter @acme/button
# Додати локальний пакет як залежність
pnpm add @acme/theme --filter @acme/button --workspace
# Додати devDependency до всіх пакетів
pnpm add -D typescript --recursive
Якщо пакет A залежить від B і B змінився—lerna run build --scope=A --include-dependents перебудує B перед A.
Типові проблеми
Версія залежності не оновлюється при lerna version—перевірте, що залежність вказана без ^ або ~, інакше Lerna не зробить hardcode-апдейт. Флаг --force-publish оновить усі пакети примусово.
CHANGELOG дублює записи—Lerna за замовчуванням додає тільки коміти, що стосуються файлів пакету. Використовуйте --changelog-include-commits-root-path для корневих комітів.
Публікація падає на CI через npm publish --dry-run у .npmrc—переконайтеся, що dry-run=false в CI оточенні.
Таймлайн
Налаштування Lerna для набору npm-пакетів з нуля—два-три дні: конфігурація, налаштування конвенціональних комітів (commitlint + husky), CI/CD для автоматичної публікації, документування процесу релізу для команди.







