index.cjs 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. // NOTICE: This file is generated by Rollup. To modify it,
  2. // please instead edit the ESM counterpart and rebuild with Rollup (npm run build).
  3. 'use strict';
  4. const selectorParser = require('postcss-selector-parser');
  5. const validateTypes = require('../../utils/validateTypes.cjs');
  6. const flattenNestedSelectorsForRule = require('../../utils/flattenNestedSelectorsForRule.cjs');
  7. const getStrippedSelectorSource = require('../../utils/getStrippedSelectorSource.cjs');
  8. const isContextFunctionalPseudoClass = require('../../utils/isContextFunctionalPseudoClass.cjs');
  9. const isNonNegativeInteger = require('../../utils/isNonNegativeInteger.cjs');
  10. const isStandardSyntaxRule = require('../../utils/isStandardSyntaxRule.cjs');
  11. const optionsMatches = require('../../utils/optionsMatches.cjs');
  12. const report = require('../../utils/report.cjs');
  13. const ruleMessages = require('../../utils/ruleMessages.cjs');
  14. const validateOptions = require('../../utils/validateOptions.cjs');
  15. const { isRoot, isSelector } = selectorParser;
  16. const ruleName = 'selector-max-attribute';
  17. const messages = ruleMessages(ruleName, {
  18. expected: (selector, max) =>
  19. `Expected "${selector}" to have no more than ${max} attribute ${
  20. max === 1 ? 'selector' : 'selectors'
  21. }`,
  22. });
  23. const meta = {
  24. url: 'https://stylelint.io/user-guide/rules/selector-max-attribute',
  25. };
  26. /** @type {import('stylelint').CoreRules[ruleName]} */
  27. const rule = (primary, secondaryOptions) => {
  28. return (root, result) => {
  29. const validOptions = validateOptions(
  30. result,
  31. ruleName,
  32. {
  33. actual: primary,
  34. possible: isNonNegativeInteger,
  35. },
  36. {
  37. actual: secondaryOptions,
  38. possible: {
  39. ignoreAttributes: [validateTypes.isString, validateTypes.isRegExp],
  40. },
  41. optional: true,
  42. },
  43. );
  44. if (!validOptions) {
  45. return;
  46. }
  47. /**
  48. * @param {import('postcss-selector-parser').Selector} resolvedSelectorNode
  49. * @param {import('postcss-selector-parser').Selector} selectorNode
  50. * @param {import('postcss').Rule} ruleNode
  51. */
  52. function checkSelector(resolvedSelectorNode, selectorNode, ruleNode) {
  53. const count = resolvedSelectorNode.reduce((total, childNode) => {
  54. if (childNode.type !== 'attribute') {
  55. // Not an attribute node -> ignore
  56. return total;
  57. }
  58. if (optionsMatches(secondaryOptions, 'ignoreAttributes', childNode.attribute)) {
  59. // it's an attribute that is supposed to be ignored
  60. return total;
  61. }
  62. total += 1;
  63. return total;
  64. }, 0);
  65. if (count > primary) {
  66. const { index, endIndex, selector: selectorStr } = getStrippedSelectorSource(selectorNode);
  67. report({
  68. ruleName,
  69. result,
  70. node: ruleNode,
  71. message: messages.expected,
  72. messageArgs: [selectorStr, primary],
  73. index,
  74. endIndex,
  75. });
  76. }
  77. }
  78. root.walkRules((ruleNode) => {
  79. if (!isStandardSyntaxRule(ruleNode)) return;
  80. flattenNestedSelectorsForRule(ruleNode, result).forEach(({ selector, resolvedSelectors }) => {
  81. resolvedSelectors.walk((childSelector) => {
  82. if (!isSelector(childSelector)) return;
  83. if (
  84. isRoot(childSelector.parent) ||
  85. isContextFunctionalPseudoClass(childSelector.parent)
  86. ) {
  87. checkSelector(childSelector, selector, ruleNode);
  88. }
  89. });
  90. });
  91. });
  92. };
  93. };
  94. rule.ruleName = ruleName;
  95. rule.messages = messages;
  96. rule.meta = meta;
  97. module.exports = rule;