diff --git a/lib/rules/no-deprecated-dollar-listeners-api.js b/lib/rules/no-deprecated-dollar-listeners-api.js index 29c0655d0..b833b462b 100644 --- a/lib/rules/no-deprecated-dollar-listeners-api.js +++ b/lib/rules/no-deprecated-dollar-listeners-api.js @@ -50,11 +50,19 @@ module.exports = { }, utils.defineVueVisitor(context, { - 'MemberExpression > ThisExpression' (node) { - if (node.parent.property.name !== '$listeners') return + 'MemberExpression' (node) { + if ( + node.property.type !== 'Identifier' || + node.property.name !== '$listeners' + ) { + return + } + if (!utils.isThis(node.object, context)) { + return + } context.report({ - node: node.parent.property, + node: node.property, messageId: 'deprecated' }) } diff --git a/lib/rules/no-deprecated-events-api.js b/lib/rules/no-deprecated-events-api.js index 8537469ee..7989ffb5e 100644 --- a/lib/rules/no-deprecated-events-api.js +++ b/lib/rules/no-deprecated-events-api.js @@ -32,11 +32,21 @@ module.exports = { create (context) { return utils.defineVueVisitor(context, { - 'CallExpression > MemberExpression > ThisExpression' (node) { - if (!['$on', '$off', '$once'].includes(node.parent.property.name)) return + 'CallExpression > MemberExpression' (node) { + const call = node.parent + if ( + call.callee !== node || + node.property.type !== 'Identifier' || + !['$on', '$off', '$once'].includes(node.property.name) + ) { + return + } + if (!utils.isThis(node.object, context)) { + return + } context.report({ - node: node.parent.parent, + node: node.property, messageId: 'noDeprecatedEventsApi' }) } diff --git a/lib/rules/require-explicit-emits.js b/lib/rules/require-explicit-emits.js index 2addf8471..c811fc9ce 100644 --- a/lib/rules/require-explicit-emits.js +++ b/lib/rules/require-explicit-emits.js @@ -262,8 +262,7 @@ module.exports = { // verify $emit if (emit && emit.name === '$emit') { - const objectType = emit.member.object.type - if (objectType === 'Identifier' || objectType === 'ThisExpression') { + if (utils.isThis(emit.member.object, context)) { // verify this.$emit() verify(emitsDeclarations, nameLiteralNode, vueNode) } diff --git a/lib/utils/index.js b/lib/utils/index.js index 3e1ce4c58..c31ed0e6e 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -41,6 +41,7 @@ const VOID_ELEMENT_NAMES = new Set(require('./void-elements.json')) const assert = require('assert') const path = require('path') const vueEslintParser = require('vue-eslint-parser') +const { findVariable } = require('eslint-utils') /** * @type { WeakMap } @@ -855,7 +856,34 @@ module.exports = { * @param {T} node * @return {T} */ - unwrapTypes + unwrapTypes, + + /** + * Check whether the given node is `this` or variable that stores `this`. + * @param {ASTNode} node The node to check + * @returns {boolean} `true` if the given node is `this`. + */ + isThis (node, context) { + if (node.type === 'ThisExpression') { + return true + } + if (node.type !== 'Identifier') { + return false + } + const variable = findVariable(context.getScope(), node) + + if (variable != null && variable.defs.length === 1) { + const def = variable.defs[0] + if ( + def.parent && + def.parent.kind === 'const' && + def.node.id.type === 'Identifier' + ) { + return def.node && def.node.init && def.node.init.type === 'ThisExpression' + } + } + return false + } } /** diff --git a/tests/lib/rules/no-deprecated-dollar-listeners-api.js b/tests/lib/rules/no-deprecated-dollar-listeners-api.js index 76f7c73cc..98a0d6270 100644 --- a/tests/lib/rules/no-deprecated-dollar-listeners-api.js +++ b/tests/lib/rules/no-deprecated-dollar-listeners-api.js @@ -101,6 +101,21 @@ ruleTester.run('no-deprecated-dollar-listeners-api', rule, { } ` + }, + { + filename: 'test.vue', + code: ` + + ` } ], @@ -179,6 +194,53 @@ ruleTester.run('no-deprecated-dollar-listeners-api', rule, { endColumn: 33 } ] + }, + { + filename: 'test.vue', + code: ` + + `, + errors: [ + { + line: 7, + column: 25, + messageId: 'deprecated' + } + ] + }, + { + filename: 'test.vue', + code: ` + + `, + errors: [ + { + line: 8, + column: 27, + messageId: 'deprecated' + } + ] } ] }) diff --git a/tests/lib/rules/no-deprecated-events-api.js b/tests/lib/rules/no-deprecated-events-api.js index 215a47d86..268c0a712 100644 --- a/tests/lib/rules/no-deprecated-events-api.js +++ b/tests/lib/rules/no-deprecated-events-api.js @@ -103,6 +103,17 @@ ruleTester.run('no-deprecated-events-api', rule, { } `, parserOptions + }, + { + filename: 'test.vue', + code: ` + export default { + mounted () { + a(this.$on) + } + } + `, + parserOptions } ], @@ -155,6 +166,24 @@ ruleTester.run('no-deprecated-events-api', rule, { message: 'The Events api `$on`, `$off` `$once` is deprecated. Using external library instead, for example mitt.', line: 4 }] + }, + { + filename: 'test.js', + code: ` + app.component('some-comp', { + mounted () { + const vm = this + vm.$on('start', function (args) { + console.log('start', args) + }) + } + }) + `, + parserOptions, + errors: [{ + message: 'The Events api `$on`, `$off` `$once` is deprecated. Using external library instead, for example mitt.', + line: 5 + }] } ] })