init-declarations.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. /**
  2. * @fileoverview A rule to control the style of variable initializations.
  3. * @author Colin Ihrig
  4. */
  5. "use strict";
  6. //------------------------------------------------------------------------------
  7. // Helpers
  8. //------------------------------------------------------------------------------
  9. /**
  10. * Checks whether or not a given node is a for loop.
  11. * @param {ASTNode} block A node to check.
  12. * @returns {boolean} `true` when the node is a for loop.
  13. */
  14. function isForLoop(block) {
  15. return block.type === "ForInStatement" ||
  16. block.type === "ForOfStatement" ||
  17. block.type === "ForStatement";
  18. }
  19. /**
  20. * Checks whether or not a given declarator node has its initializer.
  21. * @param {ASTNode} node A declarator node to check.
  22. * @returns {boolean} `true` when the node has its initializer.
  23. */
  24. function isInitialized(node) {
  25. const declaration = node.parent;
  26. const block = declaration.parent;
  27. if (isForLoop(block)) {
  28. if (block.type === "ForStatement") {
  29. return block.init === declaration;
  30. }
  31. return block.left === declaration;
  32. }
  33. return Boolean(node.init);
  34. }
  35. //------------------------------------------------------------------------------
  36. // Rule Definition
  37. //------------------------------------------------------------------------------
  38. /** @type {import('../shared/types').Rule} */
  39. module.exports = {
  40. meta: {
  41. type: "suggestion",
  42. docs: {
  43. description: "Require or disallow initialization in variable declarations",
  44. recommended: false,
  45. url: "https://eslint.org/docs/rules/init-declarations"
  46. },
  47. schema: {
  48. anyOf: [
  49. {
  50. type: "array",
  51. items: [
  52. {
  53. enum: ["always"]
  54. }
  55. ],
  56. minItems: 0,
  57. maxItems: 1
  58. },
  59. {
  60. type: "array",
  61. items: [
  62. {
  63. enum: ["never"]
  64. },
  65. {
  66. type: "object",
  67. properties: {
  68. ignoreForLoopInit: {
  69. type: "boolean"
  70. }
  71. },
  72. additionalProperties: false
  73. }
  74. ],
  75. minItems: 0,
  76. maxItems: 2
  77. }
  78. ]
  79. },
  80. messages: {
  81. initialized: "Variable '{{idName}}' should be initialized on declaration.",
  82. notInitialized: "Variable '{{idName}}' should not be initialized on declaration."
  83. }
  84. },
  85. create(context) {
  86. const MODE_ALWAYS = "always",
  87. MODE_NEVER = "never";
  88. const mode = context.options[0] || MODE_ALWAYS;
  89. const params = context.options[1] || {};
  90. //--------------------------------------------------------------------------
  91. // Public API
  92. //--------------------------------------------------------------------------
  93. return {
  94. "VariableDeclaration:exit"(node) {
  95. const kind = node.kind,
  96. declarations = node.declarations;
  97. for (let i = 0; i < declarations.length; ++i) {
  98. const declaration = declarations[i],
  99. id = declaration.id,
  100. initialized = isInitialized(declaration),
  101. isIgnoredForLoop = params.ignoreForLoopInit && isForLoop(node.parent);
  102. let messageId = "";
  103. if (mode === MODE_ALWAYS && !initialized) {
  104. messageId = "initialized";
  105. } else if (mode === MODE_NEVER && kind !== "const" && initialized && !isIgnoredForLoop) {
  106. messageId = "notInitialized";
  107. }
  108. if (id.type === "Identifier" && messageId) {
  109. context.report({
  110. node: declaration,
  111. messageId,
  112. data: {
  113. idName: id.name
  114. }
  115. });
  116. }
  117. }
  118. }
  119. };
  120. }
  121. };