NPM — менеджер пакетов, входящий в состав Node.js. Он выполняет множество функций, которые не представляется возможным рассмотреть в рамках текущего курса. Рекомендуем периодически посматривать на его команды и изучать информацию по ним. Ключевая задача, которую решает NPM — управление зависимостями, и именно о ней мы поговорим.

Практически с самого зарождения программирования: с тех пор, когда код стал храниться в памяти компьютера, появилась идея повторного использования кода не только в рамках одного проекта, но и во многих других. В разных проектах используются одни и те же функции для работы с датами, математическими формулами, всевозможными расчетами. Подобное переиспользование кода приводит к резкому росту производительности. Программисты современности могут сосредоточиться на решении уникальных задач бизнеса, вместо создания своих велосипедов для решения инфраструктурных задач. Все стало еще проще с появлением git и github.

Первоначально общий код выносился в файлы, которые программисты часто держали на своих собственных компьютерах и копировали из проекта в проект. Такой способ обладает рядом существенных недостатков:

  1. Код сложно передать другим.
  2. У каждого разработчика своя собственная копия одинаковых или почти одинаковых решений одних задач.
  3. Доработкой занимается только автор.
  4. Существует много разных копий без возможности быстрого обновления.
  5. Из-за того, что код копируется прямо в другой проект, он, как правило, модифицируется и становится специфичным.

Если использовать git, то часть проблем решается автоматически. Cоздается набор общих файлов, который называется "библиотека". Конечные проекты, в свою очередь, начинают использовать библиотеки, написанные нами или сторонними разработчиками. Но тут возникает следующая сложность: когда таких библиотек становится много, у них также появляется общий код более низкого уровня. Например, финансовые библиотеки могут использовать функции для преобразования валют. Чтобы подобный код не дублировался, хочется вынести его в общедоступное место. Этот процесс может продолжаться бесконечно. Одни библиотеки зависят от других, те в свою очередь от третьих и так далее. С точки зрения переиспользования кода все хорошо: большое число маленьких библиотек, решающих всевозможные задачи, позволяет быстро двигаться вперед в разработке. Но с точки зрения управления этим зоопарком появляются проблемы:

  1. Появляются новые версии библиотек. Из-за этого они начинают зависеть не только друг от друга, но и от разных версий друг друга. Зачем это нужно? Предположим, что создатель сторонней библиотеки, от которой зависит наша библиотека, обновил ее так, что изменил сигнатуры функций или, как говорят, сломал обратную совместимость. Теперь в них другое число параметров, другие параметры, другой возврат. Если мы обновим стороннюю библиотеку, наша первоначальная библиотека перестанет работать. Она рассчитывала на одно поведение библиотеки, от которой зависела, а получила другое.
  2. Необходимо стандартизировать способ создания библиотек. Только в таком случае станет возможным автоматизировать процесс их соединения. Стандартный веб-проект может зависеть от сотни библиотек, которые в свою очередь зависят от сотен других библиотек. Общее число зависимостей может легко перевалить за тысячу. Управлять таким количеством зависимостей вручную невозможно.
  3. Из проблемы, описанной выше, вытекает необходимость в автоматизированном средстве управления зависимостями: их обновлению, установке и удалению.

Всеми этими вопросами и занимаются пакетные менеджеры. В большинстве языков пакетный менеджер поставляется с самим языком, так как в современном мире сложно представить себе код, который не использует ни одной зависимости.

Терминология

В NPM используются следующие понятия:

  • Пакет (Package) — базовая единица, которой управляет NPM как единым целым. Может содержать любое количество файлов и кода. NPM позволяет устанавливать пакеты, обновлять или удалять.
  • Реестр (Registry) — хранилище пакетов NPM. Каждый желающий может опубликовать пакет в npm registry, потратив буквально минуту, а остальные смогут его использовать. В хранилище на текущий момент сотни тысяч пакетов и их количество стремительно растет. Исходный код пакетов, как правило, хранится на гитхабе. Несмотря на это, пакеты в npm никак не связаны с git и github.

Глобальная установка пакетов

Многие пакеты в npm представляют из себя законченные программы. Их можно установить и запустить как обычную утилиту командной строки. Попробуем установить утилиту sloc, которая умеет считать количество строк кода в указанной папке. Инструкция по установке обычно находится на странице репозитория в гитхабе.

$ sudo npm install -g sloc

Отмечу несколько моментов:

  • -g — этот флаг говорит о том, что нужно установить пакет глобально, то есть в такое место, которое доступно для всех пользователей системы.
  • sudo — нужно потому, что это место чаще всего недоступно для записи обычным пользователям (в Ubuntu это, скорее всего, /usr/local/lib).
  • после команды install можно перечислять любое количество пакетов через пробел.
# Глобальная установка пакета sloc
$ sudo npm install -g sloc
Password:
/usr/local/bin/sloc -> /usr/local/lib/node_modules/sloc/bin/sloc
+ [email protected]
updated 1 package in 3.701s

Процесс установки занимает некоторое время, в течение которого видно, как скачивается не только сам пакет, но и его зависимости. Дальше ничего делать не нужно, так как sloc создан утилитой командной строки и сразу готов к использованию.

# Создайте файл index.js в любом месте вашей файловой системы и добавьте туда любой код (например, печать на экран)
# Затем вызовите программу sloc, используя терминал. Для этого перейдите в директорию, содержащую этот файл, и выполните команду:
$ sloc index.js

---------- Result ------------

            Physical :  2
              Source :  2
             Comment :  0
 Single-line comment :  0
       Block comment :  0
               Mixed :  0
               Empty :  0
               To Do :  0

Number of files read :  1

------------------------------

Пакет

NPM работает таким образом, что объединяет понятия "проект" и "библиотека" в одно целое. И то и другое оформляется, как пакет NPM. Причем проект может быть любых размеров. Даже если ваше приложение состоит из 3 строчек кода, вам понадобится работать с ним, как с полноценным проектом.

Для создания пакета необходимо выполнить команду npm init в той директории, где вы собираетесь писать код. С этого момента эта папка будет корневой для вашего пакета.

$ npm init

После ввода данной команды вам предложат ответить на десяток вопросов, например, ввести имя вашего проекта, его описание, имя автора (вас как создателя) и тому подобное. Некоторые вопросы имеют ответы по умолчанию, например имя проекта или его версия. NPM предлагает для названия проекта использовать имя директории, внутри которой запускается npm init. Это полезное соглашение, которого стоит придерживаться (по необходимости переименуйте саму директорию).

# Если нажать Enter, то ответом на вопрос станет значение по умолчанию
package name: (js_setup_environment_course) # ответ по умолчанию указан в скобках

Если вы не понимаете значение некоторых вопросов, смело жмите Enter, потом все можно будет поправить.

Когда вопросы закончатся, npm покажет конечный результат в виде JSON. JSON — это формат для представления структурированных данных.

Если вы не знакомы с ним, то не стоит переживать, формат — не язык программирования. Достаточно выучить несколько правил по формированию данных в виде JSON, для того, чтобы чувствовать себя комфортно.

{
  "name": "My Super Project",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

В этом JSON содержатся ответы которые, вы дали ранее. NPM спросит, все ли хорошо с ним? Если вы ответите утвердительно, то NPM создаст файл package.json, куда поместит данный вывод (JSON). В дальнейшем вы всегда сможете открыть его в обычном текстовом редакторе и поправить, если понадобится. Файл package.json крайне важен для правильной работы NPM. Кроме информационных полей типа имени автора, есть и функциональные поля, которые влияют на то, как будет работать пакет. Они изучаются далее на протяжении всего курса.

Проблемы

Если в файле package.json содержатся синтаксические ошибки, иными словами, там нарушены правила форматирования JSON, то при попытке сделать что-то с проектом будут появляться подобные ошибки:

npm ERR! code EJSONPARSE
npm ERR! Failed to parse json
npm ERR! Unexpected string in JSON at position 225 while parsing '{
npm ERR!   "name": "gendiff_freetonik",
npm ERR!   "vers'
npm ERR! File: /Users/a/cpackage.json
npm ERR! Failed to parse package.json data.

или такие:

npm ERR! code EJSONPARSE
npm ERR! Failed to parse json
npm ERR! Unexpected token n in JSON at position 207 while parsing near '...
npm ERR!   "engines": {
npm ERR!     node: 8
npm ERR!   },
npm ERR!   "scri...'
npm ERR! File: /Users/a/c/package.json
npm ERR! Failed to parse package.json data.

Обычно любое сообщение с «неожиданными» (unexpected) символами или строками означает наличие синтаксической ошибки.

Воспользуйтесь валидаторами JSON для проверки верности своего файла: jsonlint.com или другими подобными инструментами, которые можно загуглить по запросу "json validator".

Другая проблема связана с тем, что NPM во время установки пакетов нередко выводит подобные сообщения:

npm WARN deprecated [email protected]: Please update to minimatch 3.0.2 or higher to avoid a RegExp DoS issue
npm WARN deprecated [email protected]: Please update to minimatch 3.0.2 or higher to avoid a RegExp DoS issue
npm WARN deprecated [email protected]: graceful-fs v3.0.0 and before will fail on node releases >= v7.0.

Бояться их не надо, это не сообщения об ошибках. Deprecated warning — сообщение, сигнализирующее о том, что нечто устарело. Под этим «нечто» может скрываться все, что угодно, и часто не по вашей вине. Например, в package.json могут использоваться свойства, которые в новых версиях NPM называются по-другому. К этим предупреждениям стоит прислушиваться, но на работу они не влияют.

Код пакета

После завершения процесса инициализации, можно приступать к самому главному — написанию кода. По умолчанию код пакета создается внутри файла index.js, лежащего в корне проекта (там, где создан файл package.json). Это не значит, что весь пакет состоит из одного файла. Файлов с кодом можно создавать сколько угодно, главное, при этом, что основная работа происходит внутри index.js, куда импортируется код из остальных файлов.

Самостоятельная работа

  • Клонируйте nodejs-package к себе на компьютер (документация по клонированию https://help.github.com/articles/cloning-a-repository/).
  • Установите (глобально) пакет sloc.
  • Выполните команду sloc path/to/nodejs-package и изучите вывод. Где path/to/nodejs-package путь до директории nodejs-package клонированного на ваш компьютер.

Дополнительные материалы

  1. NPM
  2. Документация NPM
Мы учим программированию с нуля до стажировки и работы. Попробуйте наш бесплатный курс «Введение в программирование» или полные программы обучения по Node, PHP, Python и Java.

Хекслет

Подробнее о том, почему наше обучение работает →