Работа с версиями пакетов

читать 7 мин
npmpackage managerversions

Sample Image

Версии

Когда вы работаете с менеджером пакетов, вы, наверное, обращали внимание на номера версий, например: 1.2.0, 2.5.6 и так далее. Эти цифры состоят из трех частей: мажорной версии, минорной и так называемой патч-версии.

Но что они означают? Давайте разберемся! 🚀

В npm, yarn и других пакетных менеджерах используется семантическое версионирование (SemVer), которое выглядит как X.Y.Z (Мажорная.Минорная.Патч).

  • Мажорная (Major): Ломающие изменения (2.0.0), которые могут нарушить обратную совместимость. Если вы обновляетесь до мажорной версии, то код, который работал, может перестать работать, поэтому после обновления необходимо протестировать функционал.

  • Минорная (Minor): Новые функции, совместимые со старыми (1.3.0), которые не нарушает обратную совместимость. Это значит, что после обновления ваш код должен продолжить работать так же, как и работал.

  • Патч (Patch): Исправления багов (1.2.4), которые не нарушают обратную совместимость. Обновляемся - и всё работает.

НО! Советую после любого обновления библиотек всё-таки делать регресс своего функционала.

Допустим, у нас есть пакет версии 1.2.3. Если разработчик выпускает новую версию с номером 2.0.0, это означает, что в пакет были внесены существенные изменения, которые могут нарушить обратную совместимость. Однако если разработчик выпускает версию 1.3.0, это указывает на то, что в пакет были добавлены новые функции, но обратная совместимость при этом сохраняется.

Управление версиями в package.json

В файле package.json вы указываете зависимости вашего проекта и их версии. Версии могут быть зафиксированы точно, либо можно использовать символы для указания допустимых диапазонов версий.

  • Точная версия: "react": "1.0.1" - всегда будет использоваться версия 1.0.1.

  • Символ ^: "react": "^1.0.1" - будет использоваться любая MINOR или PATCH версия, начиная с 1.0.1 (например, 1.1.0 или 1.0.2).

  • Символ ~: "react": "~1.0.1" - будет использоваться любая PATCH версия, начиная с 1.0.1 (например, 1.0.2), но не 1.1.0.

Запоминаем простое правило:

^ = «Я хочу новые функции, но не ломайте мой код» (самый частый вариант).

~ = «Только баг-фиксы, ничего не меняйте».

Как обновлять пакеты

(Далее мы будем использовать npm)

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

npm outdated

Вы увидите таблицу с колонками Current (текущая), Wanted (максимальная разрешенная символом ^ или ~) и Latest (абсолютно последняя в реестре).

PackageCurrentWantedLatestLocationDepended by
react17.0.217.0.218.2.0node_modules/reactmy-app
axios0.21.10.21.41.6.5node_modules/axiosmy-app
lodash4.17.154.17.214.17.21node_modules/lodashmy-app

Рассмотрим несколько вариантов обновлений:

  1. Безопасное обновление Чтобы обновить пакеты до версии Wanted (то есть с учетом ваших ^ и ~):

npm update

  • Эта команда обновит файлы в папке node_modules.
  • Она также обновит файл package-lock.json.
  • Важно: Она не изменит версии, прописанные в вашем package.json, если они все еще попадают под условия (например, если там написано ^1.0.0, а вы обновились до 1.5.0, текст в файле останется ^1.0.0, так как 1.5.0 подходит под это правило).
  1. Полное обновление Если вы хотите обновиться с версии 1.x.x до 2.x.x (игнорируя правила в package.json), команда npm update этого не сделает. Для одного пакета мы можем использовать команду:

npm install <имя_пакета>@latest

Это принудительно установит последнюю версию и перепишет package.json.

А чтобы обновить автоматически все пакеты, используйте утилиту npm-check-updates. Она проверяет latest-версии и перезаписывает ваш package.json. Запустите проверку (без установки утилиты):

npx npm-check-updates

Если хотите применить изменения в package.json, добавьте флаг -u:

npx npm-check-updates -u

После этого обязательно установите новые версии:

npm install

Выглядит всё просто, но на практике могут возникнуть проблемы, так что пробуйте!

Почему нельзя удалять package-lock.json?

Вообще, этот файл - это снимок вашего дерева зависимостей в проекте, и удалять его нельзя. Не знаю почему, но многие думают, что это копия package.json. Так зачем же он нужен?

Представьте: вы работаете в команде, и в вашем package.json есть зависимость с версией ^0.21.1. Сегодня вы установили пакет, и скачалась версия 0.21.1. Но наступает завтра, к вам приходит фронтенд-стажёр и устанавливает зависимости в проекте. И - о нет! - у него та же зависимость, но уже с версией 0.21.2, так как вышла новая версия. Всё из-за символа ^. Как итог: если в версии 0.21.2 разработчики случайно допустили баг, у вас проект будет работать, а у коллеги - нет. Хотя в package.json у вас написано одно и то же!

ЗАПОМИНАЕМ: package-lock.json фиксирует точные версии (например, строго 0.21.1) всех установленных пакетов и их собственных подзависимостей. Когда коллега напишет npm install, система посмотрит в лок-файл и установит в точности те же зависимости, что и у вас.

Главные функции package-lock.json:

  • Гарантия идентичности: На любом компьютере и на сервере (Production) установится один и тот же код.

  • Безопасность: Файл хранит integrity (хэш-суммы). Если кто-то взломает пакет в реестре npm и подменит код версии 0.21.1, npm заметит несовпадение хэша и выдаст ошибку.

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

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

Есть еще операторы, но я их видел очень редко. Примеры привел в таблице:

СимволПравилоПример
>Принимать обновления любой версии выше указанной>0.13.0: 0.13.1, 0.14.1, 1.1.1
<Принимать обновления любой версии ниже указанной<3.0.0: 2.0.0, 2.9.0
>=Принимать любую версию больше или равную указанной>=3.0.0: 3.0.0, 4.1.0
<=Принимать любую версию меньше или равную указанной<=3.0.0: 3.0.0, 2.9.0
=Принимать только указанную точную версию=3.0.0: 3.0.0 (not 3.0.1)
-Принимать диапазон версий (включительно)1.0.0 - 1.10.10: 1.5.0 (not 1.11.0)
||Комбинация версий (логическое ИЛИ)<2.1.0 || >2.6.0: 2.0.1, 3.1.0
&&Версии, удовлетворяющие обоим условиям (логическое И)>=1.0.0 && <2.0.0: 1.5.0 (not 2.0.0)
(space)Неявное логическое И (аналогично &&)>=1.0.0 <2.0.0: 1.5.0 (not 2.0.0)
*Принимать любую версию (wildcard / маска)*: any version available
xЛюбая версия для данной позиции1.x: 1.0.0, 1.5.0 (not 2.0.0)
XТо же, что и x (нечувствительно к регистру)1.X: 1.0.0, 1.5.0 (not 2.0.0)
(none)Принимать только указанную точную версию3.0.0: 3.0.0 (not 3.0.1)
latestВсегда устанавливать последнюю доступную версиюnpm install <package>@latest
@tagУстановить конкретный дистрибутив по тегуnpm install react@beta

И небольшой пример package.json с такими операторами:

{
  "dependencies": {
    "express": "^4.18.0", // Разрешить минорные обновления: 4.18.x, 4.19.x
    "lodash": "~4.17.21", // Разрешить только патч-обновления: только 4.17.x
    "react": ">=18.0.0", // Любая версия 18.0.0 или выше
    "typescript": "4.x", // Любая версия в рамках мажорной ветки 4.x
    "eslint": "*", // Всегда устанавливать самую последнюю версию
    "axios": ">=1.0.0 && <2.0.0", // Только версии ветки 1.x
    "my-utils": "workspace:^1.0.0", // Зависимость из рабочего пространства (monorepo)
    "local-pkg": "file:../local-package" // Зависимость из локальной папки
  },
  "devDependencies": {
    "jest": "29.0.0 - 29.5.0", // Специфический диапазон версий
    "prettier": "2.8.8", // Точная версия
    "@types/node": ">=16.0.0 <21.0.0" // Логическое «И» (через пробел)
  }
}

Шпаргалка по командам

КомандаОписание
npm outdatedПроверить наличие устаревших пакетов
npm updateОбновить пакеты в пределах диапазонов semver
npm install package@1.2.3Установить конкретную версию пакета
npm install package@latestУстановить последнюю версию пакета
npm install package@betaУстановить пре-релизную (бета) версию
npm listПоказать дерево установленных версий пакетов
npm view package versions --jsonПоказать все доступные версии пакета в формате JSON

На этом все, спасибо за прочтение 🙏

Работа с версиями пакетов | Frontend Tales