Проблема с разрешением версий библиотеки в монорепе с Yarn Workspaces
Добрый день! У нас настроена монорепа с использованием Yarn Workspaces:
"workspaces": {
"packages": [
"apps/*",
"packages/*"
]
}
В разных пакетах используются разные версии библиотеки “Admiral”:
- В пакете
apps/pkbиспользуется версия 2.0 - В пакете
apps/opsnextиспользуется версия 1.0
Версии библиотеки нельзя унифицировать. Проблема в том, что Yarn поднимает одну зависимость на уровень монорепы (версия 1.0), а более новая версия (2.0) остается на уровне пакета pkb.
При локальном запуске, по идее, система должна сначала проверять локальные node_modules в пакете, а затем подниматься на уровень монорепы. То есть, при запуске из пакета pkb должна использоваться версия 2.0. Однако на практике всегда берется версия с уровня монорепы (1.0), что приводит к ошибкам.
Я пробовал настроить в Webpack:
resolve.aliasдля библиотекиresolve.modules
Но эти изменения не дали эффекта.
Используемые версии:
- Yarn: 1.2.22
- Webpack: 5.88
Подскажите, на что стоит обратить внимание или какие есть предложения по решению этой проблемы?
В монорепе с Yarn Workspaces при использовании разных версий одной библиотеки проблема возникает из-за механизма hoisting, который поднимает зависимости на уровень корневого проекта. Для решения вашей ситуации с библиотекой “Admiral” в версиях 1.0 и 2.0 необходимо настроить nohoist в корневом package.json, чтобы предотвратить поднятие конфликтующих зависимостей, и использовать правильную стратегию разрешения модулей в Webpack.
Содержание
- Принцип работы Yarn Workspaces и hoisting
- Проблема с разными версиями зависимостей
- Решение через nohoist конфигурацию
- Настройка Webpack для разрешения версий
- Альтернативные подходы
- Проверка и отладка
Принцип работы Yarn Workspaces и hoisting
Yarn Workspaces использует механизм hoisting для оптимизации структуры зависимостей. Когда вы запускаете install, Yarn анализирует все зависимости во всех пакетах и поднимает общие зависимости на уровень корневого node_modules [1]. Это позволяет избежать дублирования и ускоряет установку.
В вашем случае с Yarn 1.2.22, когда одна и та же библиотека используется в разных версиях, Yarn выбирает версию с наибольшим количеством использований и поднимает её в корневой node_modules [6]. Библиотека “Admiral” версии 1.0 поднимается на уровень монорепы, версия 2.0 остаётся в локальном node_modules пакета pkb.
Важно: Версия Yarn 1.2.22 довольно старая и имеет отличия от современных версий в поведении workspaces [10].
Проблема с разными версиями зависимостей {#problema-s-raznymi-versiyami-zavisimostey]
Ваша ситуация классическая проблема монореп с разными версиями зависимостей [5]. Когда версия 1.0 поднята на уровень монорепы, а вам нужно использовать версию 2.0 в пакете pkb, возникает конфликт разрешения модулей.
Node.js использует алгоритм разрешения модулей, который сначала ищет зависимости в локальном node_modules, затем поднимается вверх по иерархии. Однако в случае с workspaces, механизм hoisting нарушает эту логику [8].
Симптомы проблемы:
- Версия 1.0 используется вместо 2.0 при запуске из пакета
pkb - Ошибки совместимости из-за разницы в API
- Непредсказуемое поведение при разработке
Решение через nohoist конфигурацию
Для предотвращения поднятия конфликтующих зависимостей используйте настройку nohoist в корневом package.json [2]:
{
"workspaces": {
"packages": [
"apps/*",
"packages/*"
],
"nohoist": [
"apps/pkb/node_modules/admiral",
"apps/pkb/node_modules/admiral/**"
]
}
}
Это скажет Yarn не поднимать библиотеку “Admiral” из пакета pkb на уровень монорепы [4]. После изменения конфигурации необходимо:
- Удалить все
node_modulesпапки - Удалить
yarn.lockфайл - Запустить
yarn installзаново
Важно: В Yarn 1.x
nohoistработает иначе, чем в новых версиях [7]. Для вашего случая с Yarn 1.2.22 синтаксис будет таким же.
Настройка Webpack для разрешения версий
Поскольку вы используете Webpack 5, можно дополнительно настроить разрешение модулей [9]:
// webpack.config.js
module.exports = {
resolve: {
alias: {
'admiral': path.resolve(__dirname, 'node_modules/admiral')
},
modules: [
path.resolve(__dirname, 'node_modules'),
// Добавляем локальные модули
path.resolve(__dirname, 'apps/pkb/node_modules')
]
}
};
Однако, как вы упомянули, resolve.alias и resolve.modules не дали эффекта. Это связано с тем, что проблема кроется в механизме разрешения Node.js, а не в Webpack [3].
Более эффективный подход - использование resolve.fallback в Webpack 5:
module.exports = {
resolve: {
fallback: {
'admiral': require.resolve('admiral')
}
}
};
Альтернативные подходы
1. Yarn Constraints (для современных версий)
Если вы планируете перейти на Yarn 3+, можно использовать плагин yarn-constraints [5], который позволяет контролировать зависимости между workspaces.
2. Ручное управление зависимостями
Добавить библиотеку “Admiral” версии 2.0 в корневой package.json с префиксом private: true:
{
"private": true,
"dependencies": {
"admiral": "2.0.0"
}
}
3. Использование scoped пакетов
Переименовать библиотеку в каждом пакете с использованием scope:
{
"dependencies": {
"@app/admiral-v1": "1.0.0",
"@app/admiral-v2": "2.0.0"
}
}
Проверка и отладка
После внесения изменений убедитесь, что разрешение работает правильно:
- Проверьте структуру
node_modules:
ls -la apps/pkb/node_modules/admiral
ls -la node_modules/admiral
- Запустите тестовый скрипт для проверки разрешения:
// test-resolution.js
console.log(require.resolve('admiral'));
- Используйте команду для проверки зависимостей:
yarn why admiral
Важно: В Yarn 1.2.22 иногда бывает необходимо полностью очистить кэш и переустановить зависимости после изменения
nohoistнастроек [8].
Источники
- nohoist in Workspaces | Yarn Blog
- Using different versions of a dependency in different packages of a Yarn Workspace - Stack Overflow
- Yarn workspace
nohoistoption does not seem to work as documentation states… · Issue #6412 · yarnpkg/yarn - Yarn Workspaces hoisting in nested packages with different versions of a dependency results in importing wrong dependency version · Issue #8068 · yarnpkg/yarn
- Yarn Constraints: have your monorepo under control | DATAMOLE
- Workspaces | Yarn
- nohoist with workspaces still hoisting - Stack Overflow
- Yarn nohoist without using workspaces - Stack Overflow
- A concise guide to configuring React Native with Yarn Workspaces | Medium
- Migrating our Monorepo to Yarn 2 | DoltHub Blog
Заключение
-
Основная проблема связана с механизмом hoisting в Yarn Workspaces, который поднимает общие зависимости на уровень монорепы, игнорируя локальные версии в пакетах.
-
Ключевое решение - настройка
nohoistв корневомpackage.jsonдля предотвращения поднятия конфликтующих зависимостей, особенно для библиотеки “Admiral” в пакетеpkb. -
Обязательные шаги после изменения конфигурации: удаление всех
node_modules,yarn.lockи повторная установка зависимостей. -
Для отладки используйте команды
yarn why admiralи проверку путей разрешения черезrequire.resolve('admiral'). -
Рекомендация по обновлению: рассмотрите возможность перехода на более современную версию Yarn (3+), где механизм workspaces улучшен и имеет больше инструментов для управления версиями зависимостей.