Использование API dotless

Дата публикации: 2015-11-08

Сегодня мы рассмотрим пример использования API библиотеки dotless. Эта библиотека позволяет легко интегрировать компилятор Less в программы, выполняемые платформой CLI. Она поддерживает разнообразные сценарии использования: выполнение компиляции во время сборки проекта, интеграция с ASP.NET с помощью специального обработчика, прямая работа через API. К сожалению, именно последний вариант не очень хорошо рассмотрен в документации. Этот недостаток я и постараюсь восполнить данным постом.

В моём случае была необходима интеграция dotless с экосистемой OWIN, и я предпочёл не связываться в обработчиками ASP.NET, чтобы избежать лишней зависимости от System.Web (поскольку в будущем она, насколько я понимаю, может помешать запуску проекта под ASP.NET 5).

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

Проблема, с которой я столкнулся, заключалась в следующем: при обычном, описанном в документации, способе взаимодействия с парсером (то есть при вызове Less.Parse) движок не может правильно обработать правило @import. В обычной ситуации, если @import ссылается на другие Less-файлы, расположенные в файловой системе, компилятор Less считывает эти файлы, обрабатывает их и включает результат на место импорта. При обработке содержимого с использованием метода Less.Parse этого не происходит — правила @import в таком виде и попадают в итоговый CSS-файл, что приемлемо далеко не всегда.

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

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

Для этого нужно получить экземпляр ILessEngine и выставить ему свойство CurrentDirectory. Для получения этого сервиса следует использовать DI-механизм, использованный в коде dotless. Делается это следующим образом (привожу пример на F#, но если вам потребуется — его легко можно портировать на C#, просто напишите мне в комментариях):

open dotless.Core
open dotless.Core.configuration

let path = "C:\\Temp\\less" // это путь к файлам с Less

let config = DotlessConfiguration()
let locator = ContainerFactory().GetContainer(config)
let engine = (locator.GetService (typeof<ILessEngine>)) :?> ILessEngine
engine.CurrentDirectory <- path

let fileName = Path.Combine (path, "main.less") // основной компилируемый файл
let content = File.ReadAllText fileName
let result = engine.TransformToCss (content, fileName)

После выполнения этого кода в переменную result будет помещён текст CSS-файла, полученный из корневого Less, с правильно обработанными импортами. Как видно, не так уж это оказалось и сложно. При этом мы используем только публично доступный API dotless без использования reflection или каких-то дополнительных трюков.

Продолжение: Использование dotless из C# и особенности компиляции под CLI.