Skip to main content

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

package-lock.json - це файл, який npm генерує автоматично. Він фіксує точну версію кожної залежності, включно з транзитивними, щоб npm install давав однаковий результат на будь-якій машині.

Теорія

TL;DR

  • Аналогія: package.json - це рецепт: "борошно версії 4.x." package-lock.json - товарний чек: "партія 4.17.21 від постачальника X."
  • Головна різниця: package.json дозволяє діапазони версій; package-lock.json фіксує конкретне дерево залежностей.
  • npm ci читає lockfile строго і зупиняється при будь-якому розходженні. npm install може оновити версії в межах дозволених діапазонів.
  • Завжди комітьте package-lock.json. Ніколи не додавайте його в .gitignore.

Швидкий приклад

bash
# package.json дозволяє діапазон версій echo '{"dependencies":{"lodash":"^4.17.0"}}' > package.json npm install # Встановлює lodash@4.17.21, створює package-lock.json # На іншій машині або в CI: npm ci # Встановлює рівно lodash@4.17.21, не "останню доступну"

npm ci спочатку видаляє node_modules, потім відновлює з lockfile. Без сюрпризів.

Що зберігає lockfile

Lockfile записує resolved-версію, URL реєстру і SHA-512 хеш цілісності для кожного пакету. Коли запускаєш npm ci, npm перевіряє ці хеші перед записом у node_modules. Якщо хеш не збігається, встановлення падає з помилкою.

Спрощений запис виглядає так:

json
"express": { "version": "4.18.2", "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", "integrity": "sha512-..." }

Це формат v3, запроваджений у npm 7. Він містить усі підняті (hoisted) пакети і їхні транзитивні залежності у пласкій структурі.

Ключова різниця

package.json каже "потрібен express версії приблизно 4.18.x." Реєстр може видати 4.18.2 сьогодні і 4.18.5 наступного місяця. package-lock.json записує, що саме було встановлено. Без lockfile два розробники, що запустили npm install з різницею в тиждень, можуть отримати різні версії транзитивної залежності, яку вони взагалі не додавали. І один з них отримає баг у продакшені.

Коли що використовувати

  • Командний проєкт: комітьте lockfile, запускайте npm ci в CI/CD.
  • Dockerfile для продакшену: RUN npm ci --omit=dev, не RUN npm install.
  • Соло-прототип: однаково комітьте. Майбутній ти скажеш дякую.
  • Потрібно оновити конкретний пакет: npm update lodash. Не видаляйте lockfile.

Типові помилки

1. Додавати package-lock.json у .gitignore

Це найпоширніше джерело "у мене працює, у тебе ні." Кожен розробник отримує трохи різні транзитивні залежності.

bash
# Неправильно: package-lock.json в .gitignore # Розробник A отримує lodash@4.17.21 # Розробник B тиждень потому отримує lodash@4.17.22 (інша поведінка в одному edge case)

Виправлення: приберіть з .gitignore і закомітьте.

2. Використовувати npm install в CI замість npm ci

dockerfile
# Неправильно RUN npm install # Може оновити версії якщо діапазони дозволяють # Правильно RUN npm ci --omit=dev

npm install може автоматично підтягнути новішу версію. npm ci вважає будь-яке розходження жорсткою помилкою і зупиняє білд.

3. Видаляти lockfile для вирішення merge-конфліктів

bash
rm package-lock.json && npm install # Резолвить усе з нуля

Це підтягне останні дозволені версії всіх пакетів у твоїх діапазонах. Ти втрачаєш історію залежностей і можеш отримати регресії. Для конкретних конфліктів краще npm install <package> або npm dedupe.

4. Змішувати пакетні менеджери в одному проєкті

Якщо в проєкті є package-lock.json, не запускай yarn install для додавання пакету. Отримаєш два lockfile з різними деревами залежностей. Обирай один менеджер і дотримуйся його.

Де зустрічається

Я бачив продакшн-інцидент, де транзитивна залежність випустила мінорну версію зі зламаним JSON-парсером. Команда комітила package-lock.json, але використовувала npm install у пайплайні, і оновлення проскочило. Перехід на npm ci зупинив аналогічну проблему наступного разу.

  • React-застосунки: фіксує react@18.2.0 і scheduler@0.23.0 разом для стабільного рендерингу.
  • Express-сервери: закріплює helmet@7.1.0, щоб оновлення middleware не змінило поведінку між деплоями.
  • Next.js: фіксує webpack@5.89.0 для передбачуваного результату білду.
  • Docker-образи: npm ci у Dockerfile гарантує відповідність контейнера тому, що тестували локально.

Питання на співбесіді

Q: Яка різниця між npm install і npm ci?
A: npm install створює або оновлює lockfile і встановлює пакети. npm ci видаляє node_modules, читає lockfile без змін і падає, якщо package.json і lockfile розходяться. Для CI/CD і Docker використовуй npm ci.

Q: Чи потрібно комітити package-lock.json для npm-бібліотеки (не застосунку)?
A: Ні. Бібліотеки комітять тільки package.json. Lockfile бібліотеки нав'язував би конкретні транзитивні версії всім, хто її встановлює.

Q: Що станеться, якщо хеш цілісності в lockfile не збігається з реєстром?
A: npm перезавантажує тарбол за resolved URL і повторно перевіряє SHA-512. Якщо все одно не збігається, встановлення завершується помилкою цілісності. Це захист від підміни пакетів.

Q: У монорепо з npm workspaces - один lockfile чи кілька?
A: Один, в корені. npm піднімає (hoist) спільні залежності в кореневий node_modules і записує повне дерево workspaces в цей єдиний файл.

Приклади

Фіксація залежностей для Express-застосунку

json
// package.json { "dependencies": { "express": "^4.18.0", "helmet": "^7.0.0" } }
bash
npm install # package-lock.json тепер містить: # express@4.18.2 з повним деревом транзитивних залежностей # helmet@7.1.0 з повним деревом транзитивних залежностей

Карет (^) у package.json дозволяє все від 4.18.0 до (не включаючи) 5.0.0. Lockfile обирає одну версію і тримається за неї. Коли наступного місяця вийде 4.19.0, npm ci однаково встановить 4.18.2 скрізь.

npm ci у Dockerfile для продакшену

dockerfile
FROM node:20-alpine WORKDIR /app # Копіюємо файли пакетів перед вихідним кодом COPY package.json package-lock.json ./ # Встановлюємо точні версії з lockfile, без dev-залежностей RUN npm ci --omit=dev COPY . . CMD ["node", "index.js"]

Копіювання package.json і package-lock.json перед рештою вихідного коду дозволяє Docker кешувати шар з npm ci. Якщо змінились тільки вихідні файли, Docker використовує кешований node_modules і білд стає швидшим. Якщо змінився будь-який з файлів пакетів, Docker інвалідує кеш і перевстановлює залежності.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Дочитали статтю?