comma-spacing.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. /**
  2. * @fileoverview Comma spacing - validates spacing before and after comma
  3. * @author Vignesh Anand aka vegetableman.
  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: "comma-spacing",
  30. url: "https://eslint.style/rules/comma-spacing",
  31. },
  32. },
  33. ],
  34. },
  35. type: "layout",
  36. docs: {
  37. description: "Enforce consistent spacing before and after commas",
  38. recommended: false,
  39. url: "https://eslint.org/docs/latest/rules/comma-spacing",
  40. },
  41. fixable: "whitespace",
  42. schema: [
  43. {
  44. type: "object",
  45. properties: {
  46. before: {
  47. type: "boolean",
  48. default: false,
  49. },
  50. after: {
  51. type: "boolean",
  52. default: true,
  53. },
  54. },
  55. additionalProperties: false,
  56. },
  57. ],
  58. messages: {
  59. missing: "A space is required {{loc}} ','.",
  60. unexpected: "There should be no space {{loc}} ','.",
  61. },
  62. },
  63. create(context) {
  64. const sourceCode = context.sourceCode;
  65. const tokensAndComments = sourceCode.tokensAndComments;
  66. const options = {
  67. before: context.options[0] ? context.options[0].before : false,
  68. after: context.options[0] ? context.options[0].after : true,
  69. };
  70. //--------------------------------------------------------------------------
  71. // Helpers
  72. //--------------------------------------------------------------------------
  73. // list of comma tokens to ignore for the check of leading whitespace
  74. const commaTokensToIgnore = [];
  75. /**
  76. * Reports a spacing error with an appropriate message.
  77. * @param {ASTNode} node The binary expression node to report.
  78. * @param {string} loc Is the error "before" or "after" the comma?
  79. * @param {ASTNode} otherNode The node at the left or right of `node`
  80. * @returns {void}
  81. * @private
  82. */
  83. function report(node, loc, otherNode) {
  84. context.report({
  85. node,
  86. fix(fixer) {
  87. if (options[loc]) {
  88. if (loc === "before") {
  89. return fixer.insertTextBefore(node, " ");
  90. }
  91. return fixer.insertTextAfter(node, " ");
  92. }
  93. let start, end;
  94. const newText = "";
  95. if (loc === "before") {
  96. start = otherNode.range[1];
  97. end = node.range[0];
  98. } else {
  99. start = node.range[1];
  100. end = otherNode.range[0];
  101. }
  102. return fixer.replaceTextRange([start, end], newText);
  103. },
  104. messageId: options[loc] ? "missing" : "unexpected",
  105. data: {
  106. loc,
  107. },
  108. });
  109. }
  110. /**
  111. * Adds null elements of the given ArrayExpression or ArrayPattern node to the ignore list.
  112. * @param {ASTNode} node An ArrayExpression or ArrayPattern node.
  113. * @returns {void}
  114. */
  115. function addNullElementsToIgnoreList(node) {
  116. let previousToken = sourceCode.getFirstToken(node);
  117. node.elements.forEach(element => {
  118. let token;
  119. if (element === null) {
  120. token = sourceCode.getTokenAfter(previousToken);
  121. if (astUtils.isCommaToken(token)) {
  122. commaTokensToIgnore.push(token);
  123. }
  124. } else {
  125. token = sourceCode.getTokenAfter(element);
  126. }
  127. previousToken = token;
  128. });
  129. }
  130. //--------------------------------------------------------------------------
  131. // Public
  132. //--------------------------------------------------------------------------
  133. return {
  134. "Program:exit"() {
  135. tokensAndComments.forEach((token, i) => {
  136. if (!astUtils.isCommaToken(token)) {
  137. return;
  138. }
  139. const previousToken = tokensAndComments[i - 1];
  140. const nextToken = tokensAndComments[i + 1];
  141. if (
  142. previousToken &&
  143. !astUtils.isCommaToken(previousToken) && // ignore spacing between two commas
  144. /*
  145. * `commaTokensToIgnore` are ending commas of `null` elements (array holes/elisions).
  146. * In addition to spacing between two commas, this can also ignore:
  147. *
  148. * - Spacing after `[` (controlled by array-bracket-spacing)
  149. * Example: [ , ]
  150. * ^
  151. * - Spacing after a comment (for backwards compatibility, this was possibly unintentional)
  152. * Example: [a, /* * / ,]
  153. * ^
  154. */
  155. !commaTokensToIgnore.includes(token) &&
  156. astUtils.isTokenOnSameLine(previousToken, token) &&
  157. options.before !==
  158. sourceCode.isSpaceBetweenTokens(
  159. previousToken,
  160. token,
  161. )
  162. ) {
  163. report(token, "before", previousToken);
  164. }
  165. if (
  166. nextToken &&
  167. !astUtils.isCommaToken(nextToken) && // ignore spacing between two commas
  168. !astUtils.isClosingParenToken(nextToken) && // controlled by space-in-parens
  169. !astUtils.isClosingBracketToken(nextToken) && // controlled by array-bracket-spacing
  170. !astUtils.isClosingBraceToken(nextToken) && // controlled by object-curly-spacing
  171. !(!options.after && nextToken.type === "Line") && // special case, allow space before line comment
  172. astUtils.isTokenOnSameLine(token, nextToken) &&
  173. options.after !==
  174. sourceCode.isSpaceBetweenTokens(token, nextToken)
  175. ) {
  176. report(token, "after", nextToken);
  177. }
  178. });
  179. },
  180. ArrayExpression: addNullElementsToIgnoreList,
  181. ArrayPattern: addNullElementsToIgnoreList,
  182. };
  183. },
  184. };