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