mirror of
https://github.com/less/less.js.git
synced 2026-05-01 03:00:22 -04:00
Start abstracting re-organising logic into a visitor before css output. Will allow nodes to just be 'read' and debugInfo written into a sourcemap. part 1.
This commit is contained in:
2
Makefile
2
Makefile
@@ -44,6 +44,7 @@ less:
|
||||
${SRC}/visitor.js\
|
||||
${SRC}/import-visitor.js\
|
||||
${SRC}/join-selector-visitor.js\
|
||||
${SRC}/to-css-visitor.js\
|
||||
${SRC}/extend-visitor.js\
|
||||
${SRC}/browser.js\
|
||||
build/amd.js >> ${DIST}
|
||||
@@ -68,6 +69,7 @@ rhino:
|
||||
${SRC}/visitor.js\
|
||||
${SRC}/import-visitor.js\
|
||||
${SRC}/join-selector-visitor.js\
|
||||
${SRC}/to-css-visitor.js\
|
||||
${SRC}/extend-visitor.js\
|
||||
${SRC}/functions.js\
|
||||
${SRC}/colors.js\
|
||||
|
||||
@@ -214,5 +214,6 @@ require('./visitor.js');
|
||||
require('./import-visitor.js');
|
||||
require('./extend-visitor.js');
|
||||
require('./join-selector-visitor.js');
|
||||
require('./to-css-visitor.js');
|
||||
|
||||
for (var k in less) { exports[k] = less[k]; }
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
},
|
||||
visitMedia: function (mediaNode, visitArgs) {
|
||||
var context = this.contexts[this.contexts.length - 1];
|
||||
mediaNode.ruleset.root = (context.length === 0 || context[0].multiMedia);
|
||||
mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -435,6 +435,9 @@ less.Parser = function Parser(env) {
|
||||
new(tree.processExtendsVisitor)()
|
||||
.run(evaldRoot);
|
||||
|
||||
new(tree.toCSSVisitor)()
|
||||
.run(evaldRoot);
|
||||
|
||||
var css = evaldRoot.toCSS({
|
||||
compress: Boolean(options.compress),
|
||||
dumpLineNumbers: env.dumpLineNumbers,
|
||||
|
||||
60
lib/less/to-css-visitor.js
Normal file
60
lib/less/to-css-visitor.js
Normal file
@@ -0,0 +1,60 @@
|
||||
(function (tree) {
|
||||
tree.toCSSVisitor = function() {
|
||||
this._visitor = new tree.visitor(this);
|
||||
};
|
||||
|
||||
tree.toCSSVisitor.prototype = {
|
||||
isReplacing: true,
|
||||
run: function (root) {
|
||||
return this._visitor.visit(root);
|
||||
},
|
||||
|
||||
visitRule: function (ruleNode, visitArgs) {
|
||||
visitArgs.visitDeeper = false;
|
||||
return ruleNode;
|
||||
},
|
||||
|
||||
visitDirective: function(directiveNode, visitArgs) {
|
||||
if (directiveNode.name === "@charset") {
|
||||
// Only output the debug info together with subsequent @charset definitions
|
||||
// a comment (or @media statement) before the actual @charset directive would
|
||||
// be considered illegal css as it has to be on the first line
|
||||
if (this.charset) {
|
||||
if (directiveNode.debugInfo) {
|
||||
var comment = new tree.Comment("/* " + directiveNode.toCSS({}).replace(/\n/g, "")+" */\n");
|
||||
comment.debugInfo = directiveNode.debugInfo;
|
||||
return this._visitor.visit(comment);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
this.charset = true;
|
||||
}
|
||||
return directiveNode;
|
||||
},
|
||||
|
||||
visitRuleset: function (rulesetNode, visitArgs) {
|
||||
var rule, rulesets = [];
|
||||
if (! rulesetNode.root) {
|
||||
// Compile rules and rulesets
|
||||
for (var i = 0; i < rulesetNode.rules.length; i++) {
|
||||
rule = rulesetNode.rules[i];
|
||||
|
||||
if (rule.rules) {
|
||||
rulesets.push(this._visitor.visit(rule));
|
||||
rulesetNode.rules.splice(i, 1);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (rulesets.length > 0 && rulesetNode.rules.length > 0) {
|
||||
rulesets.splice(0, 0, rulesetNode);
|
||||
}
|
||||
}
|
||||
if (rulesets.length === 0) {
|
||||
rulesets = rulesetNode;
|
||||
}
|
||||
return rulesets;
|
||||
}
|
||||
};
|
||||
|
||||
})(require('./tree'));
|
||||
@@ -9,7 +9,7 @@ tree.Alpha.prototype = {
|
||||
this.value = visitor.visit(this.value);
|
||||
},
|
||||
eval: function (env) {
|
||||
if (this.value.eval) { this.value = this.value.eval(env) }
|
||||
if (this.value.eval) { this.value = this.value.eval(env); }
|
||||
return this;
|
||||
},
|
||||
toCSS: function () {
|
||||
|
||||
@@ -8,9 +8,13 @@ tree.Comment = function (value, silent, index, currentFileInfo) {
|
||||
tree.Comment.prototype = {
|
||||
type: "Comment",
|
||||
toCSS: function (env) {
|
||||
var debugInfo = "";
|
||||
if (this.debugInfo) {
|
||||
debugInfo = tree.debugInfo(env, this);
|
||||
}
|
||||
var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced),
|
||||
isCompressed = env.compress && !this.value.match(/^\/\*!/);
|
||||
return (isReference || isCompressed) ? '' : this.value;
|
||||
return (isReference || isCompressed) ? '' : (debugInfo + this.value);
|
||||
},
|
||||
eval: function () { return this; },
|
||||
markReferenced: function () {
|
||||
|
||||
@@ -4,8 +4,8 @@ tree.Directive = function (name, value, index, currentFileInfo) {
|
||||
this.name = name;
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
this.ruleset = new(tree.Ruleset)([], value);
|
||||
this.ruleset.allowImports = true;
|
||||
this.rules = [new(tree.Ruleset)([], value)];
|
||||
this.rules[0].allowImports = true;
|
||||
} else {
|
||||
this.value = value;
|
||||
}
|
||||
@@ -14,7 +14,7 @@ tree.Directive = function (name, value, index, currentFileInfo) {
|
||||
tree.Directive.prototype = {
|
||||
type: "Directive",
|
||||
accept: function (visitor) {
|
||||
this.ruleset = visitor.visit(this.ruleset);
|
||||
this.rules = visitor.visit(this.rules);
|
||||
this.value = visitor.visit(this.value);
|
||||
},
|
||||
toCSS: function (env) {
|
||||
@@ -23,36 +23,40 @@ tree.Directive.prototype = {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (this.ruleset) {
|
||||
this.ruleset.root = true;
|
||||
return this.name + (env.compress ? '{' : ' {\n ') +
|
||||
this.ruleset.toCSS(env).trim().replace(/\n/g, '\n ') +
|
||||
(env.compress ? '}': '\n}\n');
|
||||
if (this.rules) {
|
||||
var css = "";
|
||||
for(var i = 0; i < this.rules.length; i++) {
|
||||
//this.rules[i].root = true;
|
||||
css += this.rules[i].toCSS(env).trim() + "\n";
|
||||
}
|
||||
css = css.trim().replace(/\n/g, '\n ');
|
||||
return this.name + (env.compress ? '{' : ' {\n ') + css + (env.compress ? '}': '\n}\n');
|
||||
} else {
|
||||
return this.name + ' ' + this.value.toCSS() + ';\n';
|
||||
}
|
||||
},
|
||||
eval: function (env) {
|
||||
var evaldDirective = this;
|
||||
if (this.ruleset) {
|
||||
if (this.rules) {
|
||||
env.frames.unshift(this);
|
||||
evaldDirective = new(tree.Directive)(this.name, null, this.index, this.currentFileInfo);
|
||||
evaldDirective.ruleset = this.ruleset.eval(env);
|
||||
evaldDirective.rules = [this.rules[0].eval(env)];
|
||||
evaldDirective.rules[0].root = true;
|
||||
env.frames.shift();
|
||||
}
|
||||
return evaldDirective;
|
||||
},
|
||||
variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) },
|
||||
find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) },
|
||||
rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) },
|
||||
variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); },
|
||||
find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); },
|
||||
rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); },
|
||||
markReferenced: function () {
|
||||
var rule, i;
|
||||
var i, rules;
|
||||
this.isReferenced = true;
|
||||
if (this.ruleset) {
|
||||
for (i = 0; i < this.ruleset.rules.length; i++) {
|
||||
rule = this.ruleset.rules[i];
|
||||
if (rule.markReferenced) {
|
||||
rule.markReferenced();
|
||||
if (this.rules) {
|
||||
rules = this.rules[0].rules;
|
||||
for (i = 0; i < rules.length; i++) {
|
||||
if (rules[i].markReferenced) {
|
||||
rules[i].markReferenced();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,19 +7,25 @@ tree.Media = function (value, features, index, currentFileInfo) {
|
||||
var selectors = this.emptySelectors();
|
||||
|
||||
this.features = new(tree.Value)(features);
|
||||
this.ruleset = new(tree.Ruleset)(selectors, value);
|
||||
this.ruleset.allowImports = true;
|
||||
this.rules = [new(tree.Ruleset)(selectors, value)];
|
||||
this.rules[0].allowImports = true;
|
||||
};
|
||||
tree.Media.prototype = {
|
||||
type: "Media",
|
||||
accept: function (visitor) {
|
||||
this.features = visitor.visit(this.features);
|
||||
this.ruleset = visitor.visit(this.ruleset);
|
||||
this.rules = visitor.visit(this.rules);
|
||||
},
|
||||
toCSS: function (env) {
|
||||
var features = this.features.toCSS(env);
|
||||
|
||||
var content = this.ruleset.toCSS(env).trim().replace(/\n/g, '\n ');
|
||||
var content = "";
|
||||
|
||||
for(var i = 0; i < this.rules.length; i++) {
|
||||
content += this.rules[i].toCSS(env).trim() + "\n";
|
||||
}
|
||||
|
||||
content = content.trim().replace(/\n/g, '\n ');
|
||||
|
||||
if (content.match(/\S/)) {
|
||||
return '@media ' + features + (env.compress ? '{' : ' {\n ') + content +
|
||||
@@ -36,7 +42,7 @@ tree.Media.prototype = {
|
||||
|
||||
var media = new(tree.Media)([], [], this.index, this.currentFileInfo);
|
||||
if(this.debugInfo) {
|
||||
this.ruleset.debugInfo = this.debugInfo;
|
||||
this.rules[0].debugInfo = this.debugInfo;
|
||||
media.debugInfo = this.debugInfo;
|
||||
}
|
||||
var strictMathBypass = false;
|
||||
@@ -56,8 +62,8 @@ tree.Media.prototype = {
|
||||
env.mediaPath.push(media);
|
||||
env.mediaBlocks.push(media);
|
||||
|
||||
env.frames.unshift(this.ruleset);
|
||||
media.ruleset = this.ruleset.eval(env);
|
||||
env.frames.unshift(this.rules[0]);
|
||||
media.rules = [this.rules[0].eval(env)];
|
||||
env.frames.shift();
|
||||
|
||||
env.mediaPath.pop();
|
||||
@@ -65,20 +71,19 @@ tree.Media.prototype = {
|
||||
return env.mediaPath.length === 0 ? media.evalTop(env) :
|
||||
media.evalNested(env)
|
||||
},
|
||||
variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) },
|
||||
find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) },
|
||||
rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) },
|
||||
variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); },
|
||||
find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); },
|
||||
rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); },
|
||||
emptySelectors: function() {
|
||||
var el = new(tree.Element)('', '&', 0);
|
||||
return [new(tree.Selector)([el], null, null, this.index, this.currentFileInfo)];
|
||||
},
|
||||
markReferenced: function () {
|
||||
var rule, i;
|
||||
var i, rules = this.rules[0].rules;
|
||||
this.isReferenced = true;
|
||||
for (i = 0; i < this.ruleset.rules.length; i++) {
|
||||
rule = this.ruleset.rules[i];
|
||||
if (rule.markReferenced) {
|
||||
rule.markReferenced();
|
||||
for (i = 0; i < rules.length; i++) {
|
||||
if (rules[i].markReferenced) {
|
||||
rules[i].markReferenced();
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -148,7 +153,7 @@ tree.Media.prototype = {
|
||||
}
|
||||
},
|
||||
bubbleSelectors: function (selectors) {
|
||||
this.ruleset = new(tree.Ruleset)(selectors.slice(0), [this.ruleset]);
|
||||
this.rules = [new(tree.Ruleset)(selectors.slice(0), [this.rules[0]])];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -194,26 +194,8 @@ tree.Ruleset.prototype = {
|
||||
for (var i = 0; i < this.rules.length; i++) {
|
||||
rule = this.rules[i];
|
||||
|
||||
if (rule.rules || (rule instanceof tree.Media)) {
|
||||
if (rule.rules || (rule instanceof tree.Media) || rule instanceof tree.Directive) {
|
||||
rulesets.push(rule.toCSS(env));
|
||||
} else if (rule instanceof tree.Directive) {
|
||||
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
|
||||
// be considered illegal css as it has to be on the first line
|
||||
if (env.charset) {
|
||||
if (rule.debugInfo) {
|
||||
rulesets.push(tree.debugInfo(env, rule));
|
||||
rulesets.push(new tree.Comment("/* "+cssValue.replace(/\n/g, "")+" */\n").toCSS(env));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
env.charset = true;
|
||||
}
|
||||
rulesets.push(cssValue);
|
||||
} else if (rule instanceof tree.Comment) {
|
||||
if (!rule.silent) {
|
||||
if (this.root) {
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
for(i = 0; i < nodes.length; i++) {
|
||||
var evald = this.visit(nodes[i]);
|
||||
if (evald instanceof Array) {
|
||||
newNodes = newNodes.concat(evald);
|
||||
newNodes = newNodes.concat(this.flatten(evald));
|
||||
} else {
|
||||
newNodes.push(evald);
|
||||
}
|
||||
@@ -48,6 +48,17 @@
|
||||
return newNodes;
|
||||
}
|
||||
return nodes;
|
||||
},
|
||||
flatten: function(arr, master) {
|
||||
return arr.reduce(this.flattenReduce.bind(this), master || []);
|
||||
},
|
||||
flattenReduce: function(sum, element) {
|
||||
if (element instanceof Array) {
|
||||
sum = this.flatten(element, sum);
|
||||
} else {
|
||||
sum.push(element);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user