UNPKG

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