🔥

Тред (Игорь Камышев)


Давайте сегодня обсудим конфигурацию сборки. Я довольно долго уже пытаюсь найти способ конфигурировать бандлер без боли и, кажется, нашёл достойное решение.

Кек, эта проблема вроде звучит супер-просто. Ну конфигурация, ну напиши и ладно. Но нет!

Вообще, в простом случае конфигурация сборщика — это просто один файл, в котором описано все что нужно. Для вебпака — это webpack.config.js, например. Чувствуете подвох?

Это js файл, то есть в нем может выполняться произвольный код для создания финальной конфигурации. В типичном приложении конфиг будет зависеть от перменных окруждения и аргументов командой строки.

У вас может возникнуть закономерный вопрос — почему вообще конфигурация сборки зависит от каких то аргументов командной строки и переменных окружения?

Причин может быть несколько. Как минимум, для разработки и для продакшена обычно используется чуть-чуть разных конфиг — минимификация только для прода, сорс-маты только для разработки и так далее.

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

Ну и куча более экзотических вариантов специфичных для конкретного приложения.

Короче, вариативность конфигурации бандлера — это важно.

У кого какие есть боли связанные с конфигурированием инструментов?

Самый простой способ сделать конфиг изменяющимся под нужды приложения — просто прямо в файле конфигурации завязаться на переменные окружения и аргументы командной строки.

Но, кажется, это не очень управляемая история. Вносить правки в конфиг становится просто невозможно.
notion image

Число вариантов сборки растёт быстро — дев-клиент, дев-сервер, прод-клиент, прод-сервер. Уже 4, а пока только два параметра, по которым происходят изменения.

Впервые я столкнулся с этой проблемой на одном из проектов @BreadheadStudio

У нас было 4 конфиг-файла, в которых просто было много копи-паста. И когда решили принести в проект TypeScript, сделать эту небольшую правку было сущим адом.

Тогда я подумал (не очень хорошо, как выяснилось) и решил общие части конфигурации вынести в специальный файл, а специфичных для окружениях файлах просто его расширить.
notion image

В целом, получилось неплохо, но осталось дублирование между дев-клиентским и дев-сервеным конфигами, и между прод-клиентским и прод-серверным конфигами.

А еще было дублирование между дев-серверным и про-серверным конфигами, и соответсвенно между дев-клиентским и прод-клиентским.

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

Короче, получилось 5 файлов с «базовыми» конфигами и 4 файла со «специфичными» конфигами. Да, я тогда был не сильно умным.
notion image

В итоге, стало только хуже и мы вернули все обратно, копи-паст, так копи-паст.

Следующий заход оказался удачнее. Я посмотрел прекрасный доклад youtube.com/watch?v=Tg8IVb…

И очень проникся этой идеей — вся конфигурация должна быть описана в одном файле. Просто нужно описывать ее на более высоком уровне, чем это предполагают бандлеры.

То есть, если именно в этом варианте конфига нужен хот-релоадинг, это должно определяться одной строкой — withHotReloading, а не подключением плагинов, лоадеров и вот это все.

Тогда я попробовал разбить весь конфиг на атомарные части и собрать финальную версию из этих кусочков.

Получилось как-то так
notion image

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

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

Основные штуки (транспиляция TS, например) вынесена в общий базовый конфиг, он расширяется через небольшие функции, добавляющие одно новое поведение по месту использования.

Выглядит это примерно вот так в реальном проекте. Здесь withDefaults — тот самый базовый конфиг, а все остальное — функции его расширяющие.
notion image

Внутри эти функции выглядит супер-просто, они всего лишь добавляют/расширяют поля конфига. Есть правило — функция-расширитель не может удалить что-то из конфига, и не должна ничего перезаписывать.

А как вы боретесь со сложностью и дублированием в конфигурации сборки?