UNPKG

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