From 29fc39db389a814176c044b1eb8ee785821654fe Mon Sep 17 00:00:00 2001 From: Matthew Dean Date: Fri, 25 Mar 2016 17:19:36 -0700 Subject: [PATCH] Allow custom @-rules to execute custom functions --- lib/less/functions/function-caller.js | 4 ++-- lib/less/tree/directive.js | 24 ++++++++++++++++++-- test/css/plugin.css | 4 ++++ test/less/plugin.less | 8 +++++++ test/less/plugin/plugin-custom-directives.js | 9 ++++++++ 5 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 test/less/plugin/plugin-custom-directives.js diff --git a/lib/less/functions/function-caller.js b/lib/less/functions/function-caller.js index d5358b3f5..3c7117e5a 100644 --- a/lib/less/functions/function-caller.js +++ b/lib/less/functions/function-caller.js @@ -17,13 +17,13 @@ functionCaller.prototype.call = function(args) { // https://github.com/less/less.js/issues/2477 if (Array.isArray(args)) { args = args.filter(function (item) { - if (item.type === "Comment") { + if (item && item.type && item.type === "Comment") { return false; } return true; }) .map(function(item) { - if (item.type === "Expression") { + if (item && item.type && item.type === "Expression") { var subNodes = item.value.filter(function (item) { if (item.type === "Comment") { return false; diff --git a/lib/less/tree/directive.js b/lib/less/tree/directive.js index 70be82c73..3161e996c 100644 --- a/lib/less/tree/directive.js +++ b/lib/less/tree/directive.js @@ -1,6 +1,7 @@ var Node = require("./node"), Selector = require("./selector"), - Ruleset = require("./ruleset"); + Ruleset = require("./ruleset"), + FunctionCaller = require("../functions/function-caller"); var Directive = function (name, value, rules, index, currentFileInfo, debugInfo, isRooted, visibilityInfo) { var i; @@ -57,7 +58,8 @@ Directive.prototype.genCSS = function (context, output) { } }; Directive.prototype.eval = function (context) { - var mediaPathBackup, mediaBlocksBackup, value = this.value, rules = this.rules; + var mediaPathBackup, mediaBlocksBackup, value = this.value, rules = this.rules, + result, funcCaller = new FunctionCaller(this.name, context, this.index, this.currentFileInfo); //media stored inside other directive should not bubble over it //backpup media bubbling information @@ -75,6 +77,24 @@ Directive.prototype.eval = function (context) { rules = [rules[0].eval(context)]; rules[0].root = true; } + + if (funcCaller.isValid()) { + try { + result = funcCaller.call([this.name, value ? value.value : value, rules ? rules[0] : rules]); + } catch (e) { + throw { type: e.type || "Runtime", + message: "error evaluating custom directive `" + this.name + "`" + + (e.message ? ': ' + e.message : ''), + index: this.index, filename: this.currentFileInfo.filename }; + } + + if (result != null) { + result.index = this.index; + result.currentFileInfo = this.currentFileInfo; + return result; + } + } + //restore media bubbling information context.mediaPath = mediaPathBackup; context.mediaBlocks = mediaBlocksBackup; diff --git a/test/css/plugin.css b/test/css/plugin.css index 0b6910754..440741d5b 100644 --- a/test/css/plugin.css +++ b/test/css/plugin.css @@ -47,3 +47,7 @@ prop: value; } @arbitrary value after (); +@new-directive success; +test { + foo: bar; +} diff --git a/test/less/plugin.less b/test/less/plugin.less index 66775ea9f..95ae0f9e4 100644 --- a/test/less/plugin.less +++ b/test/less/plugin.less @@ -90,4 +90,12 @@ test-directive("@charset"; '"utf-8"'); test-directive("@arbitrary"; "value after ()"); +@plugin "./plugin/plugin-custom-directives"; + +@make-directive new-directive; +@eval-rules test { + foo: bar; +} + + diff --git a/test/less/plugin/plugin-custom-directives.js b/test/less/plugin/plugin-custom-directives.js new file mode 100644 index 000000000..01f9b3754 --- /dev/null +++ b/test/less/plugin/plugin-custom-directives.js @@ -0,0 +1,9 @@ + +functions.addMultiple({ + "@make-directive" : function(name, value, rules) { + return new tree.Directive('@' + value, new tree.Anonymous('success')); + }, + "@eval-rules" : function(name, value, rules) { + return new tree.Ruleset([ new tree.Selector([new tree.Element("", value)]) ], rules.rules); + } +});