Метапакеты



Часто с возрастом появляется желание иметь пакет для пакетов — положить в него всё нужное и накатывать одной командой. Чтобы он привозил и eslint, и prettier и browserslist. Вот только npm не предназначен для метапакетов. npm не гарантирует, что транзитивные зависимости будут лежать плоско на первом уровне. Вот вообще не гарантирует. А пакеты на это завязываются.

Классика — проблемы с плагинами eslint которые не могут лежать, на втором уровне. Ну ок, это кажется починили в новой системе конфигов (я ещё не попроовал).



Или вот browserslist-useragent перенёс browserslist в peerDependencies. Что это значит? Где тут проблемы метапакетов?



Предположим такую схему зависимостей:





dependencies

<метапакет>

browserslist-useragent: 4.2.2

browserslist: 4.2.2



devDependencies

webpack

browserslist: 4.1.1





Предположим у вас npm 6 или вы работаете в режиме --legacy-peer-deps. Ставим зависимости и оказываемся в ситуации





node_modules

<метапакет>

node_modules

browserslist: 4.2.2



webpack

browserslist: 4.1.1

browserslist-useragent: 4.2.2





Вроде не страшно?



А теперь делаем npm prune --production





node_modules

<метапакет>

node_modules

browserslist: 4.2.2



browserslist-useragent: 4.2.2





Всё сломалось! browserslist-useragent больше не видит browserslist и падает.



Да, если у вас npm 7 и выше и вы перешли на новую работу с peerDeps то получите





node_modules

<метапакет>

node_modules

browserslist: 4.2.2



browserslist-useragent: 4.2.2

node_modules

browserslist: 4.2.2





Может быть. А может повезёт и оно дедупнется. Гарантий нет. В этом и проблема метапакетов. Они не дают гарантий, транзитивные зависимости нельзя магически превратить в зависимости первого уровня.



Что тут можно сделать? Ну, например, написать скрипт, который контролирует версии базовых зависимостей и сам делает пул-реквесты по их поднятию. Либо остаться на мета-пакете и очень внимательно следить за структурой.