rest-spread-spacing.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. /**
  2. * @fileoverview Enforce spacing between rest and spread operators and their expressions.
  3. * @author Kai Cataldo
  4. */
  5. "use strict";
  6. //------------------------------------------------------------------------------
  7. // Rule Definition
  8. //------------------------------------------------------------------------------
  9. /** @type {import('../shared/types').Rule} */
  10. module.exports = {
  11. meta: {
  12. type: "layout",
  13. docs: {
  14. description: "Enforce spacing between rest and spread operators and their expressions",
  15. recommended: false,
  16. url: "https://eslint.org/docs/rules/rest-spread-spacing"
  17. },
  18. fixable: "whitespace",
  19. schema: [
  20. {
  21. enum: ["always", "never"]
  22. }
  23. ],
  24. messages: {
  25. unexpectedWhitespace: "Unexpected whitespace after {{type}} operator.",
  26. expectedWhitespace: "Expected whitespace after {{type}} operator."
  27. }
  28. },
  29. create(context) {
  30. const sourceCode = context.getSourceCode(),
  31. alwaysSpace = context.options[0] === "always";
  32. //--------------------------------------------------------------------------
  33. // Helpers
  34. //--------------------------------------------------------------------------
  35. /**
  36. * Checks whitespace between rest/spread operators and their expressions
  37. * @param {ASTNode} node The node to check
  38. * @returns {void}
  39. */
  40. function checkWhiteSpace(node) {
  41. const operator = sourceCode.getFirstToken(node),
  42. nextToken = sourceCode.getTokenAfter(operator),
  43. hasWhitespace = sourceCode.isSpaceBetweenTokens(operator, nextToken);
  44. let type;
  45. switch (node.type) {
  46. case "SpreadElement":
  47. type = "spread";
  48. if (node.parent.type === "ObjectExpression") {
  49. type += " property";
  50. }
  51. break;
  52. case "RestElement":
  53. type = "rest";
  54. if (node.parent.type === "ObjectPattern") {
  55. type += " property";
  56. }
  57. break;
  58. case "ExperimentalSpreadProperty":
  59. type = "spread property";
  60. break;
  61. case "ExperimentalRestProperty":
  62. type = "rest property";
  63. break;
  64. default:
  65. return;
  66. }
  67. if (alwaysSpace && !hasWhitespace) {
  68. context.report({
  69. node,
  70. loc: operator.loc,
  71. messageId: "expectedWhitespace",
  72. data: {
  73. type
  74. },
  75. fix(fixer) {
  76. return fixer.replaceTextRange([operator.range[1], nextToken.range[0]], " ");
  77. }
  78. });
  79. } else if (!alwaysSpace && hasWhitespace) {
  80. context.report({
  81. node,
  82. loc: {
  83. start: operator.loc.end,
  84. end: nextToken.loc.start
  85. },
  86. messageId: "unexpectedWhitespace",
  87. data: {
  88. type
  89. },
  90. fix(fixer) {
  91. return fixer.removeRange([operator.range[1], nextToken.range[0]]);
  92. }
  93. });
  94. }
  95. }
  96. //--------------------------------------------------------------------------
  97. // Public
  98. //--------------------------------------------------------------------------
  99. return {
  100. SpreadElement: checkWhiteSpace,
  101. RestElement: checkWhiteSpace,
  102. ExperimentalSpreadProperty: checkWhiteSpace,
  103. ExperimentalRestProperty: checkWhiteSpace
  104. };
  105. }
  106. };