UNPKG

2.27 kBJavaScriptView Raw
1// @ts-nocheck
2
3'use strict';
4
5const isStandardSyntaxRule = require('../../utils/isStandardSyntaxRule');
6const parseSelector = require('../../utils/parseSelector');
7const report = require('../../utils/report');
8const resolvedNestedSelector = require('postcss-resolve-nested-selector');
9const ruleMessages = require('../../utils/ruleMessages');
10const selectorParser = require('postcss-selector-parser');
11const validateOptions = require('../../utils/validateOptions');
12
13const ruleName = 'selector-max-universal';
14
15const messages = ruleMessages(ruleName, {
16 expected: (selector, max) =>
17 `Expected "${selector}" to have no more than ${max} universal ${
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 function checkSelector(selectorNode, ruleNode) {
38 const count = selectorNode.reduce((total, childNode) => {
39 // Only traverse inside actual selectors
40 // All logical combinations will be resolved as nested selector in `postcss-resolve-nested-selector`
41 if (childNode.type === 'selector') {
42 checkSelector(childNode, ruleNode);
43 }
44
45 return (total += childNode.type === 'universal' ? 1 : 0);
46 }, 0);
47
48 if (selectorNode.type !== 'root' && selectorNode.type !== 'pseudo' && count > max) {
49 report({
50 ruleName,
51 result,
52 node: ruleNode,
53 message: messages.expected(selectorNode, max),
54 word: selectorNode,
55 });
56 }
57 }
58
59 root.walkRules((ruleNode) => {
60 if (!isStandardSyntaxRule(ruleNode)) {
61 return;
62 }
63
64 const selectors = [];
65
66 selectorParser()
67 .astSync(ruleNode.selector)
68 .walk((node) => {
69 if (node.type === 'selector') {
70 selectors.push(String(node).trim());
71 }
72 });
73
74 selectors.forEach((selector) => {
75 resolvedNestedSelector(selector, ruleNode).forEach((resolvedSelector) => {
76 parseSelector(resolvedSelector, result, ruleNode, (container) =>
77 checkSelector(container, ruleNode),
78 );
79 });
80 });
81 });
82 };
83}
84
85rule.ruleName = ruleName;
86rule.messages = messages;
87module.exports = rule;