NPX, або Прощайте, глобальні залежності
Гасконцам від програмування присвячується.
Коли справа стосується глобальних пакетів, всі кажуть, що це зло. Однак через деякий час у файлі README.md дивним чином виявляється інструкція типу:
npm install -g typescript
Найлютіші кричать: «Тисяча чортів, я ж сто разів казав не робити цього!» На що чують невиразну відповідь: «Так наш пакет не збирався локально, дебажити ми не могли». Що тут скажеш? Давайте ж розберемося, чи є глобальні NPM-пакети вселенським злом.
Для чого взагалі потрібні глобальні залежності
Перше і найголовніше: глобальні пакети вносять розбрат в стрункі ряди команди. І скільки б ви не вимагали: «Кожен день оновлюйте свої глобальні пакети на робочих машинах перед початком роботи, та й взагалі перед кожним комітом», — це тільки стрясати повітря. В один прекрасний день прийде QA і скаже: «Написане вами у мене не билдится!»
Якщо ви виберете налаштувати CI/CD-систему, то всі вигуки: «У нас чудово збирається локально!» — підуть Бобіку під хвіст, бо версії у вас і на білд-машині будуть зовсім різні.
Тепер трохи про безпеку. Чи знаєте ви, що всі глобальні NPM-пакети ставляться під root в Linux і macOS? Не знаєте? Так ось, вони ставляться під root. (До речі, щоб уникнути цього, пропоную прочитати матеріал за посиланнями 1 і 2 в кінці статті.)
І ще раз про «Месьє, у мене працює локально». Метод require в ноді резолвит імена залежностей наступним чином:
- Спочатку перевіряє власні node_modules проекту.
- Потім require дивиться в глобальні модулі, які для Unix-систем і для macOS лежать за адресою /usr/local/lib/node_modules (ви можете дізнатися цю адресу, набравши команду npm root -g).
- А далі, спускаючись від директорії вашого юзера до вашої робочої директорії, він буде шукати залежності у всіх зустрінутих їм node_modules-папках.
А тепер розглянемо наступний широко поширений випадок. У мене є шматок коду, який посилається на модуль, який я перетер у своїх локальних залежностях (насправді я беру найпростіший варіант — на практиці вони ще більш витончені). Локально все працює на ура, тому що в глобальних модулях сидить встановлена мною коли-то стара залежність. Але потім я буду довго мучитися і аналізувати логи в пошуках причини.
Здавалося б, що відразу приходить на розум після таких аргументів? Так як глобальні залежності є злом, потрібно прибрати їх в локальні. Цей шлях має сенс — ваш покірний слуга навіть зараз робить так для деяких проектів. Глобальні залежності прибираються в дев-депенденси, де тихо доживають свій вік. Скрипти 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
У підсумку
Переваги способу:
- Ми можемо повністю піти від глобальних пакетів.
- Ми завжди користуємося останньою версією пакета.
- У всієї команди і у всіх робочих оточень версія пакету буде одна і та ж. (Це твердження спірне, але нехай залишиться для бесіди з прочитавшими його).
- Для створення аліасів можна написати простий скрипт і запускати його на різних оточеннях як install-скрипта.
Недоліки способу:
- Витрачається час на завантаження пакета (хоча, якщо пакет вже прогрузился, npx кешує його).
- Не працює під Windows. Самий головний мінус цього способу, проте якщо використовувати розробку в контейнері, то він разом з проблемами Windows відходить на другий план.
Як доповнення хотілося б сказати, що автор не топить за якийсь окремий фреймворк. Angular використовувався тільки як приклад. З цим способом працюють будь-які тестові фреймворки, бойлерплейты, такі як React, і будь-які CLI.
Корисні посилання, про яких йшла мова у статті:
- Resolving EACCES permissions errors when installing packages globally .
- Install npm packages globally without sudo on macOS and Linux .
- Представляємо npx: утиліту для запуску npm-пакетів .
Опубліковано: 13/09/19 @ 10:00
Розділ Різне
Рекомендуємо:
Складнощі тестування мікросервісів та що з ними робити
Як ми впровадили Scrum: граблі і точки зростання
Виведення сайту по монтажу натяжних стель в топ 3
Консервація проблем замість реформ. Що не так з ініціативою Кабміну
C++ дайджест #19: підготовка до співбесід