UNPKG

1.9 kBJavaScriptView Raw
1'use strict';
2
3const isStandardSyntaxRule = require('../../utils/isStandardSyntaxRule');
4const parseSelector = require('../../utils/parseSelector');
5const report = require('../../utils/report');
6const resolvedNestedSelector = require('postcss-resolve-nested-selector');
7const ruleMessages = require('../../utils/ruleMessages');
8const validateOptions = require('../../utils/validateOptions');
9
10const ruleName = 'selector-max-combinators';
11
12const messages = ruleMessages(ruleName, {
13 expected: (selector, max) =>
14 `Expected "${selector}" to have no more than ${max} ${
15 max === 1 ? 'combinator' : 'combinators'
16 }`,
17});
18
19function rule(max) {
20 return (root, result) => {
21 const validOptions = validateOptions(result, ruleName, {
22 actual: max,
23 possible: [
24 function (max) {
25 return typeof max === 'number' && max >= 0;
26 },
27 ],
28 });
29
30 if (!validOptions) {
31 return;
32 }
33
34 function checkSelector(selectorNode, ruleNode) {
35 const count = selectorNode.reduce((total, childNode) => {
36 // Only traverse inside actual selectors
37 if (childNode.type === 'selector') {
38 checkSelector(childNode, ruleNode);
39 }
40
41 return (total += childNode.type === 'combinator' ? 1 : 0);
42 }, 0);
43
44 if (selectorNode.type !== 'root' && selectorNode.type !== 'pseudo' && count > max) {
45 report({
46 ruleName,
47 result,
48 node: ruleNode,
49 message: messages.expected(selectorNode, max),
50 word: selectorNode,
51 });
52 }
53 }
54
55 root.walkRules((ruleNode) => {
56 if (!isStandardSyntaxRule(ruleNode)) {
57 return;
58 }
59
60 ruleNode.selectors.forEach((selector) => {
61 resolvedNestedSelector(selector, ruleNode).forEach((resolvedSelector) => {
62 parseSelector(resolvedSelector, result, ruleNode, (container) =>
63 checkSelector(container, ruleNode),
64 );
65 });
66 });
67 });
68 };
69}
70
71rule.ruleName = ruleName;
72rule.messages = messages;
73module.exports = rule;