[Из песочницы] Избавляемся от дублей пакетов в бандлах
Существует много webpack пакетов находящих дубли в бандле, самый популярный из них duplicate-package-checker-webpack-plugin, но он требует пересборки проекта, а так как стояла задача автоматизировать подбор оптимальной версии пакетов, то и вовсе получилось свое альтернативное решение.
Ну или моя история как получилось уменьшить бандл на 15%, за несколько секунд.
Как и во многих крупных команиях у которых есть огромная кодовая база, много единой логики, как следствие используем общие компоненты, публикуемые в свой npm репозиторий. Публикуются они через lerna, соответственно перед каждой установкой или обновлением общих компонентов возникает вопрос, какую версию устанавливать. lerna перепаблишивает все компоненты, которые используют публикуемый компонент (если версия до этого последняя была). Соответственно, всегда есть версии нескольких компонентов, которые лучше подходят друг к другу, так как не конкурируют зависимостями.
Из open source проектов подобным образом паблишиться nivo, вот их конфиг lerna.
Как появляются тогда дубли зависимостей? И как их устранять?
Предположим, у вас есть простой проект со следующим package.json
:
{
"name": "demo-project",
"version": "1.0.0",
"dependencies": {
"@nivo/bar": "0.54.0",
"@nivo/core": "0.53.0",
"@nivo/pie": "0.54.0",
"@nivo/stream": "0.54.0"
}
}
Посмотрим, где используется @nivo/core
:
npm list @nivo/core
Видим 4 копии @nivo/core
(3 экземпляра 0.54.0
и 1 — 0.53.0
). Но если мы поменяем минорную версию @nivo/core
на 0.54.0
, дубли устранятся.
Текущий пример прост, но на практике, естественно зависимостей у каждого пакета больше, и еще нужно рассмотреть подзависимости дальше, что увеличивает сложность задачи.
И как-то очередной раз увидев огромный размер бандла, мне надоело вручную устранять дубли пакетов.
Вообще правильно сразу обновлять пакеты до последней версии, но времени как всегда нет, чтобы менять мажорные версии, а в слепую подбирать перебором подходящий пакет долго и сложно. Ведь нужно обновить версию зависимости в package.json
, установить новые зависимости, и после проверить исчезли ли дубли в билде, если нет — повторить, долго, в среднем минуты 3–4 на итерацию.
Все это монотонно и требует внимательности, поэтому решил автоматизировать.
Хотелось бы узнать дубликаты без переустанавки зависимостей, и пересборки проекта, в идеале cli приложение выводящее варианты оптимизаций за 10 секунд и все существующие дубли в проекте.
Устранение дублей можно поделить на несколько подзадач, рассмотрим их по порядку.
Первая задача. Нужно смоделировать будующее дерево зависимостей бандла только по package.json, учитывая стандартный dedupe, быстро, не более чем за 100ms.
Решил использовать package-json для получения информации по пакетам и semver для сравнения различных версий.
В итоге получился npm пакет dependencies-tree-builder, шустро моделирующий дерево зависимостей бандла только по package.json.
Выделил в отдельный компонент, ибо может быть кто-нибудь переиспользует его в комбинаторных задач с package.json.
Вторая задача. Комбинаторная задача, эффективный перебор вариантов изменения зависимостей, и сравнение нескольких вариантов деревьев, ну и естественно выбор оптимального.
Надо было как-то сравнивать качественно получившиейся деревья, и пришлось позаимтвовать идею энтропии, как количественное измерение беспорядка, взял сумму экземпляров дублей (из примера выше она равна 3).
Было бы здорово еще учитывать веса пакетов (в КБ), но к сожалению, пакета который бы быстро работал с весами я не нашел, а те что есть работают примерно по пол минуты на пакет, к примеру package-size. Так как работают по следующему принципу: создают проект с единственной зависимостью, устанавливают зависимоти, после измеряется суммарный вес папки. В итоге, другого критерия, как количество экземляров дублей не придумал.
Чтобы понять какой пакет нужно изменить, рассматриваются причины дублей, конкретее источник и эффект. Перебором устраняются дубли эффектов, насколько это возможно, а так как эффекты устраняются, то и дубли в последствии тоже.
В итоге, получилось небольшое cli приложение ostap, рекомендующее оптимльные версии для уменьшения количества экземляров дублей в бандле.
Запускается просто указанием на package.json вашего проекта.
ostap ./package.json
Еще можно использовать для быстрого просмотра всех будующих дублей без пересборки проекта меняя лишь версии в package.json.
ostap ./package.json -s
По итогу, в моем проекте суммарный вес бандлов снизился на 15%.
Если используете route-splitting, может показаться, что какие-то бандлы увеличились в весе, но возможно изменилось распределение компонентов. То есть вместо копий зависимостей на каждой странице, единственная версия перешла в общий бандл для всех страниц, поэтому нужно оценивать суммарный вес бандлов по всем страницам.
Надеюсь, статья была полезной. И кому-то информация съэкономит время. Спасибо.
Еще раз ссылочки для удобства:
- Пакет моделирующий деверо зависимостей бандла по package.json
GutHub и npm; - Оптимизатор зависимостей для устранения дублей в бандле
GutHub и npm.
Если есть интересные идеи пишите в issue на guthub’е, обсудим.