-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
77 lines (67 loc) · 2.51 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
var stylelint = require('stylelint');
var resolvNested = require('postcss-resolve-nested-selector');
var ruleName = 'selector-max-depth';
var messages = stylelint.utils.ruleMessages(ruleName, {
rejected: function (selector, maxDepth) {
return 'Expected \'' + selector + '\' to have depths not greater than ' +
maxDepth;
}
});
module.exports = stylelint.createPlugin(ruleName, function (max) {
return function (root, result) {
// Checks the main rule value
var validOptions = stylelint.utils.validateOptions({
ruleName: ruleName,
result: result,
// The only option is the value, so we check if it is a positive number
actual: max,
possible: [function (x) {
return typeof x === 'number' && x > 0;
}]
});
// Checks a simple selector's (without :not/:matches) depth
function checkDepth(selector, rule) {
var depth;
// Stripping "[...]", "(...)", "+", "~" (the last two might come with
// spaces) for simplicity and since we don't need those to count depth
var stripped = selector.replace(/\(.*?\)|\[.*?\]|(\s*)[+~](\s*)/g, []);
// Trimming redundant spaces
stripped = stripped.replace(/\s+/g, ' ');
// Finally find everything between spaces and ">" (possibly with spaces)
depth = stripped.match(/[^\s>]+/g).length;
if (depth > max) {
stylelint.utils.report({
ruleName: ruleName,
result: result,
node: rule,
message: messages.rejected(selector, max)
});
}
}
if (!validOptions) { return; }
root.walkRules(function (rule) {
// There can be a comma-separated selectors set, so using
// `rule.selectors` instead of `rule.selector`
rule.selectors.forEach(function (selector) {
// Don't check selectors with interpolation
if (/#{.+?}|@{.+?}|\$\(.+?\)/.test(selector)) {
return;
}
resolvNested(selector, rule).forEach(function (resolvedSelector) {
var notReg = /:not\((.*?)\)/g;
var notRegDel = /(:not\((.*?)\))/;
var match = notReg.exec(resolvedSelector);
// 1. Find :not, check its contents
while (match !== null) {
checkDepth(match[1], rule);
match = notReg.exec(resolvedSelector);
}
// 2. Delete :not and :matches, check the rest
checkDepth(resolvedSelector.replace(notRegDel, ''), rule);
});
});
});
};
});
module.exports.ruleName = ruleName;
module.exports.messages = messages;