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