Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allows root (non-value) functions in Less #2785

Merged
merged 9 commits into from
Mar 15, 2016
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