mirror of https://github.com/lukechilds/node.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
162 lines
5.0 KiB
162 lines
5.0 KiB
/**
|
|
* @fileoverview Rule to check for the usage of var.
|
|
* @author Jamund Ferguson
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Helpers
|
|
//------------------------------------------------------------------------------
|
|
|
|
const SCOPE_NODE_TYPE = /^(?:Program|BlockStatement|SwitchStatement|ForStatement|ForInStatement|ForOfStatement)$/;
|
|
|
|
/**
|
|
* Gets the scope node which directly contains a given node.
|
|
*
|
|
* @param {ASTNode} node - A node to get. This is a `VariableDeclaration` or
|
|
* an `Identifier`.
|
|
* @returns {ASTNode} A scope node. This is one of `Program`, `BlockStatement`,
|
|
* `SwitchStatement`, `ForStatement`, `ForInStatement`, and
|
|
* `ForOfStatement`.
|
|
*/
|
|
function getScopeNode(node) {
|
|
while (node) {
|
|
if (SCOPE_NODE_TYPE.test(node.type)) {
|
|
return node;
|
|
}
|
|
|
|
node = node.parent;
|
|
}
|
|
|
|
/* istanbul ignore next : unreachable */
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Checks whether a given variable is redeclared or not.
|
|
*
|
|
* @param {escope.Variable} variable - A variable to check.
|
|
* @returns {boolean} `true` if the variable is redeclared.
|
|
*/
|
|
function isRedeclared(variable) {
|
|
return variable.defs.length >= 2;
|
|
}
|
|
|
|
/**
|
|
* Checks whether a given variable is used from outside of the specified scope.
|
|
*
|
|
* @param {ASTNode} scopeNode - A scope node to check.
|
|
* @returns {Function} The predicate function which checks whether a given
|
|
* variable is used from outside of the specified scope.
|
|
*/
|
|
function isUsedFromOutsideOf(scopeNode) {
|
|
|
|
/**
|
|
* Checks whether a given reference is inside of the specified scope or not.
|
|
*
|
|
* @param {escope.Reference} reference - A reference to check.
|
|
* @returns {boolean} `true` if the reference is inside of the specified
|
|
* scope.
|
|
*/
|
|
function isOutsideOfScope(reference) {
|
|
const scope = scopeNode.range;
|
|
const id = reference.identifier.range;
|
|
|
|
return id[0] < scope[0] || id[1] > scope[1];
|
|
}
|
|
|
|
return function(variable) {
|
|
return variable.references.some(isOutsideOfScope);
|
|
};
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Rule Definition
|
|
//------------------------------------------------------------------------------
|
|
|
|
module.exports = {
|
|
meta: {
|
|
docs: {
|
|
description: "require `let` or `const` instead of `var`",
|
|
category: "ECMAScript 6",
|
|
recommended: false
|
|
},
|
|
|
|
schema: [],
|
|
fixable: "code"
|
|
},
|
|
|
|
create(context) {
|
|
const sourceCode = context.getSourceCode();
|
|
|
|
/**
|
|
* Checks whether it can fix a given variable declaration or not.
|
|
* It cannot fix if the following cases:
|
|
*
|
|
* - A variable is declared on a SwitchCase node.
|
|
* - A variable is redeclared.
|
|
* - A variable is used from outside the scope.
|
|
*
|
|
* ## A variable is declared on a SwitchCase node.
|
|
*
|
|
* If this rule modifies 'var' declarations on a SwitchCase node, it
|
|
* would generate the warnings of 'no-case-declarations' rule. And the
|
|
* 'eslint:recommended' preset includes 'no-case-declarations' rule, so
|
|
* this rule doesn't modify those declarations.
|
|
*
|
|
* ## A variable is redeclared.
|
|
*
|
|
* The language spec disallows redeclarations of `let` declarations.
|
|
* Those variables would cause syntax errors.
|
|
*
|
|
* ## A variable is used from outside the scope.
|
|
*
|
|
* The language spec disallows accesses from outside of the scope for
|
|
* `let` declarations. Those variables would cause reference errors.
|
|
*
|
|
* @param {ASTNode} node - A variable declaration node to check.
|
|
* @returns {boolean} `true` if it can fix the node.
|
|
*/
|
|
function canFix(node) {
|
|
const variables = context.getDeclaredVariables(node);
|
|
const scopeNode = getScopeNode(node);
|
|
|
|
return !(
|
|
node.parent.type === "SwitchCase" ||
|
|
variables.some(isRedeclared) ||
|
|
variables.some(isUsedFromOutsideOf(scopeNode))
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Reports a given variable declaration node.
|
|
*
|
|
* @param {ASTNode} node - A variable declaration node to report.
|
|
* @returns {void}
|
|
*/
|
|
function report(node) {
|
|
const varToken = sourceCode.getFirstToken(node);
|
|
|
|
context.report({
|
|
node,
|
|
message: "Unexpected var, use let or const instead.",
|
|
|
|
fix(fixer) {
|
|
if (canFix(node)) {
|
|
return fixer.replaceText(varToken, "let");
|
|
}
|
|
return null;
|
|
}
|
|
});
|
|
}
|
|
|
|
return {
|
|
VariableDeclaration(node) {
|
|
if (node.kind === "var") {
|
|
report(node);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
};
|
|
|