Skip to content

Commit 39150d5

Browse files
committed
Simplified API on less object
e.g. "less.Ruleset()" instead of "new less.tree.Ruleset()" Auto-casting of string values into nodes for AtRule, Declaration, Selector, Value e.g. "less.Selector('&.a')" instead of "new tree.Selector(new tree.Element(new tree.Combinator('&'), '.a'))"
1 parent a38f8a1 commit 39150d5

File tree

15 files changed

+192
-93
lines changed

15 files changed

+192
-93
lines changed

lib/less/environment/abstract-plugin-loader.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ AbstractPluginLoader.prototype.evalPlugin = function(contents, context, pluginOp
5454
registry = functionRegistry.create();
5555

5656
try {
57-
loader = new Function("module", "require", "functions", "tree", "fileInfo", contents);
58-
pluginObj = loader(localModule, this.require, registry, this.less.tree, fileInfo);
57+
loader = new Function("module", "require", "functions", "tree", "less", "fileInfo", contents);
58+
pluginObj = loader(localModule, this.require, registry, this.less.tree, this.less, fileInfo);
5959

6060
if (!pluginObj) {
6161
pluginObj = localModule.exports;

lib/less/index.js

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module.exports = function(environment, fileManagers) {
22
var SourceMapOutput, SourceMapBuilder, ParseTree, ImportManager, Environment;
33

4-
var less = {
4+
var initial = {
55
version: [3, 0, 0],
66
data: require('./data'),
77
tree: require('./tree'),
@@ -26,5 +26,28 @@ module.exports = function(environment, fileManagers) {
2626
logger: require('./logger')
2727
};
2828

29-
return less;
29+
// Create a public API
30+
31+
var ctor = function(t) {
32+
return function() {
33+
var obj = Object.create(t.prototype);
34+
t.apply(obj, Array.prototype.slice.call(arguments, 0));
35+
return obj;
36+
};
37+
};
38+
var t, api = Object.create(initial);
39+
for (var n in initial.tree) {
40+
t = initial.tree[n];
41+
if (typeof t === "function") {
42+
api[n] = ctor(t);
43+
}
44+
else {
45+
api[n] = Object.create(null);
46+
for (var o in t) {
47+
api[n][o] = ctor(t[o]);
48+
}
49+
}
50+
}
51+
52+
return api;
3053
};

lib/less/parser/parser.js

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,61 @@ var Parser = function Parser(context, imports, fileInfo) {
8080
};
8181
}
8282

83+
/**
84+
* Used after initial parsing to create nodes on the fly
85+
*
86+
* @param {String} str - string to parse
87+
* @param {Array} parseList - array of parsers to run input through e.g. ["value", "important"]
88+
* @param {Number} currentIndex - start number to begin indexing
89+
* @param {Object} fileInfo - fileInfo to attach to created nodes
90+
*/
91+
function parseNode(str, parseList, currentIndex, fileInfo, callback) {
92+
var result, returnNodes = [];
93+
var parser = parserInput;
94+
95+
try {
96+
parser.start(str, false, function fail(msg, index) {
97+
callback({
98+
message: msg,
99+
index: index + currentIndex
100+
});
101+
});
102+
for(var x = 0, p, i; (p = parseList[x]); x++) {
103+
i = parser.i;
104+
result = parsers[p]();
105+
if (result) {
106+
result._index = i + currentIndex;
107+
result._fileInfo = fileInfo;
108+
returnNodes.push(result);
109+
}
110+
else {
111+
returnNodes.push(null);
112+
}
113+
}
114+
115+
var endInfo = parser.end();
116+
if (endInfo.isFinished) {
117+
callback(null, returnNodes);
118+
}
119+
else {
120+
callback(true, null);
121+
}
122+
} catch (e) {
123+
throw new LessError({
124+
index: e.index + currentIndex,
125+
message: e.message
126+
}, imports, fileInfo.filename);
127+
}
128+
}
129+
83130
//
84131
// The Parser
85132
//
86133
return {
87134
parserInput: parserInput,
88135
imports: imports,
89136
fileInfo: fileInfo,
137+
parseNode: parseNode,
90138
//
91139
// Parse an input string into an abstract syntax tree,
92140
// @param str A string containing 'less' markup
@@ -133,8 +181,8 @@ var Parser = function Parser(context, imports, fileInfo) {
133181
});
134182

135183
tree.Node.prototype.parse = this;
136-
137-
root = new(tree.Ruleset)(null, this.parsers.primary());
184+
root = new tree.Ruleset(null, this.parsers.primary());
185+
tree.Node.prototype.rootNode = root;
138186
root.root = true;
139187
root.firstRoot = true;
140188

@@ -1008,7 +1056,7 @@ var Parser = function Parser(context, imports, fileInfo) {
10081056
if (! e) {
10091057
parserInput.save();
10101058
if (parserInput.$char('(')) {
1011-
if ((v = this.selector()) && parserInput.$char(')')) {
1059+
if ((v = this.selector(false)) && parserInput.$char(')')) {
10121060
e = new(tree.Paren)(v);
10131061
parserInput.forget();
10141062
} else {
@@ -1059,14 +1107,8 @@ var Parser = function Parser(context, imports, fileInfo) {
10591107
}
10601108
},
10611109
//
1062-
// A CSS selector (see selector below)
1063-
// with less extensions e.g. the ability to extend and guard
1064-
//
1065-
lessSelector: function () {
1066-
return this.selector(true);
1067-
},
1068-
//
10691110
// A CSS Selector
1111+
// with less extensions e.g. the ability to extend and guard
10701112
//
10711113
// .class > div + h1
10721114
// li a:hover
@@ -1075,7 +1117,7 @@ var Parser = function Parser(context, imports, fileInfo) {
10751117
//
10761118
selector: function (isLess) {
10771119
var index = parserInput.i, elements, extendList, c, e, allExtends, when, condition;
1078-
1120+
isLess = isLess !== false;
10791121
while ((isLess && (extendList = this.extend())) || (isLess && (when = parserInput.$str("when"))) || (e = this.element())) {
10801122
if (when) {
10811123
condition = expect(this.conditions, 'expected condition');
@@ -1165,7 +1207,7 @@ var Parser = function Parser(context, imports, fileInfo) {
11651207
}
11661208

11671209
while (true) {
1168-
s = this.lessSelector();
1210+
s = this.selector();
11691211
if (!s) {
11701212
break;
11711213
}

lib/less/plugin-manager.js

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
var utils = require('./utils');
12
/**
23
* Plugin Manager
34
*/
@@ -53,13 +54,6 @@ PluginManager.prototype.get = function(filename) {
5354
return this.pluginCache[filename];
5455
};
5556

56-
// Object.getPrototypeOf shim for visitor upgrade
57-
if (!Object.getPrototypeOf) {
58-
Object.getPrototypeOf = function getPrototypeOf(object) {
59-
return object.constructor ? object.constructor.prototype : void 0;
60-
};
61-
}
62-
6357
function upgradeVisitors(visitor, oldType, newType) {
6458

6559
if (visitor['visit' + oldType] && !visitor['visit' + newType]) {
@@ -78,7 +72,7 @@ PluginManager.prototype.addVisitor = function(visitor) {
7872
var proto;
7973
// 2.x to 3.x visitor compatibility
8074
try {
81-
proto = Object.getPrototypeOf(visitor);
75+
proto = utils.getPrototype(visitor);
8276
upgradeVisitors(proto, 'Directive', 'AtRule');
8377
upgradeVisitors(proto, 'Rule', 'Declaration');
8478
}

lib/less/tree/atrule.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
var Node = require("./node"),
22
Selector = require("./selector"),
3-
Ruleset = require("./ruleset");
3+
Ruleset = require("./ruleset"),
4+
Value = require('./value'),
5+
Anonymous = require('./anonymous');
46

57
var AtRule = function (name, value, rules, index, currentFileInfo, debugInfo, isRooted, visibilityInfo) {
68
var i;
79

810
this.name = name;
9-
this.value = value;
11+
this.value = (value instanceof Node) ? value : (value ? new Anonymous(value) : value);
1012
if (rules) {
1113
if (Array.isArray(rules)) {
1214
this.rules = rules;

lib/less/tree/declaration.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
var Node = require("./node"),
22
Value = require("./value"),
3-
Keyword = require("./keyword");
3+
Keyword = require("./keyword"),
4+
Anonymous = require("./anonymous");
45

56
var Declaration = function (name, value, important, merge, index, currentFileInfo, inline, variable) {
67
this.name = name;
7-
this.value = (value instanceof Node) ? value : new Value([value]); //value instanceof tree.Value || value instanceof tree.Ruleset ??
8+
this.value = (value instanceof Node) ? value : new Value([value ? new Anonymous(value) : null]);
89
this.important = important ? ' ' + important.trim() : '';
910
this.merge = merge;
1011
this._index = index;

lib/less/tree/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
var tree = {};
1+
var tree = Object.create(null);
22

33
tree.Node = require('./node');
44
tree.Alpha = require('./alpha');

lib/less/tree/ruleset.js

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -298,26 +298,21 @@ Ruleset.prototype.parseValue = function(toParse) {
298298
var self = this;
299299
function transformDeclaration(decl) {
300300
if (decl.value instanceof Anonymous && !decl.parsed) {
301-
try {
302-
this.parse.parserInput.start(decl.value.value, false, function fail(msg, index) {
303-
decl.parsed = true;
304-
return decl;
301+
this.parse.parseNode(
302+
decl.value.value,
303+
["value", "important"],
304+
decl.value.getIndex(),
305+
decl.fileInfo(),
306+
function(err, result) {
307+
if (err) {
308+
decl.parsed = true;
309+
}
310+
if (result) {
311+
decl.value = result[0];
312+
decl.important = result[1] || '';
313+
decl.parsed = true;
314+
}
305315
});
306-
var result = this.parse.parsers.value();
307-
var important = this.parse.parsers.important();
308-
309-
var endInfo = this.parse.parserInput.end();
310-
if (endInfo.isFinished) {
311-
decl.value = result;
312-
decl.imporant = important;
313-
decl.parsed = true;
314-
}
315-
} catch (e) {
316-
throw new LessError({
317-
index: e.index + decl.value.getIndex(),
318-
message: e.message
319-
}, this.parse.imports, decl.fileInfo().filename);
320-
}
321316

322317
return decl;
323318
}

lib/less/tree/selector.js

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
var Node = require("./node"),
2-
Element = require("./element");
2+
Element = require("./element"),
3+
LessError = require("../less-error");
34

45
var Selector = function (elements, extendList, condition, index, currentFileInfo, visibilityInfo) {
5-
this.elements = elements;
66
this.extendList = extendList;
77
this.condition = condition;
88
this._index = index;
99
this._fileInfo = currentFileInfo;
10+
this.elements = this.getElements(elements);
1011
if (!condition) {
1112
this.evaldCondition = true;
1213
}
@@ -27,13 +28,33 @@ Selector.prototype.accept = function (visitor) {
2728
}
2829
};
2930
Selector.prototype.createDerived = function(elements, extendList, evaldCondition) {
31+
elements = this.getElements(elements);
3032
var info = this.visibilityInfo();
3133
evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition;
3234
var newSelector = new Selector(elements, extendList || this.extendList, null, this.getIndex(), this.fileInfo(), info);
3335
newSelector.evaldCondition = evaldCondition;
3436
newSelector.mediaEmpty = this.mediaEmpty;
3537
return newSelector;
3638
};
39+
Selector.prototype.getElements = function(els) {
40+
if (typeof els === "string") {
41+
this.parse.parseNode(
42+
els,
43+
["selector"],
44+
this._index,
45+
this._fileInfo,
46+
function(err, result) {
47+
if (err) {
48+
throw new LessError({
49+
index: e.index + currentIndex,
50+
message: e.message
51+
}, this.parse.imports, this._fileInfo.filename);
52+
}
53+
els = result[0].elements;
54+
});
55+
}
56+
return els;
57+
};
3758
Selector.prototype.createEmptySelectors = function() {
3859
var el = new Element('', '&', this._index, this._fileInfo),
3960
sels = [new Selector([el], null, null, this._index, this._fileInfo)];
@@ -45,7 +66,7 @@ Selector.prototype.match = function (other) {
4566
len = elements.length,
4667
olen, i;
4768

48-
other.CacheElements();
69+
other.cacheElements();
4970

5071
olen = other._elements.length;
5172
if (olen === 0 || len < olen) {
@@ -60,7 +81,7 @@ Selector.prototype.match = function (other) {
6081

6182
return olen; // return number of matched elements
6283
};
63-
Selector.prototype.CacheElements = function() {
84+
Selector.prototype.cacheElements = function() {
6485
if (this._elements) {
6586
return;
6687
}

lib/less/tree/value.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
1-
var Node = require("./node");
1+
var Node = require("./node"),
2+
Anonymous = require("./anonymous");
3+
24

35
var Value = function (value) {
4-
this.value = value;
56
if (!value) {
67
throw new Error("Value requires an array argument");
78
}
9+
if (!Array.isArray(value)) {
10+
this.value = [ value ];
11+
}
12+
else {
13+
this.value = value;
14+
}
815
};
916
Value.prototype = new Node();
1017
Value.prototype.type = "Value";

0 commit comments

Comments
 (0)