jsx-space-before-closing.js 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. /**
  2. * @fileoverview Validate spacing before closing bracket in JSX.
  3. * @author ryym
  4. * @deprecated
  5. */
  6. 'use strict';
  7. const getTokenBeforeClosingBracket = require('../util/getTokenBeforeClosingBracket');
  8. const docsUrl = require('../util/docsUrl');
  9. const log = require('../util/log');
  10. const report = require('../util/report');
  11. const getSourceCode = require('../util/eslint').getSourceCode;
  12. let isWarnedForDeprecation = false;
  13. // ------------------------------------------------------------------------------
  14. // Rule Definition
  15. // ------------------------------------------------------------------------------
  16. const messages = {
  17. noSpaceBeforeClose: 'A space is forbidden before closing bracket',
  18. needSpaceBeforeClose: 'A space is required before closing bracket',
  19. };
  20. /** @type {import('eslint').Rule.RuleModule} */
  21. module.exports = {
  22. meta: {
  23. deprecated: true,
  24. replacedBy: ['jsx-tag-spacing'],
  25. docs: {
  26. description: 'Enforce spacing before closing bracket in JSX',
  27. category: 'Stylistic Issues',
  28. recommended: false,
  29. url: docsUrl('jsx-space-before-closing'),
  30. },
  31. fixable: 'code',
  32. messages,
  33. schema: [{
  34. enum: ['always', 'never'],
  35. }],
  36. },
  37. create(context) {
  38. const configuration = context.options[0] || 'always';
  39. // --------------------------------------------------------------------------
  40. // Public
  41. // --------------------------------------------------------------------------
  42. return {
  43. JSXOpeningElement(node) {
  44. if (!node.selfClosing) {
  45. return;
  46. }
  47. const sourceCode = getSourceCode(context);
  48. const leftToken = getTokenBeforeClosingBracket(node);
  49. const closingSlash = /** @type {import('eslint').AST.Token} */ (sourceCode.getTokenAfter(leftToken));
  50. if (leftToken.loc.end.line !== closingSlash.loc.start.line) {
  51. return;
  52. }
  53. if (configuration === 'always' && !sourceCode.isSpaceBetweenTokens(leftToken, closingSlash)) {
  54. report(context, messages.needSpaceBeforeClose, 'needSpaceBeforeClose', {
  55. loc: closingSlash.loc.start,
  56. fix(fixer) {
  57. return fixer.insertTextBefore(closingSlash, ' ');
  58. },
  59. });
  60. } else if (configuration === 'never' && sourceCode.isSpaceBetweenTokens(leftToken, closingSlash)) {
  61. report(context, messages.noSpaceBeforeClose, 'noSpaceBeforeClose', {
  62. loc: closingSlash.loc.start,
  63. fix(fixer) {
  64. const previousToken = sourceCode.getTokenBefore(closingSlash);
  65. return fixer.removeRange([previousToken.range[1], closingSlash.range[0]]);
  66. },
  67. });
  68. }
  69. },
  70. Program() {
  71. if (isWarnedForDeprecation) {
  72. return;
  73. }
  74. log('The react/jsx-space-before-closing rule is deprecated. '
  75. + 'Please use the react/jsx-tag-spacing rule with the '
  76. + '"beforeSelfClosing" option instead.');
  77. isWarnedForDeprecation = true;
  78. },
  79. };
  80. },
  81. };