--- /dev/null
+/**
+ * @fileoverview Rule to flag use of console object
+ * @author Nicholas C. Zakas
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("./utils/ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ type: "suggestion",
+
+ docs: {
+ description: "disallow the use of `console`",
+ category: "Possible Errors",
+ recommended: false,
+ url: "https://eslint.org/docs/rules/no-console"
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ allow: {
+ type: "array",
+ items: {
+ type: "string"
+ },
+ minItems: 1,
+ uniqueItems: true
+ }
+ },
+ additionalProperties: false
+ }
+ ],
+
+ messages: {
+ unexpected: "Unexpected console statement."
+ }
+ },
+
+ create(context) {
+ const options = context.options[0] || {};
+ const allowed = options.allow || [];
+
+ /**
+ * Checks whether the given reference is 'console' or not.
+ * @param {eslint-scope.Reference} reference The reference to check.
+ * @returns {boolean} `true` if the reference is 'console'.
+ */
+ function isConsole(reference) {
+ const id = reference.identifier;
+
+ return id && id.name === "console";
+ }
+
+ /**
+ * Checks whether the property name of the given MemberExpression node
+ * is allowed by options or not.
+ * @param {ASTNode} node The MemberExpression node to check.
+ * @returns {boolean} `true` if the property name of the node is allowed.
+ */
+ function isAllowed(node) {
+ const propertyName = astUtils.getStaticPropertyName(node);
+
+ return propertyName && allowed.indexOf(propertyName) !== -1;
+ }
+
+ /**
+ * Checks whether the given reference is a member access which is not
+ * allowed by options or not.
+ * @param {eslint-scope.Reference} reference The reference to check.
+ * @returns {boolean} `true` if the reference is a member access which
+ * is not allowed by options.
+ */
+ function isMemberAccessExceptAllowed(reference) {
+ const node = reference.identifier;
+ const parent = node.parent;
+
+ return (
+ parent.type === "MemberExpression" &&
+ parent.object === node &&
+ !isAllowed(parent)
+ );
+ }
+
+ /**
+ * Reports the given reference as a violation.
+ * @param {eslint-scope.Reference} reference The reference to report.
+ * @returns {void}
+ */
+ function report(reference) {
+ const node = reference.identifier.parent;
+
+ context.report({
+ node,
+ loc: node.loc,
+ messageId: "unexpected"
+ });
+ }
+
+ return {
+ "Program:exit"() {
+ const scope = context.getScope();
+ const consoleVar = astUtils.getVariableByName(scope, "console");
+ const shadowed = consoleVar && consoleVar.defs.length > 0;
+
+ /*
+ * 'scope.through' includes all references to undefined
+ * variables. If the variable 'console' is not defined, it uses
+ * 'scope.through'.
+ */
+ const references = consoleVar
+ ? consoleVar.references
+ : scope.through.filter(isConsole);
+
+ if (!shadowed) {
+ references
+ .filter(isMemberAccessExceptAllowed)
+ .forEach(report);
+ }
+ }
+ };
+ }
+};