selectorAttributeOperatorSpaceChecker.js 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  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
  66. ? operator.charAt(0)
  67. : operator.charAt(operator.length - 1),
  68. operator,
  69. ),
  70. node,
  71. index: attributeNode.sourceIndex + index,
  72. result: options.result,
  73. ruleName: options.checkedRuleName,
  74. });
  75. },
  76. });
  77. }
  78. });
  79. };