
Думаю многие из вас уже сталкивались с локальной разработкой npm-пакетов. Обычно никаких трудностей это не вызывает: создаём папку, запускаем npm init, пишем тесты, дальше используем npm link (либо просто симлинк) и «шлифуем» api до полной готовности.
Звучит просто… только если вы не используете Babel, Rollup, Webpack и т.п. Иными словами, всё хорошо, пока проект не нужно собрать перед публикацией, да ещё с модификацией исходного кода. Кроме того, одновременно разрабатываемых пакетов может быть больше чем один, что в разы усложняет «жизнь». Чтобы исправить эту ситуацию, пришлось сделать маленькую утилиту npmy, под катом небольшая статья с описанием тех. процесса работы и пример использования.
Итак, как я уже говорил, основная проблема локальной разработки — это использование scripts/хуков (prepublish, prepublishOnly и т.д.), именно по этой причине npm link не подходит, ведь по сути — это банальный симлинк, да ещё по завершению разработки нужно не забывать про npm unlink.
Поэтому я принялся за свое решение, которое:
- Имеет простую настройку.
- Эмулирует полный цикл публикации.
- Создает симлинк на псевдо-опубликованную версию (далее ПОВ).
- Следит за изменениями и обновляет ПОВ.
Настройка проекта
Первой мыслью было добавить правила прямо в package.json, но это неправильно, ведь это именно локальная разработка, поэтому правила было решено размещать в .npmyrc, который без труда можно добавить в .gitignore.
Сам файл — ни что иное, как простой JSON-объект, у которого:
key— название зависимости изpackage.json;value— локальный путь до разрабатываемого пакета (относительный или абсолютный).
Всё, на этом конфигурация закончена.
Запуск
Заходим в папку с .npmyrc и запускаем npmy, который:
- Читает
.npmyrc. - Фильтруем список зависимостей на два списка для:
- установки из NPM;
- локальной установки.
- Установка зависимостей из NPM.
- Псевдо-публикация пакетов из
.npmyrc. - Создание симлинка на ПОВ.
- Запуск отслеживания изменений (watch).
Процесс псевдо-публикации
Это самое интересное, ради чего всё и затевалось. Для начала вспомним, как это работает в оригинальном npm.

Как видите, тут нас ждет сюрприз, prepublish и prepare выполняются как на npm publish, так и на npm install (без аргументов). Поэтому если вам нужна сборка проекта перед публикацией, используйте prepublishOnly, но только начиная с 5 версии. Хоть этот хук и добавили в 4, работает он неправильно, и вместо собранного пакета уедет не пойми что, увы.
В моём процессе перед запуском всех хуков есть ещё одно звено, а именно создание копии проекта (вместе с node_modules):
- Копия создается через
rsyncв темповую папку. - Модифицируется
package.json, из которого убираетсяnpm test, чтобы не тормозить процесс псевдо-публикации. - Затем для копии запускаются все хуки соответствующие процессу публикации.
- И в финальный штрих: удаление всего, что не соответствует секции
files. - Profit.
Вуаля, теперь мы имеем версию пакета, которую бы вы получили при установки из npm. Также при каждом изменении исходников, ПОВ будет обновлена автоматом.
Кроме этого, npmy не забывает про секцию bin в package.json и корректно создаёт симлинки на объявленные там скрипты.
Итого
npm install -g npmy- Создаем
.npmyrcв проекте - Запускаем
npmy
Спасибо за внимание, надеюсь утилита будет полезна не только мне. :]
P.S. Инструмент новый, так что не стесняйтесь писать о проблемах.
Комментарии (20)

Delagen
19.07.2017 20:14Какие только костыли люди не придумают для своего псевдо удобства

RubaXa
19.07.2017 20:15+1А в чём костыльность, если не секрет?

Delagen
20.07.2017 11:19-1Разрабатываю около 20 пакетов… все стоит тупо и работает
причем есть несколько корневых. Разрабатывается и на TS и на JS.и бекэнд и фронт
node_modules просто симлинк на папку с зависимостями
Все правится на живую и тестируется. Для тестирование сборки есть CI
Всякими псевдоскриптами потом только усложняется развертывание места разработчика.
RubaXa
20.07.2017 11:27+1node_modules просто симлинк на папку с зависимостями
Я в начале написал, что симлинки не спасают, если у вас есть цикл публикации, например esnext в es5, никакой симлинк не спасёт, потому что
./index.js, нужно перезаписать в тот же./index.js.
Всякими псевдоскриптами потом только усложняется развертывание места разработчика.
Они усложняют ровно так же, как и создание симлинка, только в отличии от вашего способа, мне не нужно делать это руками, достаточно запустить
npmyи он сделает это за меня, да так, что результат будет идентичен работеnpm install.
Кроме этого, как я и написал, если у вас нет сложного цикла публикации, симлинк вполне решение, хотя у него есть ограничения, например с
dependencies, которые должны быть установлены неnode_modulesразрабатываемого пакета, а именно в пакет, который использует эту зависимость. При симлинке такие манипуляции приходиться делать руками.

KIlLXXXVI
20.07.2017 09:22+1Какое счастье, что мне достаточно просто симлинка. Статья весёлая и хуки интересные)

RubaXa
20.07.2017 09:49А если учесть все проблемы с «на какой же хук собрать проект», то совсем хорошо :]
npm-разработчики те ещё молодцы, будем надеяться, что в следующей версии они ничего не сломают ;]
comerc
20.07.2017 10:29Интересно, а вместе с Yarn полетит, или npm-only?

RubaXa
20.07.2017 11:00Ни я, ни в компании, мы не используем Yarn, поэтому, да, npm-only.

Delagen
20.07.2017 11:22Это правильно, прыгание по разным пакетным менеджерам создает больше проблем.
Вообще Yarn с своим приростом скоростью (которая уже не значительна для NPM 5) был просто хайп.
Он был еще более менее адекватен для систем сборки, чтобы ускорить установку зависимостей.
Но для места разработчика и прочих применений профит дутого яйца не стоит. Как будто я сижу каждые пять минут обновляют зависимости. Разработчик это делает ну раз в день, а то и в неделю. И подождать 1-3 минуты как бы не особо проблема. Можно чай попить )
RubaXa
20.07.2017 11:32Больше скажу, мы проблему решили ещё до появления Yarn, прикрутив локальный registry и спец кэширование, которое запускается
npm installи достаёт пакеты из локального кеша. Т.е. не нужно изобретать целый пакетный менеджер, чтобы заставить NPM работать быстро, а теперь и подавно ;]
Delagen
20.07.2017 11:47Ну у меня тоже локальный registry есть и кэш на CI не чистится, поэтому все довольно шустро.
А самый тяжелый сборочный модуль, вообще не ставится а просто линкуется из глобальных.
Поэтому таких проблем вообще не испытывал. Хотя npm 5 все равно немного поломал… Но в принципе все решаемо и довольно несложно. Сейчас все и на npm 5.3 прекрасно работает, единственное бесит что он с линкованными пакетами пока очень плохо работает https://github.com/npm/npm/issues/16788
RubaXa
20.07.2017 11:53+2Вот и я об этом, Yarn в целом не про скорость был, хоть это и была одна из фитч, но главное для них было быстрый
shrinkwrapиlock-файл, зачем это обычным разрабам, для меня загадка.
comerc
21.07.2017 12:58Ну очень привык к yarn upgrade-interactive. Есть ли подобное для npm? Нашёл npm-upgrade — почти, но оно пошаговое.

RubaXa
21.07.2017 13:00Эээ, но вот почему-то в моей работе нет такого кейса, как массовое обновление, обычно это очень обдуманный и точечный процесс на конкретную версию, а не просто up до последней.

SaitoNakamura
21.07.2017 20:34+1В свежем проекте где на ходу принимаются решения и устанавливаются и удаляются пакеты это очень критично
Плюс это позволяет приятно экспериментировать, создал папку, быстро поставил что нужно, попробовал, удалил
npm v5 решил эту проблемы, но последние полгода до его выхода pnpm и yarn решали

SaitoNakamura
20.07.2017 22:27Добавить поддержку будет очень легко в силу того что они практически совместимы
comerc
Lerna? Не, не слышал.
RubaXa
Слышал, но она про монорепозиторий с кучей пакетов и управление им, это частный случай, а я решал общий. Да и в целом, она про цикл выпуска монорепозитория.
RubaXa
Дополню.
Lerna — это про монорепозиторий, притом ещё с определёнными ограничениями, результат её работы банальный симлинк на папку в проекте, а это значит, что prepublish у таких пакетов использовать нельзя, код должен работать «как есть».
Кроме этого, lerna не позволяет залинковать произвольный пакет в зависимостях, она работает только со «своими».
npmy же в свою очередь позволяет подменить любую зависимость и направить её в любое место на диске, но не просто на исходники, а именно на псевдо-опубликованную версию.