lib/less/js/lib/less/tree/ruleset.js in less-2.3.3 vs lib/less/js/lib/less/tree/ruleset.js in less-2.4.0
- old
+ new
@@ -5,26 +5,38 @@
this.rules = rules;
this._lookups = {};
this.strictImports = strictImports;
};
tree.Ruleset.prototype = {
+ type: "Ruleset",
+ accept: function (visitor) {
+ this.selectors = visitor.visit(this.selectors);
+ this.rules = visitor.visit(this.rules);
+ },
eval: function (env) {
var selectors = this.selectors && this.selectors.map(function (s) { return s.eval(env) });
var ruleset = new(tree.Ruleset)(selectors, this.rules.slice(0), this.strictImports);
var rules;
ruleset.originalRuleset = this;
ruleset.root = this.root;
+ ruleset.firstRoot = this.firstRoot;
ruleset.allowImports = this.allowImports;
if(this.debugInfo) {
ruleset.debugInfo = this.debugInfo;
}
// push the current ruleset to the frames stack
env.frames.unshift(ruleset);
+ // currrent selectors
+ if (!env.selectors) {
+ env.selectors = [];
+ }
+ env.selectors.unshift(this.selectors);
+
// Evaluate imports
if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) {
ruleset.evalImports(env);
}
@@ -39,17 +51,25 @@
var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0;
// Evaluate mixin calls.
for (var i = 0; i < ruleset.rules.length; i++) {
if (ruleset.rules[i] instanceof tree.mixin.Call) {
- rules = ruleset.rules[i].eval(env);
+ rules = ruleset.rules[i].eval(env).filter(function(r) {
+ if ((r instanceof tree.Rule) && r.variable) {
+ // do not pollute the scope if the variable is
+ // already there. consider returning false here
+ // but we need a way to "return" variable from mixins
+ return !(ruleset.variable(r.name));
+ }
+ return true;
+ });
ruleset.rules.splice.apply(ruleset.rules, [i, 1].concat(rules));
i += rules.length-1;
ruleset.resetCache();
}
}
-
+
// Evaluate everything else
for (var i = 0, rule; i < ruleset.rules.length; i++) {
rule = ruleset.rules[i];
if (! (rule instanceof tree.mixin.Definition)) {
@@ -57,10 +77,11 @@
}
}
// Pop the stack
env.frames.shift();
+ env.selectors.shift();
if (env.mediaBlocks) {
for(var i = mediaBlockCount; i < env.mediaBlocks.length; i++) {
env.mediaBlocks[i].bubbleSelectors(selectors);
}
@@ -113,16 +134,13 @@
},
variable: function (name) {
return this.variables()[name];
},
rulesets: function () {
- if (this._rulesets) { return this._rulesets }
- else {
- return this._rulesets = this.rules.filter(function (r) {
- return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition);
- });
- }
+ return this.rules.filter(function (r) {
+ return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition);
+ });
},
find: function (selector, self) {
self = self || this;
var rules = [], rule, match,
key = selector.toCSS();
@@ -149,32 +167,27 @@
//
// Entry point for code generation
//
// `context` holds an array of arrays.
//
- toCSS: function (context, env) {
+ toCSS: function (env) {
var css = [], // The CSS output
rules = [], // node.Rule instances
_rules = [], //
rulesets = [], // node.Ruleset instances
- paths = [], // Current selectors
selector, // The fully rendered selector
debugInfo, // Line number debugging
rule;
- if (! this.root) {
- this.joinSelectors(paths, context, this.selectors);
- }
-
// Compile rules and rulesets
for (var i = 0; i < this.rules.length; i++) {
rule = this.rules[i];
if (rule.rules || (rule instanceof tree.Media)) {
- rulesets.push(rule.toCSS(paths, env));
+ rulesets.push(rule.toCSS(env));
} else if (rule instanceof tree.Directive) {
- var cssValue = rule.toCSS(paths, env);
+ var cssValue = rule.toCSS(env);
// Output only the first @charset definition as such - convert the others
// to comments in case debug is enabled
if (rule.name === "@charset") {
// Only output the debug info together with subsequent @charset definitions
// a comment (or @media statement) before the actual @charset directive would
@@ -197,36 +210,48 @@
rules.push(rule.toCSS(env));
}
}
} else {
if (rule.toCSS && !rule.variable) {
+ if (this.firstRoot && rule instanceof tree.Rule) {
+ throw { message: "properties must be inside selector blocks, they cannot be in the root.",
+ index: rule.index, filename: rule.currentFileInfo ? rule.currentFileInfo.filename : null};
+ }
rules.push(rule.toCSS(env));
} else if (rule.value && !rule.variable) {
rules.push(rule.value.toString());
}
}
}
+ // Remove last semicolon
+ if (env.compress && rules.length) {
+ rule = rules[rules.length - 1];
+ if (rule.charAt(rule.length - 1) === ';') {
+ rules[rules.length - 1] = rule.substring(0, rule.length - 1);
+ }
+ }
+
rulesets = rulesets.join('');
// If this is the root node, we don't render
// a selector, or {}.
// Otherwise, only output if this ruleset has rules.
if (this.root) {
css.push(rules.join(env.compress ? '' : '\n'));
} else {
if (rules.length > 0) {
debugInfo = tree.debugInfo(env, this);
- selector = paths.map(function (p) {
+ selector = this.paths.map(function (p) {
return p.map(function (s) {
return s.toCSS(env);
}).join('').trim();
}).join(env.compress ? ',' : ',\n');
// Remove duplicates
for (var i = rules.length - 1; i >= 0; i--) {
- if (_rules.indexOf(rules[i]) === -1) {
+ if (rules[i].slice(0, 2) === "/*" || _rules.indexOf(rules[i]) === -1) {
_rules.unshift(rules[i]);
}
}
rules = _rules;
@@ -337,15 +362,15 @@
//construct the joined selector - if & is the first thing this will be empty,
// if not newJoinedSelector will be the last set of elements in the selector
if (sel.length > 0) {
newSelectorPath = sel.slice(0);
lastSelector = newSelectorPath.pop();
- newJoinedSelector = new(tree.Selector)(lastSelector.elements.slice(0));
+ newJoinedSelector = new(tree.Selector)(lastSelector.elements.slice(0), selector.extendList);
newJoinedSelectorEmpty = false;
}
else {
- newJoinedSelector = new(tree.Selector)([]);
+ newJoinedSelector = new(tree.Selector)([], selector.extendList);
}
//put together the parent selectors after the join
if (parentSel.length > 1) {
afterParentJoin = afterParentJoin.concat(parentSel.slice(1));
@@ -384,16 +409,18 @@
if (currentElements.length > 0) {
this.mergeElementsOnToSelectors(currentElements, newSelectors);
}
for(i = 0; i < newSelectors.length; i++) {
- paths.push(newSelectors[i]);
+ if (newSelectors[i].length > 0) {
+ paths.push(newSelectors[i]);
+ }
}
},
mergeElementsOnToSelectors: function(elements, selectors) {
- var i, sel;
+ var i, sel, extendList;
if (selectors.length == 0) {
selectors.push([ new(tree.Selector)(elements) ]);
return;
}
@@ -401,10 +428,10 @@
for(i = 0; i < selectors.length; i++) {
sel = selectors[i];
// if the previous thing in sel is a parent this needs to join on to it
if (sel.length > 0) {
- sel[sel.length - 1] = new(tree.Selector)(sel[sel.length - 1].elements.concat(elements));
+ sel[sel.length - 1] = new(tree.Selector)(sel[sel.length - 1].elements.concat(elements), sel[sel.length - 1].extendList);
}
else {
sel.push(new(tree.Selector)(elements));
}
}