index.js 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. // @ts-nocheck
  2. 'use strict';
  3. const isStandardSyntaxRule = require('../../utils/isStandardSyntaxRule');
  4. const parseSelector = require('../../utils/parseSelector');
  5. const report = require('../../utils/report');
  6. const resolvedNestedSelector = require('postcss-resolve-nested-selector');
  7. const ruleMessages = require('../../utils/ruleMessages');
  8. const selectorParser = require('postcss-selector-parser');
  9. const validateOptions = require('../../utils/validateOptions');
  10. const ruleName = 'selector-max-universal';
  11. const messages = ruleMessages(ruleName, {
  12. expected: (selector, max) =>
  13. `Expected "${selector}" to have no more than ${max} universal ${
  14. max === 1 ? 'selector' : 'selectors'
  15. }`,
  16. });
  17. function rule(max) {
  18. return (root, result) => {
  19. const validOptions = validateOptions(result, ruleName, {
  20. actual: max,
  21. possible: [
  22. function (max) {
  23. return typeof max === 'number' && max >= 0;
  24. },
  25. ],
  26. });
  27. if (!validOptions) {
  28. return;
  29. }
  30. function checkSelector(selectorNode, ruleNode) {
  31. const count = selectorNode.reduce((total, childNode) => {
  32. // Only traverse inside actual selectors
  33. // All logical combinations will be resolved as nested selector in `postcss-resolve-nested-selector`
  34. if (childNode.type === 'selector') {
  35. checkSelector(childNode, ruleNode);
  36. }
  37. return (total += childNode.type === 'universal' ? 1 : 0);
  38. }, 0);
  39. if (selectorNode.type !== 'root' && selectorNode.type !== 'pseudo' && count > max) {
  40. report({
  41. ruleName,
  42. result,
  43. node: ruleNode,
  44. message: messages.expected(selectorNode, max),
  45. word: selectorNode,
  46. });
  47. }
  48. }
  49. root.walkRules((ruleNode) => {
  50. if (!isStandardSyntaxRule(ruleNode)) {
  51. return;
  52. }
  53. const selectors = [];
  54. selectorParser()
  55. .astSync(ruleNode.selector)
  56. .walk((node) => {
  57. if (node.type === 'selector') {
  58. selectors.push(String(node).trim());
  59. }
  60. });
  61. selectors.forEach((selector) => {
  62. resolvedNestedSelector(selector, ruleNode).forEach((resolvedSelector) => {
  63. parseSelector(resolvedSelector, result, ruleNode, (container) =>
  64. checkSelector(container, ruleNode),
  65. );
  66. });
  67. });
  68. });
  69. };
  70. }
  71. rule.ruleName = ruleName;
  72. rule.messages = messages;
  73. module.exports = rule;