Что на самом деле делает npm install: реестр, package.json и файлы блокировки
Практическое руководство по npm: реестр и CLI, как команда npm install устанавливает пакеты, что записывается в файлах package.json и package-lock.json, а также как публиковать свои пакеты.
Почти каждый проект на JavaScript, независимо от того, работает ли он с Node.js, React или Next.js, начинается с одного и того же командного приказа, и многие разработчики выполняют его ежедневно, не имея четкого представления о том, что он делает. Знание того, откуда берутся пакеты, как npm решает, что установить, и для чего предназначены файлы package.json, package-lock.json и node_modules, помогает более легко отлаживать проблемы с установкой. В этом руководстве рассматриваются все аспекты, от первой установки до публикации собственного пакета.
Команды, которые используют все
Наиболее распространенный способ — установка всего, что уже указано в проекте:
npm install
Не менее часто используется добавление библиотеки по ее названию, например веб-фреймворка Express:
npm install express
или HTTP-клиента Axios:
npm install axios
За этими однострочными командами скрывается несколько вопросов. Откуда загружается код? Как npm узнает, от чего зависит проект? Для чего существует package.json? И почему папка node_modules становится такой большой?
Что такое npm
npm — это стандартный менеджер пакетов для Node.js, поставляемый вместе с ним. Он предоставляет доступ к огромному каталогу повторно используемых пакетов, опубликованных другими разработчиками, что позволяет использовать проверенный код в своем проекте вместо того, чтобы писать его заново.
Можно представить его как общественную библиотеку кода. Предположим, что сервер Node.js нуждается в HTTP-сервере. Вместо того чтобы с нуля создавать механизмы маршрутизации и промежуточные компоненты, достаточно добавить библиотеку Express:
npm install express
Нужно вызвать другие API? Добавьте HTTP-клиент:
npm install axios
Проверка входящих данных согласно схеме осуществляется с помощью библиотеки Zod:
npm install zod
Bcrypt отвечает за хеширование паролей:
npm install bcrypt
Это главное преимущество экосистемы: когда существует хорошо протестированное решение, редко бывает причина писать свое собственное. С другой стороны, каждый установленный пакет — это код, которому вы теперь доверяете, поэтому предпочитайте поддерживаемые и широко используемые библиотеки и сохраняйте список небольшим. Чтобы понять, почему это важно, прочитайте о том, как работают атаки на цепочку поставок npm.
Экосистема, а не просто команда
Новички часто воспринимают npm как просто слово, введенное в терминал. На самом деле это название охватывает несколько взаимодействующих компонентов:
- Реестр npm: общедоступный сервис, где пакеты публикуются и загружаются.
- CLI npm: инструмент командной строки, который взаимодействует с реестром и управляет зависимостями.
package.json: манифест, описывающий ваш проект и зависимости от него.Публичные пакеты бесплатны: вы можете установить любой публичный пакет без создания учетной записи. Такие низкие барьеры способствовали распространению JavaScript с открытым исходным кодом по всему миру. Учетная запись нужна только для публикации.
Что происходит при выполнении команды npm install
Возьмем проект, в котором используется Express:
npm install express
В общих чертах:
- npm находит пакет в реестре и выбирает версию, соответствующую указанному диапазону (последнюю, если он не указан).
- Он разрешает зависимости самого пакета и его внутренние зависимости, формируя полную структуру.
- Он загружает все, что еще не находится в кэше, и сохраняет его в папке
node_modules.
package.json и точную структуру решённых зависимостей в package-lock.json.Именно так один короткий командный приказ позволяет включить целую структуру функционала.
package.json: манифест проекта
В центре каждого проекта Node.js находится один файл:
package.json
Он служит своего рода удостоверением личности и конфигурацией проекта одновременно, содержа его название, версию, описание, скрипты, зависимости для работы и разработки, а также данные автора. Простой пример включает указание файла-входа main, один скрипт и одну зависимость:
{
"name": "my-project",
"version": "1.0.0",
"description": "My Node.js application",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"express": "^5.1.0"
}
}
name
name определяет проект или пакет. Если вы его публикуете, именно это значение люди вводят для установки, поэтому оно должно быть уникальным в реестре.
"name": "my-project"
version
version содержит текущую версию пакета.
"version": "1.0.0"
Это особенно важно при публикации. npm использует семантическую версионирование (MAJOR.MINOR.PATCH): кардинальные изменения увеличивают основное число, совместимые функции — вторичное, а исправления — патч-номер.
Описание
description — это однострочное краткое описание того, что делает проект, которое отображается в результатах поиска в реестре.
"description": "My Node.js application"
Скрипты
scripts определяет именованные сокращения для часто используемых команд. Здесь start запускает основной файл проекта:
"scripts": {
"start": "node index.js"
}
Без него придется вводить полную команду:
node index.js
С ним это делается так же:
npm start
Ключевое требование — единообразие: каждый участник и каждая задача CI выполняют одну и ту же команду с определенным именем. Собственные имена, такие как build, запускаются с помощью команды npm run build.
Зависимости: от чего зависит ваш проект
Представьте платформу для доставки еды. В её бэкенде могут использоваться:
- Express для API
- Zod для валидации
- bcrypt для хеширования паролей
- библиотека JSON Web Token для аутентификации
- Mongoose для работы с MongoDB
Каждый из этих инструментов является зависимостью. Когда вы устанавливаете одну из них:
npm install express
npm добавляет запись в package.json:
"dependencies": {
"express": "^5.1.0"
}
Знак вверху в ^5.1.0 обозначает диапазон: принимаются все последующие версии 5.x, но не 6.0.0. Эта запись сообщает всем, кто читает проект, что для его работы необходим Express. Инструменты, нужные только во время разработки, такие как тест-запускатели, помещаются в devDependencies с помощью команды npm install --save-dev.
Почему одна команда может использоваться всей командой
Предположим, вы присоединяетесь к команде и клонируете её репозиторий. Никто не ожидает, что вы вручную скачаете пятьдесят библиотек. Вы выполняете:
npm install
npm считывает указанные зависимости и устанавливает их все. Именно возможность воссоздания рабочей среды на основе описания делает менеджеры пакетов неотъемлемыми инструментами.
node_modules: место хранения установленного кода
После установки появляется новая папка:
node_modules/
В ней находятся пакеты, которые вы запросили, а также все зависимости от них, поэтому размер этой папки бывает велик. Типичная структура:
my-project/
│
├── node_modules/
├── package.json
├── package-lock.json
└── index.js
Не добавляйте папку node_modules в Git. Она большая, может содержать версии кода для конкретных платформ, и её всегда можно воссоздать. Добавьте её в файл .gitignore и сохраняйте вместо неё эти файлы:
package.json
package-lock.json
Затем любой другой человек может запустить:
npm install
и получить те же самые зависимости.
package-lock.json: точная запись состава
Рядом с описанием проекта находится ещё один файл:
package-lock.json
Почему оба файла? Краткий ответ:
package.jsonуказывает, что требуется проекту, обычно в виде диапазонов версий.package-lock.jsonфиксирует точно то, что было установлено: каждая версия пакета во всей структуре проекта, а также хэши целостности.
Поскольку диапазоны могут со временем совпадать с более новыми версиями, файл lockfile обеспечивает одинаковые версии на ноутбуках, у коллег и в системах CI. Сохраняйте его, а в CI предпочитайте использовать npm ci, который устанавливает пакеты строго согласно данным lockfile.
Команды npm, которые стоит знать
Добавление пакета:
npm install express
Установка всего, что указано в проекте:
npm install
Удаление пакета из node_modules и package.json:
npm uninstall express
Обновление пакетов до самых новых версий, разрешенных диапазонами (без перехода на новую мажорную версию):
npm update
Вывести версию npm:
npm -v
Вывести версию Node.js:
node -v
Показать аккаунт npm, под которым вы вошли:
npm whoami
От потребителя к публикатору
Вы также можете вносить вклад в создание пакетов. Опубликуйте полезную библиотеку, и другие смогут установить её как обычную:
npm install your-package
Этот цикл поддерживает жизнедеятельность открытого кода: вы развиваете чужие работы, создаёте собственные и делитесь ими с другими.
Шаги публикации по порядку
Вам нужен аккаунт npm. Войдите в него из терминала:
npm login
Подтвердите, какой аккаунт находится в активном состоянии:
npm whoami
Перейдите в папку проекта:
cd myproject
Убедитесь, что в проекте есть действительный манифест с уникальным name и version:
package.json
Затем опубликуйте проект:
npm publish
Теперь этот пакет доступен всем. Два совета: сначала выполните команду npm pack --dry-run, чтобы узнать, какие файлы будут включены, и всегда обновляйте version при каждой публикации, поскольку опубликованная версия не может быть использована повторно. Ознакомьтесь с текущей документацией npm по требованиям к безопасности аккаунтов, таким как двухфакторная аутентификация.
Как все взаимосвязано
Весь процесс выглядит следующим образом:
Developer
↓
npm install
↓
npm Registry
↓
Package + Dependencies
↓
node_modules
↓
package.json
↓
Your Application
Вы выполняете команду, CLI загружает пакеты из реестра, код попадает в папку node_modules, манифест и файл lockfile фиксируют эти данные, а ваше приложение импортирует его. Именно эта цепочка позволяет разработчикам эффективно повторно использовать, управлять, делиться и публиковать код.
Основные выводы
NPM может казаться просто набором команд в терминале, но как только вы поймете, что происходит за кулисами:
npm install
Система становится более понятной для анализа. JavaScript предоставляет язык, Node.js — среду выполнения, а npm — экосистему для обмена кодом. На практике:
package.jsonописывает намерения;package-lock.jsonфиксирует реальное состояние. Сохраняйте оба файла.- Никогда не сохраняйте папку
node_modules. - Диапазон с указателем позволяет выполнять небольшие обновления и исправления, причем команда
npm updateработает внутри этого диапазона. - Для автоматизированных установок используйте команду
npm ci. - Добавляйте зависимости осознанно; каждая из них — это код, которому вы доверяете.
Так что в следующий раз, когда вы будете писать:
npm install
помните, что вы подключаете свой проект к экосистеме, созданной разработчиками со всего мира.
Связанная литература
- Что JSON.stringify тихо удаляет, преобразует и отказывается сериализовать — Узнайте, какие значения JavaScript игнорируются или изменяются при использовании JSON.stringify, как функции toJSON, заменители и восстановители решают эти проблемы, и когда structuredClone является более подходящим инструментом.
- Что на самом деле гарантирует async/await и что оставляет за вами — Поймите, что на самом деле приостанавливает выполнение оператора await, как избежать сериализованных запросов, и почему для обработки ошибок, отмены операций, соблюдения порядка выполнения и повторных попыток требуются решения, выходящие за рамки async/await.