no-inner-declarations.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. /**
  2. * @fileoverview Rule to enforce declarations in program or function body root.
  3. * @author Brandon Mills
  4. */
  5. "use strict";
  6. //------------------------------------------------------------------------------
  7. // Requirements
  8. //------------------------------------------------------------------------------
  9. const astUtils = require("./utils/ast-utils");
  10. //------------------------------------------------------------------------------
  11. // Rule Definition
  12. //------------------------------------------------------------------------------
  13. const validParent = new Set(["Program", "StaticBlock", "ExportNamedDeclaration", "ExportDefaultDeclaration"]);
  14. const validBlockStatementParent = new Set(["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"]);
  15. /**
  16. * Finds the nearest enclosing context where this rule allows declarations and returns its description.
  17. * @param {ASTNode} node Node to search from.
  18. * @returns {string} Description. One of "program", "function body", "class static block body".
  19. */
  20. function getAllowedBodyDescription(node) {
  21. let { parent } = node;
  22. while (parent) {
  23. if (parent.type === "StaticBlock") {
  24. return "class static block body";
  25. }
  26. if (astUtils.isFunction(parent)) {
  27. return "function body";
  28. }
  29. ({ parent } = parent);
  30. }
  31. return "program";
  32. }
  33. /** @type {import('../shared/types').Rule} */
  34. module.exports = {
  35. meta: {
  36. type: "problem",
  37. docs: {
  38. description: "Disallow variable or `function` declarations in nested blocks",
  39. recommended: false,
  40. url: "https://eslint.org/docs/latest/rules/no-inner-declarations"
  41. },
  42. schema: [
  43. {
  44. enum: ["functions", "both"]
  45. },
  46. {
  47. type: "object",
  48. properties: {
  49. blockScopedFunctions: {
  50. enum: ["allow", "disallow"]
  51. }
  52. },
  53. additionalProperties: false
  54. }
  55. ],
  56. messages: {
  57. moveDeclToRoot: "Move {{type}} declaration to {{body}} root."
  58. }
  59. },
  60. create(context) {
  61. const sourceCode = context.sourceCode;
  62. const ecmaVersion = context.languageOptions.ecmaVersion;
  63. const blockScopedFunctions = context.options[1]?.blockScopedFunctions ?? "allow";
  64. /**
  65. * Ensure that a given node is at a program or function body's root.
  66. * @param {ASTNode} node Declaration node to check.
  67. * @returns {void}
  68. */
  69. function check(node) {
  70. const parent = node.parent;
  71. if (
  72. parent.type === "BlockStatement" && validBlockStatementParent.has(parent.parent.type)
  73. ) {
  74. return;
  75. }
  76. if (validParent.has(parent.type)) {
  77. return;
  78. }
  79. context.report({
  80. node,
  81. messageId: "moveDeclToRoot",
  82. data: {
  83. type: (node.type === "FunctionDeclaration" ? "function" : "variable"),
  84. body: getAllowedBodyDescription(node)
  85. }
  86. });
  87. }
  88. return {
  89. FunctionDeclaration(node) {
  90. const isInStrictCode = sourceCode.getScope(node).upper.isStrict;
  91. if (blockScopedFunctions === "allow" && ecmaVersion >= 2015 && isInStrictCode) {
  92. return;
  93. }
  94. check(node);
  95. },
  96. VariableDeclaration(node) {
  97. if (context.options[0] === "both" && node.kind === "var") {
  98. check(node);
  99. }
  100. }
  101. };
  102. }
  103. };