jsx-equals-spacing.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. /**
  2. * @fileoverview Disallow or enforce spaces around equal signs in JSX attributes.
  3. * @author ryym
  4. */
  5. 'use strict';
  6. const docsUrl = require('../util/docsUrl');
  7. const getSourceCode = require('../util/eslint').getSourceCode;
  8. const report = require('../util/report');
  9. // ------------------------------------------------------------------------------
  10. // Rule Definition
  11. // ------------------------------------------------------------------------------
  12. const messages = {
  13. noSpaceBefore: 'There should be no space before \'=\'',
  14. noSpaceAfter: 'There should be no space after \'=\'',
  15. needSpaceBefore: 'A space is required before \'=\'',
  16. needSpaceAfter: 'A space is required after \'=\'',
  17. };
  18. /** @type {import('eslint').Rule.RuleModule} */
  19. module.exports = {
  20. meta: {
  21. docs: {
  22. description: 'Enforce or disallow spaces around equal signs in JSX attributes',
  23. category: 'Stylistic Issues',
  24. recommended: false,
  25. url: docsUrl('jsx-equals-spacing'),
  26. },
  27. fixable: 'code',
  28. messages,
  29. schema: [{
  30. enum: ['always', 'never'],
  31. }],
  32. },
  33. create(context) {
  34. const config = context.options[0] || 'never';
  35. /**
  36. * Determines a given attribute node has an equal sign.
  37. * @param {ASTNode} attrNode - The attribute node.
  38. * @returns {boolean} Whether or not the attriute node has an equal sign.
  39. */
  40. function hasEqual(attrNode) {
  41. return attrNode.type !== 'JSXSpreadAttribute' && attrNode.value !== null;
  42. }
  43. // --------------------------------------------------------------------------
  44. // Public
  45. // --------------------------------------------------------------------------
  46. return {
  47. JSXOpeningElement(node) {
  48. node.attributes.forEach((attrNode) => {
  49. if (!hasEqual(attrNode)) {
  50. return;
  51. }
  52. const sourceCode = getSourceCode(context);
  53. const equalToken = sourceCode.getTokenAfter(attrNode.name);
  54. const spacedBefore = sourceCode.isSpaceBetweenTokens(attrNode.name, equalToken);
  55. const spacedAfter = sourceCode.isSpaceBetweenTokens(equalToken, attrNode.value);
  56. if (config === 'never') {
  57. if (spacedBefore) {
  58. report(context, messages.noSpaceBefore, 'noSpaceBefore', {
  59. node: attrNode,
  60. loc: equalToken.loc.start,
  61. fix(fixer) {
  62. return fixer.removeRange([attrNode.name.range[1], equalToken.range[0]]);
  63. },
  64. });
  65. }
  66. if (spacedAfter) {
  67. report(context, messages.noSpaceAfter, 'noSpaceAfter', {
  68. node: attrNode,
  69. loc: equalToken.loc.start,
  70. fix(fixer) {
  71. return fixer.removeRange([equalToken.range[1], attrNode.value.range[0]]);
  72. },
  73. });
  74. }
  75. } else if (config === 'always') {
  76. if (!spacedBefore) {
  77. report(context, messages.needSpaceBefore, 'needSpaceBefore', {
  78. node: attrNode,
  79. loc: equalToken.loc.start,
  80. fix(fixer) {
  81. return fixer.insertTextBefore(equalToken, ' ');
  82. },
  83. });
  84. }
  85. if (!spacedAfter) {
  86. report(context, messages.needSpaceAfter, 'needSpaceAfter', {
  87. node: attrNode,
  88. loc: equalToken.loc.start,
  89. fix(fixer) {
  90. return fixer.insertTextAfter(equalToken, ' ');
  91. },
  92. });
  93. }
  94. }
  95. });
  96. },
  97. };
  98. },
  99. };