index.js 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. 'use strict';
  2. const isStandardSyntaxRule = require('../../utils/isStandardSyntaxRule');
  3. const { levelOneAndTwoPseudoElements } = require('../../reference/selectors');
  4. const parseSelector = require('../../utils/parseSelector');
  5. const report = require('../../utils/report');
  6. const ruleMessages = require('../../utils/ruleMessages');
  7. const validateOptions = require('../../utils/validateOptions');
  8. const ruleName = 'selector-pseudo-element-colon-notation';
  9. const messages = ruleMessages(ruleName, {
  10. expected: (q) => `Expected ${q} colon pseudo-element notation`,
  11. });
  12. const meta = {
  13. url: 'https://stylelint.io/user-guide/rules/list/selector-pseudo-element-colon-notation',
  14. fixable: true,
  15. };
  16. /** @type {import('stylelint').Rule} */
  17. const rule = (primary, _secondaryOptions, context) => {
  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. const selector = ruleNode.selector;
  37. // get out early if no pseudo elements or classes
  38. if (!selector.includes(':')) {
  39. return;
  40. }
  41. const fixedSelector = parseSelector(selector, result, ruleNode, (selectors) => {
  42. selectors.walkPseudos((pseudo) => {
  43. const pseudoElement = pseudo.value.replace(/:/g, '');
  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. if (context.fix) {
  55. pseudo.replaceWith(pseudo.clone({ value: fixedColon + pseudoElement }));
  56. return;
  57. }
  58. report({
  59. message: messages.expected(primary),
  60. node: ruleNode,
  61. index: pseudo.sourceIndex,
  62. endIndex: pseudo.sourceIndex + (isDouble ? 2 : 1),
  63. result,
  64. ruleName,
  65. });
  66. });
  67. });
  68. if (context.fix && fixedSelector) {
  69. ruleNode.selector = fixedSelector;
  70. }
  71. });
  72. };
  73. };
  74. rule.ruleName = ruleName;
  75. rule.messages = messages;
  76. rule.meta = meta;
  77. module.exports = rule;