UNPKG

2.37 kBJavaScriptView Raw
1// @ts-nocheck
2
3'use strict';
4
5const isContextFunctionalPseudoClass = require('../../utils/isContextFunctionalPseudoClass');
6const isStandardSyntaxRule = require('../../utils/isStandardSyntaxRule');
7const keywordSets = require('../../reference/keywordSets');
8const parseSelector = require('../../utils/parseSelector');
9const report = require('../../utils/report');
10const resolvedNestedSelector = require('postcss-resolve-nested-selector');
11const ruleMessages = require('../../utils/ruleMessages');
12const validateOptions = require('../../utils/validateOptions');
13
14const ruleName = 'selector-max-pseudo-class';
15
16const messages = ruleMessages(ruleName, {
17 expected: (selector, max) =>
18 `Expected "${selector}" to have no more than ${max} pseudo-${max === 1 ? 'class' : 'classes'}`,
19});
20
21function 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 // Only traverse inside actual selectors and context functional pseudo-classes
39 if (childNode.type === 'selector' || isContextFunctionalPseudoClass(childNode)) {
40 checkSelector(childNode, ruleNode);
41 }
42
43 // Exclude pseudo elements from the count
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
86rule.ruleName = ruleName;
87rule.messages = messages;
88module.exports = rule;