UNPKG

2.33 kBJavaScriptView Raw
1'use strict';
2
3const isLogicalCombination = require('../../utils/isLogicalCombination');
4const isStandardSyntaxRule = require('../../utils/isStandardSyntaxRule');
5const parseSelector = require('../../utils/parseSelector');
6const report = require('../../utils/report');
7const resolvedNestedSelector = require('postcss-resolve-nested-selector');
8const ruleMessages = require('../../utils/ruleMessages');
9const validateOptions = require('../../utils/validateOptions');
10
11const ruleName = 'selector-max-compound-selectors';
12
13const messages = ruleMessages(ruleName, {
14 expected: (selector, max) =>
15 `Expected "${selector}" to have no more than ${max} compound ${
16 max === 1 ? 'selector' : 'selectors'
17 }`,
18});
19
20function rule(max) {
21 return (root, result) => {
22 const validOptions = validateOptions(result, ruleName, {
23 actual: max,
24 possible: [
25 function (max) {
26 return typeof max === 'number' && max > 0;
27 },
28 ],
29 });
30
31 if (!validOptions) {
32 return;
33 }
34
35 // Finds actual selectors in selectorNode object and checks them
36 function checkSelector(selectorNode, rule) {
37 let compoundCount = 1;
38
39 selectorNode.each((childNode) => {
40 // Only traverse inside actual selectors and logical combinations
41 if (childNode.type === 'selector' || isLogicalCombination(childNode)) {
42 checkSelector(childNode, rule);
43 }
44
45 // Compound selectors are separated by combinators, so increase count when meeting one
46 if (childNode.type === 'combinator') {
47 compoundCount++;
48 }
49 });
50
51 if (selectorNode.type !== 'root' && selectorNode.type !== 'pseudo' && compoundCount > max) {
52 report({
53 ruleName,
54 result,
55 node: rule,
56 message: messages.expected(selectorNode, max),
57 word: selectorNode,
58 });
59 }
60 }
61
62 root.walkRules((rule) => {
63 if (!isStandardSyntaxRule(rule)) {
64 return;
65 }
66
67 // Using `rule.selectors` gets us each selector if there is a comma separated set
68 rule.selectors.forEach((selector) => {
69 resolvedNestedSelector(selector, rule).forEach((resolvedSelector) => {
70 // Process each resolved selector with `checkSelector` via postcss-selector-parser
71 parseSelector(resolvedSelector, result, rule, (s) => checkSelector(s, rule));
72 });
73 });
74 });
75 };
76}
77
78rule.ruleName = ruleName;
79rule.messages = messages;
80module.exports = rule;