Пакеты и версии
Что это такое
Пакет — это опубликованный каталог кода с именем и версией. Диапазон версий — правило, которое говорит, какие версии этого пакета ваш проект готов принять. Семантическое версионирование наделяет эти числа смыслом: мажор — для несовместимых изменений, минор — для добавлений, патч — для исправлений; а lockfile записывает точные установленные версии, чтобы следующая установка привела к тому же дереву.
Почему это важно
Воспроизводимость сборки держится на двух вещах: на диапазонах в манифесте и на lockfile рядом с ним. Если lockfile не закоммичен, установка через месяцы подтянет более новый минор с регрессией, и сбой всплывёт в сборке, которая ничего не меняла. Однако стоит закоммитить lockfile — и дерево зависимостей закреплено, а обновление становится осознанным шагом с диффом для проверки; в этом же слое в проект проникает проблема цепочки поставок, и рассуждать о ней невозможно, не зная, какие диапазоны вы объявили.
Ключевые идеи
Семантическое версионирование — обещание, а не гарантия
1.4.2 означает мажор 1, минор 4, патч 2. Мажорный скачок может вас сломать, минорный обязан только добавлять, патч — только исправлять. Обещание держится на авторе, поэтому диапазон, доверяющий минорным версиям, доверяет каждому сопровождающему в дереве.
{
"dependencies": {
"left-pad": "1.3.0",
"date-fns": "^3.6.0",
"lodash": "~4.17.21"
}
}
^3.6.0 принимает любую 3.x не ниже 3.6.0; ~4.17.21 принимает 4.17.x; версия без знака закрепляет точно.
Lockfile — гарантия воспроизводимости
Lockfile записывает каждый пакет дерева, включая транзитивные, которые вы никогда не называли, вместе с разрешённой версией и хешем целостности. Коммитьте его. npm ci ставит строго по нему и падает, когда манифест и lockfile расходятся, — именно так и нужно вести себя в непрерывной интеграции.
dependencies и devDependencies — разные обещания
dependencies нужны, чтобы приложение работало; devDependencies — чтобы собрать или протестировать его. Сборщик компилирует ваш исходный код, поэтому большинство инструментов сборки живёт в devDependencies, а библиотека времени выполнения, которая попадает в бандл, — по сути зависимость сборки. Неверное разделение влияет в основном на то, что установит потребитель пакета и что подтянет производственная установка.
Транзитивные зависимости доминируют в дереве
Большая часть того, что вы устанавливаете, требуется чем-то другим. npm ls <name> и npm explain <name> показывают, какой родитель втянул пакет и почему; именно с этого стоит начать, когда в дереве оказываются две версии одной библиотеки и одна из них старая.
Одноранговые зависимости выражают требование к хосту
Плагин объявляет свой хост одноранговой зависимостью: он ожидает, что совместимую версию библиотеки предоставит приложение. Предупреждение о peer-зависимости обычно означает, что в дереве оказались две копии библиотеки, а две копии библиотеки состояния — настоящая ошибка, а не косметика.
Проверяйте и обновляйте по расписанию
Уведомления об уязвимостях приходят постоянно, и читать их во время инцидента уже поздно. Ежемесячный проход с обновлением патчей и миноров держит будущий мажорный переход небольшим, а закоммиченный lockfile делает каждый шаг проверяемым.
Практика
Докажите, что lockfile делает то, что обещает
Удалите установленные модули, но оставьте lockfile, переустановите и запишите разрешённую версию одной транзитивной зависимости. Затем вручную измените в lockfile версию этого пакета и запустите установку снова.
Готово, когда: вторая установка слушается lockfile, а не диапазона в манифесте, и вы можете назвать команду, которая его игнорирует.
Проследите одну транзитивную зависимость
Выберите пакет, который вы не устанавливали напрямую, и выясните, какой родитель его требует и в какой версии.
Готово, когда: вы можете назвать родителя, объявленный им диапазон и есть ли в дереве вторая копия того же пакета.
Частые ошибки
- Незакоммиченный lockfile — сборки перестают быть воспроизводимыми, и регрессия появляется без изменений кода; исправить: коммитьте его и ставьте через
npm ciв непрерывной интеграции. latestкак диапазон версий — каждая установка может быть другой программой; исправить: объявите диапазон, а результат разрешения запишите в lockfile.- Удаление lockfile ради исправления конфликта — конфликт исчезает вместе с воспроизводимостью; исправить: найдите дубликат через
npm lsи разберитесь с ним осознанно. - Инструменты сборки в
dependencies— потребители устанавливают весь инструментарий без всякой пользы; исправить: перенесите инструменты сборки и тестов вdevDependencies. - Обновление только когда заставили — скачок на два года сводит все ломающие изменения в одну непроверяемую ветку; исправить: обновляйтесь по расписанию небольшими шагами.
Что читать дальше
- Скрипты и рабочие пространства (02-frontend/04-package-management-npm/02-scripts-and-workspaces) — как сделать манифест точкой входа в проект.
- Сканирование зависимостей (SCA) (07-security/04-dependency-scanning-sca) — автоматическая проверка этого дерева на известные уязвимости.
- Управление пакетами (npm) (02-frontend/04-package-management-npm) — обзор трека, если вы начали отсюда.