Навіщо використовувати залежності від рівних у npm для плагінів?


218

Чому, наприклад, плагін Grunt визначає свою залежність від grunt як " однорангову залежність "?

Чому плагін не може мати Grunt як власну залежність у grunt-plug / node_modules ?

Тут описані залежності від рівних: https://nodejs.org/en/blog/npm/peer-dependitions/

Але я насправді не розумію.

Приклад

На даний момент я працюю з AppGyver Steroids, який використовує завдання Grunt для зведення моїх вихідних файлів у / dist / папку для подання на локальному пристрої. Я зовсім новий в npm та бурчання, тому хочу повністю зрозуміти, що відбувається.

Поки що я розумію:

[rootfolder] /package.json повідомляє npm, що залежить від grunt-steroidsпакету npm для розробки:

  "devDependencies": {
    "grunt-steroids": "0.x"
  },

Добре. Запуск npm install у [rootfolder] виявляє залежність та встановлює grunt-steroids у [rootfolder] / node_modules / grunt-steroids .

Потім Npm зчитує [rootfolder] /node_modules/grunt-steroids/package.json, щоб він міг встановлювати grunt-steroidsвласні залежності:

"devDependencies": {
    "grunt-contrib-nodeunit": "0.3.0",
    "grunt": "0.4.4"
  },
"dependencies": {
    "wrench": "1.5.4",
    "chalk": "0.3.0",
    "xml2js": "0.4.1",
    "lodash": "2.4.1"
  },
"peerDependencies": {
    "grunt": "0.4.4",
    "grunt-contrib-copy": "0.5.0",
    "grunt-contrib-clean": "0.5.0",
    "grunt-contrib-concat": "0.4.0",
    "grunt-contrib-coffee": "0.10.1",
    "grunt-contrib-sass": "0.7.3",
    "grunt-extend-config": "0.9.2"
  },

Пакети " залежності " встановлюються в [rootfolder] / node_modules / grunt-steroids / node_modules, що для мене є логічним.

" DevDependitions " не встановлено, що, я впевнений, контролюється npm виявленням, яке я просто намагаюся використовувати grunt-steroids, а не розвиватися на ньому.

Але тоді у нас є " peerDependitions ".

Вони встановлені в [rootfolder] / node_modules , і я не розумію, чому там, а не в [rootfolder] / node_modules / grunt-steroids / node_modules, щоб уникнути конфліктів з іншими плагінами grunt (або будь-яким іншим)?

Відповіді:


421

TL; DR: [1] - peerDependencies це залежності, які піддаються (і очікується, що їх буде використовувати) споживчий код, на відміну від "приватних" залежностей, які не піддаються впливу, і є лише деталями реалізації.

Проблема залежностей від однолітків вирішується

Модульна система NPM є ієрархічною. Однією з великих переваг для більш простих сценаріїв є те, що при встановленні пакету npm цей пакет приносить з собою власні залежності, щоб він вийшов з коробки.

Але проблеми виникають, коли:

  • І ваш проект, і деякий модуль, який ви використовуєте, залежать від іншого модуля.
  • Три модулі повинні говорити один з одним.

У прикладі

Скажімо, ви будуєте, YourCoolProjectі ви використовуєте і те, JacksModule 1.0і інше JillsModule 2.0. І припустимо, що це JacksModuleтеж залежить JillsModule, скажімо, від іншої версії 1.0. Поки ці 2 версії не відповідають, немає жодної проблеми. Те, що JacksModuleвикористовується JillsModuleпід поверхнею, - лише деталь реалізації. Ми збираємося JillsModuleвдвічі, але це невелика ціна, яку потрібно платити, коли ми отримуємо стабільне програмне забезпечення поза коробкою.

Але що робити, якщо якимось чином JacksModuleвикриває свою залежність JillsModule. Він приймає екземпляр, JillsClassнаприклад ... Що відбувається, коли ми створюємо new JillsClassвикористовувану версію 2.0бібліотеки і передаємо її разом jacksFunction? Все пекло зірветься! Прості речі на кшталт jillsObject instanceof JillsClassнесподівано повернуться, falseтому що jillsObjectнасправді є примірником іншої JillsClass , 2.0версії.

Як це вирішують залежності від рівних

Вони кажуть нпм

Мені потрібен цей пакет, але мені потрібна версія, яка є частиною проекту, а не якась версія, приватна для мого модуля.

Коли npm побачить, що ваш пакет встановлюється в проект, у якого немає такої залежності, або у нього є несумісна версія , він попередить користувача в процесі встановлення.

Коли слід використовувати одноліткові залежності?

  • Коли ви будуєте бібліотеку, яку використовуватимуть інші проекти, і
  • Ця бібліотека використовує деякі інші бібліотеки та
  • Ви очікуєте, що користувач також повинен працювати з цією іншою бібліотекою

Поширені сценарії - плагіни для більших рамок. Подумайте про такі речі, як Gulp, Grunt, Babel, Mocha тощо. Якщо ви пишете плагін Gulp, ви хочете, щоб цей плагін працював з тим самим Gulp, який використовує проект користувача, а не з вашою приватною версією Gulp.


Анотації

  1. Надто довго; не читав. Використовується для позначення короткого резюме для тексту, який вважається занадто довгим.

2
Одна важлива річ, яку я помітив і ніде не сказаний, коли ми будуємо плагін, чи повинен у нас бути дублікат залежностей від пакета для рівних залежностей? На прикладі ОП ми бачимо, що "grunt": "0.4.4"це і в devDependpendes, і в peerDependpendes, і для мене є сенс мати там дублікат, тому що це означає, що мені потрібен цей gruntпакет для мого власного використання, але і що мої користувачі бібліотека може використовувати власну версію, якщо вона дотримується блокування версії peerDependitions. Це правильно? Або приклад ОП дуже поганий?
Vadorequest

4
Я можу собі уявити, як люди, які створюють плагін Grunt, є фанатами Grunt :) Як такому, їм здається природним самим використовувати Grunt для процесу складання свого плагіна .... Але чому вони хочуть заблокувати діапазон версій Grunt, їхній плагін працює з процесом збирання, який вони використовують для його створення? Додавання його як залежність від розробника дозволяє їм розв'язати це. В основному є 2 фази: час побудови та час виконання. Залежність розробників потрібна під час збирання. Регулярні та однолітні залежності потрібні під час виконання. Звичайно, із залежностей залежностей все стає заплутаним швидко :)
Stijn de Witt

1
Дякую за цю відповідь! Просто щоб прояснити, в вашому прикладі, якщо JacksModuleзалежить від того, JillsModule ^1.0.0з JillsModuleбудучи рівноправним залежність JacksModuleі YourCoolProjectвикористовували JacksModuleі JillsModule ^2.0.0, ми отримаємо попередження про залежності однолітків по НПМ, який порадить нам встановити , JillsModule ^1.0.0як добре. Але що станеться тоді? YourCoolProjectтепер буде дві версії для перевезення JillsModuleчерез import jillsModule from "..."? І як я пам’ятаю, що коли я використовую, JacksModuleмені потрібно передати його екземпляр JillsModule v1.0.0?
тонікс

1
@tonix Ну, це справді буде проблема з несумісністю версії. peerDependitions цього не вирішує. Але це допомагає зробити проблему явною. Тому що це буде чітко показувати невідповідність версії замість того, щоб використовувати дві версії мовчки. Розробник програми, який вибирає бібліотеки, повинен знайти рішення.
Штійн де Вітт

2
@tonix Або третій варіант: клонуйте JacksModuleрепо, оновіть його залежно від JillsModule ^2.0.0та запропонуйте піар для технічного обслуговування проекту. Це може допомогти спочатку надіслати помилку, сказавши, що ця залежність застаріла, і ви хочете допомогти її оновити. Якщо ви зробите хороший піар, більшість технічних працівників бібліотеки об'єднають його і подякують за це. Якщо технічне обслуговування не реагує, ви можете опублікувати свою вилку в просторах назв NPM під своїм ім’ям та використовувати її. У будь-якому випадку є рішення, але peerDependenciesце не вирішується самостійно.
Штійн де Вітт

26

Я рекомендую вам спочатку прочитати статтю ще раз. Це трохи заплутано, але приклад із Winston-mail показує вам відповідь, чому:

Наприклад, зробимо вигляд, що winston-mail@0.2.3вказано "winston": "0.5.x"в його "dependencies"об'єкті, оскільки це остання версія, на яку було протестовано. Як розробник програми, ви хочете отримати найновіші та найкращі речі, тому ви шукаєте найновіші версії winstonта winston-mailта додаєте їх у свій package.json як

{
  "dependencies": {  
    "winston": "0.6.2",  
    "winston-mail": "0.2.3"  
  }  
}

Але тепер, запуск npm встановлення призводить до несподіваного графіка залежності

├── winston@0.6.2  
└─┬ winston-mail@0.2.3                
  └── winston@0.5.11

У цьому випадку можлива наявність декількох версій пакета, які могли б викликати деякі проблеми. Рівні залежності дозволяють розробникам npm переконатися, що користувач має певний модуль (у кореневій папці). Але ви правильні з тим, що опис однієї конкретної версії пакета призведе до проблем з іншими пакетами, використовуючи інші версії. Це питання стосується розробників npm, як зазначено в статтях

Порада : вимоги до рівних залежностей, на відміну від звичайних залежностей, повинні бути поблажливішими . Ви не повинні блокувати свої однорангові залежності до конкретних версій патчу.

Тому розробники повинні дотримуватися semver для визначення peerDependitions. Ви повинні відкрити випуск для пакету грунтових стероїдів на GitHub ...


1
Ви говорите це, multiple versions of a package which would cause some issuesале чи не в цьому суть менеджера пакунків? Вони навіть далі обговорюють це в тій самій статті, де в проекті є дві версії одного пакету: одна, надана розробником, та одна, що надається сторонній бібліотекою.
Адам Бек

1
Я думаю, що я розумію сенс залежності від однолітків, але, winstonнаприклад, я зараз не можу використовувати winston-mailбібліотеку, оскільки моя версія не відповідає рівності однолітків? Я набагато скоріше мати тимчасовий перехід від найновішого і найбільшого для 1 бібліотеки, ніж взагалі не мати змоги ним користуватися.
Адам Бек

1
для вашого першого коментаря, наскільки я розумію і використовую його, це стосується тестування, наприклад, якщо у вас є пакет, який ви перевірили на певний сторонній пакет, ви не можете бути впевнені, що якщо такий зміни ваших залежностей (виправлення помилок, оновлення основних функцій), що ваш пакет працюватиме. Тому ви можете вказати конкретну версію плагіна і зберегти свої тести.
Фер До

1
Що стосується вашого другого коментаря: саме тому в документах вони говорять про те, що розробники повинні бути поблажливішими до залежностей від пакунків і використовувати semver, наприклад замість "0.2.1", "~ 0.2.1" -> дозволяє "0.2.x", але не "0.3.x", або "> = 0.2.1" -> все, від "0.2.x" до "1.x" або "x.2.". .. (але не дуже бажано, щоб пакет npm пішов з ~
Fer До

15

peerDependencies пояснено найпростішим можливим прикладом:

{
  "name": "myPackage",
  "dependencies": {
    "foo": "^4.0.0",
    "react": "^15.0.0"
  }
}


{
  "name": "foo"
  "peerDependencies": {
    "react": "^16.0.0"
  }
}

запуск npm install у myPackage видасть помилку, оскільки він намагається встановити версію React ^15.0.0AND foo, сумісну лише з React ^16.0.0.

peerDependitions НЕ встановлено.


чому б просто не поставити react 16 як dep всередині foo? таким чином і 15 і 16 будуть доступними, і foo може використовувати 16, а mypackage може використовувати 15?
nitinsh99

React - це фреймворк, який завантажується під час виконання, для того, щоб React 15 та React 16 існували на одній сторінці, вам потрібно було б одночасно завантажуватись, що було б надзвичайно важко та проблематично для кінцевого користувача. Якщо fooпрацює як з React 15, так і з React 16, він може перелічити свою peerDependency як >=15 < 17.
Йенс Бодал

nitinsh99 моя відповідь полягала в тому, щоб пояснити мету peerDependitions найпростішим можливим прикладом, а не як позбутися від помилки, кинутої peerDependitions
Крістофер Токар

@ nitinsh99 додавання реакції всередині пакета залежність забезпечить питання, як гачки - декілька реагує в пакеті
Masood
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.