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 */ 18function 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 */ 53function 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 */ 79function 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 */ 98function 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 112module.exports = { 113 meta: { 114 deprecated: true, 115 replacedBy: ["id-denylist"], 116 117 type: "suggestion", 118 119 docs: { 120 description: "disallow specified identifiers", 121 category: "Stylistic Issues", 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: { 134 restricted: "Identifier '{{name}}' is restricted." 135 } 136 }, 137 138 create(context) { 139 140 const denyList = new Set(context.options); 141 const reportedNodes = new Set(); 142 143 let globalScope; 144 145 /** 146 * Checks whether the given name is restricted. 147 * @param {string} name The name to check. 148 * @returns {boolean} `true` if the name is restricted. 149 * @private 150 */ 151 function isRestricted(name) { 152 return denyList.has(name); 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. 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. 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) { 208 if (!reportedNodes.has(node)) { 209 context.report({ 210 node, 211 messageId: "restricted", 212 data: { 213 name: node.name 214 } 215 }); 216 reportedNodes.add(node); 217 } 218 } 219 220 return { 221 222 Program() { 223 globalScope = context.getScope(); 224 }, 225 226 Identifier(node) { 227 if (isRestricted(node.name) && shouldCheck(node)) { 228 report(node); 229 } 230 } 231 }; 232 } 233}; 234