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