This repository has been archived by the owner on Dec 30, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 20
/
index.js
118 lines (93 loc) · 2.89 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
115
116
117
118
const postcss = require('postcss');
const vars = require('postcss-simple-vars');
const PLUGIN_NAME = 'postcss-each';
const SEPARATOR = /\s+in\s+/;
function checkParams(params) {
if (!SEPARATOR.test(params)) return 'Missed "in" keyword in @each';
const [name, values] = params.split(SEPARATOR).map(str => str.trim());
if (!name.match(/\$[_a-zA-Z]?\w+/)) return 'Missed variable name in @each';
if (!values.match(/(\w+\,?\s?)+/)) return 'Missed values list in @each';
return null;
}
function tokenize(str) {
return postcss.list.comma(str).map(str => str.replace(/^\$/, ''));
}
function paramsList(params) {
let [vars, values] = params.split(SEPARATOR).map(tokenize);
let matched = false;
values = values.map(value => {
let match = value.match(/^\((.*)\)$/);
if (match) matched = true;
return match ? postcss.list.comma(match[1]) : value;
});
values = matched ? values : [values];
return {
names: values.map((_, i) => vars[i]),
indexName: vars[values.length],
values: values,
};
}
function processRules(rule, params) {
params.values[0].forEach((_, i) => {
let vals = {};
params.names.forEach((name, j) => {
vals[name] = params.values[j][i];
});
if (params.indexName) vals[params.indexName] = i;
rule.nodes.forEach(node => {
const proxy = postcss.rule({ nodes: [node] });
const { root } = postcss([vars({ only: vals })]).process(proxy);
rule.parent.insertBefore(rule, root.nodes[0].nodes[0]);
});
});
}
function processEach(rule) {
const params = ` ${rule.params} `;
const error = checkParams(params);
if (error) throw rule.error(error);
const parsedParams = paramsList(params);
processRules(rule, parsedParams);
rule.remove();
}
function rulesExists(css) {
let rulesLength = 0;
css.walkAtRules('each', () => rulesLength++);
return rulesLength;
}
function processLoop(css, afterEach, beforeEach) {
if (afterEach) {
css = postcss(afterEach).process(css).root;
}
css.walkAtRules('each', (rule) => {
processEach(rule);
processLoop(rule.root());
});
if (beforeEach) {
css = postcss(beforeEach).process(css).root;
}
if (rulesExists(css)) processLoop(css, afterEach, beforeEach);
};
const pluginCreator = (opts = {}) => {
const hasPlugins = opts && opts.plugins;
const hasAfterEach = hasPlugins && opts.plugins.afterEach && opts.plugins.afterEach.length;
const hasBeforeEach = hasPlugins && opts.plugins.beforeEach && opts.plugins.beforeEach.length;
if (hasAfterEach || hasBeforeEach) {
return {
postcssPlugin: PLUGIN_NAME,
Once: (css) => processLoop(
css,
hasAfterEach && opts.plugins.afterEach,
hasBeforeEach && opts.plugins.beforeEach
),
};
} else {
return {
postcssPlugin: PLUGIN_NAME,
AtRule: {
each: processEach,
},
};
}
};
pluginCreator.postcss = true;
module.exports = pluginCreator;