company_banner

Обзор npm 7

    13 октября состоялся релиз npm@7.0.0. Релиз носит экспериментальный характер и доступен для скачивания из публичного npm-реестра с тегом next-7. Также npm 7 поставляется вместе с node.js 15.

    Как вы помните, нечетные версии node.js тоже скорее являются нестабильными предрелизами. Только четные версии получают статус LTS и рекомендуются для использования в продакшене. Но сейчас мы поговорим исключительно об npm, а если быть более точным, то разговор пойдет о нескольких очень ожидаемых фичах, вошедших в свежий релиз.

    1. Workspaces

    Вне сомнений, npm workspaces — самая ожидаемая фича npm@7. Workspaces позиционируется как набор инструментов для управления несколькими пакетами из одного верхнеуровнего, проще говоря — для управления монорепозиторием (proof). 

    Поскольку сейчас монорепозитории явно пользуются спросом сообщества, звучит такое представление очень многообещающе. Но давайте разбираться, какой же набор инструментов дает новый npm.

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

    {
      "name": "root-project",
      "workspaces": [
        "workspace-a",
        "packages/*"
      ]
    }

    При запуске команды npm installв корневые node_modules будут добавлены симлинки на все пакеты из рабочей области. Также будут установлены зависимости, описанные в package.json каждого дочернего пакета, и сформирован package-lock.json новой версии, поддерживающей монорепозиторий.

    Важно отметить, что выполнение команды npm ci проигнорирует новое свойство в package.json и установит только то, что найдет в package-lock.json.

    И на этом весь инструментарий заканчивается. То есть npm пока умеет только резолвить зависимости. Например, вот так будет выглядеть команда установки модуля babel-core с помощью lerna в пакет монорепозитория awesome-package:

    lerna add babel-core --scope=awesome-package

    Чтобы сделать аналогичное действие с npm, придется немного поприседать и для начала установить в пакет awesome-package пакет babel-core с помощью флага --prefix:

    npm i babel-core --prefix=packages/awesome-package

    Эта команда добавит зависимость в package.json, создаст package-lock.json и node_modules в корне пакета. Но нужно понимать, что в таком случае npm знает только о пакете и зависимостях из packages/awesome-package/package.json и точно ничего не знает о воркспейсе. Чтобы все встало на свои места придется еще раз запустить полную установку моделей в корне проекта командой npm i

    Так npm пройдется по всем пакетам в проекте и зарезолвит модули «правильно» с учетом настроек воркспейса. Но этот способ обречен на провал, как только в одном из пакетов проекта обнаружится зависимость от другого пакета из этого же проекта. В таком случае есть риск получить подобную ошибку:

    Эта ошибка говорит о том, что пакет lib-a отсутствует в используемом npm-реестре. Но этот пакет существует только в рамках проекта. А что, если пакет с таким именем будет найден в реестре? Тогда в ваш проект будет установлен совсем не тот пакет, который вы ожидали.

    Но, несмотря на скудность инструментария, npm workspaces — отличная отправная точка для создания удобного CLI для работы с монорепозиториями.

    2. Автоматически устанавливаемые peerDependencies

    До npm@7 пользователю приходилось самому устанавливать peerDependencies, npm мог кинуть предупреждение об отсутствии или неправильных версиях таких зависимостей.

    Как и следует из заголовка, в npm@7 при установке зависимостей peerDependencies будут резолвиться автоматически. Кроме того, npm будет вываливать ошибки о несоответствии версии уже установленной библиотеки и версии из peerDependencies и прерывать процесс установки. 

    Это достаточно серьезные ломающие изменения, и не во всех кейсах разработчики с ходу смогут разрулить все конфликты версий. Поэтому разработчики предусмотрели флаг --legacy-peer-deps , который отключает поведение автоустановки.

    Очень коротко о мотивации внедрения фичи: автоматическая установка решает проблемы с неправильным резолвом зависимостей и является частью алгоритма «максимально простой дедупликации». Более подробно о мотивации можно почитать вот здесь.

    3. Поддержка yarn.lock

    Дано: проект с package.json и yarn.lock.

    Действия: запускаем npm install в корне проекта и видим следующее:

    1. yarn.lock все там же.

    2. Появился package-lock.json.

    3. package.json остался нетронутым.

    Фактически это работало так же и в npm 6. Тогда в чем заключается поддержка yarn.lock?

    При запуске npm не находит нативный для себя lock-файл, но находит yarn.lock. Конечно же, считывает его и на основе полученной информации устанавливает зависимости и формирует package-lock.json. Интересно, что после формирования package-lock.json-файла npm больше не обращается к yarn.lock.

    Кажется, что команда npm перепутала слово «поддержка» со словом «миграция». Но мигрировать с yarn на самом деле станет более безопасно.

    4. The standalone npx is deprecated

    Это значит, что автономные версии npx более не будут поддерживать. Под капотом npx будет обращаться к npm exec и не будет иметь своего собственного алгоритма запуска.

    npm exec, что ты за зверь такой?

    npm execпозволяет вам запускать произвольную команду из пакета npm (установленного локально или извлеченного удаленно) в контексте, аналогичном запуску через npm run 

    Тут есть одно большое отличие от npx ранних версий: npm execявно разделяет аргументы вызываемой утилиты и собственные с помощью --

    Для примера:

    npx foo@latest bar --package=@npmcli/foo

    Это будет соответствовать вот такой команде:

    foo bar --package=@npmcli/foo

    Флаг --packageбыл передан дальше, потому что npx считает, что после позиционного аргумента идут аргументы самой программы. В случае использования npm exec:

    npm exec foo@latest bar --package=@npmcli/foo

    Это будет соответствовать такой команде:

    foo@latest bar

    Чтобы получить эквивалентную команду, нужно выполнить:

    npm exec -- foo@latest bar --package=@npmcli/foo 

    И еще одно важное отличие от npx ранних версий с точки зрения безопасности: если запускаемый пакет отсутствует в зависимостях проекта, то npm спросит о необходимости запуска команды. Это поведение можно предупредить с помощью флагов --yes и --no

    5. Бонус-фича из npm@7.1 — npm set-script

    Маленькая, но приятная фича из npm@7.1. npm set-script позволяет устанавливать команды в секции scripts в файле package.json.

    Например, с помощью этой фичи вы сможете не покидая терминала установить зависимость, прописать ее использование в секции scripts и сразу запустить через npm run:

    npm init -y && npm install --save-dev http-server && npm set-script start "http-server ."&& npm start

    Вместо заключения

    Долгожданная седьмая версия npm получилась неоднозначной.

    С одной стороны — ломающие изменения установки peerDependencies, с другой — workspaces и более безопасный npx. Но на это как раз и нужны developer preview релизы.

    Будем надеяться, что команда npm будет прислушиваться к сообществу и разовьет нативный для экосистемы инструмент в удобном для всех направлении.

    Полезные ресурсы

    1. Workspaces

    2. Предложение и мотивация изменения алгоритма установки peerDependencies

    3. Why keep package-lock.json?

    4. npm-exec

    5. npm set-script

    Tinkoff
    IT’s Tinkoff — просто о сложном

    Comments 0

    Only users with full accounts can post comments. Log in, please.