NPX, або Прощайте, глобальні залежності

Гасконцам від програмування присвячується.

Коли справа стосується глобальних пакетів, всі кажуть, що це зло. Однак через деякий час у файлі README.md дивним чином виявляється інструкція типу:

npm install -g typescript

Найлютіші кричать: «Тисяча чортів, я ж сто разів казав не робити цього!» На що чують невиразну відповідь: «Так наш пакет не збирався локально, дебажити ми не могли». Що тут скажеш? Давайте ж розберемося, чи є глобальні NPM-пакети вселенським злом.

Для чого взагалі потрібні глобальні залежності

Перше і найголовніше: глобальні пакети вносять розбрат в стрункі ряди команди. І скільки б ви не вимагали: «Кожен день оновлюйте свої глобальні пакети на робочих машинах перед початком роботи, та й взагалі перед кожним комітом», — це тільки стрясати повітря. В один прекрасний день прийде QA і скаже: «Написане вами у мене не билдится!»

Якщо ви виберете налаштувати CI/CD-систему, то всі вигуки: «У нас чудово збирається локально!» — підуть Бобіку під хвіст, бо версії у вас і на білд-машині будуть зовсім різні.

Тепер трохи про безпеку. Чи знаєте ви, що всі глобальні NPM-пакети ставляться під root в Linux і macOS? Не знаєте? Так ось, вони ставляться під root. (До речі, щоб уникнути цього, пропоную прочитати матеріал за посиланнями 1 і 2 в кінці статті.)

І ще раз про «Месьє, у мене працює локально». Метод require в ноді резолвит імена залежностей наступним чином:

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

Здавалося б, що відразу приходить на розум після таких аргументів? Так як глобальні залежності є злом, потрібно прибрати їх в локальні. Цей шлях має сенс — ваш покірний слуга навіть зараз робить так для деяких проектів. Глобальні залежності прибираються в дев-депенденси, де тихо доживають свій вік. Скрипти npm будуть з радістю підхоплювати їх не гірше глобальних. Здавалося б, і вовки ситі, і вівці цілі. Однак такі дії загрожують великим перевантаженням дів-секції і, як наслідок, сприяють довгому виконання команди npm install, а також ведуть до великого споживання дискового простору на девелоперської машині (п'ять проектів — п'ять карм, три еслинта, чотири тайпскрипта). Тобто ми приходимо до того, навіщо писалися глобальні депенденси, — і це повторне споживання місця різними пакетами для одних і тих же залежностей.

NPX як спосіб піти від глобальних пакетів

Але тепер розглянемо новий шлях. Новим шляхом буде використання на проекті NPX. Цей інструмент призначений для полегшення запуску скриптів і зниження залежності розробника і від глобальних, і від локальних прив'язок в цілому. Як це працює?

NPX — це інструмент, який потрібен для спрощення використання утиліт і виконуваних файлів. Також він допомагає використовувати утиліти без run-скрипта і запускати команди з різними версіями ноди. Для тих, хто хоче більше дізнатися саме про NPX, я рекомендую матеріал за посиланням 3 в кінці статті, де в легкій формі викладено базові принципи та можливості використання цієї утиліти.

Припустимо, я розробник на Angular 8, але вперто не хочу ставити Angular CLI собі в глобальні складання: він застаріє, і мені треба буде кожен раз переставляти його. Є вихід — робити все руками. Але я лінивий програміст, який володіє знаннями shell. І що я роблю в такому випадку? Ті програми, які викликаються вкрай рідко, я буду викликати так, як є — через NPX. І для створення нового проекту на Angular мені потрібно буде набрати наступну команду:

$npx -p @angular/cli ng my new-new-project

Визначившись з тим, чого я хочу, отримую наступний проект з секцією скриптів в project.json.

 "scripts": {
 "нг": "ng",
 "start": "ng serve",
 "build": "ng build",
 "test": "ng test",
 "lint": "ng lint",
 e2e: "ng e2e"
}

Зараз ви скажете: «Ага, нічого не вийшло». Дійсно, запустити я нічого не зможу, тому що у мене немає Angular CLI на локальному пристрої. А тепер давайте замислимося, що таке ng? А ng — це всього лише псевдонім. І тому ми пишемо лаконічну рядок у shell (для людей, які користуються ОС Windows, такий трюк не спрацює):

$alias ng='npx -p @angular/cli ng'

І тепер ми можемо створювати і запускати нові проекти, компоненти, тести і т. д. Наступною командою перевіряємо, чи не з'явилося нових глобальних залежностей:

npm list -g --depth=0

Крім вже існуючих залежностей, нічого не має з'явитися.

І як тільки захочемо прибрати цей псевдонім, ми робимо команду:

unalias ng

У підсумку

Переваги способу:

  1. Ми можемо повністю піти від глобальних пакетів.
  2. Ми завжди користуємося останньою версією пакета.
  3. У всієї команди і у всіх робочих оточень версія пакету буде одна і та ж. (Це твердження спірне, але нехай залишиться для бесіди з прочитавшими його).
  4. Для створення аліасів можна написати простий скрипт і запускати його на різних оточеннях як install-скрипта.

Недоліки способу:

  1. Витрачається час на завантаження пакета (хоча, якщо пакет вже прогрузился, npx кешує його).
  2. Не працює під Windows. Самий головний мінус цього способу, проте якщо використовувати розробку в контейнері, то він разом з проблемами Windows відходить на другий план.

Як доповнення хотілося б сказати, що автор не топить за якийсь окремий фреймворк. Angular використовувався тільки як приклад. З цим способом працюють будь-які тестові фреймворки, бойлерплейты, такі як React, і будь-які CLI.

Корисні посилання, про яких йшла мова у статті:

  1. Resolving EACCES permissions errors when installing packages globally .
  2. Install npm packages globally without sudo on macOS and Linux .
  3. Представляємо npx: утиліту для запуску npm-пакетів .

Опубліковано: 13/09/19 @ 10:00
Розділ Різне

Рекомендуємо:

Складнощі тестування мікросервісів та що з ними робити
Як ми впровадили Scrum: граблі і точки зростання
Виведення сайту по монтажу натяжних стель в топ 3
Консервація проблем замість реформ. Що не так з ініціативою Кабміну
C++ дайджест #19: підготовка до співбесід