UNPKG

4.97 kBJavaScriptView Raw
1/**
2 * @fileoverview Rule to enforce consistent naming of "this" context variables
3 * @author Raphael Pigulla
4 */
5"use strict";
6
7//------------------------------------------------------------------------------
8// Rule Definition
9//------------------------------------------------------------------------------
10
11module.exports = {
12 meta: {
13 type: "suggestion",
14
15 docs: {
16 description: "enforce consistent naming when capturing the current execution context",
17 category: "Stylistic Issues",
18 recommended: false,
19 url: "https://eslint.org/docs/rules/consistent-this"
20 },
21
22 schema: {
23 type: "array",
24 items: {
25 type: "string",
26 minLength: 1
27 },
28 uniqueItems: true
29 },
30
31 messages: {
32 aliasNotAssignedToThis: "Designated alias '{{name}}' is not assigned to 'this'.",
33 unexpectedAlias: "Unexpected alias '{{name}}' for 'this'."
34 }
35 },
36
37 create(context) {
38 let aliases = [];
39
40 if (context.options.length === 0) {
41 aliases.push("that");
42 } else {
43 aliases = context.options;
44 }
45
46 /**
47 * Reports that a variable declarator or assignment expression is assigning
48 * a non-'this' value to the specified alias.
49 * @param {ASTNode} node The assigning node.
50 * @param {string} name the name of the alias that was incorrectly used.
51 * @returns {void}
52 */
53 function reportBadAssignment(node, name) {
54 context.report({ node, messageId: "aliasNotAssignedToThis", data: { name } });
55 }
56
57 /**
58 * Checks that an assignment to an identifier only assigns 'this' to the
59 * appropriate alias, and the alias is only assigned to 'this'.
60 * @param {ASTNode} node The assigning node.
61 * @param {Identifier} name The name of the variable assigned to.
62 * @param {Expression} value The value of the assignment.
63 * @returns {void}
64 */
65 function checkAssignment(node, name, value) {
66 const isThis = value.type === "ThisExpression";
67
68 if (aliases.indexOf(name) !== -1) {
69 if (!isThis || node.operator && node.operator !== "=") {
70 reportBadAssignment(node, name);
71 }
72 } else if (isThis) {
73 context.report({ node, messageId: "unexpectedAlias", data: { name } });
74 }
75 }
76
77 /**
78 * Ensures that a variable declaration of the alias in a program or function
79 * is assigned to the correct value.
80 * @param {string} alias alias the check the assignment of.
81 * @param {Object} scope scope of the current code we are checking.
82 * @private
83 * @returns {void}
84 */
85 function checkWasAssigned(alias, scope) {
86 const variable = scope.set.get(alias);
87
88 if (!variable) {
89 return;
90 }
91
92 if (variable.defs.some(def => def.node.type === "VariableDeclarator" &&
93 def.node.init !== null)) {
94 return;
95 }
96
97 /*
98 * The alias has been declared and not assigned: check it was
99 * assigned later in the same scope.
100 */
101 if (!variable.references.some(reference => {
102 const write = reference.writeExpr;
103
104 return (
105 reference.from === scope &&
106 write && write.type === "ThisExpression" &&
107 write.parent.operator === "="
108 );
109 })) {
110 variable.defs.map(def => def.node).forEach(node => {
111 reportBadAssignment(node, alias);
112 });
113 }
114 }
115
116 /**
117 * Check each alias to ensure that is was assigned to the correct value.
118 * @returns {void}
119 */
120 function ensureWasAssigned() {
121 const scope = context.getScope();
122
123 aliases.forEach(alias => {
124 checkWasAssigned(alias, scope);
125 });
126 }
127
128 return {
129 "Program:exit": ensureWasAssigned,
130 "FunctionExpression:exit": ensureWasAssigned,
131 "FunctionDeclaration:exit": ensureWasAssigned,
132
133 VariableDeclarator(node) {
134 const id = node.id;
135 const isDestructuring =
136 id.type === "ArrayPattern" || id.type === "ObjectPattern";
137
138 if (node.init !== null && !isDestructuring) {
139 checkAssignment(node, id.name, node.init);
140 }
141 },
142
143 AssignmentExpression(node) {
144 if (node.left.type === "Identifier") {
145 checkAssignment(node, node.left.name, node.right);
146 }
147 }
148 };
149
150 }
151};