Skip to content

Commit

Permalink
Merge pull request #2785 from less/root-functions
Browse files Browse the repository at this point in the history
Allows root (non-value) functions in Less
  • Loading branch information
matthew-dean committed Mar 15, 2016
2 parents d094d7e + 87d59fa commit 2f07cd7
Show file tree
Hide file tree
Showing 58 changed files with 265 additions and 25 deletions.
5 changes: 5 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ node_js:
- "4.0.0"
- "0.12"
- "0.10"
before_install:
- mkdir travis-phantomjs
- wget https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2 -O $PWD/travis-phantomjs/phantomjs-2.1.1-linux-x86_64.tar.bz2
- tar -xvf $PWD/travis-phantomjs/phantomjs-2.1.1-linux-x86_64.tar.bz2 -C $PWD/travis-phantomjs
- export PATH=$PWD/travis-phantomjs/phantomjs-2.1.1-linux-x86_64/bin:$PATH
install:
- npm install -g grunt-cli
- npm install
Expand Down
46 changes: 39 additions & 7 deletions lib/less/parser/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ var Parser = function Parser(context, imports, fileInfo) {
}

node = mixin.definition() || this.rule() || this.ruleset() ||
mixin.call() || this.rulesetCall() || this.directive();
mixin.call() || this.rulesetCall() || this.entities.call() || this.directive();
if (node) {
root.push(node);
} else {
Expand Down Expand Up @@ -370,19 +370,51 @@ var Parser = function Parser(context, imports, fileInfo) {
return new(tree.Call)(name, args, index, fileInfo);
},
arguments: function () {
var args = [], arg;
var argsSemiColon = [], argsComma = [],
expressions = [],
isSemiColonSeparated, value, arg;

parserInput.save();

while (true) {
arg = this.assignment() || parsers.expression();

arg = parsers.detachedRuleset() || this.assignment() || parsers.expression();

if (!arg) {
break;
}
args.push(arg);
if (! parserInput.$char(',')) {
break;

value = arg;

if (arg.value && arg.value.length == 1) {
value = arg.value[0];
}

if (value) {
expressions.push(value);
}

argsComma.push(value);

if (parserInput.$char(',')) {
continue;
}

if (parserInput.$char(';') || isSemiColonSeparated) {

isSemiColonSeparated = true;

if (expressions.length > 1) {
value = new(tree.Value)(expressions);
}
argsSemiColon.push(value);

expressions = [];
}
}
return args;

parserInput.forget();
return isSemiColonSeparated ? argsSemiColon : argsComma;
},
literal: function () {
return this.dimension() ||
Expand Down
2 changes: 1 addition & 1 deletion lib/less/tree/anonymous.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ var Anonymous = function (value, index, currentFileInfo, mapLines, rulesetLike,
this.mapLines = mapLines;
this.currentFileInfo = currentFileInfo;
this.rulesetLike = (typeof rulesetLike === 'undefined') ? false : rulesetLike;

this.allowRoot = true;
this.copyVisibilityInfo(visibilityInfo);
};
Anonymous.prototype = new Node();
Expand Down
11 changes: 7 additions & 4 deletions lib/less/tree/call.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,21 @@ Call.prototype.eval = function (context) {
var args = this.args.map(function (a) { return a.eval(context); }),
result, funcCaller = new FunctionCaller(this.name, context, this.index, this.currentFileInfo);

if (funcCaller.isValid()) { // 1.
if (funcCaller.isValid()) {
try {
result = funcCaller.call(args);
if (result != null) {
return result;
}
} catch (e) {
throw { type: e.type || "Runtime",
message: "error evaluating function `" + 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;
}
}

return new Call(this.name, args, this.index, this.currentFileInfo);
Expand Down
1 change: 1 addition & 0 deletions lib/less/tree/comment.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ var Comment = function (value, isLineComment, index, currentFileInfo) {
this.value = value;
this.isLineComment = isLineComment;
this.currentFileInfo = currentFileInfo;
this.allowRoot = true;
};
Comment.prototype = new Node();
Comment.prototype.type = "Comment";
Expand Down
1 change: 1 addition & 0 deletions lib/less/tree/directive.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ var Directive = function (name, value, rules, index, currentFileInfo, debugInfo,
this.debugInfo = debugInfo;
this.isRooted = isRooted || false;
this.copyVisibilityInfo(visibilityInfo);
this.allowRoot = true;
};

Directive.prototype = new Node();
Expand Down
1 change: 1 addition & 0 deletions lib/less/tree/extend.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ var Extend = function Extend(selector, option, index, currentFileInfo, visibilit
this.parent_ids = [this.object_id];
this.currentFileInfo = currentFileInfo || {};
this.copyVisibilityInfo(visibilityInfo);
this.allowRoot = true;

switch(option) {
case "all":
Expand Down
1 change: 1 addition & 0 deletions lib/less/tree/import.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ var Import = function (path, features, options, index, currentFileInfo, visibili
this.path = path;
this.features = features;
this.currentFileInfo = currentFileInfo;
this.allowRoot = true;

if (this.options.less !== undefined || this.options.inline) {
this.css = !this.options.less || this.options.inline;
Expand Down
1 change: 1 addition & 0 deletions lib/less/tree/media.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ var Media = function (value, features, index, currentFileInfo, visibilityInfo) {
this.rules = [new Ruleset(selectors, value)];
this.rules[0].allowImports = true;
this.copyVisibilityInfo(visibilityInfo);
this.allowRoot = true;
};
Media.prototype = new Directive();
Media.prototype.type = "Media";
Expand Down
1 change: 1 addition & 0 deletions lib/less/tree/mixin-call.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ var MixinCall = function (elements, args, index, currentFileInfo, important) {
this.index = index;
this.currentFileInfo = currentFileInfo;
this.important = important;
this.allowRoot = true;
};
MixinCall.prototype = new Node();
MixinCall.prototype.type = "MixinCall";
Expand Down
1 change: 1 addition & 0 deletions lib/less/tree/mixin-definition.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ var Definition = function (name, params, rules, condition, variadic, frames, vis
this.optionalParameters = optionalParameters;
this.frames = frames;
this.copyVisibilityInfo(visibilityInfo);
this.allowRoot = true;
};
Definition.prototype = new Ruleset();
Definition.prototype.type = "MixinDefinition";
Expand Down
1 change: 1 addition & 0 deletions lib/less/tree/rule.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ var Rule = function (name, value, important, merge, index, currentFileInfo, inli
this.inline = inline || false;
this.variable = (variable !== undefined) ? variable
: (name.charAt && (name.charAt(0) === '@'));
this.allowRoot = true;
};

function evalName(context, name) {
Expand Down
1 change: 1 addition & 0 deletions lib/less/tree/ruleset-call.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ var Node = require("./node"),

var RulesetCall = function (variable) {
this.variable = variable;
this.allowRoot = true;
};
RulesetCall.prototype = new Node();
RulesetCall.prototype.type = "RulesetCall";
Expand Down
1 change: 1 addition & 0 deletions lib/less/tree/ruleset.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ var Ruleset = function (selectors, rules, strictImports, visibilityInfo) {
this._lookups = {};
this.strictImports = strictImports;
this.copyVisibilityInfo(visibilityInfo);
this.allowRoot = true;
};
Ruleset.prototype = new Node();
Ruleset.prototype.type = "Ruleset";
Expand Down
29 changes: 20 additions & 9 deletions lib/less/visitors/to-css-visitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,23 +199,34 @@ ToCSSVisitor.prototype = {
return directiveNode;
},

checkPropertiesInRoot: function(rules) {
var ruleNode;
checkValidNodes: function(rules, isRoot) {
if (!rules) {
return;
}

for (var i = 0; i < rules.length; i++) {
ruleNode = rules[i];
if (ruleNode instanceof tree.Rule && !ruleNode.variable) {
throw { message: "properties must be inside selector blocks, they cannot be in the root.",
index: ruleNode.index, filename: ruleNode.currentFileInfo ? ruleNode.currentFileInfo.filename : null};
var ruleNode = rules[i];
if (isRoot && ruleNode instanceof tree.Rule && !ruleNode.variable) {
throw { message: "Properties must be inside selector blocks. They cannot be in the root",
index: ruleNode.index, filename: ruleNode.currentFileInfo && ruleNode.currentFileInfo.filename};
}
if (ruleNode instanceof tree.Call) {
throw { message: "Function '" + ruleNode.name + "' is undefined",
index: ruleNode.index, filename: ruleNode.currentFileInfo && ruleNode.currentFileInfo.filename};
}
if (!ruleNode.allowRoot) {
throw { message: ruleNode.type + " node returned by a function is not valid here",
index: ruleNode.index, filename: ruleNode.currentFileInfo && ruleNode.currentFileInfo.filename};
}
}
},

visitRuleset: function (rulesetNode, visitArgs) {
//at this point rulesets are nested into each other
var rule, rulesets = [];
if (rulesetNode.firstRoot) {
this.checkPropertiesInRoot(rulesetNode.rules);
}

this.checkValidNodes(rulesetNode.rules, rulesetNode.firstRoot);

if (! rulesetNode.root) {
//remove invisible paths
this._compileRulesetPaths(rulesetNode);
Expand Down
5 changes: 5 additions & 0 deletions test/css/plugin.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
@charset "utf-8";
.other {
trans: transitive;
}
Expand Down Expand Up @@ -42,3 +43,7 @@
result: local;
}
}
.root {
prop: value;
}
@arbitrary value after ();
2 changes: 1 addition & 1 deletion test/less/errors/detached-ruleset-3.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
SyntaxError: properties must be inside selector blocks, they cannot be in the root. in {path}detached-ruleset-3.less on line 2, column 3:
SyntaxError: Properties must be inside selector blocks. They cannot be in the root in {path}detached-ruleset-3.less on line 2, column 3:
1 @a: {
2 b: 1;
3 };
2 changes: 2 additions & 0 deletions test/less/errors/functions-1.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@plugin "../plugin/plugin-tree-nodes";
test-undefined();
3 changes: 3 additions & 0 deletions test/less/errors/functions-1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SyntaxError: Function 'test-undefined' is undefined in {path}functions-1.less on line 2, column 1:
1 @plugin "../plugin/plugin-tree-nodes";
2 test-undefined();
2 changes: 2 additions & 0 deletions test/less/errors/functions-10-keyword.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@plugin "../plugin/plugin-tree-nodes";
test-keyword();
3 changes: 3 additions & 0 deletions test/less/errors/functions-10-keyword.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SyntaxError: Keyword node returned by a function is not valid here in {path}functions-10-keyword.less on line 2, column 1:
1 @plugin "../plugin/plugin-tree-nodes";
2 test-keyword();
2 changes: 2 additions & 0 deletions test/less/errors/functions-11-operation.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@plugin "../plugin/plugin-tree-nodes";
test-operation();
3 changes: 3 additions & 0 deletions test/less/errors/functions-11-operation.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SyntaxError: Operation node returned by a function is not valid here in {path}functions-11-operation.less on line 2, column 1:
1 @plugin "../plugin/plugin-tree-nodes";
2 test-operation();
2 changes: 2 additions & 0 deletions test/less/errors/functions-12-quoted.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@plugin "../plugin/plugin-tree-nodes";
test-quoted();
3 changes: 3 additions & 0 deletions test/less/errors/functions-12-quoted.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SyntaxError: Quoted node returned by a function is not valid here in {path}functions-12-quoted.less on line 2, column 1:
1 @plugin "../plugin/plugin-tree-nodes";
2 test-quoted();
2 changes: 2 additions & 0 deletions test/less/errors/functions-13-selector.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@plugin "../plugin/plugin-tree-nodes";
test-selector();
3 changes: 3 additions & 0 deletions test/less/errors/functions-13-selector.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SyntaxError: Selector node returned by a function is not valid here in {path}functions-13-selector.less on line 2, column 1:
1 @plugin "../plugin/plugin-tree-nodes";
2 test-selector();
2 changes: 2 additions & 0 deletions test/less/errors/functions-14-url.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@plugin "../plugin/plugin-tree-nodes";
test-url();
3 changes: 3 additions & 0 deletions test/less/errors/functions-14-url.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SyntaxError: Url node returned by a function is not valid here in {path}functions-14-url.less on line 2, column 1:
1 @plugin "../plugin/plugin-tree-nodes";
2 test-url();
2 changes: 2 additions & 0 deletions test/less/errors/functions-15-value.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@plugin "../plugin/plugin-tree-nodes";
test-value();
3 changes: 3 additions & 0 deletions test/less/errors/functions-15-value.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SyntaxError: Value node returned by a function is not valid here in {path}functions-15-value.less on line 2, column 1:
1 @plugin "../plugin/plugin-tree-nodes";
2 test-value();
2 changes: 2 additions & 0 deletions test/less/errors/functions-2-alpha.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@plugin "../plugin/plugin-tree-nodes";
test-alpha();
3 changes: 3 additions & 0 deletions test/less/errors/functions-2-alpha.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SyntaxError: Alpha node returned by a function is not valid here in {path}functions-2-alpha.less on line 2, column 1:
1 @plugin "../plugin/plugin-tree-nodes";
2 test-alpha();
2 changes: 2 additions & 0 deletions test/less/errors/functions-3-assignment.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@plugin "../plugin/plugin-tree-nodes";
test-assignment();
3 changes: 3 additions & 0 deletions test/less/errors/functions-3-assignment.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SyntaxError: Assignment node returned by a function is not valid here in {path}functions-3-assignment.less on line 2, column 1:
1 @plugin "../plugin/plugin-tree-nodes";
2 test-assignment();
2 changes: 2 additions & 0 deletions test/less/errors/functions-4-call.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@plugin "../plugin/plugin-tree-nodes";
test-call();
3 changes: 3 additions & 0 deletions test/less/errors/functions-4-call.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SyntaxError: Function 'foo' is undefined in {path}functions-4-call.less on line 2, column 1:
1 @plugin "../plugin/plugin-tree-nodes";
2 test-call();
1 change: 1 addition & 0 deletions test/less/errors/functions-5-color-2.less
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rgba(0,0,0,0);
2 changes: 2 additions & 0 deletions test/less/errors/functions-5-color-2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
SyntaxError: Color node returned by a function is not valid here in {path}functions-5-color-2.less on line 1, column 1:
1 rgba(0,0,0,0);
2 changes: 2 additions & 0 deletions test/less/errors/functions-5-color.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@plugin "../plugin/plugin-tree-nodes";
test-color();
3 changes: 3 additions & 0 deletions test/less/errors/functions-5-color.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SyntaxError: Color node returned by a function is not valid here in {path}functions-5-color.less on line 2, column 1:
1 @plugin "../plugin/plugin-tree-nodes";
2 test-color();
2 changes: 2 additions & 0 deletions test/less/errors/functions-6-condition.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@plugin "../plugin/plugin-tree-nodes";
test-condition();
3 changes: 3 additions & 0 deletions test/less/errors/functions-6-condition.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SyntaxError: Condition node returned by a function is not valid here in {path}functions-6-condition.less on line 2, column 1:
1 @plugin "../plugin/plugin-tree-nodes";
2 test-condition();
2 changes: 2 additions & 0 deletions test/less/errors/functions-7-dimension.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@plugin "../plugin/plugin-tree-nodes";
test-dimension();
3 changes: 3 additions & 0 deletions test/less/errors/functions-7-dimension.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SyntaxError: Dimension node returned by a function is not valid here in {path}functions-7-dimension.less on line 2, column 1:
1 @plugin "../plugin/plugin-tree-nodes";
2 test-dimension();
2 changes: 2 additions & 0 deletions test/less/errors/functions-8-element.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@plugin "../plugin/plugin-tree-nodes";
test-element();
3 changes: 3 additions & 0 deletions test/less/errors/functions-8-element.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SyntaxError: Element node returned by a function is not valid here in {path}functions-8-element.less on line 2, column 1:
1 @plugin "../plugin/plugin-tree-nodes";
2 test-element();
2 changes: 2 additions & 0 deletions test/less/errors/functions-9-expression.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@plugin "../plugin/plugin-tree-nodes";
test-expression();
3 changes: 3 additions & 0 deletions test/less/errors/functions-9-expression.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SyntaxError: Expression node returned by a function is not valid here in {path}functions-9-expression.less on line 2, column 1:
1 @plugin "../plugin/plugin-tree-nodes";
2 test-expression();
2 changes: 1 addition & 1 deletion test/less/errors/property-in-root.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
SyntaxError: properties must be inside selector blocks, they cannot be in the root. in {path}property-in-root.less on line 2, column 3:
SyntaxError: Properties must be inside selector blocks. They cannot be in the root in {path}property-in-root.less on line 2, column 3:
1 .a() {
2 prop:1;
3 }
2 changes: 1 addition & 1 deletion test/less/errors/property-in-root2.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
SyntaxError: properties must be inside selector blocks, they cannot be in the root. in {path}property-in-root.less on line 2, column 3:
SyntaxError: Properties must be inside selector blocks. They cannot be in the root in {path}property-in-root.less on line 2, column 3:
1 .a() {
2 prop:1;
3 }
2 changes: 1 addition & 1 deletion test/less/errors/property-in-root3.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
SyntaxError: properties must be inside selector blocks, they cannot be in the root. in {path}property-in-root3.less on line 1, column 1:
SyntaxError: Properties must be inside selector blocks. They cannot be in the root in {path}property-in-root3.less on line 1, column 1:
1 prop:1;
2 .a {
Loading

0 comments on commit 2f07cd7

Please sign in to comment.