nonblock-statement-body-position.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. /**
  2. * @fileoverview enforce the location of single-line statements
  3. * @author Teddy Katz
  4. * @deprecated in ESLint v8.53.0
  5. */
  6. "use strict";
  7. //------------------------------------------------------------------------------
  8. // Rule Definition
  9. //------------------------------------------------------------------------------
  10. const POSITION_SCHEMA = { enum: ["beside", "below", "any"] };
  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: "nonblock-statement-body-position",
  30. url: "https://eslint.style/rules/nonblock-statement-body-position",
  31. },
  32. },
  33. ],
  34. },
  35. type: "layout",
  36. docs: {
  37. description: "Enforce the location of single-line statements",
  38. recommended: false,
  39. url: "https://eslint.org/docs/latest/rules/nonblock-statement-body-position",
  40. },
  41. fixable: "whitespace",
  42. schema: [
  43. POSITION_SCHEMA,
  44. {
  45. properties: {
  46. overrides: {
  47. properties: {
  48. if: POSITION_SCHEMA,
  49. else: POSITION_SCHEMA,
  50. while: POSITION_SCHEMA,
  51. do: POSITION_SCHEMA,
  52. for: POSITION_SCHEMA,
  53. },
  54. additionalProperties: false,
  55. },
  56. },
  57. additionalProperties: false,
  58. },
  59. ],
  60. messages: {
  61. expectNoLinebreak: "Expected no linebreak before this statement.",
  62. expectLinebreak: "Expected a linebreak before this statement.",
  63. },
  64. },
  65. create(context) {
  66. const sourceCode = context.sourceCode;
  67. //----------------------------------------------------------------------
  68. // Helpers
  69. //----------------------------------------------------------------------
  70. /**
  71. * Gets the applicable preference for a particular keyword
  72. * @param {string} keywordName The name of a keyword, e.g. 'if'
  73. * @returns {string} The applicable option for the keyword, e.g. 'beside'
  74. */
  75. function getOption(keywordName) {
  76. return (
  77. (context.options[1] &&
  78. context.options[1].overrides &&
  79. context.options[1].overrides[keywordName]) ||
  80. context.options[0] ||
  81. "beside"
  82. );
  83. }
  84. /**
  85. * Validates the location of a single-line statement
  86. * @param {ASTNode} node The single-line statement
  87. * @param {string} keywordName The applicable keyword name for the single-line statement
  88. * @returns {void}
  89. */
  90. function validateStatement(node, keywordName) {
  91. const option = getOption(keywordName);
  92. if (node.type === "BlockStatement" || option === "any") {
  93. return;
  94. }
  95. const tokenBefore = sourceCode.getTokenBefore(node);
  96. if (
  97. tokenBefore.loc.end.line === node.loc.start.line &&
  98. option === "below"
  99. ) {
  100. context.report({
  101. node,
  102. messageId: "expectLinebreak",
  103. fix: fixer => fixer.insertTextBefore(node, "\n"),
  104. });
  105. } else if (
  106. tokenBefore.loc.end.line !== node.loc.start.line &&
  107. option === "beside"
  108. ) {
  109. context.report({
  110. node,
  111. messageId: "expectNoLinebreak",
  112. fix(fixer) {
  113. if (
  114. sourceCode
  115. .getText()
  116. .slice(tokenBefore.range[1], node.range[0])
  117. .trim()
  118. ) {
  119. return null;
  120. }
  121. return fixer.replaceTextRange(
  122. [tokenBefore.range[1], node.range[0]],
  123. " ",
  124. );
  125. },
  126. });
  127. }
  128. }
  129. //----------------------------------------------------------------------
  130. // Public
  131. //----------------------------------------------------------------------
  132. return {
  133. IfStatement(node) {
  134. validateStatement(node.consequent, "if");
  135. // Check the `else` node, but don't check 'else if' statements.
  136. if (node.alternate && node.alternate.type !== "IfStatement") {
  137. validateStatement(node.alternate, "else");
  138. }
  139. },
  140. WhileStatement: node => validateStatement(node.body, "while"),
  141. DoWhileStatement: node => validateStatement(node.body, "do"),
  142. ForStatement: node => validateStatement(node.body, "for"),
  143. ForInStatement: node => validateStatement(node.body, "for"),
  144. ForOfStatement: node => validateStatement(node.body, "for"),
  145. };
  146. },
  147. };