Коротко: npm ls —all выводит полное дерево пакетов в node_modules и показывает, как реально разрешились версии и связи; по этому снимку легко найти конфликты, дубли, нестыковки peer-зависимостей и причины раздувания дерева. Развернутый контекст встречается и в публикациях вроде Глубокий разбор команды npm ls —all: как строить и интерпретировать дерево зависимостей, но здесь взгляд собран в один цельный практический маршрут — от первых симптомов до чистого lockfile.
Любой проект на JavaScript живет среди множества пакетов, как город среди магистралей: часть улиц знакома, часть уходит под землю, часть петляет в непредсказуемых развязках. Команда npm ls — это фонарь в этих подземных галереях: она не просто называет соседей, а рисует карту, по которой видно, кто к кому пришит и почему некоторые дороги внезапно раздваиваются.
Когда дерево становится густым, зрение подводит раньше логики. Поэтому важнее научиться не механически читать вывод, а слышать в нем ритм системы управления версиями: где semver отпускает вольности, где lockfile держит курс, где hoisting выносит общие модули наверх, а где peer-зависимости требуют равного соседа и обижаются, если его нет. Из этого и строится практика: смотреть не на шум, а на сигналы.
Что именно выводит npm ls —all и зачем это нужно
npm ls —all строит полное дерево установленных пакетов и их связей на диске, отображая реальные версии и пометки состояний. Снимок помогает понять, почему оказалась выбрана конкретная версия, где появилось дублирование, и какие зависимости отсутствуют или конфликтуют.
Команда читает package.json и package-lock.json, сверяется с содержимым node_modules и выводит иерархию: от корня проекта до последнего листа, не скрывая промежуточные уровни. Польза начинается там, где по списку видно не только имена, но и структуру: кто тянет библиотеку, какой диапазон версий запрошен, как именно lockfile зафиксировал разрешение, и не случилась ли подмена из-за несовместимых peer-зависимостей. В JSON-режиме дерево превращается в пригодный для машинной обработки граф, что удобно для автоматических проверок, а в текстовом — видно важные пометки: deduped, extraneous, invalid, unmet peer. По сути это не просто перечень, а слепок состояния инсталляции в момент вызова, который легко сопоставить с ожиданиями из манифестов.
| Флаг | Короткий ответ | Что добавляет к картине |
|---|---|---|
--all |
Показывает все уровни | Раскрывает полное дерево, включая вложенные пакеты, а не только верхние уровни |
--depth=N |
Ограничивает глубину | Удобно для быстрой ориентации и проверки верхнего слоя зависимостей |
--json |
Структурированный вывод | Годится для парсинга, CI-правил и собственных скриптов анализа |
--long |
Больше метаданных | Показывает описания, домашние страницы, лицензии, пометки |
--parseable |
Пути в одной строке | Минималистичный формат для простых грепов и пайпов |
--production |
Скрывает devDeps | Полезно для рантайм-окружений и образов, где важны только прод-зависимости |
Интерпретация вывода удобнее всего, когда понять цель: найти лишнее, доказать причину конфликта или зафиксировать опорные версии. От задачи зависит флаг: срежиссировать глубину, включить JSON, отфильтровать dev, чтобы не заблудиться в ветвях там, где нужно увидеть форму кроны.
Как устроено дерево: корень, узлы, дубли и peer-зависимости
Дерево npm строится от корня проекта к листьям: каждый узел — пакет с версией, а рёбра — связи зависимостей. Дубли возникают, когда разные ветви требуют несовместимые версии; peer-зависимости формируют ожидание соседа на одном уровне, а не внутри ветви.
Корнем выступает текущий проект: его манифест задает исходные желания — зависимости и их диапазоны. Далее идут узлы-пакеты, каждый со своими зависимостями, образуя ветви. Если два пакета требуют несовместимые версии общего модуля, появляются параллельные экземляры: один поднимется выше за счет hoisting, другой спрячется глубже. Это не ошибка, а компромисс: стабильность важнее экономии места, пока границы несовместимы.
Peer-зависимости добавляют социальную норму: пакет сообщает, что должен жить рядом с указанным соседом определенного диапазона версий. Такой сосед не устанавливается автоматически, он предъявляет требование к окружению. Если окружение не отвечает, ls пометит unmet peer — это повод проверить, есть ли общий предок, который может удовлетворить обоих, или стоит отредактировать корневые зависимости.
В крупных деревьях встречаются отдельные классы узлов: optional-зависимости, чья установка зависит от платформы; bundled-пакеты, приходящие вместе с дистрибутивом; и симлинки локальных пакетов в режиме разработки. Каждая из этих форм влияет на структуру, и ls их спокойным тоном отображает, не навешивая ярлыков, а лишь констатируя факты.
Какие пометки и аномалии показывает npm ls и что они значат
Пометки ls сигналят о несовпадении ожиданий и факта: extraneous — лишний пакет, invalid — несоответствие манифестов и диска, unmet peer — конфликт соседства, deduped — успешно поднятая общая копия. Каждая метка — подсказка к действию.
Extraneous обычно появляется, когда в node_modules лежит пакет, который не фигурирует ни в зависимостях проекта, ни в зависимостях зависимостей согласно lockfile. Такое случается после ручных манипуляций или наследия старых установок. Invalid замечается, если версия на диске не соответствует заявленной в lockfile или повреждены метаданные. Unmet peer — тонкий сигнал: функционально проект может работать, но границы совместимости по замыслу авторов пакетов нарушены. Deduped — результат hoisting, когда общая версия, удовлетворяющая нескольким ветвям, вынесена наверх, экономя место и снижая дублирование.
| Метка в выводе | Короткое значение | Типичное действие |
|---|---|---|
| deduped | Экземпляр поднят наверх | Проверить, не скрыла ли дедупликация несовместимые peer-границы |
| extraneous | Лишний пакет на диске | Удалить node_modules и переустановить, проверить скрипты пост-установки |
| invalid | Несогласованность версий | Пересобрать lockfile, запустить npm ci или npm install с чистого состояния |
| UNMET PEER DEPENDENCY | Нарушено требование соседа | Поставить или обновить peer в корне, выровнять диапазоны |
| missing | Ожидание без реализации | Проверить кэш, права доступа и целостность lockfile |
Сигналы не всегда требуют немедленного вмешательства. Unmet peer может жить в дев-окружении, пока не влияет на сборку; extraneous иногда оставляет артефакты локального линка. Но как только симптомы начинают напоминать каскад — увеличивается время сборки, множатся копии одного и того же пакета, — стоит вернуться к корню: переснять lockfile, выровнять границы semver и убедиться, что hoisting не спрятал проблему под один общий ковёр.
Как читать дерево: от симптома к причине и обратно
Чтение дерева — это путь от заметного симптома к точке расхождения: найти конфликтующую ветвь, понять, кто её тянет, и решить, где лучше поставить заплату — в корне, в диапазоне, в lockfile или в конфигурации менеджера пакетов. Полезна ритмичная последовательность шагов.
Дерево подсказывает маршрут, если идти не наскоком, а по тропе причинно-следственных связей. Сначала замечается симптом: неожиданно тянется старая версия библиотеки, появляются дубли, ломается сборка из-за peer. Затем фиксируется контекст: на какой ветви встретился артефакт, кто его родитель, есть ли альтернативный экземпляр выше. Дальше включается поиск корневой причины: какая зависимость запросила узкий диапазон, что в lockfile закрепило выбор, чем отличается состояние в CI от локального. Наконец, решается, где исправить: обновить прямую зависимость проекта, ослабить диапазон, добавить недостающий peer, или удалить lockfile и воспроизвести установку в детерминированном режиме npm ci. Важен темп: не пытаться переглянуть всё дерево сразу, а следовать ветвью вверх, словно вынимая нитку из клубка — пока она не приведет к первому узлу, где выбор стал неизбежным.
- Снять снимок:
npm ls --all --json > tree.jsonи текстовый для чтения глазом. - Найти узел-симптом: конкретный пакет с «лишней» версией, пометкой или дубликатом.
- Подняться на уровень выше: кто тянет этот узел, какой диапазон запрошен.
- Сравнить с альтернативой: есть ли сверху другой экземпляр или deduped-форма.
- Проверить lockfile: чем был закреплен выбор и где его изменить безопаснее.
- Закрепить решение: обновить зависимости, добавить peer, пересобрать установку.
| Симптом | Возможная причина | Рациональный ход |
|---|---|---|
| Две версии одного пакета | Несовместимые диапазоны в разных ветвях | Найти общий допустимый диапазон, обновить прямую зависимость, запустить dedupe |
| UNMET PEER DEPENDENCY | Отсутствует или несовместим сосед | Установить peer в корне, синхронизировать мажорные версии |
| extraneous | Артефакты старой установки | Очистить node_modules и кэш, переустановить детерминированно |
| invalid | Lockfile и диск разошлись | Перегенерировать lockfile, сверить версии в CI и локально |
| Сборка внезапно стала медленной | Разрослось дерево, упало дедуплицирование | Проанализировать вершины с большим фан-аутом, обновить узкие диапазоны |
Semver, lockfile и hoisting: кто на что влияет в разрешении версий
Semver задаёт допустимые коридоры, lockfile закрепляет конкретный выбор, hoisting поднимает общие версии выше для экономии и согласованности. Их взаимодействие объясняет, почему на диске оказывается именно та версия, которую показывает ls.
Диапазоны в package.json описывают свободу: каретка разрешает минорные и патч-обновления, тильда — только патч, прямое указание — жёстко фиксирует. Lockfile отливает выбор в камень, чтобы повторная установка не зависела от времени и настроения реестра. Hoisting, в свою очередь, собирает совместимые версии наверх, сокращая глубину и повторения. Когда диапазоны несовместимы, hoisting прекращается, появляются параллельные экземпляры — ls это показывает предельно честно, без украшений.
| Запись в package.json | Что допускает | Как влияет на итог |
|---|---|---|
^1.2.3 |
1.x.x, начиная с 1.2.3 | Позволяет hoisting до общей 1.y.z; риск неожиданных минорных изменений |
~1.2.3 |
1.2.x, начиная с 1.2.3 | Стабилизирует минор, легче контролировать поведение |
1.2.3 |
Только 1.2.3 | Исключает плавное обновление; возможно больше дублей при разном пиннинге |
>=1.2 <2 |
Любая 1.x.x после 1.2 | Гибкость и риск расхождений без lockfile |
Практика показывает, что излишне узкие пины множат копии и ломают hoisting, а чрезмерно широкие диапазоны в паре с нестрогим процессом обновлений создают дрейф зависимостей. Баланс достигается простой дисциплиной: фиксировать на уровне lockfile, периодически обновлять осмысленно, а диапазоны держать такими, чтобы экосистема пакетов могла находить общую версию без надрыва. Там, где peer-зависимости требуют конкретного мажора, лучше синхронизировать его явно.
Масштаб и производительность: когда дерево тянется на тысячи пакетов
В больших проектах дерево легко уходит в тысячи узлов, и чтение превращается в работу с картой масштаба: нужны фильтры, резюме и автоматические проверки. npm ls стоит запускать адресно и хранить промежуточные снимки.
Полезно смотреть на дерево не целиком, а фрагментами: ограничивать глубину, фильтровать по именам пакетов, экспортировать JSON и анализировать его инструментами визуализации графов. Снимки удобно хранить рядом с коммитами, чтобы видеть, как каждое обновление меняло структуру. Для CI проверка может включать не только линтеры кода, но и «линтеры дерева» — скрипты, которые запрещают появление лишних мажорных ветвей или выявляют внезапное удвоение критических библиотек. Так дерево перестает быть темным лесом и становится управляемым городом, где магистрали спроектированы, а развязки предсказуемы.
- Использовать
--depthи точечные grep/JSON-фильтры по ключевым пакетам. - Автоматизировать анализ: сохранять
npm ls --jsonи сравнивать диффом на CI. - Контролировать фан-аут корневых пакетов и частоту дублей одних и тех же мажоров.
- Периодически проводить «санитарный день»: обновления совместимых миноров и пересборка.
- Разгружать devDependencies, не тянуть в прод окружение лишнее.
Инструменты рядом: npm why, Yarn, pnpm и как трактовать различия
npm ls показывает состояние дерева, а npm why объясняет причину появления конкретного узла. Yarn и pnpm формируют дерево иначе: отличия в hoisting и структуре узлов влияют на картину и трактовку пометок.
Когда цель — понять, почему конкретная библиотека оказалась в проекте, полезно спросить why: команда проложит обратный маршрут по зависимостям и выведет цепочку, за которую стоит зацепиться. Yarn в классическом режиме поднимает зависимости агрессивнее, PNPM наоборот строит плоское пространство ссылок через content-addressable store — в результате ls в разных менеджерах даёт разные снимки одной логики. Это не мешает смыслу: везде видно причины дублей, несобранных peer и неожиданных миноров, просто карта чертится другими штрихами. Важно понимать, какой менеджер отвечает за инсталляцию, и читать дерево правилами его мира.
| Инструмент | Стратегия | Чем помогает в анализе |
|---|---|---|
| npm ls | Показывает текущее дерево | Общая картина узлов и пометок, быстрая диагностика |
| npm why | Выводит цепочку причины | Точечный ответ «почему пакет здесь» |
| Yarn | Hoisting интенсивнее | Меньше дублей при согласованных диапазонах |
| pnpm | Жесткое разделение версий через store | Чёткая изоляция, быстрый повтор, иная визуальная глубина |
При смене менеджера важно переосмыслить вывод: знакомые пометки могут называться иначе, структура станет площе или, напротив, расширится за счёт ссылок. Но логика поиска причины не меняется: найти цепочку, выровнять диапазоны, обновить корень, зафиксировать в lockfile.
Частые ошибки при разборе дерева и удобный чек-лист
Ошибки рождаются из поспешности: лечить симптом вместо причины, обновлять всё сразу, игнорировать peer-сигналы, забывать про lockfile. Рабочий чек-лист снижает шум и экономит часы расследований.
- Не «чинить» дубликат жёстким пином, пока не ясна несовместимость диапазонов.
- Не игнорировать unmet peer в сборке: в рантайме это может обернуться тонкой поломкой.
- Не смешивать менеджеры пакетов в одном репозитории.
- Всегда фиксировать изменения lockfile отдельно, чтобы видеть влияние на дерево.
- Проверять консистентность: локальная установка должна совпадать с CI через
npm ci. - Держать dev-зависимости в своих границах, не тащить их в прод.
- Периодически запускать анализ дублей и фан-аута ключевых модулей.
Вопросы и ответы
Что показывает npm ls без флагов и зачем включать —all?
Без флагов npm ls ограничивается верхним уровнем зависимостей проекта, чтобы не перегружать вывод. Флаг —all раскрывает полную глубину дерева, что необходимо для поиска дублей, конфликтов peer-зависимостей и анализа реального состояния node_modules.
Верхний уровень полезен для быстрой ориентации: видно прямых соседей и их закрепленные версии. Но проблемы обычно прячутся глубже — там, где встречаются несовместимые диапазоны и где hoisting не сработал. Полная глубина выводит на сцену все роли, а не только главных акторов, и позволяет пройти по цепочке причин от симптома к узлу-источнику.
Как быстро найти, кто тянет конкретный пакет в проект?
Для точечного поиска удобнее использовать npm why, который выведет цепочку зависимостей до искомого пакета. В связке с npm ls это даёт и причину, и контекст: видно, где узел стоит и какие соседи с ним конфликтуют.
Если why недоступен в окружении, помогает grep по текстовому выводу или парсинг JSON-дерева: по имени узла находится все вхождения, затем по родителям поднимается к первой точке, где выбор становится обязательным. Такой метод хорош для автоматизации на CI, где регулярные проверки держат дерево в тонусе.
Откуда берутся пометки UNMET PEER DEPENDENCY и опасны ли они?
UNMET PEER DEPENDENCY появляется, когда пакет ожидает соседа определенной версии на том же уровне, а окружение этому не соответствует. Опасность зависит от контекста: иногда это лишь предупреждение, но для инструментов сборки и плагинов часто критично.
Peer-зависимости придуманы для плагинных архитектур, где важно жить рядом с хост-пакетом одинакового мажора. Нарушение границы может проявиться не сразу, а только в определённом сценарии. Лучше устранить сигнал: обновить корневую зависимость, поставить недостающего соседа и убедиться, что диапазоны совпадают.
Почему в дереве отображаются две разные версии одной библиотеки?
Разные ветви потребовали несовместимые версии, и менеджер установил по экземпляру для каждой, чтобы не ломать взаимную совместимость. Это нормальная ситуация при конфликтующих диапазонах semver.
Дальше полезно выяснить, где границы можно сблизить: обновить один из пакетов до версии, совместимой с общей, или ослабить чрезмерно узкий пин. После выравнивания hoisting вынесет общий модуль наверх, и дубли исчезнут. Если этого не сделать, нагрузка и время сборки будут расти по мере разветвления дерева.
Чем отличается npm ls от вывода у Yarn или pnpm и как сравнивать?
Различия продиктованы стратегией установки: Yarn склонен поднимать зависимости агрессивнее, PNPM изолирует версии через store, создавая плоское пространство ссылок. Поэтому структура деревьев и пометки различаются, хотя логика причин остается общей.
Сравнивать корректнее на уровне вопросов: кто тянет пакет, почему выбранная версия такая, где нарушены peer-границы. Для этого везде найдутся аналоги ls и why, а структура лишь подскажет, как именно рисовать маршрут по узлам.
Как зафиксировать дерево, чтобы повторить установку на CI без сюрпризов?
Нужна дисциплина lockfile: коммитить изменения, устанавливать зависимости через npm ci и следить, чтобы локальные версии npm и Node соответствовали CI. Тогда дерево воспроизводимо и ls на разных машинах совпадает.
Если lockfile разошелся с node_modules, лучше очистить каталог и выполнить свежую установку. Для регулярного контроля помогает проверка хеша lockfile в пайплайне и скрипт, который сравнивает снимки дерева между коммитами.
Можно ли использовать npm ls как часть аудита безопасности?
Сам по себе ls — диагност о структуре, а не сканер уязвимостей. Но на его основе легко выявить неожиданные мажоры и лишние узлы, а затем уже подключить npm audit или сторонние сервисы для поиска конкретных уязвимостей.
Аудит начинается с порядка: чистое дерево с минимальным количеством дублей и согласованными версиями сокращает площадь атаки и упрощает патчинг. ls делает видимым именно это — а уже после в ход идут базы знаний об уязвимостях.
Финальный аккорд: дерево как карта решений, а не просто список
Вывод npm ls — это не перечень имён, а трассировка смыслов: где проект берёт устойчивость, где полагается на гибкость, где зависимость ждёт соседа, а где менеджер бережно выносит общие части наверх. Когда к дереву относиться как к карте, путь от симптома к причине занимает минуты, а не дни.
В этом и заключается зрелая работа с зависимостями: слышать систему версионирования, видеть роль lockfile, уважать правила peer-зависимостей и не превращать тонкую настройку в гонку за «зелёным» билдом любой ценой. Культура чтения дерева выстраивает предсказуемость: сборки стабильно повторяются, обновления проходят осмысленно, а каждая новая ветвь укладывается в общий рисунок, как будто всегда там была.
How To: быстро навести порядок с помощью npm ls —all
- Сделать снимок:
npm ls --all --json > tree.jsonи параллельно текстовый вывод. - Выбрать симптом: дубли, unmet peer, странная версия ключевого пакета.
- Проложить маршрут: по родителям узла найти первую точку жёсткого выбора.
- Сверить правила: диапазоны semver, требования peer, состояние lockfile.
- Принять решение: обновить корневую зависимость, выровнять версии, добавить peer.
- Зафиксировать: пересобрать установку через
npm ci, закоммитить lockfile и вновь проверитьnpm ls.

