Куда npm устанавливает пакеты? И как найти папку node_modules?
Как 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 — но тогда версии могут измениться.