Skip to content

Commit b73d7c5

Browse files
authored
Fix false negatives when using handler property in vue/no-arrow-functions-in-watch rule (#1368)
1 parent 3f2b669 commit b73d7c5

File tree

5 files changed

+124
-29
lines changed

5 files changed

+124
-29
lines changed

Diff for: lib/rules/no-arrow-functions-in-watch.js

+12-8
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,18 @@ module.exports = {
2828
return
2929
}
3030
for (const property of watchValue.properties) {
31-
if (
32-
property.type === 'Property' &&
33-
property.value.type === 'ArrowFunctionExpression'
34-
) {
35-
context.report({
36-
node: property,
37-
message: 'You should not use an arrow function to define a watcher.'
38-
})
31+
if (property.type !== 'Property') {
32+
continue
33+
}
34+
35+
for (const handler of utils.iterateWatchHandlerValues(property)) {
36+
if (handler.type === 'ArrowFunctionExpression') {
37+
context.report({
38+
node: handler,
39+
message:
40+
'You should not use an arrow function to define a watcher.'
41+
})
42+
}
3943
}
4044
}
4145
})

Diff for: lib/rules/no-unused-properties.js

+13-20
Original file line numberDiff line numberDiff line change
@@ -634,24 +634,17 @@ module.exports = {
634634
if (watcher.type === 'object') {
635635
const property = watcher.property
636636
if (property.kind === 'init') {
637-
/** @type {Expression | null} */
638-
let handlerValueNode = null
639-
if (property.value.type === 'ObjectExpression') {
640-
const handler = utils.findProperty(property.value, 'handler')
641-
if (handler) {
642-
handlerValueNode = handler.value
643-
}
644-
} else {
645-
handlerValueNode = property.value
646-
}
647-
if (
648-
handlerValueNode &&
649-
(handlerValueNode.type === 'Literal' ||
650-
handlerValueNode.type === 'TemplateLiteral')
651-
) {
652-
const name = utils.getStringLiteralValue(handlerValueNode)
653-
if (name != null) {
654-
watcherUsedProperties.add(name)
637+
for (const handlerValueNode of utils.iterateWatchHandlerValues(
638+
property
639+
)) {
640+
if (
641+
handlerValueNode.type === 'Literal' ||
642+
handlerValueNode.type === 'TemplateLiteral'
643+
) {
644+
const name = utils.getStringLiteralValue(handlerValueNode)
645+
if (name != null) {
646+
watcherUsedProperties.add(name)
647+
}
655648
}
656649
}
657650
}
@@ -699,11 +692,11 @@ module.exports = {
699692
if (
700693
utils.getStaticPropertyName(parentParentProperty) !==
701694
'computed' ||
702-
utils.getStaticPropertyName(property) !== 'handler'
695+
utils.getStaticPropertyName(property) !== 'get'
703696
) {
704697
return
705698
}
706-
// check { computed: { foo: { handler: (vm) => vm.prop } } }
699+
// check { computed: { foo: { get: () => vm.prop } } }
707700
} else {
708701
return
709702
}

Diff for: lib/utils/index.js

+30
Original file line numberDiff line numberDiff line change
@@ -1476,6 +1476,13 @@ module.exports = {
14761476
}
14771477
},
14781478

1479+
/**
1480+
* Return generator with the all handler nodes defined in the given watcher property.
1481+
* @param {Property|Expression} property
1482+
* @returns {IterableIterator<Expression>}
1483+
*/
1484+
iterateWatchHandlerValues,
1485+
14791486
/**
14801487
* Wraps composition API trace map in both 'vue' and '@vue/composition-api' imports
14811488
* @param {import('eslint-utils').TYPES.TraceMap} map
@@ -1990,3 +1997,26 @@ function getComponentComments(context) {
19901997
componentComments.set(context, tokens)
19911998
return tokens
19921999
}
2000+
2001+
/**
2002+
* Return generator with the all handler nodes defined in the given watcher property.
2003+
* @param {Property|Expression} property
2004+
* @returns {IterableIterator<Expression>}
2005+
*/
2006+
function* iterateWatchHandlerValues(property) {
2007+
const value = property.type === 'Property' ? property.value : property
2008+
if (value.type === 'ObjectExpression') {
2009+
const handler = findProperty(value, 'handler')
2010+
if (handler) {
2011+
yield handler.value
2012+
}
2013+
} else if (value.type === 'ArrayExpression') {
2014+
for (const element of value.elements.filter(isDef)) {
2015+
if (element.type !== 'SpreadElement') {
2016+
yield* iterateWatchHandlerValues(element)
2017+
}
2018+
}
2019+
} else {
2020+
yield value
2021+
}
2022+
}

Diff for: tests/lib/rules/no-arrow-functions-in-watch.js

+45
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,51 @@ ruleTester.run('no-arrow-functions-in-watch', rule, {
218218
line: 15
219219
}
220220
]
221+
},
222+
{
223+
filename: 'test.vue',
224+
code: `
225+
export default {
226+
watch: {
227+
foo:{
228+
handler: function() {},
229+
},
230+
bar:{
231+
handler: () => {}
232+
}
233+
}
234+
}`,
235+
errors: [
236+
{
237+
message: 'You should not use an arrow function to define a watcher.',
238+
line: 8
239+
}
240+
]
241+
},
242+
{
243+
filename: 'test.vue',
244+
code: `
245+
export default {
246+
watch: {
247+
e: [
248+
'handle1',
249+
(val, oldVal) => { /* ... */ },
250+
{
251+
handler: (val, oldVal) => { /* ... */ },
252+
}
253+
],
254+
}
255+
}`,
256+
errors: [
257+
{
258+
message: 'You should not use an arrow function to define a watcher.',
259+
line: 6
260+
},
261+
{
262+
message: 'You should not use an arrow function to define a watcher.',
263+
line: 8
264+
}
265+
]
221266
}
222267
]
223268
})

Diff for: tests/lib/rules/no-unused-properties.js

+24-1
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,29 @@ tester.run('no-unused-properties', rule, {
263263
</script>
264264
`
265265
},
266+
{
267+
filename: 'test.vue',
268+
code: `
269+
<script>
270+
export default {
271+
props: ['foo'],
272+
watch: {
273+
foo: [
274+
'bar',
275+
{
276+
handler: 'baz'
277+
}
278+
],
279+
},
280+
methods: {
281+
bar () {},
282+
baz () {},
283+
}
284+
};
285+
</script>
286+
`,
287+
options: allOptions
288+
},
266289

267290
// data used as a template identifier
268291
{
@@ -1114,7 +1137,7 @@ tester.run('no-unused-properties', rule, {
11141137
props: ['x'],
11151138
computed: {
11161139
y: {
1117-
handler: (vm) => vm.x * 2
1140+
get: () => this.x * 2
11181141
}
11191142
}
11201143
};

0 commit comments

Comments
 (0)