Исполнение JavaScript кода с помощью LESS CSS препроцессора

На днях, когда я модернизировал LiveReload приложение, некоторые из миксинов в LESS начали сбоить. В процессе отладки я обнаружил, что некоторые из них исполняли JavaScript код непосредственно в LESS CSS. Я понятия не имел, что это вообще возможно и, как и со всем остальным в JavaScript, мне пришлось поэкспериментировать. У меня нет конкретных идей, для чего это можно использовать, но вот что удалось обнаружить. Пожалуйста, заметьте, что это все лишь гипотеза, так как нет никакой документации (по крайней мере мне её обнаружить не удалось) по использованию JavaScript в LESS CSS.

JavaScript может быть только частью операции присвоения. Под этим я имею в виду, что нельзя просто выполнить JavaScript в LESS CSS. Вместо этого, вы должны выполнить JavaScript, а затем присвоить результат LESS переменной или CSS свойству. Например:

@foo: `javascript` ;

Для обозначения JavaScript кода, вам необходимо обернуть его апострофами. После этого, LESS будет интерпретировать выражение и возвращать результат в виде строки. Он делает это, «как есть», оставляя все кавычки на месте. Если нужно удалить окружающие кавычки, то можно добавить тильду:

@foo: ~`"обрамляющие кавычки будут удалены"`

Вот к чему я пришел: я определил объект с «API», который содержит мои функции. Затем я вставил этот объект API в глобальную контейнер, что позволяет получить к нему доступ из любого другого блока JavaScript:

// Используйте символ апострофа, для того что бы использовать JavaScript непосредственно 
// в LESS CSS. Мы используем функцию так как LESS (это мое предположение) вызывает
// .toString() и хранит полученное значение. Похоже, свой исходный код при вызове 
// .toString() возвращают только функции. Это их особенность позволяет нам реализовать 
// повторное  использование кода в других блоках.
@api: `function(){
    
    // Определяем API, которое хотим использовать в других JavaScript блоках
    var api = {
        
        // Это я просто тестирую всякие JavaScript-штуки.
        getContent: function() {
 
            return( "Эт' ваш контент!" );
 
        },
        
        
        // Когда выполняется JavaScript выражение, можно выполнять только одно выражение
        // (то есть, никаких точек с запятой). Если вы, конечно, не используете
        // функцию. Суть запуска run() в том, что это позволит войти в её контекст и получить
        // доступ к API. API передается как единственный аргумент колбека.
        // --
        // ВНИМАНИЕ: колбек ДОЛЖЕН ВОЗВРАЩАТЬ ЗНАЧЕНИЕ, что бы LESS мог его получить.
        run: function( callback ) {
 
            return( callback( api ) );

        }

    };
 

    // Return the public API. Since this JavaScript expression is return the parent
    // Function, it will have to invoked in a different JavaScript context to actually
    // get access to the API.
    
    // Возвращает API. Так как это JavaScript выражение возвращает родительскую функцию,
    // ему придется вызывать другой контекст, для получения доступа к API.
    return( api );

}` ;

// Я присваиваю API имя "api" в глобальном пространстве имен. Это можно было бы сделать
// и в предыдущем блоке кода, но мне нравится идея разделить код на две части, каждая
// из которых отвечает за что то свое.
// --
// ВНИМАНИЕ: я использую самовызывающуюся функцию что бы убедится, что "this" указывает
// на глобальный контекст, а не на контекст выполнения (на случай, если он будет отличатся).
@apiGlobalInjector: `(function() {
    
    // Вставляем API и храним его в глобальном объекте
    this.api = (@{api})();
 
    // JavaScript ДОЛЖЕН ВОЗВРАЩАТЬ что то, что бы LESS CSS мог присвоить переменной какое то значение.
    return( "Помещено в глобальный контекст" );

})()` ;

 
// ---------------------------------------------------------- //
// ---------------------------------------------------------- //


h1:before {
    content: `api.getContent()` ;
}

h2:before {
    content: `api.run(function( api ) {
    return( api.getContent().toUpperCase() );
    })` ;
}

Когда исполняется блок JavaScript кода, LESS CSS не позволяет использовать точку с запятой, если вы не находитесь внутри функции. Как следствие я создал метод .run(), который, по сути, просто способ для создания функции, которая передает внутрь себя API. Таким образом можно выполнять множество выражений и возвращать полученное значение.

В любом случае, когда вы компилируете вышеуказанный файл, LESS CSS сгенерирует следующее:

h1:before {
    content: "Эт' ваш контент!";
}
h2:before {
    content: "ЭТ' ВАШ КОНТЕНТ!";
}

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