template-curly-spacing.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. /**
  2. * @fileoverview Rule to enforce spacing around embedded expressions of template strings
  3. * @author Toru Nagashima
  4. * @deprecated in ESLint v8.53.0
  5. */
  6. "use strict";
  7. //------------------------------------------------------------------------------
  8. // Requirements
  9. //------------------------------------------------------------------------------
  10. const astUtils = require("./utils/ast-utils");
  11. //------------------------------------------------------------------------------
  12. // Rule Definition
  13. //------------------------------------------------------------------------------
  14. /** @type {import('../types').Rule.RuleModule} */
  15. module.exports = {
  16. meta: {
  17. deprecated: {
  18. message: "Formatting rules are being moved out of ESLint core.",
  19. url: "https://eslint.org/blog/2023/10/deprecating-formatting-rules/",
  20. deprecatedSince: "8.53.0",
  21. availableUntil: "11.0.0",
  22. replacedBy: [
  23. {
  24. message:
  25. "ESLint Stylistic now maintains deprecated stylistic core rules.",
  26. url: "https://eslint.style/guide/migration",
  27. plugin: {
  28. name: "@stylistic/eslint-plugin",
  29. url: "https://eslint.style",
  30. },
  31. rule: {
  32. name: "template-curly-spacing",
  33. url: "https://eslint.style/rules/template-curly-spacing",
  34. },
  35. },
  36. ],
  37. },
  38. type: "layout",
  39. docs: {
  40. description:
  41. "Require or disallow spacing around embedded expressions of template strings",
  42. recommended: false,
  43. url: "https://eslint.org/docs/latest/rules/template-curly-spacing",
  44. },
  45. fixable: "whitespace",
  46. schema: [{ enum: ["always", "never"] }],
  47. messages: {
  48. expectedBefore: "Expected space(s) before '}'.",
  49. expectedAfter: "Expected space(s) after '${'.",
  50. unexpectedBefore: "Unexpected space(s) before '}'.",
  51. unexpectedAfter: "Unexpected space(s) after '${'.",
  52. },
  53. },
  54. create(context) {
  55. const sourceCode = context.sourceCode;
  56. const always = context.options[0] === "always";
  57. /**
  58. * Checks spacing before `}` of a given token.
  59. * @param {Token} token A token to check. This is a Template token.
  60. * @returns {void}
  61. */
  62. function checkSpacingBefore(token) {
  63. if (!token.value.startsWith("}")) {
  64. return; // starts with a backtick, this is the first template element in the template literal
  65. }
  66. const prevToken = sourceCode.getTokenBefore(token, {
  67. includeComments: true,
  68. }),
  69. hasSpace = sourceCode.isSpaceBetween(prevToken, token);
  70. if (!astUtils.isTokenOnSameLine(prevToken, token)) {
  71. return;
  72. }
  73. if (always && !hasSpace) {
  74. context.report({
  75. loc: {
  76. start: token.loc.start,
  77. end: {
  78. line: token.loc.start.line,
  79. column: token.loc.start.column + 1,
  80. },
  81. },
  82. messageId: "expectedBefore",
  83. fix: fixer => fixer.insertTextBefore(token, " "),
  84. });
  85. }
  86. if (!always && hasSpace) {
  87. context.report({
  88. loc: {
  89. start: prevToken.loc.end,
  90. end: token.loc.start,
  91. },
  92. messageId: "unexpectedBefore",
  93. fix: fixer =>
  94. fixer.removeRange([prevToken.range[1], token.range[0]]),
  95. });
  96. }
  97. }
  98. /**
  99. * Checks spacing after `${` of a given token.
  100. * @param {Token} token A token to check. This is a Template token.
  101. * @returns {void}
  102. */
  103. function checkSpacingAfter(token) {
  104. if (!token.value.endsWith("${")) {
  105. return; // ends with a backtick, this is the last template element in the template literal
  106. }
  107. const nextToken = sourceCode.getTokenAfter(token, {
  108. includeComments: true,
  109. }),
  110. hasSpace = sourceCode.isSpaceBetween(token, nextToken);
  111. if (!astUtils.isTokenOnSameLine(token, nextToken)) {
  112. return;
  113. }
  114. if (always && !hasSpace) {
  115. context.report({
  116. loc: {
  117. start: {
  118. line: token.loc.end.line,
  119. column: token.loc.end.column - 2,
  120. },
  121. end: token.loc.end,
  122. },
  123. messageId: "expectedAfter",
  124. fix: fixer => fixer.insertTextAfter(token, " "),
  125. });
  126. }
  127. if (!always && hasSpace) {
  128. context.report({
  129. loc: {
  130. start: token.loc.end,
  131. end: nextToken.loc.start,
  132. },
  133. messageId: "unexpectedAfter",
  134. fix: fixer =>
  135. fixer.removeRange([token.range[1], nextToken.range[0]]),
  136. });
  137. }
  138. }
  139. return {
  140. TemplateElement(node) {
  141. const token = sourceCode.getFirstToken(node);
  142. checkSpacingBefore(token);
  143. checkSpacingAfter(token);
  144. },
  145. };
  146. },
  147. };