Существует много 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%.
В репозитории есть раздел quick start.
Если используете route-splitting, может показаться, что какие-то бандлы увеличились в весе, но возможно изменилось распределение компонентов. То есть вместо копий зависимостей на каждой странице, единственная версия перешла в общий бандл для всех страниц, поэтому нужно оценивать суммарный вес бандлов по всем страницам.
Надеюсь, статья была полезной. И кому-то информация съэкономит время. Спасибо.
Еще раз ссылочки для удобства:
- Пакет моделирующий деверо зависимостей бандла по package.json
GutHub; - Оптимизатор зависимостей для устранения дублей в бандле
GutHub.
Если есть интересные идеи пишите в issue на guthub'е, обсудим.
Поддержите пожалуйста проект звездочкой на GutHub.
Комментарии (26)
itwillwork2 Автор
29.03.2019 11:04+3если версии не фиксировать, то например при каждом ребилде jenkins'ом вы не можете гарантировать, что все работает, ибо существует вероятность, что какой-нибудь джун запаблишил не рабочий код и сломал компонент, а npm подтянет последнюю версию полагая, что она стабильней
vintage
29.03.2019 13:06-2CI для того и существует, чтобы сразу понимать, что что-то сломалось. Если это ваш джун, то почему у него есть право коммитить в мастер без тестирования и код ревью? Если это разработчик внешней зависимости, то это хороший повод задуматься о том, стоит ли зависеть от кода, разрабатываемого джунами.
itwillwork2 Автор
29.03.2019 13:27ну не обязательно джун, а кто-нибудь из комманды вполне разумный, внесет изменения, которые пройдут ревью у тим лида, а в вашем проекте что-то порушится из-за них. В команде в которой множество разработчиков знания распределены, не все знают нюансы каждого пакета.
inoyakaigor
29.03.2019 13:21+3Напомниаю, что чтобы потестить не ставя этот пакет локально/глобально можно сделать так:
npx ostap ./package.json -d
Psychosynthesis
30.03.2019 02:01У меня сейчас нет возможности проверить, скажите, пожалуйста, ostap добавлен в общий репозиторий npm? То есть, смогу ли я установить его обычным npm install -g?
vintage
А вы попробуйте не фиксировать версии зависимостей и жить сразу станет легче :-)
Tarik02
Или веселее не в самом хорошем смысле :-)
vintage
Можно каждое утро делать пробежку, а можно раз в месяц задыхаться от подъёма на пару этажей по лестнице. Вы сами решаете держать ли себя в форме и держать руку на пульсе, либо лежать и тухнуть, пока вас жёстко не сбросят с дивана.
Tarik02
Если это была аналогия, то намного полезнее будет делать регулярную пробежку, то-есть самому исправлять зависимости, а не упасть в рандомном месте...
Drag13
Они все равно зафиксированы в package-lock. А если вы призываете еще и от него отказаться то удачи ловить баги с прода которые не воспроизводятся ни на одной машине у девов :).
itwillwork2 Автор
с package-lock есть ньюанс, что если в нем будут конфилкты при мердже будет очень больно
justboris
А npm-merge-driver пробовали?
vintage
Зачем мне удача, если дев в любой момент может взять тот же артефакт сборки (например, докер-контейнер) и развернув у себя в точности воспроизвести продакшен окружение, а не надеяться на то, что он переключился на правильную ревизию, что результат сборки зависит исключительно от package-lock (что далеко не всегда так, например, при выгрузке свежих данных со внешнего сервиса при сборке), что никто не забыл закоммитить актуальный package-lock, что никто нигде не подменил и не удалил содержимое пакета (что особенно актуально для приватных репозиториев), что версия ноды и нпм/ярна совпадают с продом, и тд и тп?
Drag13
Затем что не у всех есть докер, не все умеют правильный докер (я — не умею), и наконец, затем что многим он не нужен в принципе. Проще проблему изначально не создавать, а не героически ее бороть докером.
vintage
Слово «например» вы проигнорировали сознательно или бессознательно? Артефакты сборки могут быть разные, но их не может не быть, иначе вам нечего выкладывать на сервер.
Drag13
Ок, у меня есть артефакты сборки. Они минифицированы (если мы говорим про js) или в бинарном виде (если про компилируемый код), они настроены на прод сервисы, они могут содержать закрытую информацию (даже если это не совсем правильно). И что вы таки предлагаете с этим делать?
vintage
Сорсмапы или отсутствие минификации.
Отладочные символы.
Не надо зашивать конфиги в бинарники.
В отдельный артефакт.
Drag13
Можно и так. А можно держать package-lock и будет вам счастье в 99,99% за куда меньшие усилия. А если вдруг (может у вас ентерпрайз) есть свой частный npm репозиторий, то и в 100% будет вам счастье.
vintage
Сомнительное счастье жить с протухшими зависимостями.
Tom910
На самом деле package-lock и не фиксированные версии это компромис, когда не все разваливается при малейшей проблеме, но и при необходимости все просто обновляется. У нас такой подход используется
dagen
А зачем козе баян? Всмысле зачем брать тот же артефакт сборки для отладки, если можно зафиксировать версии, что будет гарантировать, что артефакты будут собираться с нужными версиями.
Без лока версий всех зависимостей никто не может гарантировать, что в артефакте использованы нужные версии.
vintage
Не будет. Примеры я привёл в комментарии, на который вы отвечали.
А это и не надо гарантировать. Гарантировать нужно лишь два инварианта:
Артефакты это гарантируют. Исходные коды, хоть трижды обложитесь локами, — нет.
dagen
Товарищ, я нигде не говорил и про то, что артефакты не нужны (более того, артефакт в том или ином виде у всех есть). Каждый сам кузнец своего счастья, и раз вам хочется получать менее стабильную сборку артефактов — дело ваше.