🔥

Тред (Богдан Чадкин)


Поговорим немного о node_modules и как сделать их меньше. Зачем тратить на это время? Мой личный мотив это опен сорс. Я контрибьючу во множество проектов и каждые две-три недели приходиться вычищать десятки гигабайт node_modules.

Важно рассматривать несколько факторов, например, размер пакета. Пакет может дублироваться между раными репозиториями. Разные версии пакета могут дублироваться внутри одного проекта, например, так project - lodash@4 - webpack-bundle-analyzer -- lodash@3 - mjml -- lodash@3

В пакетах может быть опубликовано много лишнего, например тесты, фикстуры, конфиги редакторов. Мой любимый пример, кто-то закомитил файлик tags. Всего-то 42MB. unpkg.com/browse/react-f…

Решение тут простое, добавить поле "files" в package.json со списком который нужно опубликовать. Поддерживать такой список несложно. Readme, license, changelog публикуются всегда. Достаточно только указать папку lib и/или dist. github.com/ryanseddon/rea…

Так же пакеты часто публикуют сурсмапы, которые могут превышать размер кода в несколько раз. Нельзя сказать что сурсмапы бесполезны и решением может быть отказ от лишних бандлов, например, в апреле когда все lts версии ноду буду поддерживать esm. unpkg.com/browse/swagger…

.npmignore напротив довольно неэффективное решение. Список ненужного вести достаточно тяжело и такой список начинает копироваться из проекта в проект, а затем появляется новый редактор или новый инструмент или новая папочка с фикстурами, и пакет после публикации раздувается.

@jsunderhood Проблема практически сводится на нет, если использовать pnpm
Согласен, yarn pnp и pnpm эффективно решают проблему дублирования внутри одного репозитория и даже между репозиториями. Однако у нас есть yarn с node_modules и npm с большой аудиторией. twitter.com/theDjaler/stat…

Следующий фактор - это количество прямых и транзитивных зависимостей, зачастую необоснованное. К примеру, тот же lodash. Очень много проектов для ноды без необходимости поддерживать ie до сих пор используют lodash вместо нативных forEach,some,reduce,filter или чудесного for/of.

Другой пример нобосновынных зависимостей это micromatch - популярный glob matcher. Версия три поддерживала старую ноду, поэтому тащила кучу полифилов целый фреймворк для плагинов. К счастью версия 4 решила эту проблему, однако многие зависимости застряли на старой версии.

Большое количество зависимостей/модулей так же негативно влияет на время запуска node приложений. Даже если лениво загружать модули, время доставки фичи все равно увеличивается, в печальных случаях на несколько секунд. Спасибо алгоритму резолва модулей.

Как я анализирую node_modules. Самое простое (bash only), это вывести список веток и отсортировать их размер du -ckd1 node_modules | sort -n

Второй вариант это написать утилиту которая которая берет размер из статы каждого файла в node_modules и показывает размер каждого пакета без учета вложенных node_modules. Не пугайтесь, прошерстить всю черную дыру можно за несколько секунд.

Очень много дублированных пакетов одной версии появляется со временем после множества манипуляций с зависимостями. Для yarn есть отличная утилита yarn-deduplicate. В своем проекте я запустил ее только для пакетов babel. В результате ~0.5GB испарились.

Еще один способ анализа это чтение локфайла. У yarn.lock достаточно приятный синтакс, с package-lock.json чуть по сложнее. Но в целом появляется некая картина какие зависимости стоит обновить, а какие убрать. К примеру до сих пор можно найти inherits, xtend и прочие.

В последнее время появляется очень хорошая тенденция избавляться от большой цепочки зависимостей. Так webpack выкинул полифилы, next.js бандлит множество зависимостей с помощью ncc. jest избавиться от jsdom из коробки и старого sane вотчера.

Есть и проблемные места. Тот же jsdom не собирается отказываться от "request" который некоторое время назад задепрекейтили. Другая крайность это маниакальная жажда поддержки старых (небезопасных!) версий. github.com/yannickcr/esli… github.com/yannickcr/esli…

Причем вы можете увидеть что полифилы - это не безвредные однострочники в данном случае packagephobia.com/result?p=array…

Такие проекты как find-up и множество производных, можно заменить на миниатюрный escalade или просто скопировать решение в свой проект. github.com/sindresorhus/f… github.com/lukeed/escalad…

Бампить мажорные версии полезно вместе с поддержкой только последних lts ноды. Тогда множество пакетов такие как util.promisify можно заменить на доступные в ноде или нативно (Object.assign). packagephobia.com/result?p=util.…

Резюмируя - добавляйте "files" в package.json пакетов - убирайте поддержку старой ноды и используйте нативные фичи - заменяйте громоздкие пакеты на миниатюрные альтернативы - по желанию можно бандлить зависимости (смотрите rollup и next.js)