jsx-equals-spacing.js 3.5 KB

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