Що таке 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.
Швидкий приклад
# 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. Якщо хеш не збігається, встановлення падає з помилкою.
Спрощений запис виглядає так:
"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
Це найпоширніше джерело "у мене працює, у тебе ні." Кожен розробник отримує трохи різні транзитивні залежності.
# Неправильно: package-lock.json в .gitignore
# Розробник A отримує lodash@4.17.21
# Розробник B тиждень потому отримує lodash@4.17.22 (інша поведінка в одному edge case)Виправлення: приберіть з .gitignore і закомітьте.
2. Використовувати npm install в CI замість npm ci
# Неправильно
RUN npm install # Може оновити версії якщо діапазони дозволяють
# Правильно
RUN npm ci --omit=devnpm install може автоматично підтягнути новішу версію. npm ci вважає будь-яке розходження жорсткою помилкою і зупиняє білд.
3. Видаляти lockfile для вирішення merge-конфліктів
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-застосунку
// package.json
{
"dependencies": {
"express": "^4.18.0",
"helmet": "^7.0.0"
}
}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 для продакшену
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 інвалідує кеш і перевстановлює залежності.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.