selectorAttributeOperatorSpaceChecker.js 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. 'use strict';
  2. const isStandardSyntaxRule = require('../utils/isStandardSyntaxRule');
  3. const parseSelector = require('../utils/parseSelector');
  4. const report = require('../utils/report');
  5. const styleSearch = require('style-search');
  6. /**
  7. * @param {{
  8. * root: import('postcss').Root,
  9. * result: import('stylelint').PostcssResult,
  10. * locationChecker: (opts: { source: string, index: number, err: (msg: string) => void }) => void,
  11. * checkedRuleName: string,
  12. * checkBeforeOperator: boolean,
  13. * fix: ((attributeNode: import('postcss-selector-parser').Attribute) => boolean) | null,
  14. * }} options
  15. * @returns {void}
  16. */
  17. module.exports = function selectorAttributeOperatorSpaceChecker(options) {
  18. options.root.walkRules((rule) => {
  19. if (!isStandardSyntaxRule(rule)) {
  20. return;
  21. }
  22. if (!rule.selector.includes('[') || !rule.selector.includes('=')) {
  23. return;
  24. }
  25. let hasFixed = false;
  26. const selector = rule.raws.selector ? rule.raws.selector.raw : rule.selector;
  27. const fixedSelector = parseSelector(selector, options.result, rule, (selectorTree) => {
  28. selectorTree.walkAttributes((attributeNode) => {
  29. const operator = attributeNode.operator;
  30. if (!operator) {
  31. return;
  32. }
  33. const attributeNodeString = attributeNode.toString();
  34. styleSearch({ source: attributeNodeString, target: operator }, (match) => {
  35. const index = options.checkBeforeOperator ? match.startIndex : match.endIndex - 1;
  36. checkOperator(attributeNodeString, index, rule, attributeNode, operator);
  37. });
  38. });
  39. });
  40. if (hasFixed && fixedSelector) {
  41. if (!rule.raws.selector) {
  42. rule.selector = fixedSelector;
  43. } else {
  44. rule.raws.selector.raw = fixedSelector;
  45. }
  46. }
  47. /**
  48. * @param {string} source
  49. * @param {number} index
  50. * @param {import('postcss').Node} node
  51. * @param {import('postcss-selector-parser').Attribute} attributeNode
  52. * @param {string} operator
  53. */
  54. function checkOperator(source, index, node, attributeNode, operator) {
  55. options.locationChecker({
  56. source,
  57. index,
  58. err: (msg) => {
  59. if (options.fix && options.fix(attributeNode)) {
  60. hasFixed = true;
  61. return;
  62. }
  63. report({
  64. message: msg.replace(
  65. options.checkBeforeOperator ? operator[0] : operator[operator.length - 1],
  66. operator,
  67. ),
  68. node,
  69. index: attributeNode.sourceIndex + index,
  70. result: options.result,
  71. ruleName: options.checkedRuleName,
  72. });
  73. },
  74. });
  75. }
  76. });
  77. };