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