Skip to content

Commit d191481

Browse files
authored
Changed vue/no-deprecated-dollar-listeners-api and vue/no-deprecated-events-api rules to track the this variable. (#1143)
1 parent c54b13a commit d191481

6 files changed

+145
-9
lines changed

lib/rules/no-deprecated-dollar-listeners-api.js

+11-3
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,19 @@ module.exports = {
5050
},
5151
utils.defineVueVisitor(context,
5252
{
53-
'MemberExpression > ThisExpression' (node) {
54-
if (node.parent.property.name !== '$listeners') return
53+
'MemberExpression' (node) {
54+
if (
55+
node.property.type !== 'Identifier' ||
56+
node.property.name !== '$listeners'
57+
) {
58+
return
59+
}
60+
if (!utils.isThis(node.object, context)) {
61+
return
62+
}
5563

5664
context.report({
57-
node: node.parent.property,
65+
node: node.property,
5866
messageId: 'deprecated'
5967
})
6068
}

lib/rules/no-deprecated-events-api.js

+13-3
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,21 @@ module.exports = {
3232
create (context) {
3333
return utils.defineVueVisitor(context,
3434
{
35-
'CallExpression > MemberExpression > ThisExpression' (node) {
36-
if (!['$on', '$off', '$once'].includes(node.parent.property.name)) return
35+
'CallExpression > MemberExpression' (node) {
36+
const call = node.parent
37+
if (
38+
call.callee !== node ||
39+
node.property.type !== 'Identifier' ||
40+
!['$on', '$off', '$once'].includes(node.property.name)
41+
) {
42+
return
43+
}
44+
if (!utils.isThis(node.object, context)) {
45+
return
46+
}
3747

3848
context.report({
39-
node: node.parent.parent,
49+
node: node.property,
4050
messageId: 'noDeprecatedEventsApi'
4151
})
4252
}

lib/rules/require-explicit-emits.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -262,8 +262,7 @@ module.exports = {
262262

263263
// verify $emit
264264
if (emit && emit.name === '$emit') {
265-
const objectType = emit.member.object.type
266-
if (objectType === 'Identifier' || objectType === 'ThisExpression') {
265+
if (utils.isThis(emit.member.object, context)) {
267266
// verify this.$emit()
268267
verify(emitsDeclarations, nameLiteralNode, vueNode)
269268
}

lib/utils/index.js

+29-1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ const VOID_ELEMENT_NAMES = new Set(require('./void-elements.json'))
4141
const assert = require('assert')
4242
const path = require('path')
4343
const vueEslintParser = require('vue-eslint-parser')
44+
const { findVariable } = require('eslint-utils')
4445

4546
/**
4647
* @type { WeakMap<RuleContext, Token[]> }
@@ -855,7 +856,34 @@ module.exports = {
855856
* @param {T} node
856857
* @return {T}
857858
*/
858-
unwrapTypes
859+
unwrapTypes,
860+
861+
/**
862+
* Check whether the given node is `this` or variable that stores `this`.
863+
* @param {ASTNode} node The node to check
864+
* @returns {boolean} `true` if the given node is `this`.
865+
*/
866+
isThis (node, context) {
867+
if (node.type === 'ThisExpression') {
868+
return true
869+
}
870+
if (node.type !== 'Identifier') {
871+
return false
872+
}
873+
const variable = findVariable(context.getScope(), node)
874+
875+
if (variable != null && variable.defs.length === 1) {
876+
const def = variable.defs[0]
877+
if (
878+
def.parent &&
879+
def.parent.kind === 'const' &&
880+
def.node.id.type === 'Identifier'
881+
) {
882+
return def.node && def.node.init && def.node.init.type === 'ThisExpression'
883+
}
884+
}
885+
return false
886+
}
859887
}
860888

861889
/**

tests/lib/rules/no-deprecated-dollar-listeners-api.js

+62
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,21 @@ ruleTester.run('no-deprecated-dollar-listeners-api', rule, {
101101
}
102102
</script>
103103
`
104+
},
105+
{
106+
filename: 'test.vue',
107+
code: `
108+
<script>
109+
export default {
110+
computed: {
111+
foo () {
112+
const {vm} = this
113+
return vm.$listeners
114+
}
115+
}
116+
}
117+
</script>
118+
`
104119
}
105120
],
106121

@@ -179,6 +194,53 @@ ruleTester.run('no-deprecated-dollar-listeners-api', rule, {
179194
endColumn: 33
180195
}
181196
]
197+
},
198+
{
199+
filename: 'test.vue',
200+
code: `
201+
<script>
202+
export default {
203+
computed: {
204+
foo () {
205+
const vm = this
206+
return vm.$listeners
207+
}
208+
}
209+
}
210+
</script>
211+
`,
212+
errors: [
213+
{
214+
line: 7,
215+
column: 25,
216+
messageId: 'deprecated'
217+
}
218+
]
219+
},
220+
{
221+
filename: 'test.vue',
222+
code: `
223+
<script>
224+
export default {
225+
computed: {
226+
foo () {
227+
const vm = this
228+
function fn() {
229+
return vm.$listeners
230+
}
231+
return fn()
232+
}
233+
}
234+
}
235+
</script>
236+
`,
237+
errors: [
238+
{
239+
line: 8,
240+
column: 27,
241+
messageId: 'deprecated'
242+
}
243+
]
182244
}
183245
]
184246
})

tests/lib/rules/no-deprecated-events-api.js

+29
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,17 @@ ruleTester.run('no-deprecated-events-api', rule, {
103103
}
104104
`,
105105
parserOptions
106+
},
107+
{
108+
filename: 'test.vue',
109+
code: `
110+
export default {
111+
mounted () {
112+
a(this.$on)
113+
}
114+
}
115+
`,
116+
parserOptions
106117
}
107118
],
108119

@@ -155,6 +166,24 @@ ruleTester.run('no-deprecated-events-api', rule, {
155166
message: 'The Events api `$on`, `$off` `$once` is deprecated. Using external library instead, for example mitt.',
156167
line: 4
157168
}]
169+
},
170+
{
171+
filename: 'test.js',
172+
code: `
173+
app.component('some-comp', {
174+
mounted () {
175+
const vm = this
176+
vm.$on('start', function (args) {
177+
console.log('start', args)
178+
})
179+
}
180+
})
181+
`,
182+
parserOptions,
183+
errors: [{
184+
message: 'The Events api `$on`, `$off` `$once` is deprecated. Using external library instead, for example mitt.',
185+
line: 5
186+
}]
158187
}
159188
]
160189
})

0 commit comments

Comments
 (0)