Про то, какие плюшки дает монорепозиторий писать не буду, об этом уже написано вот тут или тут и еще в куче мест.
В этой заметке я опишу, как создать монорепозиторий, используя npm workspaces (доступны с npm v7), предже всего это делается для того, чтобы избежать лишних vendor lock'ов.
То есть идея заключается в том, что есть npm и он занимается менеджментом зависимостей, а также линкует мои пакеты между собой, что позволит мне удобно переиспользовать код.
Здесь не будет затронута тема автоматического выпуска версий пакетов, деплоя и наверное еще много чего, опишу то, что лично я буду использовать, по ходу буду добавлять заметки, в том числе и для себя самого.
Начало
Создаем директорию для нашей мнонорепы, переходим в нее cd /myrepo и создаем корневой package.json.
npm init -yНемного подредактируем package.json: Делаем его приватным, и указываем, где будут располагаться наши workspaces (далее я буду называть их пакетами) - я указал директорию apps для приложений и packages для пакетов/библиотек, которые могут поставляться как обычные npm пакеты:
"name": "@myrepo/root",
"private": true,
"workspaces": ["apps/*", "packages/**"],Пример из жизни (про
packages/**) : на работе есть проект, в котором один из пакетов - библиотека React компонентов (своего рода пакет с пакетами), которая собирается как в один пакет со всеми компонентами, так и в несколько пакетов (кнопка отдельно, инпут отдельно и так далее), все это поставляется как отдельные npm пакеты и у каждого компонента могут быть свои зависимости.Поэтому, если я захочу написать в рамках монорепы свою библиотеку компонентов - то мой package.json уже готов к этому 😁
Добавляем packages
В моем случае ситуация следующая:
- одно реакт приложение на Next.js
- один сервис на NodeJS
- prisma ORM, схему и клиент для которой будут использовать два других пакета.
- Один npm пакет/библиотека (пока приватный)
Процесс переноса существующих приложений не должен занять много времени, в моем случае мне потребовалось выполнить следующие шаги:
- Скопировать весь код проектов кроме
package-lock.jsonиnode_modulesв соотвествующие директории иpackagesи немного поменять ихpackage.json:
//apps/web-app/package.json
"name": "@myrepo/web-app",
"private": true // Тут в зависимости от того, хотите ли вы публиковать пакет в npm или нет
//apps/node-app/package.json
"name": "@myrepo/node-app",
"private": true // Тут в зависимости от того, хотите ли вы публиковать пакет в npm или нет
//packages/flowchart/package.json
"name": "@myrepo/flowchart",
"private": true // Тут в зависимости от того, хотите ли вы публиковать пакет в npm или нет- Вынес общие
dependenciesиdevDependenciesв корневойpackage.json(у которогоname: @myrepo/root) - Добавил файлик
.nvmrcв корень проекта с таким содержимым (ваша версия ноды может отличаться):
16.15.0Это нужно чтобы на моем ноуте и на сервере все работало на одной и той же версии ноды. Чтобы переключать версии ноды у вас также должен быть установлен сам nvm
Все что связано с prisma можно пропустить, если вам не надо*
-
Вынес все, что касается prisma из
packages/web-appвpackages/prisma(но package.json, как для отдельного пакета создавать не стал, работает нормально и без него), получилась такая структура:./packages/prismaschema.prisma
У меня уже есть заметка про: Как подключиться к MYSQL БД хостинга Beget.ru Поэтому файлик только один :)
-
В корневой package.json добавляем следующую строчку, чтобы указать, где сейчас следует искать схему призмы для базы данных:
// ./package.json
"prisma": {
"schema": "packages/prisma/schema.prisma"
},-
Находясь в корне проекта устанавливаем зависимости (после этого у нас появится единственная папка
node_modulesв корне нашей монорепы):nvm use- чтобы выбрать версию ноды в .nvmrcnpm install- эту же команду надо запускать, каждый раз, когда добавляете очередной пакет(workspace), чтобы npm правильно слинковал пакеты
-
Так как призма генерует клиента в папку
node_modules/.prisma/client, а папокnode_modulesвнутри packages у нас больше нет, то везде, где мы ипортируемimport { PrismaClient } from "@prisma/client";- увидим ошибки (по крайней мере, если у вас typescript), поэтому теперь надо сгенерировать клиента для призмы заново:npx prisma generate
Дальше было немного специфичного для меня рефакторинга, который не имеет смысл описывать, в целом на этом этапе основа заложена:
- все зависимости хранятся в одном месте - есть одна корневая директория node_modules
- есть возможность без проблем шарить и переиспользовать код из пакетов так, как если бы это были обычные npm пакеты (например
import { some } from '@myrepo/some-package')
Полезные команды для монорепы
- Запустить во всех воркспейсах какой-то скрипт:
npm run some-script --workspaces --if-present .
Если не указать --if-present, то команда будет падать с ошибкой, если не во всех воркспейсах есть скрипт. Таким образом я могу запустить сборку сразу нескольких связанных проектов в монорепозитории одной командой, просто добавить одну строчку в поле scripts корневого package.json:
"scripts": {
"build": "npm run build --workspaces --if-present"
}После этого достаточно набрать в терминале npm run dev:apps
- Установка пакетов в один из пакетов (воркспейсов)
npm install somePackage -w=@myrepo/some-workspaceВместо заключения
К посту прикреплена ссылка на монорепозиторий, с настроенной esbuild сборкой для каждой директории внутри packages, он может послужить отправной точкой, если хотите например запилить ui кит, где каждый компонент будет собираться в отдельный пакет









