Сборка с использованием ссылок на проекты

TypeScript реализует возможность, благодаря которой можно более эффективно работать одновременно над множеством проектов. Данный механизм значительно упростил работу с популярными в последнее время монорепозиториями - проектами, состоящими из отдельных, зависимых друг от друга и логически схожих самостоятельных частей. И хотя в данном механизме нет ничего сложного, ему рассмотрению будет посвящена вся текущая глава.

Ссылки на проекты

Ссылки на проекты, путем объединения множества зависимых частей в общий процесс сборки, позволяют значительно сократить время сборки проекта. В этом компилятору tsc помогают алгоритмы интеллектуальных инкрементальных сборок, позволяющие компилировать только нуждающиеся в этом части. При этом под словом «часть» подразумевается не только фрагмент программы, но и отдельные проекты (модули). Кроме этого, ссылки на проекты после сборки повторяют исходную структуру связанных зависимостей, что немаловажно при работе с разбитыми на части проектами. Другими словами, ссылки на проекты позволяют компилировать множество отдельных проектов по правилам описанных, в уникальных для каждого проекта, tsconfig.json. В результате этого будет получено множество завершенных частей программы (библиотеки, динамически подгружаемые части приложения, микросервисы). Добиться подобного без использования ссылок на проекты невозможно, поскольку результатом обычной компиляции является только одна часть программы, собранная по правилам одного конфигурационного файла.

Проект, который использует другие проекты в качестве своих зависимостей, должен указать ссылки на них в своем конфигурационном файле. Для этих целей в tsconfig.json существует специальное свойство верхнего уровня references. Свойство references ассоциируется с массивом объектов, поля которого описывают установки для связываемых проектов. Одним из ключевых полей является path, в качестве значения которого указывается ссылка на конфигурационный файл tsconfig.json проекта-зависимости, либо директорию, которая его содержит.

json
{
 "references": [
   { "path": "path/to/tsconfig.json" },
   { "path": "path/to/dir/with/tsconfig-json/" }
 ]
}

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

json
{
 "compilerOptions": {
   "composite": true
 }
}

Данный флаг гарантирует активацию других параметров, необходимых для более эффективной и быстрой работы поиска зависимостей. Активация флага composite переопределяет часть стандартного поведения. Например, если параметр rootDir не установлен явно, то он будет ссылаться на директорию, содержащую конфигурационный файл tsconfig.json, хотя обычно ссылается на директорию из которой был запущен процесс компиляции.

При разработке с использованием загружаемых через npm сторонних библиотек, выступающих в качестве источника информации о типах, применяются поставляемые в комплекте файлы декларации .d.ts. Разработчики могут быть в них уверены, так как после загрузки исходный код библиотек не изменяется. Когда в качестве зависимости выступает ссылка на проект, то изменение исходного кода является нормальной практикой. По этой причине, в качестве источников информации о типах используются сами файлы .ts или .tsx, составляющие эти проекты, поскольку только таким образом можно быть уверенным, что работа происходит с актуальной версией.

Если исходный код проектов, выступающих в качестве зависимостей, не подвержен частым изменениям, то сборку проекта можно ускорить за счет установки значения false опции компилятора disableSourceOfProjectReferenceRedirect, что уведомит компилятор об исключении из наблюдения файлов проекта и указания в качестве источников типов файлов деклараций .d.ts. Данная опция указывается в конфигурационном файле проекта, выступающего в качестве зависимости. При этом нужно не забыть активировать саму генерацию деклараций, установив флаг declaration в true.

ts
{
 "compilerOptions": {
   "disableSourceOfProjectReferenceRedirect": true,
   "declaration": true,
   "composite": true,

   "outDir": "path/to/outdir/"
 }

Это ускорит процесс сборки и разработки, но потребует дополнительных усилий от разработчиков на пре-компиляцию необходимую для получения .d.ts. Кроме того процесс сборки потребуется повторять при каждом изменении исходного кода проекта. Поэтому необходимость в опции disableSourceOfProjectReferenceRedirect кажется разумной лишь на очень больших проектах.

Разработчикам плагинов для сборщиков проектов будет не лишним узнать, что TypeScript реализует возможность позволяющую сторонним инструментам взаимодействовать с данным механизмом при помощи специального api.

Механизм ссылок на проекты неоценим на крупных проектах, а для случаев работы над небольшими проектами, существует флаг компилятора --build. Команда компилятора --build ожидает перечисление конфигурационных файлов и может быть использована совместно с такими специфичными для данной команды флагами, как --verbose, --dry, --clean и --force.

Флаг --verbose выводит подробную информацию о сборке и может сочетаться с любыми другими флагами. Флаг --dry выполняет сборку, не порождая выходные файлы. Флаг --clean удаляет выходные файлы, соответствующие заданным входным. --force принудительно выполняет не инкрементальную сборку. Кроме того, команда --build может сочетаться с такой командой как --watch, которая из всех специфичных для --build флагов сочетается только с флагом --verbose.

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