computed-property-spacing.js 6.5 KB

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