index.mjs 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. import getRuleSelector from '../../utils/getRuleSelector.mjs';
  2. import isStandardSyntaxRule from '../../utils/isStandardSyntaxRule.mjs';
  3. import { levelOneAndTwoPseudoElements } from '../../reference/selectors.mjs';
  4. import parseSelector from '../../utils/parseSelector.mjs';
  5. import report from '../../utils/report.mjs';
  6. import ruleMessages from '../../utils/ruleMessages.mjs';
  7. import validateOptions from '../../utils/validateOptions.mjs';
  8. const ruleName = 'selector-pseudo-element-colon-notation';
  9. const messages = ruleMessages(ruleName, {
  10. expected: (type) => `Expected ${type} colon pseudo-element notation`,
  11. });
  12. const meta = {
  13. url: 'https://stylelint.io/user-guide/rules/selector-pseudo-element-colon-notation',
  14. fixable: true,
  15. };
  16. /** @type {import('stylelint').CoreRules[ruleName]} */
  17. const rule = (primary) => {
  18. return (root, result) => {
  19. const validOptions = validateOptions(result, ruleName, {
  20. actual: primary,
  21. possible: ['single', 'double'],
  22. });
  23. if (!validOptions) {
  24. return;
  25. }
  26. let fixedColon = '';
  27. if (primary === 'single') {
  28. fixedColon = ':';
  29. } else if (primary === 'double') {
  30. fixedColon = '::';
  31. }
  32. root.walkRules((ruleNode) => {
  33. if (!isStandardSyntaxRule(ruleNode)) {
  34. return;
  35. }
  36. // get out early if no pseudo elements or classes
  37. if (!ruleNode.selector.includes(':')) {
  38. return;
  39. }
  40. const selectorRoot = parseSelector(getRuleSelector(ruleNode), result, ruleNode);
  41. if (!selectorRoot) return;
  42. selectorRoot.walkPseudos((pseudo) => {
  43. const pseudoElement = pseudo.value.replaceAll(':', '');
  44. if (!levelOneAndTwoPseudoElements.has(pseudoElement.toLowerCase())) {
  45. return;
  46. }
  47. const isDouble = pseudo.value.startsWith('::');
  48. if (primary === 'single' && !isDouble) {
  49. return;
  50. }
  51. if (primary === 'double' && isDouble) {
  52. return;
  53. }
  54. const fix = () => {
  55. pseudo.replaceWith(pseudo.clone({ value: fixedColon + pseudoElement }));
  56. ruleNode.selector = selectorRoot.toString();
  57. };
  58. report({
  59. message: messages.expected,
  60. messageArgs: [primary],
  61. node: ruleNode,
  62. index: pseudo.sourceIndex,
  63. endIndex: pseudo.sourceIndex + (isDouble ? 2 : 1),
  64. result,
  65. ruleName,
  66. fix: {
  67. apply: fix,
  68. node: ruleNode,
  69. },
  70. });
  71. });
  72. });
  73. };
  74. };
  75. rule.ruleName = ruleName;
  76. rule.messages = messages;
  77. rule.meta = meta;
  78. export default rule;