154 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			154 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /**
 | |
|  * @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/latest/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 = [];
 | |
|         const sourceCode = context.sourceCode;
 | |
| 
 | |
|         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.
 | |
|          * @param {ASTNode} node The node that represents the scope to check.
 | |
|          * @returns {void}
 | |
|          */
 | |
|         function ensureWasAssigned(node) {
 | |
|             const scope = sourceCode.getScope(node);
 | |
| 
 | |
|             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);
 | |
|                 }
 | |
|             }
 | |
|         };
 | |
| 
 | |
|     }
 | |
| };
 |