]>
Commit | Line | Data |
---|---|---|
eb39fafa DC |
1 | /** |
2 | * @fileoverview Rule that warns when identifier names that are | |
6f036462 | 3 | * specified in the configuration are used. |
eb39fafa | 4 | * @author Keith Cirkel (http://keithcirkel.co.uk) |
609c276f | 5 | * @deprecated in ESLint v7.5.0 |
eb39fafa DC |
6 | */ |
7 | ||
8 | "use strict"; | |
9 | ||
10 | //------------------------------------------------------------------------------ | |
11 | // Helpers | |
12 | //------------------------------------------------------------------------------ | |
13 | ||
14 | /** | |
15 | * Checks whether the given node represents assignment target in a normal assignment or destructuring. | |
16 | * @param {ASTNode} node The node to check. | |
17 | * @returns {boolean} `true` if the node is assignment target. | |
18 | */ | |
19 | function isAssignmentTarget(node) { | |
20 | const parent = node.parent; | |
21 | ||
22 | return ( | |
23 | ||
24 | // normal assignment | |
25 | ( | |
26 | parent.type === "AssignmentExpression" && | |
27 | parent.left === node | |
28 | ) || | |
29 | ||
30 | // destructuring | |
31 | parent.type === "ArrayPattern" || | |
32 | parent.type === "RestElement" || | |
33 | ( | |
34 | parent.type === "Property" && | |
35 | parent.value === node && | |
36 | parent.parent.type === "ObjectPattern" | |
37 | ) || | |
38 | ( | |
39 | parent.type === "AssignmentPattern" && | |
40 | parent.left === node | |
41 | ) | |
42 | ); | |
43 | } | |
44 | ||
45 | /** | |
46 | * Checks whether the given node represents an imported name that is renamed in the same import/export specifier. | |
47 | * | |
48 | * Examples: | |
49 | * import { a as b } from 'mod'; // node `a` is renamed import | |
50 | * export { a as b } from 'mod'; // node `a` is renamed import | |
51 | * @param {ASTNode} node `Identifier` node to check. | |
52 | * @returns {boolean} `true` if the node is a renamed import. | |
53 | */ | |
54 | function isRenamedImport(node) { | |
55 | const parent = node.parent; | |
56 | ||
57 | return ( | |
58 | ( | |
59 | parent.type === "ImportSpecifier" && | |
60 | parent.imported !== parent.local && | |
61 | parent.imported === node | |
62 | ) || | |
63 | ( | |
64 | parent.type === "ExportSpecifier" && | |
65 | parent.parent.source && // re-export | |
66 | parent.local !== parent.exported && | |
67 | parent.local === node | |
68 | ) | |
69 | ); | |
70 | } | |
71 | ||
72 | /** | |
73 | * Checks whether the given node is a renamed identifier node in an ObjectPattern destructuring. | |
74 | * | |
75 | * Examples: | |
76 | * const { a : b } = foo; // node `a` is renamed node. | |
77 | * @param {ASTNode} node `Identifier` node to check. | |
78 | * @returns {boolean} `true` if the node is a renamed node in an ObjectPattern destructuring. | |
79 | */ | |
80 | function isRenamedInDestructuring(node) { | |
81 | const parent = node.parent; | |
82 | ||
83 | return ( | |
84 | ( | |
85 | !parent.computed && | |
86 | parent.type === "Property" && | |
87 | parent.parent.type === "ObjectPattern" && | |
88 | parent.value !== node && | |
89 | parent.key === node | |
90 | ) | |
91 | ); | |
92 | } | |
93 | ||
94 | /** | |
95 | * Checks whether the given node represents shorthand definition of a property in an object literal. | |
96 | * @param {ASTNode} node `Identifier` node to check. | |
97 | * @returns {boolean} `true` if the node is a shorthand property definition. | |
98 | */ | |
99 | function isShorthandPropertyDefinition(node) { | |
100 | const parent = node.parent; | |
101 | ||
102 | return ( | |
103 | parent.type === "Property" && | |
104 | parent.parent.type === "ObjectExpression" && | |
105 | parent.shorthand | |
106 | ); | |
107 | } | |
108 | ||
109 | //------------------------------------------------------------------------------ | |
110 | // Rule Definition | |
111 | //------------------------------------------------------------------------------ | |
112 | ||
113 | module.exports = { | |
114 | meta: { | |
6f036462 TL |
115 | deprecated: true, |
116 | replacedBy: ["id-denylist"], | |
117 | ||
eb39fafa DC |
118 | type: "suggestion", |
119 | ||
120 | docs: { | |
121 | description: "disallow specified identifiers", | |
eb39fafa DC |
122 | recommended: false, |
123 | url: "https://eslint.org/docs/rules/id-blacklist" | |
124 | }, | |
125 | ||
126 | schema: { | |
127 | type: "array", | |
128 | items: { | |
129 | type: "string" | |
130 | }, | |
131 | uniqueItems: true | |
132 | }, | |
133 | messages: { | |
6f036462 | 134 | restricted: "Identifier '{{name}}' is restricted." |
eb39fafa DC |
135 | } |
136 | }, | |
137 | ||
138 | create(context) { | |
139 | ||
6f036462 | 140 | const denyList = new Set(context.options); |
eb39fafa DC |
141 | const reportedNodes = new Set(); |
142 | ||
143 | let globalScope; | |
144 | ||
145 | /** | |
6f036462 | 146 | * Checks whether the given name is restricted. |
eb39fafa | 147 | * @param {string} name The name to check. |
6f036462 | 148 | * @returns {boolean} `true` if the name is restricted. |
eb39fafa DC |
149 | * @private |
150 | */ | |
6f036462 TL |
151 | function isRestricted(name) { |
152 | return denyList.has(name); | |
eb39fafa DC |
153 | } |
154 | ||
155 | /** | |
156 | * Checks whether the given node represents a reference to a global variable that is not declared in the source code. | |
157 | * These identifiers will be allowed, as it is assumed that user has no control over the names of external global variables. | |
158 | * @param {ASTNode} node `Identifier` node to check. | |
159 | * @returns {boolean} `true` if the node is a reference to a global variable. | |
160 | */ | |
161 | function isReferenceToGlobalVariable(node) { | |
162 | const variable = globalScope.set.get(node.name); | |
163 | ||
164 | return variable && variable.defs.length === 0 && | |
165 | variable.references.some(ref => ref.identifier === node); | |
166 | } | |
167 | ||
168 | /** | |
169 | * Determines whether the given node should be checked. | |
170 | * @param {ASTNode} node `Identifier` node. | |
171 | * @returns {boolean} `true` if the node should be checked. | |
172 | */ | |
173 | function shouldCheck(node) { | |
174 | const parent = node.parent; | |
175 | ||
176 | /* | |
177 | * Member access has special rules for checking property names. | |
6f036462 TL |
178 | * Read access to a property with a restricted name is allowed, because it can be on an object that user has no control over. |
179 | * Write access isn't allowed, because it potentially creates a new property with a restricted name. | |
eb39fafa DC |
180 | */ |
181 | if ( | |
182 | parent.type === "MemberExpression" && | |
183 | parent.property === node && | |
184 | !parent.computed | |
185 | ) { | |
186 | return isAssignmentTarget(parent); | |
187 | } | |
188 | ||
189 | return ( | |
190 | parent.type !== "CallExpression" && | |
191 | parent.type !== "NewExpression" && | |
192 | !isRenamedImport(node) && | |
193 | !isRenamedInDestructuring(node) && | |
194 | !( | |
195 | isReferenceToGlobalVariable(node) && | |
196 | !isShorthandPropertyDefinition(node) | |
197 | ) | |
198 | ); | |
199 | } | |
200 | ||
201 | /** | |
202 | * Reports an AST node as a rule violation. | |
203 | * @param {ASTNode} node The node to report. | |
204 | * @returns {void} | |
205 | * @private | |
206 | */ | |
207 | function report(node) { | |
609c276f TL |
208 | |
209 | /* | |
210 | * We used the range instead of the node because it's possible | |
211 | * for the same identifier to be represented by two different | |
212 | * nodes, with the most clear example being shorthand properties: | |
213 | * { foo } | |
214 | * In this case, "foo" is represented by one node for the name | |
215 | * and one for the value. The only way to know they are the same | |
216 | * is to look at the range. | |
217 | */ | |
218 | if (!reportedNodes.has(node.range.toString())) { | |
eb39fafa DC |
219 | context.report({ |
220 | node, | |
6f036462 | 221 | messageId: "restricted", |
eb39fafa DC |
222 | data: { |
223 | name: node.name | |
224 | } | |
225 | }); | |
609c276f | 226 | reportedNodes.add(node.range.toString()); |
eb39fafa | 227 | } |
609c276f | 228 | |
eb39fafa DC |
229 | } |
230 | ||
231 | return { | |
232 | ||
233 | Program() { | |
234 | globalScope = context.getScope(); | |
235 | }, | |
236 | ||
237 | Identifier(node) { | |
6f036462 | 238 | if (isRestricted(node.name) && shouldCheck(node)) { |
eb39fafa DC |
239 | report(node); |
240 | } | |
241 | } | |
242 | }; | |
243 | } | |
244 | }; |