Как ранее было отмечено в предыдущем посте концепция рабочего каталога Angular закономерно эволюционировала до поддержки полноценных workspace - проверенный и понятный способ управления сложностью при масштабировании процесса разработки (Visual Studio Solution , SBT Multi-Project builds , Gradle Multi-Project Builds , RushJS , Lerna и др. ).
Для каждого, кто практически сталкивался с Angular проектом средней руки, не будет секретом, что angular.json может легко содержать тысячи строк убористого JSON, с невероятным, даже нарочито избыточным количеством дублирующейся информации. Проблема отнюдь не новая и дающая о себе знать при масштабировании процесса разработки сложного продукта. Компактность и человеко-читаемость формата явно не была в приоритетах, и как-бы намекает, что человеку тут и нечего делать. Из этого и будем исходить.
Некоторые проблемы
Многословность. Ориентироваться в длинной "портянке" весьма некомфортно. При внесении изменений есть высокий риск сделать их не в том месте. Что, так же, затруднает понимание при анализе истории изменений.
Нарушение принципа DRY. При изменении путей к файлам необходимо аккуратно вносить множественные идентичные измененения во множестве различных параметров.
Отсутствие локальности. Имеется ввиду, что файл angular.json, описывающий настройки конкретного проекта, и сам проект находятся в разных местах. Что несколько затрудняет быстрое понимание каких проектов какие изменения касаются. А так же делает чуть более сложным создание нового проекта на основе уже существующего.
Вариант решения проблем
Стандартом de facto в отрасли для конфигурационных файлов стало использование YAML, как чуть более human friendly формата с точки зрения восприятия. Его опциональная поддержка, наряду с JSON5 будет весьма полезной. Возможность определять контекстные переменные и определять значения параметров через интерполяцию частично бы избавило от заботы переопределять значения множества параметров.
Декомпозиция большого фала на логически независимые, и значимые в контексте, более мелкие фалы (общие настройки, настройки конкретных проектов и т.д.) существенно упрощает и восприятие и понимание их содержимого. Спецификация JSON Reference вполне органично способна решить эту задачу, а так же открывает возможности прямого переиспользования типичных настроек, вместо их множественного копирования.
После того, как будет выполнена декомпозиция файла angular.json на логические составляющие, то настройки проектов логично хранить в папках соответствующих проектов.
Как только всё будет готово, то останется выполнить обратную задачу: для каждого проекта вычислить все значения параметров на основе контекстно определённых переменных и $ref
ссылок, и далее объединить все итоговые проектые конфигурации с глобальными настройками в единый angular.json файл.
Пример реализации
Сразу хочется отметить, что реализация концепции технически не является сложной задачей и вполне по силам одному профессиональному разработчику на пару дней. В связи с чем, возможно, что разработка собственного решения в вашем случае будет более предпочтительным вариантом, т.к. открывает практически безграничные возможности его адаптации под специфические требования и пожелания.
У нас получился инструмент ngx-ws, на примере которого далее и рассмотрим как идея работает на практике.
Подготовка
Создадим небольшой простой workspace c группой проектов.
Декомпозиция
Преобразуем полученный angular.json в YAML формат, декомпозируем его на файлы angular-workspace.yaml и серию файлов angular-project.yaml отдельно для каждого проекта.
Содержимое оригинального JSON поместим в свойство angular для angular-workspace.yaml
, а конфигурацию каждого проекта в свойство project для каждого angular-project.yaml
Интерполяция
Файлы поддерживают секцию vars, которая определяет контекстные переменные файла. Для angular-project.yaml
переменная name
предопределена именем соответсвующего проекту каталога и её объявлять не обязательно. Для библиотек получим примерно вот такую картину:
Поскольку, по очевидным причинам, такое определение для всех библиотек в нашем случае (в общем случае для всех проектов одного типа) будет идентичным, то определение можно перенести в angular-workspace.yaml
:
а само определение переписать в значительно более компактном виде:
Глобальная предопределённая переменная wsPath
содержит полный путьangular-workspace.yaml
файла и инструкция реализует стандартный механизм ссылки на внешние ресурсы.
Точно так же можно переиспользовать значения внутри отдельного файла.
Ресурсы
Типичной ситуацией является включение в разные проекты внешних зависимостей, что стандартно требует однотипных настроек во множестве мест. Для упрощения, мы ввели понятие ресурсов и реализовали простой механизм их подключения к конкретным проектам.
Получается вот так:
Сборка
Устанавливаем пакет
npm i -g ngx-ws
Запускаем сборку
rm angular.json; ngx-ws -v
или, в нашем случае, выполнить
npm run rebuild
Видим, что сгенерированный angular.json
идентичен оргинальному. Команду можно (хотя и совсем не обязательно) включить в пайплайн CI-сборки или разработки, а файл angular.json
рассматривать как автоматически генерируемый.
Заключение
Рассмотрены лишь некоторые аспекты. Есть и другие, такие как полноценная локализация в файловой структуре зависимостей проектов, как, например, это сделано в RushJS . В целом, данный подход хорошо зарекомендовал себя на практике и успешо используется в ряде проектов на протяжении уже нескольких лет. В дизайн решения заложена совместимость с потенциальными изменениями в формате angular.json и стандартными toolchain экосистемы, что практически сводит к минимуму риски, связанные с его внедрением, в то же время, решая вышеописанные проблемы.
Ссылки
Github проект с примером
NPM-пакет ngx-ws
lehkap
Имхо, yaml не более человеко-читаемый, а самое главное намного менее человеко-редактируемый формат, по сравнению с json.
Launcelot
полностью согласен с вами
saaivs Автор
С вами сложно не согласиться. Особенно в части человеко-редактируемости. Поэтому поддержка YAML не более чем возможность, чтоб просто обеспечить выбор. Всё описанное в посте будет работать точно так же, если вместо YAML будет JSON5.