jsx-equals-spacing.js 3.4 KB

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