Skip to content

Commit e3731c3

Browse files
authored
feat: support less each function (#127)
1 parent 9392246 commit e3731c3

File tree

3 files changed

+96
-47
lines changed

3 files changed

+96
-47
lines changed

lib/LessParser.js

Lines changed: 73 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,25 @@ module.exports = class LessParser extends Parser {
2828
variableNode(this.lastNode);
2929
}
3030

31+
each(tokens) {
32+
// prepend a space so the `name` will be parsed correctly
33+
tokens[0][1] = ` ${tokens[0][1]}`;
34+
35+
const firstParenIndex = tokens.findIndex((t) => t[0] === '(');
36+
const lastParen = tokens.reverse().find((t) => t[0] === ')');
37+
const lastParenIndex = tokens.reverse().indexOf(lastParen);
38+
const paramTokens = tokens.splice(firstParenIndex, lastParenIndex);
39+
const params = paramTokens.map((t) => t[1]).join('');
40+
41+
for (const token of tokens.reverse()) {
42+
this.tokenizer.back(token);
43+
}
44+
45+
this.atrule(this.tokenizer.nextToken());
46+
this.lastNode.function = true;
47+
this.lastNode.params = params;
48+
}
49+
3150
init(node, line, column) {
3251
super.init(node, line, column);
3352
this.lastNode = node;
@@ -53,6 +72,53 @@ module.exports = class LessParser extends Parser {
5372
}
5473
}
5574

75+
mixin(tokens) {
76+
const [first] = tokens;
77+
const identifier = first[1].slice(0, 1);
78+
const bracketsIndex = tokens.findIndex((t) => t[0] === 'brackets');
79+
const firstParenIndex = tokens.findIndex((t) => t[0] === '(');
80+
let important = '';
81+
82+
// fix for #86. if rulesets are mixin params, they need to be converted to a brackets token
83+
if ((bracketsIndex < 0 || bracketsIndex > 3) && firstParenIndex > 0) {
84+
const lastParenIndex = tokens.findIndex((t) => t[0] === ')');
85+
86+
const contents = tokens.slice(firstParenIndex, lastParenIndex + firstParenIndex);
87+
const brackets = contents.map((t) => t[1]).join('');
88+
const [paren] = tokens.slice(firstParenIndex);
89+
const start = [paren[2], paren[3]];
90+
const [last] = tokens.slice(lastParenIndex, lastParenIndex + 1);
91+
const end = [last[2], last[3]];
92+
const newToken = ['brackets', brackets].concat(start, end);
93+
94+
const tokensBefore = tokens.slice(0, firstParenIndex);
95+
const tokensAfter = tokens.slice(lastParenIndex + 1);
96+
tokens = tokensBefore;
97+
tokens.push(newToken);
98+
tokens = tokens.concat(tokensAfter);
99+
}
100+
101+
const importantIndex = tokens.findIndex((t) => importantPattern.test(t[1]));
102+
103+
if (importantIndex > 0) {
104+
[, important] = tokens[importantIndex];
105+
tokens.splice(importantIndex, 1);
106+
}
107+
108+
for (const token of tokens.reverse()) {
109+
this.tokenizer.back(token);
110+
}
111+
112+
this.atrule(this.tokenizer.nextToken());
113+
this.lastNode.mixin = true;
114+
this.lastNode.raws.identifier = identifier;
115+
116+
if (important) {
117+
this.lastNode.important = true;
118+
this.lastNode.raws.important = important;
119+
}
120+
}
121+
56122
other(token) {
57123
if (!isInlineComment.bind(this)(token)) {
58124
super.other(token);
@@ -84,56 +150,18 @@ module.exports = class LessParser extends Parser {
84150
unknownWord(tokens) {
85151
// NOTE: keep commented for examining unknown structures
86152
// console.log('unknown', tokens);
87-
// console.log(this.root.first);
88153

89154
const [first] = tokens;
90155

156+
// #121 support `each` - http://lesscss.org/functions/#list-functions-each
157+
if (tokens[0][1] === 'each' && tokens[1][0] === '(') {
158+
this.each(tokens);
159+
return;
160+
}
161+
91162
// TODO: move this into a util function/file
92163
if (isMixinToken(first)) {
93-
const identifier = first[1].slice(0, 1);
94-
const bracketsIndex = tokens.findIndex((t) => t[0] === 'brackets');
95-
const firstParenIndex = tokens.findIndex((t) => t[0] === '(');
96-
let important = '';
97-
98-
// fix for #86. if rulesets are mixin params, they need to be converted to a brackets token
99-
if ((bracketsIndex < 0 || bracketsIndex > 3) && firstParenIndex > 0) {
100-
const lastParenIndex = tokens.findIndex((t) => t[0] === ')');
101-
102-
const contents = tokens.slice(firstParenIndex, lastParenIndex + firstParenIndex);
103-
const brackets = contents.map((t) => t[1]).join('');
104-
const [paren] = tokens.slice(firstParenIndex);
105-
const start = [paren[2], paren[3]];
106-
const [last] = tokens.slice(lastParenIndex, lastParenIndex + 1);
107-
const end = [last[2], last[3]];
108-
const newToken = ['brackets', brackets].concat(start, end);
109-
110-
const tokensBefore = tokens.slice(0, firstParenIndex);
111-
const tokensAfter = tokens.slice(lastParenIndex + 1);
112-
tokens = tokensBefore;
113-
tokens.push(newToken);
114-
tokens = tokens.concat(tokensAfter);
115-
}
116-
117-
const importantIndex = tokens.findIndex((t) => importantPattern.test(t[1]));
118-
119-
if (importantIndex > 0) {
120-
[, important] = tokens[importantIndex];
121-
tokens.splice(importantIndex, 1);
122-
}
123-
124-
for (const token of tokens.reverse()) {
125-
this.tokenizer.back(token);
126-
}
127-
128-
this.atrule(this.tokenizer.nextToken());
129-
this.lastNode.mixin = true;
130-
this.lastNode.raws.identifier = identifier;
131-
132-
if (important) {
133-
this.lastNode.important = true;
134-
this.lastNode.raws.important = important;
135-
}
136-
164+
this.mixin(tokens);
137165
return;
138166
}
139167

lib/LessStringifier.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ const Stringifier = require('postcss/lib/stringifier');
22

33
module.exports = class LessStringifier extends Stringifier {
44
atrule(node, semicolon) {
5-
if (!node.mixin && !node.variable) {
5+
if (!node.mixin && !node.variable && !node.function) {
66
super.atrule(node, semicolon);
77
return;
88
}
99

10-
let name = `${node.raws.identifier || '@'}${node.name}`;
10+
const identifier = node.function ? '' : node.raws.identifier || '@';
11+
let name = `${identifier}${node.name}`;
1112
let params = node.params ? this.rawValue(node, 'params') : '';
1213
const important = node.raws.important || '';
1314

test/parser/function.test.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
const test = require('ava');
2+
3+
const { parse, nodeToString } = require('../../lib');
4+
5+
test('each (#121)', (t) => {
6+
const params = `(@colors, {
7+
.@{value}-color {
8+
color: @value;
9+
}
10+
})`;
11+
const less = `each${params};`;
12+
const root = parse(less);
13+
const { first } = root;
14+
15+
t.is(first.name, 'each');
16+
t.is(first.params, params);
17+
t.truthy(first.function);
18+
19+
t.is(nodeToString(root), less);
20+
});

0 commit comments

Comments
 (0)