| /** | 
|  * @fileoverview Rule to flag when the same variable is declared more then once. | 
|  * @author Ilya Volodin | 
|  */ | 
|   | 
| "use strict"; | 
|   | 
| //------------------------------------------------------------------------------ | 
| // Requirements | 
| //------------------------------------------------------------------------------ | 
|   | 
| const astUtils = require("./utils/ast-utils"); | 
|   | 
| //------------------------------------------------------------------------------ | 
| // Rule Definition | 
| //------------------------------------------------------------------------------ | 
|   | 
| module.exports = { | 
|     meta: { | 
|         type: "suggestion", | 
|   | 
|         docs: { | 
|             description: "disallow variable redeclaration", | 
|             category: "Best Practices", | 
|             recommended: true, | 
|             url: "https://eslint.org/docs/rules/no-redeclare" | 
|         }, | 
|   | 
|         messages: { | 
|             redeclared: "'{{id}}' is already defined.", | 
|             redeclaredAsBuiltin: "'{{id}}' is already defined as a built-in global variable.", | 
|             redeclaredBySyntax: "'{{id}}' is already defined by a variable declaration." | 
|         }, | 
|   | 
|         schema: [ | 
|             { | 
|                 type: "object", | 
|                 properties: { | 
|                     builtinGlobals: { type: "boolean", default: true } | 
|                 }, | 
|                 additionalProperties: false | 
|             } | 
|         ] | 
|     }, | 
|   | 
|     create(context) { | 
|         const options = { | 
|             builtinGlobals: Boolean( | 
|                 context.options.length === 0 || | 
|                 context.options[0].builtinGlobals | 
|             ) | 
|         }; | 
|         const sourceCode = context.getSourceCode(); | 
|   | 
|         /** | 
|          * Iterate declarations of a given variable. | 
|          * @param {escope.variable} variable The variable object to iterate declarations. | 
|          * @returns {IterableIterator<{type:string,node:ASTNode,loc:SourceLocation}>} The declarations. | 
|          */ | 
|         function *iterateDeclarations(variable) { | 
|             if (options.builtinGlobals && ( | 
|                 variable.eslintImplicitGlobalSetting === "readonly" || | 
|                 variable.eslintImplicitGlobalSetting === "writable" | 
|             )) { | 
|                 yield { type: "builtin" }; | 
|             } | 
|   | 
|             for (const id of variable.identifiers) { | 
|                 yield { type: "syntax", node: id, loc: id.loc }; | 
|             } | 
|   | 
|             if (variable.eslintExplicitGlobalComments) { | 
|                 for (const comment of variable.eslintExplicitGlobalComments) { | 
|                     yield { | 
|                         type: "comment", | 
|                         node: comment, | 
|                         loc: astUtils.getNameLocationInGlobalDirectiveComment( | 
|                             sourceCode, | 
|                             comment, | 
|                             variable.name | 
|                         ) | 
|                     }; | 
|                 } | 
|             } | 
|         } | 
|   | 
|         /** | 
|          * Find variables in a given scope and flag redeclared ones. | 
|          * @param {Scope} scope - An eslint-scope scope object. | 
|          * @returns {void} | 
|          * @private | 
|          */ | 
|         function findVariablesInScope(scope) { | 
|             for (const variable of scope.variables) { | 
|                 const [ | 
|                     declaration, | 
|                     ...extraDeclarations | 
|                 ] = iterateDeclarations(variable); | 
|   | 
|                 if (extraDeclarations.length === 0) { | 
|                     continue; | 
|                 } | 
|   | 
|                 /* | 
|                  * If the type of a declaration is different from the type of | 
|                  * the first declaration, it shows the location of the first | 
|                  * declaration. | 
|                  */ | 
|                 const detailMessageId = declaration.type === "builtin" | 
|                     ? "redeclaredAsBuiltin" | 
|                     : "redeclaredBySyntax"; | 
|                 const data = { id: variable.name }; | 
|   | 
|                 // Report extra declarations. | 
|                 for (const { type, node, loc } of extraDeclarations) { | 
|                     const messageId = type === declaration.type | 
|                         ? "redeclared" | 
|                         : detailMessageId; | 
|   | 
|                     context.report({ node, loc, messageId, data }); | 
|                 } | 
|             } | 
|         } | 
|   | 
|         /** | 
|          * Find variables in the current scope. | 
|          * @param {ASTNode} node The node of the current scope. | 
|          * @returns {void} | 
|          * @private | 
|          */ | 
|         function checkForBlock(node) { | 
|             const scope = context.getScope(); | 
|   | 
|             /* | 
|              * In ES5, some node type such as `BlockStatement` doesn't have that scope. | 
|              * `scope.block` is a different node in such a case. | 
|              */ | 
|             if (scope.block === node) { | 
|                 findVariablesInScope(scope); | 
|             } | 
|         } | 
|   | 
|         return { | 
|             Program() { | 
|                 const scope = context.getScope(); | 
|   | 
|                 findVariablesInScope(scope); | 
|   | 
|                 // Node.js or ES modules has a special scope. | 
|                 if ( | 
|                     scope.type === "global" && | 
|                     scope.childScopes[0] && | 
|   | 
|                     // The special scope's block is the Program node. | 
|                     scope.block === scope.childScopes[0].block | 
|                 ) { | 
|                     findVariablesInScope(scope.childScopes[0]); | 
|                 } | 
|             }, | 
|   | 
|             FunctionDeclaration: checkForBlock, | 
|             FunctionExpression: checkForBlock, | 
|             ArrowFunctionExpression: checkForBlock, | 
|   | 
|             BlockStatement: checkForBlock, | 
|             ForStatement: checkForBlock, | 
|             ForInStatement: checkForBlock, | 
|             ForOfStatement: checkForBlock, | 
|             SwitchStatement: checkForBlock | 
|         }; | 
|     } | 
| }; |