Семантическая версия Что означают числа в вашем package.json

Обновлено

Каждый файл package.json содержит строки версий, но большинство разработчиков просто доверяют символу caret, не понимая, что он позволяет. В этом руководстве объясняется контракт MAJOR.MINOR.PATCH и ровно какие обновления ^, ~, >= и * будут приниматься.

Семантическая версия: что означают цифры в вашем package.json 1
Реклама · УДАЛИТЬ?

Каждый проект на JavaScript имеет package.json. Большинство разработчиков вводили npm install some-library сотни раз, не задумываясь о версии строки. Но если спросить кого-то, что ^1.2.3 на самом деле позволяет — то есть, какие версии npm будет с радостью включать — вы часто получите молчание.

Это не тривиальная дыра. Неправильное понимание диапазона версий — это то, как обычный npm install на новом компьютере внезапно нарушает CI-трубу, которая работала без проблем в течение месяцев. Понимание контракта, лежащего в основе этих чисел, является одним из тех низко-затратных, высокодоходных элементов, которые отделяют разработчиков, которые устраняют проблемы с версиями за минуты, от тех, кто тратит часы на это.

Контракт MAJOR.MINOR.PATCH

Семантическая версия (semver) — это формат из трёх цифр: MAJOR.MINOR.PATCH. Каждая позиция несёт конкретное обещание о том, что изменилось:

  • МАЙОР — разрушительные изменения. Обновление на уровне главной версии может потребовать обновления вашего кода.
  • МИНОР — новые функции, совместимые с обратной совместимостью. Ваш существующий код должен продолжать работать.
  • ОБНОВИТЬ — исправление ошибок. Безопасно обновлять; без изменений в API.

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

На практике, поддерживатели иногда ошибаются и внедряют разрушительные изменения в мажорную версию. Semver не защищает сама по себе — это конвенция. Но она даёт вам рамку для рассуждения о рисках, и именно на этой основе строятся все операторы диапазонов.

Что считается разрушительным изменением?

Разрушительное изменение — это всё, что заставляет потребителей обновлять свой код:

  • Удаление или переименование экспортируемой функции, класса или константы
  • Изменение сигнатуры функции — удаление параметров, добавление обязательных параметров или изменение типов возвращаемых значений
  • Изменение наблюдаемого поведения таким образом, что правильный вызов теперь ведёт себя неправильно
  • Изменение формата конфигурационного файла в несовместимом способе

Добавление нового необязательного параметра? Это MINOR. Добавление нового экспорта? MINOR. Исправление ошибки, на которую кто-то ошибочно полагался как на функцию? Это вопрос суждения, но по конвенции это PATCH. Если не уверены, увеличьте MINOR и документируйте это явно.

Операторы диапазонов в package.json

Откройте любой package.json и вы увидите строки версий вроде "^4.17.21" или "~1.0.4". Эти не точные фиксации — это диапазоны которые сообщают npm или yarn, какие версии допустимы при разрешении дерева зависимостей.

Карета ^ — Совместимая версия

Карета — это стандартный оператор, когда вы запускаете npm install. Это означает: «принимайте любую версию, которая не меняет левую ненулевую цифру». На практике, для стабильных пакетов, это означает ту же главную версию, любую мажорную или патч-версию:

^1.2.3  →  >=1.2.3 <2.0.0   (any 1.x.x at or above 1.2.3)
^0.2.3  →  >=0.2.3 <0.3.0   (zero-major: pins to minor)
^0.0.3  →  >=0.0.3 <0.0.4   (zero-zero: pins to exact patch)

Поведение с нулевой главной версией является намеренным. Пакеты на 0.x.x обозначают «неустойчивый API» — любая мажорная версия может нарушить что-то. Карета уважает этот сигнал и становится намного более консервативной.

Тильда ~ — Обновления на уровне патча только

Тильда более консервативна. Она принимает новые патчи, но не затрагивает мажорную версию:

~1.2.3  →  >=1.2.3 <1.3.0   (any 1.2.x at or above 1.2.3)
~1.2    →  >=1.2.0 <1.3.0
~1      →  >=1.0.0 <2.0.0   (when only major given — same as ^1)

Используйте ~ тогда, когда вы хотите исправления ошибок, но не уверены, что библиотека соблюдает semver при мажорных обновлениях, или когда ваш код тесно связан с определённой поверхностью API, и новые версии функций исторически сопровождаются незначительными изменениями поведения.

Операторы сравнения: >=, <=, >

Вы можете писать произвольные диапазоны с помощью операторов сравнения. Пространство между двумя ограничениями означает И:

>=1.2.0           # at least 1.2.0, no upper bound
>=1.2.0 <2.0.0   # same as ^1.2.0 (explicit AND)
>1.2.0 <=1.5.0   # between these values, exclusive/inclusive

Это чаще всего встречается в peerDependencies, где библиотека говорит что-то вроде "react": ">=16.8.0 <19.0.0" для объявления совместимости с версиями хоста.

Звёздочки: * и x

Формы с звёздочкой используются в основном для удобства чтения документации; npm рассматривает их как эквивалент кареты/тильды с нулевым основанием:

  • * или "" — любая версия. Не используйте это в производственной среде.
  • 1.x или 1.x.x — любая 1.x.x (такая же как ^1.0.0)
  • 1.2.x — любая 1.2.x (такая же как ~1.2.0)

Предварительные версии

Предварительные версии выглядят как 1.0.0-alpha.1, 2.0.0-beta.3, или 3.1.0-rc.1. Они считаются ниже той версии, у которой одинаковые цифры — 1.0.0-alpha.1 < 1.0.0.

Критически, операторы диапазонов не автоматически включают предварительные версии. Диапазон ^1.0.0 не включит 1.1.0-beta.1даже тогда, когда технически он удовлетворяет >=1.0.0 <2.0.0. Вам нужно явно указать:

npm install some-library@next
npm install some-library@2.0.0-beta.3

Это намеренная безопасность. Вы редко захотите, чтобы CI автоматически выбрал предварительную версию зависимости, потому что она случайно удовлетворяет диапазону версий. Если вы проверяете предварительные версии, делайте это намеренно. -alpha сборка зависимости, потому что она удовлетворяет диапазону версий. Если вы проверяете предварительные выпуски, делайте это намеренно.

Файлы блокировки не являются опциональными

Когда npm разрешает вашу ^1.2.3 диапазон, он выбирает самую высокую совместимую версию, доступную в тот момент в тот момент. Запустите npm install сегодня и вы получите 1.5.0. Запустите его через шесть месяцев и вы можете получить 1.9.2. Тот же package.json, разные деревья зависимостей, потенциально разное поведение.

Это и решает файл блокировки. package-lock.json (npm) и yarn.lock (yarn) записывают точную версию установленной для каждой зависимости — прямой и транзитивной. Когда кто-то клонирует ваш репозиторий и запускает npm ci, они получают идентичное дерево.

Включите файл блокировки. Всегда. Не включать его означает:

  • Разные разработчики могут использовать разные версии зависимостей, не осознавая этого
  • Ваша среда CI может медленно отклоняться от вашей локальной среды
  • Обновление транзитивной зависимости может изменить поведение в производстве без видимых изменений в вашем git diff

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

"latest" в package.json всегда ошибка

Иногда вы видите это в package.json:

"dependencies": {
  "some-package": "latest"
}

Не делайте этого. "latest" отображает ту версию, которая сейчас помечена latest на npm — она меняется каждый раз, когда разработчик публикует новую версию. Новый npm install на любом новом компьютере может получить совершенно другую главную версию, которую вы проверяли.

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

Проверка того, удовлетворяет ли версия диапазону

Если вы не уверены, удовлетворяет ли версия заданному диапазону — особенно в случае пакетов с нулевой главной версией или сложных выражений — инструмент Расчет версий по Semver и проверка диапазонов на iotools.cloud даст вам немедленный ответ. Введите диапазон (^1.2.3, ~0.5.0, >=2.0.0 <3.0.0) и кандидатскую версию, и он скажет вам, удовлетворяет ли ограничение.

Это полезно при проверке запросов на обновление зависимостей, отладке того, почему npm install разрешён неожиданной версией, или проверке диапазона перед публикацией библиотеки. peerDependencies диапазон до публикации библиотеки.

Скорое руководство

ОператорПримерРазрешается как
^^1.2.3>=1.2.3 <2.0.0
~~1.2.3>=1.2.3 <1.3.0
>=>=1.2.01.2.0 или выше, без верхнего предела
**Любая версия (избегайте)
x1.2.xЛюбая 1.2.x патч
точный1.2.3Точно 1.2.3
Хотите убрать рекламу? Откажитесь от рекламы сегодня

Установите наши расширения

Добавьте инструменты ввода-вывода в свой любимый браузер для мгновенного доступа и более быстрого поиска

в Расширение Chrome в Расширение края в Расширение Firefox в Расширение Opera

Табло результатов прибыло!

Табло результатов — это интересный способ следить за вашими играми, все данные хранятся в вашем браузере. Скоро появятся новые функции!

Реклама · УДАЛИТЬ?
Реклама · УДАЛИТЬ?
Реклама · УДАЛИТЬ?

новости с техническими моментами

Примите участие

Помогите нам продолжать предоставлять ценные бесплатные инструменты

Купи мне кофе
Реклама · УДАЛИТЬ?