index.js 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. 'use strict';
  2. const getRuleSelector = require('../../utils/getRuleSelector');
  3. const isStandardSyntaxRule = require('../../utils/isStandardSyntaxRule');
  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 { assertString } = require('../../utils/validateTypes');
  9. const ruleName = 'selector-attribute-quotes';
  10. const messages = ruleMessages(ruleName, {
  11. expected: (value) => `Expected quotes around "${value}"`,
  12. rejected: (value) => `Unexpected quotes around "${value}"`,
  13. });
  14. const meta = {
  15. url: 'https://stylelint.io/user-guide/rules/list/selector-attribute-quotes',
  16. };
  17. const acceptedQuoteMark = '"';
  18. /** @type {import('stylelint').Rule} */
  19. const rule = (primary, _secondaryOptions, context) => {
  20. return (root, result) => {
  21. const validOptions = validateOptions(result, ruleName, {
  22. actual: primary,
  23. possible: ['always', 'never'],
  24. });
  25. if (!validOptions) {
  26. return;
  27. }
  28. root.walkRules((ruleNode) => {
  29. if (!isStandardSyntaxRule(ruleNode)) {
  30. return;
  31. }
  32. if (!ruleNode.selector.includes('[') || !ruleNode.selector.includes('=')) {
  33. return;
  34. }
  35. parseSelector(getRuleSelector(ruleNode), result, ruleNode, (selectorTree) => {
  36. let selectorFixed = false;
  37. selectorTree.walkAttributes((attributeNode) => {
  38. if (!attributeNode.operator) {
  39. return;
  40. }
  41. if (!attributeNode.quoted && primary === 'always') {
  42. if (context.fix) {
  43. selectorFixed = true;
  44. attributeNode.quoteMark = acceptedQuoteMark;
  45. } else {
  46. assertString(attributeNode.value);
  47. complain(
  48. messages.expected(attributeNode.value),
  49. attributeNode.sourceIndex + attributeNode.offsetOf('value'),
  50. );
  51. }
  52. }
  53. if (attributeNode.quoted && primary === 'never') {
  54. if (context.fix) {
  55. selectorFixed = true;
  56. attributeNode.quoteMark = null;
  57. } else {
  58. assertString(attributeNode.value);
  59. complain(
  60. messages.rejected(attributeNode.value),
  61. attributeNode.sourceIndex + attributeNode.offsetOf('value'),
  62. );
  63. }
  64. }
  65. });
  66. if (selectorFixed) {
  67. ruleNode.selector = selectorTree.toString();
  68. }
  69. });
  70. /**
  71. * @param {string} message
  72. * @param {number} index
  73. */
  74. function complain(message, index) {
  75. report({
  76. message,
  77. index,
  78. result,
  79. ruleName,
  80. node: ruleNode,
  81. });
  82. }
  83. });
  84. };
  85. };
  86. rule.ruleName = ruleName;
  87. rule.messages = messages;
  88. rule.meta = meta;
  89. module.exports = rule;