Что такое package.json?

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

package.json

Проблема

В приведенном выше package.json видно, что объект “dependencies” сопоставляет имя пакета с диапазоном версий.

Объект dependencies содержит имя пакета, сопоставленное с диапазоном версий.

Файл package.json всегда предоставляет диапазон версий для зависимости, но никогда  —  точную версию.

Это делает npm install недетерминированной командой. Поэтому, если запустить npm install cегодня, а затем запустить ее снова через 3 месяца, можно получить не то же самое дерево node_modules.

Кроме того, если другой разработчик клонирует проект и через несколько дней запустит npm install, у него может оказаться другое дерево зависимостей node_modules. Когда несколько разработчиков работают над одним и тем же репозиторием (что чаще всего и происходит в каждой организации), это может стать большой проблемой и привести к несоответствию установленных зависимостей или, что еще хуже, к критическим изменениям.

Каково же решение этой проблемы? Для начала разберемся, что означает диапазон версий. Строка, содержащая одно или несколько чисел, разделенных пробелами, называется диапазоном версий. Эти числа также содержат некоторые специальные символы, такие как ^ ~ < ||. Например, ^1.0.4, ~2.3, 4.4.x, >=2.3.4, <1.0.9 ||.

Разные символы обозначают разные стратегии обновления версий

Эти символы передают npm разные команды.

Допустим, требуется установить пакет “foo”. После запуска npm i foo в файле package.json появится запись следующего содержания:

{
"dependencies":{
"foo": "^2.3.0",
...
...
}
}

Здесь установлен “foo” версии 2.3.0 [major minor patch]. Символ каретки несет в себе определенную информацию.

^2.3.0 [^  —  символ каретки]  —  указание npm обновить версию до минорной (второстепенной) версии и патча, но не до мажорной (основной) версии. То есть 2.3.4, 2.3.9, 2.4.5, 2.8, но не 3.0.0 и т. д.

~2.3.0 [~  —  символ тильды]  —  указание npm обновлять до версии патча, но не до минорной и мажорной версий. То есть 2.3.4, 2.3.9, но не 2.4.0 и т. д.

Есть множество других символов, обозначающих различные стратегии обновления версий npm. Лучшим справочником в этом отношении является официальный сайт npm.

Итак, при запуске npm install несколько дней спустя версия "foo": "^2.3.0" (на данный момент минорная/патч) может автоматически обновиться, что нежелательно. Но не волнуйтесь: на помощь придет package-lock.json.

Что такое package-lock.json?

Файл блокировки генерируется и воспроизводится при изменении node_modules или package.json

package-lock.json  —  это лок-файл (файл блокировки), содержащий информацию о зависимостях/пакетах с их точными номерами версий (*важно), которые были установлены для проекта node.js.

  • Это помогает членам команды, работающим над одним и тем же репозиторием, инсталлировать именно те версии пакетов, которые были установлены ранее, даже если для пакетов были выпущены новые версии. Это обеспечивает одинаковое дерево node_modules на разных компьютерах/средах.
  • Файл package-lock.json используется для фиксации зависимостей к определенному номеру версии.
  • Этот файл автоматически генерируется (или воспроизводится) при изменении дерева node_modules или файла package.json.
  • Всякий раз при клонировании репозитория и запуска npm i на новом компьютере, npm сначала обратит внимание на наличие файла package-lock.json. При обнаружении он начнет установку пакетов, указанных в этом файле. В противном случае заглянет в файл package.json и начнет установку необходимых зависимых пакетов (разъяснение на этот счет будет далее в этой статье).
Файл package-lock.json обеспечивает одинаковое дерево node_modules во всех случаях

Нужно ли коммитить package-lock.json?

Да, этот файл должен быть зафиксирован в исходном репозитории, чтобы при клонировании репозитория разработчики могли инсталлировать зависимости, точно соответствующие тем, которые были первоначально установлены на компьютере/в среде. В основном это делается для репликации сред node.js, установленных на разных компьютерах.

Начиная с npm v7, lockfile (лок-файл) содержит достаточно информации обо всем дереве пакетов, что снижает необходимость чтения файлов package.json и тем самым увеличивает производительность.

Почему/когда npm install переписывает package-lock.json?

Разъяснение: npm install учитывает package-lock.json, только если устанавливаемый пакет(ы) находится в диапазоне версий package.json.

Если версия пакета, указанная в лок-файле, не входит в диапазон версий файла package.json, пакеты обновляются, а package-lock.jsonперезаписывается. Чтобы вместо перезаписи package-lock.json установка завершилась неудачей, используется npm ci.

Пример

Вы объявляете зависимость в package.json следующим образом:

"foo": "^2.3.0"

Затем выполняете команду npm install, которая создаст package-lock.json с версией:

"foo": "2.3.0"

Через несколько дней выходит более новая минорная версия “foo”, например 2.4.0, и тогда происходит следующее.

npm install: версия package-lock находится в пределах диапазона (т.е. ^2.3.0), поэтому устанавливается 2.3.0.

npm ci: в любом случае учитывается только package-lock.json, поэтому устанавливается 2.3.0.

Далее необходимо вручную обновить package.json до:

"foo": "^2.4.0"

Затем повторите запуск.

npm install: версия package-lock не входит в диапазон (т.е. ^2.4.0), поэтому устанавливается 2.4.0, а package-lock.json переписывается и теперь показывает "foo": "2.4.0".

npm ci: эта команда в любом случае учитывает только package-lock.json, но поскольку версия не находится в диапазоне, она выдает ошибку.


Команда npm ci похожа на npm install за исключением того, что она предназначена для использования в автоматизированных средах, таких как тестовые платформы, непрерывная интеграция и развертывание, или в любой другой ситуации, когда нужно убедиться, что выполняется чистая установка зависимостей (npm docs).


Выводы

  1. npm install не является детерминированной командой, что создает проблему при работе над репозиторием (с несколькими разработчиками), содержащим тысячи зависимостей.
  2. Файл package-lock.json гарантирует, что при каждом запуске npm install будет создаваться одно и то же дерево node_modules.
  3. Более новая команда npm ci гарантирует, что ВСЕГДА будет создаваться одно и то же дерево node_modules. В противном случае происходит ошибка.

Читайте также:

Читайте нас в TelegramVK и Дзен


Перевод статьи Atharva Kulkarni: package-lock.json explained completely

Предыдущая статьяПрощай, Python! Здравствуй, C#! 
Следующая статьяБазовый класс Android ViewModel за 5 минут