-
Notifications
You must be signed in to change notification settings - Fork 15
/
index.js
114 lines (96 loc) · 3.92 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
var postcss = require('postcss');
var list = require('postcss/lib/list');
var vars = require('postcss-simple-vars');
module.exports = postcss.plugin('postcss-for', function (opts) {
opts = opts || {};
opts.nested = opts.nested || true;
var parentsHaveIterator, manageIterStack, checkNumber, checkParams, processLoops, processOriginalLoops, unrollLoop;
var iterStack = [];
parentsHaveIterator = function (rule, param) {
if(rule.parent == null) { return false; }
if(rule.parent.type === 'root') { return false; }
if(rule.parent.params == null) { return false; }
var parentIterVar = list.space(rule.parent.params);
if (parentIterVar[0] == null) { return false; }
if (parentIterVar[0] === param) { return true; }
if ( iterStack.indexOf(param) !== -1) { return true; }
return parentsHaveIterator(rule.parent, param);
};
manageIterStack = function (rule) {
if (rule.parent.type !== 'root') {
var parentIterVar = rule.parent.params && list.space(rule.parent.params)[0];
if (iterStack.indexOf(parentIterVar) === -1) {
// If parent isn't in stack, wipe stack
iterStack.splice(0, iterStack.length);
} else {
// If parent is in stack, remove stack after parent
iterStack.splice(iterStack.indexOf(parentIterVar) + 1, iterStack.length - iterStack.indexOf(parentIterVar) - 1);
}
} else {
// If parent (root) isn't in stack, wipe stack
iterStack.splice(0, iterStack.length);
}
// Push current rule on stack regardless
iterStack.push( list.space(rule.params)[0] );
};
checkNumber = function (rule) {
return function (param) {
if (isNaN(parseInt(param)) || !param.match(/^-?\d+\.?\d*$/)) {
if (param.indexOf('$') !== -1) {
if( !parentsHaveIterator(rule, param) ) {
throw rule.error('External variable (not from a parent for loop) cannot be used as a range parameter', { plugin: 'postcss-for' });
}
} else {
throw rule.error('Range parameter should be a number', { plugin: 'postcss-for' });
}
}
};
};
checkParams = function (rule, params) {
if (!params[0].match(/(^|[^\w])\$([\w\d-_]+)/) ||
params[1] !== 'from' ||
params[3] !== 'to' ||
params[5] !== 'by' ^ params[5] === undefined ) {
throw rule.error('Wrong loop syntax', { plugin: 'postcss-for' });
}
[params[2], params[4], params[6] || '0'].forEach(checkNumber(rule));
};
unrollLoop = function (rule) {
var params = list.space(rule.params);
checkParams(rule, params);
var iterator = params[0].slice(1),
index = +params[2],
top = +params[4],
dir = top < index ? -1 : 1,
by = (params[6] || 1) * dir;
var value = {};
for ( var i = index; i * dir <= top * dir; i = i + by ) {
var content = rule.clone();
value[iterator] = i;
vars({ only: value })(content);
if (opts.nested) processLoops(content);
rule.parent.insertBefore(rule, content.nodes);
}
if ( rule.parent ) rule.remove();
};
processLoops = function (css) {
css.walkAtRules(function (rule) {
if ( rule.name === 'for' ) {
unrollLoop(rule);
}
});
};
processOriginalLoops = function (css) {
css.walkAtRules(function (rule) {
if ( rule.name === 'for' ) {
if (rule.parent) {
manageIterStack(rule);
}
unrollLoop(rule);
}
});
};
return function (css) {
processOriginalLoops(css);
};
});