1/** 2 * @fileoverview Rule to flag use of console object 3 * @author Nicholas C. Zakas 4 */ 5 6"use strict"; 7 8//------------------------------------------------------------------------------ 9// Requirements 10//------------------------------------------------------------------------------ 11 12const astUtils = require("./utils/ast-utils"); 13 14//------------------------------------------------------------------------------ 15// Rule Definition 16//------------------------------------------------------------------------------ 17 18module.exports = { 19 meta: { 20 type: "suggestion", 21 22 docs: { 23 description: "disallow the use of `console`", 24 category: "Possible Errors", 25 recommended: false, 26 url: "https://eslint.org/docs/rules/no-console" 27 }, 28 29 schema: [ 30 { 31 type: "object", 32 properties: { 33 allow: { 34 type: "array", 35 items: { 36 type: "string" 37 }, 38 minItems: 1, 39 uniqueItems: true 40 } 41 }, 42 additionalProperties: false 43 } 44 ], 45 46 messages: { 47 unexpected: "Unexpected console statement." 48 } 49 }, 50 51 create(context) { 52 const options = context.options[0] || {}; 53 const allowed = options.allow || []; 54 55 /** 56 * Checks whether the given reference is 'console' or not. 57 * @param {eslint-scope.Reference} reference The reference to check. 58 * @returns {boolean} `true` if the reference is 'console'. 59 */ 60 function isConsole(reference) { 61 const id = reference.identifier; 62 63 return id && id.name === "console"; 64 } 65 66 /** 67 * Checks whether the property name of the given MemberExpression node 68 * is allowed by options or not. 69 * @param {ASTNode} node The MemberExpression node to check. 70 * @returns {boolean} `true` if the property name of the node is allowed. 71 */ 72 function isAllowed(node) { 73 const propertyName = astUtils.getStaticPropertyName(node); 74 75 return propertyName && allowed.indexOf(propertyName) !== -1; 76 } 77 78 /** 79 * Checks whether the given reference is a member access which is not 80 * allowed by options or not. 81 * @param {eslint-scope.Reference} reference The reference to check. 82 * @returns {boolean} `true` if the reference is a member access which 83 * is not allowed by options. 84 */ 85 function isMemberAccessExceptAllowed(reference) { 86 const node = reference.identifier; 87 const parent = node.parent; 88 89 return ( 90 parent.type === "MemberExpression" && 91 parent.object === node && 92 !isAllowed(parent) 93 ); 94 } 95 96 /** 97 * Reports the given reference as a violation. 98 * @param {eslint-scope.Reference} reference The reference to report. 99 * @returns {void} 100 */ 101 function report(reference) { 102 const node = reference.identifier.parent; 103 104 context.report({ 105 node, 106 loc: node.loc, 107 messageId: "unexpected" 108 }); 109 } 110 111 return { 112 "Program:exit"() { 113 const scope = context.getScope(); 114 const consoleVar = astUtils.getVariableByName(scope, "console"); 115 const shadowed = consoleVar && consoleVar.defs.length > 0; 116 117 /* 118 * 'scope.through' includes all references to undefined 119 * variables. If the variable 'console' is not defined, it uses 120 * 'scope.through'. 121 */ 122 const references = consoleVar 123 ? consoleVar.references 124 : scope.through.filter(isConsole); 125 126 if (!shadowed) { 127 references 128 .filter(isMemberAccessExceptAllowed) 129 .forEach(report); 130 } 131 } 132 }; 133 } 134}; 135