Menu
Русский

npm с нуля: install, init и управление зависимостями

Разбираемся, как на самом деле работает npm: установка пакетов, npm init, dev-зависимости, обновления и что вообще творится внутри node_modules и lock-файла.

Что такое npm на самом деле

npm — это три вещи, объединённые под одним именем. Во-первых, это реестр — огромная публичная база JavaScript-пакетов на npmjs.com. Во-вторых, это утилита командной строки, которая идёт в комплекте с Node.js и отвечает за установку и управление пакетами. И в-третьих, это спецификация (формат package.json), описывающая, что нужно проекту.

Когда вы выполняете npm install express, CLI обращается к реестру, скачивает express вместе со всеми его зависимостями, складывает файлы в папку node_modules и записывает пакет с его версией в ваш package.json. Вот и весь цикл.

Если Node.js уже установлен, то npm у вас тоже есть. Проверим:

node --version
npm --version

Если обе команды вывели версию — всё готово, можно двигаться дальше.

Создание проекта: npm init

В любом проекте на Node.js должен быть файл package.json. Это манифест проекта: в нём указаны название, версия, скрипты и список зависимостей. Быстрее всего сгенерировать его командой npm init -y — она создаст файл со значениями по умолчанию:

mkdir my-app
cd my-app
npm init -y

Получится примерно такое:

{
  "name": "my-app",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

Если убрать -y, npm проведёт вас по каждому полю в интерактивном режиме. В любом случае на выходе получится package.json — файл, к которому цепляется всё остальное. Подробно разберём его поля на следующей странице.

Установка пакета через npm install

Когда package.json уже есть, пакет ставится командой npm install (или коротко npm i):

npm install lodash

Происходит сразу три вещи:

  1. npm скачивает lodash и все его зависимости в папку node_modules/.
  2. В package.json в раздел dependencies добавляется строка "lodash": "^4.17.21" (или та версия, которая актуальна на момент установки).
  3. Создаётся файл package-lock.json, в котором зафиксированы точные версии всех пакетов из дерева зависимостей.

Теперь библиотеку можно использовать:

index.js
Output
Click Run to see the output here.

Вызов require (или import, если у вас ESM-проект) находит пакет, заглядывая в node_modules. Путь писать не нужно — за вас это делает модульный резолвер Node.

dependencies и devDependencies: в чём разница

Далеко не все пакеты нужны приложению в продакшене. Тестовые фреймворки, линтеры и сборщики работают только во время разработки. Такие зависимости ставятся с флагом --save-dev (или коротко -D):

npm install --save-dev jest
npm install -D eslint prettier

Такие пакеты попадают в devDependencies, а не в dependencies:

{
  "dependencies": {
    "lodash": "^4.17.21"
  },
  "devDependencies": {
    "jest": "^29.7.0",
    "eslint": "^8.57.0",
    "prettier": "^3.2.5"
  }
}

На продакшн-сервере команда npm install --omit=dev полностью пропускает dev-зависимости — установка получается легче и быстрее. Правильное разделение здесь важнее, чем кажется на первый взгляд: случайно забытый webpack в dependencies будет раздувать каждый продакшн-деплой.

Установка всех зависимостей разом

Когда вы клонируете репозиторий, в котором уже есть package.json, перечислять каждый пакет вручную не нужно. Достаточно выполнить:

npm install

Запустив npm install без аргументов, npm читает package.json (и чётко следует версиям из package-lock.json), после чего устанавливает всё дерево зависимостей в node_modules. Это первая команда, которую вы выполняете на любом свежем клоне репозитория.

Именно поэтому node_modules должен лежать в .gitignore. Эта папка воспроизводится из lock-файла, весит прилично и меняется при каждом запуске npm install у любого разработчика. В репозиторий коммитим только package.json и package-lock.jsonnode_modules каждый сгенерирует у себя сам.

Как обновлять пакеты через npm

Команда npm outdated покажет, что отстало от актуальных версий:

npm outdated

Вы увидите таблицу со столбцами Current, Wanted и Latest. Wanted — это самая свежая версия, которую разрешает диапазон в package.json (для ^4.17.21 это всё, что ниже 5.0.0). Latest — это самая новая опубликованная версия, и вполне возможно, что это мажорный релиз, на который вы ещё не перешли.

Чтобы обновиться в рамках разрешённого диапазона:

npm update

Чтобы обновиться до самой свежей версии, включая мажорные обновления, просто установите пакет заново с флагом @latest:

npm install lodash@latest

Смена мажорной версии может сломать ваш код — как раз об этом и сигнализирует номер версии. Прежде чем шагать через такую границу, обязательно загляните в changelog.

Как удалить пакет через npm uninstall

Удаление пакета работает симметрично установке:

npm uninstall lodash

Пакет удалится из node_modules, и запись в package.json тоже исчезнет. Если это была dev-зависимость, можно добавить -D — npm, конечно, разберётся и сам, но с явным флагом меньше сюрпризов в скриптах.

Глобальная установка против локальной

В подавляющем большинстве случаев пакеты ставятся локально — привязанными к одному проекту, внутрь его node_modules. Исключение — консольные утилиты, которые хочется вызывать из любой точки системы:

npm install -g typescript
npm install -g http-server

Глобальная установка кладёт инструмент в системное место и прописывает его bin в PATH, благодаря чему команды вроде tsc или http-server можно запускать из любой директории. Правда, такие пакеты не привязаны к конкретному проекту и легко расходятся по версиям между машинами.

Для разовых команд удобнее золотая середина — npx, который идёт в комплекте с npm:

npx create-react-app my-app
npx prettier --write .

npx позволяет запустить пакет без глобальной установки — он подтягивает его по требованию, выполняет, и на этом всё. Для утилит, которые нужны один раз, это гораздо аккуратнее, чем навсегда ставить их глобально.

Минимальная шпаргалка

Команды, которыми реально пользуешься каждый день:

npm init -y                     # создать package.json
npm install                     # установить всё из package.json
npm install <pkg>               # добавить runtime-зависимость
npm install -D <pkg>            # добавить dev-зависимость
npm install -g <pkg>            # установить CLI-утилиту глобально
npm uninstall <pkg>             # удалить зависимость
npm outdated                    # посмотреть, что устарело
npm update                      # обновить в пределах разрешённых диапазонов
npm install <pkg>@latest        # перейти на самую свежую версию
npm run <script>                # запустить скрипт из package.json
npx <pkg>                       # запустить пакет без установки

Вот, пожалуй, и всё главное про npm. Остальное — публикация пакетов, workspaces, scoped-пакеты — подтянете, когда реально понадобится.

Что на самом деле лежит в node_modules

Ещё одна полезная ментальная модель. node_modules — это почти плоская папка, в которой лежат все пакеты, от которых зависит ваш проект, плюс всё, от чего зависят они, и так по цепочке. Ставите один пакет — и вместе с ним может приехать ещё сотня. Это нормально. Там, где возможно, npm дедуплицирует зависимости: если два пакета хотят одну и ту же версию lodash, она будет лежать в одном экземпляре.

Lock-файл (package-lock.json) фиксирует точную версию каждого из этих пакетов — ту, которая была реально установлена. Именно благодаря ему сборки воспроизводимы: два разработчика, запустив npm install с одним и тем же lock-файлом, получат побайтово одинаковое дерево зависимостей — хоть сегодня, хоть через полгода.

Относитесь к node_modules как к сгенерированной папке. Никогда не правьте файлы внутри — ваши изменения исчезнут при первой же установке зависимостей.

Дальше: package.json

package.json — это тот самый файл, который npm постоянно читает и переписывает за кулисами. Как только вы разберётесь с его полями — scripts, main, type, диапазоны версий, engines — npm перестанет быть чёрным ящиком и превратится в понятный инструмент. Об этом и поговорим дальше.

Часто задаваемые вопросы

Что такое npm?

npm — это штатный пакетный менеджер Node.js. Он идёт в комплекте с самим Node, хостит огромный публичный реестр JavaScript-пакетов и даёт консольную утилиту для их установки, обновления и публикации. Когда вы пишете npm install lodash, npm скачивает lodash из реестра в папку node_modules и записывает его в package.json.

Чем dependencies отличаются от devDependencies?

dependencies — это пакеты, без которых приложение не запустится в проде: условные express или react. devDependencies нужны только на этапе разработки и сборки — тест-раннеры, бандлеры, линтеры. Ставятся они командой npm install --save-dev <pkg> (или коротко -D). В проде можно выполнить npm install --omit=dev, и dev-зависимости будут пропущены.

Стоит ли коммитить node_modules в git?

Нет, не надо. node_modules легко разрастается до сотен мегабайт, и при этом его можно в любой момент воспроизвести из package.json и package-lock.json. Добавьте папку в .gitignore, а в репозиторий коммитьте lock-файл. Любой, кто клонирует проект, запустит npm install и получит ровно то же дерево зависимостей.

В чём разница между глобальной и локальной установкой npm?

Локальная установка (npm install <pkg>) кладёт пакет внутрь node_modules вашего проекта и прописывает его в package.json. Глобальная (npm install -g <pkg>) ставит пакет в систему целиком — обычно так делают с CLI-утилитами, которые нужны везде. Для зависимостей конкретного проекта всегда лучше локальная установка, чтобы версии были зафиксированы отдельно под каждый проект.

Учитесь программировать с Coddy

НАЧАТЬ