computed-property-spacing.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. /**
  2. * @fileoverview Disallows or enforces spaces inside computed properties.
  3. * @author Jamund Ferguson
  4. */
  5. "use strict";
  6. const astUtils = require("./utils/ast-utils");
  7. //------------------------------------------------------------------------------
  8. // Rule Definition
  9. //------------------------------------------------------------------------------
  10. /** @type {import('../shared/types').Rule} */
  11. module.exports = {
  12. meta: {
  13. type: "layout",
  14. docs: {
  15. description: "Enforce consistent spacing inside computed property brackets",
  16. recommended: false,
  17. url: "https://eslint.org/docs/rules/computed-property-spacing"
  18. },
  19. fixable: "whitespace",
  20. schema: [
  21. {
  22. enum: ["always", "never"]
  23. },
  24. {
  25. type: "object",
  26. properties: {
  27. enforceForClassMembers: {
  28. type: "boolean",
  29. default: true
  30. }
  31. },
  32. additionalProperties: false
  33. }
  34. ],
  35. messages: {
  36. unexpectedSpaceBefore: "There should be no space before '{{tokenValue}}'.",
  37. unexpectedSpaceAfter: "There should be no space after '{{tokenValue}}'.",
  38. missingSpaceBefore: "A space is required before '{{tokenValue}}'.",
  39. missingSpaceAfter: "A space is required after '{{tokenValue}}'."
  40. }
  41. },
  42. create(context) {
  43. const sourceCode = context.getSourceCode();
  44. const propertyNameMustBeSpaced = context.options[0] === "always"; // default is "never"
  45. const enforceForClassMembers = !context.options[1] || context.options[1].enforceForClassMembers;
  46. //--------------------------------------------------------------------------
  47. // Helpers
  48. //--------------------------------------------------------------------------
  49. /**
  50. * Reports that there shouldn't be a space after the first token
  51. * @param {ASTNode} node The node to report in the event of an error.
  52. * @param {Token} token The token to use for the report.
  53. * @param {Token} tokenAfter The token after `token`.
  54. * @returns {void}
  55. */
  56. function reportNoBeginningSpace(node, token, tokenAfter) {
  57. context.report({
  58. node,
  59. loc: { start: token.loc.end, end: tokenAfter.loc.start },
  60. messageId: "unexpectedSpaceAfter",
  61. data: {
  62. tokenValue: token.value
  63. },
  64. fix(fixer) {
  65. return fixer.removeRange([token.range[1], tokenAfter.range[0]]);
  66. }
  67. });
  68. }
  69. /**
  70. * Reports that there shouldn't be a space before the last token
  71. * @param {ASTNode} node The node to report in the event of an error.
  72. * @param {Token} token The token to use for the report.
  73. * @param {Token} tokenBefore The token before `token`.
  74. * @returns {void}
  75. */
  76. function reportNoEndingSpace(node, token, tokenBefore) {
  77. context.report({
  78. node,
  79. loc: { start: tokenBefore.loc.end, end: token.loc.start },
  80. messageId: "unexpectedSpaceBefore",
  81. data: {
  82. tokenValue: token.value
  83. },
  84. fix(fixer) {
  85. return fixer.removeRange([tokenBefore.range[1], token.range[0]]);
  86. }
  87. });
  88. }
  89. /**
  90. * Reports that there should be a space after the first token
  91. * @param {ASTNode} node The node to report in the event of an error.
  92. * @param {Token} token The token to use for the report.
  93. * @returns {void}
  94. */
  95. function reportRequiredBeginningSpace(node, token) {
  96. context.report({
  97. node,
  98. loc: token.loc,
  99. messageId: "missingSpaceAfter",
  100. data: {
  101. tokenValue: token.value
  102. },
  103. fix(fixer) {
  104. return fixer.insertTextAfter(token, " ");
  105. }
  106. });
  107. }
  108. /**
  109. * Reports that there should be a space before the last token
  110. * @param {ASTNode} node The node to report in the event of an error.
  111. * @param {Token} token The token to use for the report.
  112. * @returns {void}
  113. */
  114. function reportRequiredEndingSpace(node, token) {
  115. context.report({
  116. node,
  117. loc: token.loc,
  118. messageId: "missingSpaceBefore",
  119. data: {
  120. tokenValue: token.value
  121. },
  122. fix(fixer) {
  123. return fixer.insertTextBefore(token, " ");
  124. }
  125. });
  126. }
  127. /**
  128. * Returns a function that checks the spacing of a node on the property name
  129. * that was passed in.
  130. * @param {string} propertyName The property on the node to check for spacing
  131. * @returns {Function} A function that will check spacing on a node
  132. */
  133. function checkSpacing(propertyName) {
  134. return function(node) {
  135. if (!node.computed) {
  136. return;
  137. }
  138. const property = node[propertyName];
  139. const before = sourceCode.getTokenBefore(property, astUtils.isOpeningBracketToken),
  140. first = sourceCode.getTokenAfter(before, { includeComments: true }),
  141. after = sourceCode.getTokenAfter(property, astUtils.isClosingBracketToken),
  142. last = sourceCode.getTokenBefore(after, { includeComments: true });
  143. if (astUtils.isTokenOnSameLine(before, first)) {
  144. if (propertyNameMustBeSpaced) {
  145. if (!sourceCode.isSpaceBetweenTokens(before, first) && astUtils.isTokenOnSameLine(before, first)) {
  146. reportRequiredBeginningSpace(node, before);
  147. }
  148. } else {
  149. if (sourceCode.isSpaceBetweenTokens(before, first)) {
  150. reportNoBeginningSpace(node, before, first);
  151. }
  152. }
  153. }
  154. if (astUtils.isTokenOnSameLine(last, after)) {
  155. if (propertyNameMustBeSpaced) {
  156. if (!sourceCode.isSpaceBetweenTokens(last, after) && astUtils.isTokenOnSameLine(last, after)) {
  157. reportRequiredEndingSpace(node, after);
  158. }
  159. } else {
  160. if (sourceCode.isSpaceBetweenTokens(last, after)) {
  161. reportNoEndingSpace(node, after, last);
  162. }
  163. }
  164. }
  165. };
  166. }
  167. //--------------------------------------------------------------------------
  168. // Public
  169. //--------------------------------------------------------------------------
  170. const listeners = {
  171. Property: checkSpacing("key"),
  172. MemberExpression: checkSpacing("property")
  173. };
  174. if (enforceForClassMembers) {
  175. listeners.MethodDefinition =
  176. listeners.PropertyDefinition = listeners.Property;
  177. }
  178. return listeners;
  179. }
  180. };