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.
151 lines
4.8 KiB
151 lines
4.8 KiB
/** |
|
* @fileoverview Rule to enforce consistent naming of "this" context variables |
|
* @author Raphael Pigulla |
|
*/ |
|
"use strict"; |
|
|
|
//------------------------------------------------------------------------------ |
|
// Rule Definition |
|
//------------------------------------------------------------------------------ |
|
|
|
/** @type {import('../shared/types').Rule} */ |
|
module.exports = { |
|
meta: { |
|
type: "suggestion", |
|
|
|
docs: { |
|
description: "Enforce consistent naming when capturing the current execution context", |
|
recommended: false, |
|
url: "https://eslint.org/docs/rules/consistent-this" |
|
}, |
|
|
|
schema: { |
|
type: "array", |
|
items: { |
|
type: "string", |
|
minLength: 1 |
|
}, |
|
uniqueItems: true |
|
}, |
|
|
|
messages: { |
|
aliasNotAssignedToThis: "Designated alias '{{name}}' is not assigned to 'this'.", |
|
unexpectedAlias: "Unexpected alias '{{name}}' for 'this'." |
|
} |
|
}, |
|
|
|
create(context) { |
|
let aliases = []; |
|
|
|
if (context.options.length === 0) { |
|
aliases.push("that"); |
|
} else { |
|
aliases = context.options; |
|
} |
|
|
|
/** |
|
* Reports that a variable declarator or assignment expression is assigning |
|
* a non-'this' value to the specified alias. |
|
* @param {ASTNode} node The assigning node. |
|
* @param {string} name the name of the alias that was incorrectly used. |
|
* @returns {void} |
|
*/ |
|
function reportBadAssignment(node, name) { |
|
context.report({ node, messageId: "aliasNotAssignedToThis", data: { name } }); |
|
} |
|
|
|
/** |
|
* Checks that an assignment to an identifier only assigns 'this' to the |
|
* appropriate alias, and the alias is only assigned to 'this'. |
|
* @param {ASTNode} node The assigning node. |
|
* @param {Identifier} name The name of the variable assigned to. |
|
* @param {Expression} value The value of the assignment. |
|
* @returns {void} |
|
*/ |
|
function checkAssignment(node, name, value) { |
|
const isThis = value.type === "ThisExpression"; |
|
|
|
if (aliases.includes(name)) { |
|
if (!isThis || node.operator && node.operator !== "=") { |
|
reportBadAssignment(node, name); |
|
} |
|
} else if (isThis) { |
|
context.report({ node, messageId: "unexpectedAlias", data: { name } }); |
|
} |
|
} |
|
|
|
/** |
|
* Ensures that a variable declaration of the alias in a program or function |
|
* is assigned to the correct value. |
|
* @param {string} alias alias the check the assignment of. |
|
* @param {Object} scope scope of the current code we are checking. |
|
* @private |
|
* @returns {void} |
|
*/ |
|
function checkWasAssigned(alias, scope) { |
|
const variable = scope.set.get(alias); |
|
|
|
if (!variable) { |
|
return; |
|
} |
|
|
|
if (variable.defs.some(def => def.node.type === "VariableDeclarator" && |
|
def.node.init !== null)) { |
|
return; |
|
} |
|
|
|
/* |
|
* The alias has been declared and not assigned: check it was |
|
* assigned later in the same scope. |
|
*/ |
|
if (!variable.references.some(reference => { |
|
const write = reference.writeExpr; |
|
|
|
return ( |
|
reference.from === scope && |
|
write && write.type === "ThisExpression" && |
|
write.parent.operator === "=" |
|
); |
|
})) { |
|
variable.defs.map(def => def.node).forEach(node => { |
|
reportBadAssignment(node, alias); |
|
}); |
|
} |
|
} |
|
|
|
/** |
|
* Check each alias to ensure that is was assigned to the correct value. |
|
* @returns {void} |
|
*/ |
|
function ensureWasAssigned() { |
|
const scope = context.getScope(); |
|
|
|
aliases.forEach(alias => { |
|
checkWasAssigned(alias, scope); |
|
}); |
|
} |
|
|
|
return { |
|
"Program:exit": ensureWasAssigned, |
|
"FunctionExpression:exit": ensureWasAssigned, |
|
"FunctionDeclaration:exit": ensureWasAssigned, |
|
|
|
VariableDeclarator(node) { |
|
const id = node.id; |
|
const isDestructuring = |
|
id.type === "ArrayPattern" || id.type === "ObjectPattern"; |
|
|
|
if (node.init !== null && !isDestructuring) { |
|
checkAssignment(node, id.name, node.init); |
|
} |
|
}, |
|
|
|
AssignmentExpression(node) { |
|
if (node.left.type === "Identifier") { |
|
checkAssignment(node, node.left.name, node.right); |
|
} |
|
} |
|
}; |
|
|
|
} |
|
};
|
|
|