Avatar ·

Куда npm устанавливает пакеты? И как найти папку node_modules?

Всем привет! Я только начал изучать Node.js и npm, и у меня возникла путаница с тем, куда именно npm устанавливает пакеты. Вот ситуация: я создал проект, выполнил `npm init -y`, потом установил express командой `npm install express`. В папке проекта появилась папка `node_modules`. Но когда я установил глобально пакет nodemon через `npm install -g nodemon`, я не нашел его в своем проекте. Где он тогда? И почему некоторые пакеты ставятся в проект, а некоторые — куда-то еще? Еще я заметил, что когда я клонирую чужой проект с GitHub и делаю `npm install`, пакеты устанавливаются в корневую папку проекта. Но что если у меня есть вложенные папки с package.json? Например, структура: ``` my-project/ ├── package.json ├── server/ │ └── package.json └── client/ └── package.json ``` Если я выполню `npm install` в корне, попадут ли пакеты в `server/node_modules`? Или npm создаст одну общую папку? Я боюсь случайно засорить систему или получить конфликты версий. Объясните, пожалуйста, как работает система установки пакетов в npm на примере JavaScript. ```javascript // Пример моего package.json { "name": "my-app", "version": "1.0.0", "dependencies": { "express": "^4.18.0" } } ``` <<>>

Как npm определяет место установки пакетов

В npm есть два режима установки: локальный (по умолчанию) и глобальный (с флагом -g). Каждый из них использует свою директорию.

1. Локальная установка (без флага -g)

Когда вы пишете npm install express (без -g), npm ищет ближайший package.json в текущей или родительских папках и создает папку node_modules рядом с ним. Все зависимости складываются туда. В вашем примере:

my-project/
├── package.json        ← npm нашел этот файл
├── node_modules/       ← и создал папку здесь
│   └── express/
│       └── ...

Если выполнить npm install в папке server/ (где есть свой package.json), npm создаст server/node_modules/ — независимо от корневого проекта.

2. Глобальная установка (с флагом -g)

Команда npm install -g nodemon устанавливает пакет в системную директорию, которая доступна из любого места. Найти её можно командой:

npm root -g

Типичные пути:

  • Linux/Mac: /usr/local/lib/node_modules
  • Windows: C:\Users\Имя\AppData\Roaming\npm\node_modules

Глобальные пакеты обычно используются для CLI-утилит (nodemon, create-react-app, eslint), которые должны быть доступны из терминала.

3. Как npm разрешает зависимости во вложенных проектах

Если у вас структура с несколькими package.json, npm работает по принципу ближайшего node_modules. Для файла my-project/server/index.js:

  • Сначала npm ищет server/node_modules/
  • Если пакета нет — поднимается в my-project/node_modules/
  • Если и там нет — ищет глобально (но это плохая практика)

Пример: если в server/package.json указана зависимость "lodash": "^4.17.0", а в корневом package.json — "lodash": "^4.17.21", npm установит две копии в соответствующие node_modules. Это позволяет избежать конфликтов версий.

4. Как проверить, куда установился пакет

Воспользуйтесь командой для просмотра пути к конкретному пакету:

npm ls express
# Выведет: my-app@1.0.0 /home/user/my-project
# └── express@4.18.2

Чтобы увидеть полное дерево зависимостей:

npm ls --all

Для глобальных пакетов:

npm ls -g --depth=0

5. Важные моменты

  • Не редактируйте node_modules вручную — все изменения сбросятся при следующем npm install
  • Не добавляйте node_modules в Git — используйте .gitignore с записью node_modules/
  • Для монорепозиториев используйте npm workspaces — они позволяют хранить общие зависимости в корне, а специфичные — в подпроектах

Пример правильной структуры с workspaces:

my-project/
├── package.json          ← содержит "workspaces": ["packages/*"]
├── node_modules/         ← общие зависимости
├── packages/
│   ├── server/
│   │   └── package.json  ← только специфичные для сервера зависимости
│   └── client/
│       └── package.json
<<>>

Альтернативный подход: использование npm ci для чистых установок

Если вы работаете в команде или на CI/CD сервере, вместо npm install лучше использовать npm ci. Эта команда:

  • Удаляет существующую папку node_modules
  • Устанавливает пакеты строго по package-lock.json (игнорируя версии с ^ или ~)
  • Работает в 2-3 раза быстрее, так как не вычисляет дерево зависимостей заново

Пример использования в Dockerfile:

FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production  # только production-зависимости
COPY . .
CMD ["node", "server.js"]

Главное отличие от npm install: npm ci требует наличия package-lock.json и никогда не обновляет его. Это гарантирует, что все разработчики получат одинаковые версии пакетов.

Если вы случайно удалили package-lock.json, восстановите его из Git или перегенерируйте через npm install — но тогда версии могут измениться.