Использование API dotless
Сегодня мы рассмотрим пример использования 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.