Repository: vuejs/eslint-plugin-vue (js)

Issue:
[New Rule] `vue/no-console`
<!--
  Before proposing a new rule, please make sure it hasn't been posted already.
  You can see all open propositions here:
  https://github.com/vuejs/eslint-plugin-vue/issues?q=is%3Aopen+is%3Aissue+label%3A%22new+rule+proposition%22
-->

**Please describe what the rule should do:**

<!-- A clear and concise description of what the rule should do. -->

Since Vue ^3.3 now [supports console in the template](https://github.com/vuejs/core/issues/7939), a `vue/no-console` rule is needed to regulate its usage.

`vue/no-console` should work exactly the same as [`no-console`](https://eslint.org/docs/latest/rules/no-console) but in the template.

**What category should the rule belong to?**

<!-- (place an "X" next to just one item) -->

[ ] Enforces code style (layout)
[x] Warns about a potential error (problem)
[ ] Suggests an alternate way of doing something (suggestion)
[ ] Other (please specify:)

**Provide 2-3 code examples that this rule should warn about:**

With `'vue/no-console': 'error'`, the following code should throw:

```vue
<template>
  <button @click="console.log"></button>
</template>
```

Pull request description:
Add `vue/no-console` rule
Closes #2155 

Patch:
diff --git a/docs/rules/index.md b/docs/rules/index.md
index d73923f1..c3e4b8ed 100644
--- a/docs/rules/index.md
+++ b/docs/rules/index.md
@@ -299,6 +299,7 @@ The following rules extend the rules provided by ESLint itself and apply them to
 | [vue/keyword-spacing](./keyword-spacing.md) | Enforce consistent spacing before and after keywords in `<template>` | :wrench: | :lipstick: |
 | [vue/max-len](./max-len.md) | enforce a maximum line length in `.vue` files |  | :lipstick: |
 | [vue/multiline-ternary](./multiline-ternary.md) | Enforce newlines between operands of ternary expressions in `<template>` | :wrench: | :lipstick: |
+| [vue/no-console](./no-console.md) | Disallow the use of `console` in `<template>` |  | :hammer: |
 | [vue/no-constant-condition](./no-constant-condition.md) | Disallow constant expressions in conditions in `<template>` |  | :warning: |
 | [vue/no-empty-pattern](./no-empty-pattern.md) | Disallow empty destructuring patterns in `<template>` |  | :warning: |
 | [vue/no-extra-parens](./no-extra-parens.md) | Disallow unnecessary parentheses in `<template>` | :wrench: | :lipstick: |
diff --git a/docs/rules/no-console.md b/docs/rules/no-console.md
new file mode 100644
index 00000000..e03628a3
--- /dev/null
+++ b/docs/rules/no-console.md
@@ -0,0 +1,28 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-console
+description: Disallow the use of `console` in `<template>`
+---
+# vue/no-console
+
+> Disallow the use of `console` in `<template>`
+
+- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge>
+
+## :book: Rule Details
+
+This rule is the same rule as core [no-console] rule but it applies to the expressions in `<template>`.
+
+## :books: Further Reading
+
+- [no-console]
+
+[no-console]: https://eslint.org/docs/latest/rules/no-console
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-console.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-console.js)
+
+<sup>Taken with ❤️ [from ESLint core](https://eslint.org/docs/rules/no-console)</sup>
diff --git a/lib/index.js b/lib/index.js
index 7bd0fa3b..8ac5d938 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -66,6 +66,7 @@ module.exports = {
     'no-boolean-default': require('./rules/no-boolean-default'),
     'no-child-content': require('./rules/no-child-content'),
     'no-computed-properties-in-data': require('./rules/no-computed-properties-in-data'),
+    'no-console': require('./rules/no-console'),
     'no-constant-condition': require('./rules/no-constant-condition'),
     'no-custom-modifiers-on-v-model': require('./rules/no-custom-modifiers-on-v-model'),
     'no-deprecated-data-object-declaration': require('./rules/no-deprecated-data-object-declaration'),
diff --git a/lib/rules/no-console.js b/lib/rules/no-console.js
new file mode 100644
index 00000000..cf425403
--- /dev/null
+++ b/lib/rules/no-console.js
@@ -0,0 +1,44 @@
+/**
+ * @author ItMaga <https://github.com/ItMaga>
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+const utils = require('../utils')
+
+// eslint-disable-next-line no-invalid-meta, no-invalid-meta-docs-categories
+module.exports = utils.wrapCoreRule('no-console', {
+  create(context) {
+    const options = context.options[0] || {}
+    const allowed = options.allow || []
+
+    /**
+     * Copied from the core rule `no-console`.
+     * Checks whether the property name of the given MemberExpression node
+     * is allowed by options or not.
+     * @param {MemberExpression} node The MemberExpression node to check.
+     * @returns {boolean} `true` if the property name of the node is allowed.
+     */
+    function isAllowed(node) {
+      const propertyName = utils.getStaticPropertyName(node)
+
+      return propertyName && allowed.includes(propertyName)
+    }
+
+    return {
+      MemberExpression(node) {
+        if (
+          node.object.type === 'Identifier' &&
+          node.object.name === 'console' &&
+          !isAllowed(node)
+        ) {
+          context.report({
+            node: node.object,
+            loc: node.object.loc,
+            messageId: 'unexpected'
+          })
+        }
+      }
+    }
+  }
+})


Test patch:
diff --git a/tests/lib/rules/no-console.js b/tests/lib/rules/no-console.js
new file mode 100644
index 00000000..a0c27304
--- /dev/null
+++ b/tests/lib/rules/no-console.js
@@ -0,0 +1,127 @@
+/**
+ * @author ItMaga <https://github.com/ItMaga>
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+const RuleTester = require('eslint').RuleTester
+const rule = require('../../../lib/rules/no-console')
+
+const tester = new RuleTester({
+  parser: require.resolve('vue-eslint-parser'),
+  parserOptions: {
+    ecmaVersion: 2020,
+    sourceType: 'module'
+  }
+})
+
+tester.run('no-console', rule, {
+  valid: [
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <button @click="Console.log">button</button>
+      </template>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <button>{{ console.warn('warn') }}</button>
+      </template>
+      `,
+      options: [{ allow: ['warn'] }]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <button :foo="console.error">button</button>
+      </template>
+      `,
+      options: [{ allow: ['error'] }]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <button :foo="console.log">{{ console.log('test') }}</button>
+        {{ console.warn('test') }}
+        {{ console.info('test') }}
+      </template>
+      `,
+      options: [{ allow: ['log', 'warn', 'info'] }]
+    }
+  ],
+  invalid: [
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <button @click="console.log">button</button>
+      </template>
+      `,
+      errors: [
+        {
+          message: 'Unexpected console statement.',
+          line: 3,
+          column: 25
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <button :foo="console.log">{{ console.log('test') }}</button>
+      </template>
+      `,
+      errors: [
+        {
+          message: 'Unexpected console statement.',
+          line: 3,
+          column: 23
+        },
+        {
+          message: 'Unexpected console statement.',
+          line: 3,
+          column: 39
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <button @click="() => console.log">button</button>
+      </template>
+      `,
+      errors: [
+        {
+          message: 'Unexpected console statement.',
+          line: 3,
+          column: 31
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <button @click="console.log">button</button>
+        {{ console.error('test') }}
+      </template>
+      `,
+      options: [{ allow: ['error'] }],
+      errors: [
+        {
+          message: 'Unexpected console statement.',
+          line: 3,
+          column: 25
+        }
+      ]
+    }
+  ]
+})