Pull to refresh

Diarrhea для вашего бэкенда на Node.JS — уменьшаем вес сборки

JavaScript *Server optimization *Node.JS *

Наверняка вы часто замечали, сколько всякого мусора лежит внутри node modules. Это тесты, бенчмарки, ридми файлы, лицензии, тайпскрипт, и ещё безумное количество мусора, который можно более-менее безопасно удалить. Что мы собственно и сделаем в этом посте.
Картинку про вес node module я и так упоминал последние несколько публикаций, так что вот вам другая, которая в целом отражает текущую ситуацию. В качестве саундтрека к посту рекомендуется Little Big, “Life in da trash”.



Начало


Итак, в очередной раз случайно зайдя внутрь node modules, я погрустил, и начал я с того, что для эксперимента руками написал несколько скриптов, которые прогнал через свой проект. Выглядели они примерно так:


find ./ -iname "tests" -exec rm -rf {} \;
find ./node_modules -iname "test" -exec rm -rf {} \;
find ./node_modules -iname "*.gif" -exec rm -rf {} \;
find ./node_modules -iname "*.jpg" -exec rm -rf {} \;
find ./node_modules -iname "*.jpeg" -exec rm -rf {} \;
find ./node_modules -iname "*.png" -exec rm -rf {} \;
find ./ -iname "*.md" -exec rm -rf {} \;
find ./ -iname "*.log" -exec rm -rf {} \;
find ./ -iname "LICENSE*" -exec rm -rf {} \;
find ./ -iname "README*" -exec rm -rf {} \;
find ./ -iname "LICENCE*" -exec rm -rf {} \;
find ./ -iname "AUTHORS*" -exec rm -rf {} \;
find ./ -iname "NOTICE*" -exec rm -rf {} \;
find ./ -iname "changelog*" -exec rm -rf {} \;
find ./ -iname ".travis.yml" -exec rm -rf {} \;
find ./ -iname ".coveralls.yml" -exec rm -rf {} \;
find ./ -iname ".npmignore" -exec rm -rf {} \;
find ./ -iname ".gitignore" -exec rm -rf {} \;
find ./ -iname ".jshintrc" -exec rm -rf {} \;
find ./ -iname ".eslintrc" -exec rm -rf {} \;
find ./ -iname ".jscs.json" -exec rm -rf {} \;
find ./ -iname ".editorconfig" -exec rm -rf {} \;
find ./ -iname ".vs" -exec rm -rf {} \;
find ./ -iname ".babelrc" -exec rm -rf {} \;

В целом, это было уже неплохо, и давало некую экономию, но хотелось какого-то более элегантного и общего решения. И достаточно быстро я нашёл модуль, который делает именно то, что мне хотелось — ModClean.


Modclean


У модуля есть куча настроек, а ещё списки “лишних файлов” в виде плагинов, которые подключаются как npm пакеты. По умолчанию идёт вот такой список.


В нём есть три уровня файлов по безопасности удаления — очень рекомендую посмотреть список и увидеть, что может вызвать у вас проблемы. Например, я не понял, с какой стати в список включили “semver”...


Единственный минус — почему-то автор модуля не подумал, что вы так же можете захотеть удалить весь мусор перед сборкой в собственном проекте — например, вам абсолютно не нужны файлы тестов на продакшне…
Правда, оказалось, что это легко обойти, указав в качестве директории модулей директорию проекта:


modclean --modules-dir .

Результаты


Понятно, что результаты могут быть самые разные на разных проектах, но у меня вышло от 10% до 20% экономии. В запущенных случаях может быть и больше. Это может показаться не самой большой цифрой, но это уменьшение требуемого для хранения места на 20% и более быстрая на 20% раскатка проектов буквально из ничего — поэтому почему бы и нет?
Для любопытных на сайте модуля так же есть бенчмарки по удалению, но лучше просто попробовать его у себя.


Diarrhea


На этой позитивной ноте хотелось бы сказать, что я решил свою задачу, и всем рекомендую использовать этот прекрасный модуль. Но вы же видели в заголовке слово “diarrhea”, а не “modclean”, да? Это значит, что остался ещё один нюанс. Нюанс довольно толстый в прямом смысле слова — оказалось, что у модуля 123 транзитивных зависимости и он весит 4МБ. Что в целом уже считается более-менее стандартом для модулей ноды, но не может не доставить душевной боли.


При этом 1.2 мегабайта занимает дико популярный модуль update-notifier, всё что он делает — это отрисовывает вот такую рамочку в случае, если доступны обновления:

1 мегабайт на это, Карл! Мне, как Node.JS разработчику, хочется провалиться от стыда.


А большую часть остального места занимает модуль clui, в котором содержатся всякие украшательства для работы с терминалом — из которых используется только пара функций.


В общем, я не смог этого вынести, и сделал свою сборку — без блекджека и всего остального. Уведомление об апдейтах я оттуда полностью выпилил, а нужные функции из толстых модулей с большим количеством зависимостей я собрал в минифицированный bundle при помощи webpack. В целом, можно было ещё немного поиграть и ещё уменьшить вес модуля — но я дошёл до веса в 700 килобайт и пока успокоился. Так же я добавил опцию --root, которая позволяет более очевидно чистить не зависимости, а корень вашего проекта.


Issues по всем указанным поводам я закинул в modclean, а так же сделал туда пулл реквест, и если всё будет одобрено, то я задепрекейчу этот форк в пользу основного модуля — но пока есть ощущение, что modclean заброшен — так что если будут хорошие идеи, закидывайте их ко мне.


Установить мою версию можно, как всегда, из npm.


Предупреждение


Дважды убедитесь, что вы сохранили всё нужное в репозитории — diarrhea может удалять лишние файлы, нужные файлы, зубы, и убивать котят. Снимаю за это всякую ответственность.


Интеграция в CI


Лучше всего использовать diarrhea так:


  1. Сначала сносим весь мусор из node modules;
  2. Потом прогоняем юнит тесты, чтобы убедиться, что не снесли ничего лишнего;
  3. Потом сносим мусор из основного проекта, включая собственно тесты;
  4. Делаем npm prune --production;
  5. Собираем артефакт.

ToDo


Модуль делался на скорую руку для личного использования, поэтому в перспективе хотелось бы добавить следующее:


  • Тесты. Как ни странно, в modClean их вообще нет;
  • Совместимость с нодой до 8ой версии. Так как там уже webpack, то не сложно;
  • Выпилить ещё несколько зависимостей, в которых нет особого смысла;
  • Минимальную версию без терминальных украшательств для CI

Выводы


  • В который раз пугаюсь тому, какие жуткие вещи творит низкий порог вхождения в мою любимую экосистему. С этим определённо нужно что-то делать, и я надеюсь, что это будет как-то решено на уровне npm. Но прямо сейчас я вижу, что этот прекрасный update-notifier находится в зависимостях самого npm, что очень сильно огорчает. У этого несчастного нотифаера — 2 миллиона загрузок в неделю! 2 миллиона мегабайт (почти 2 терабайта) потрачено на долбаную рамочку с сообщением об обновлении!
  • Всегда проверяйте вес своих зависимостей, даже на бэкенде;
  • Чистите ваши зависимости;
  • Как ни странно, понемного прихожу к мысли о том, что бэкендовые зависимости тоже нужно собирать в общий бандл. Что сейчас делается довольно легко и непринуждённо любым выбранным вами инструментом, и не добавляет боли при отладке — ведь можно сгенерировать сурсмапы. Единственный минус — далеко не каждый пакет можно включить в бандл, регулярно сталкиваюсь с какими-то проблемами в этом, по большей части связанными с тем, что все любят как попало подключать динамические зависимости. Например, тот же update-notifier в бандл засунуть нельзя.

Ну что же, на этом всё.


Если вам было интересно, то вам так же могут понравиться мои другие статьи по этой теме:



Так же могу рассказать ещё о различных случаях упоротого использования Node.JS на бэкенде, участвуйте в опросе, если вам интересно.

Only registered users can participate in poll. Log in, please.
О чём ещё рассказать?
11.63% Зачем и как использовать mocha в несколько потоков 5
27.91% Как внедрять линтинг в проекте, где 4000 файлов содержат огромное количество ошибок 12
6.98% Зачем и как грузить логи из кибаны в MySQL или Slack бота 3
41.86% Зачем собирать backend модули в bundle 18
11.63% Ни о чём, прекратите уже эту ересь и переходите на Go 5
43 users voted. 16 users abstained.
Tags:
Hubs:
Total votes 6: ↑5 and ↓1 +4
Views 3K
Comments Comments 31

Please pay attention