Skip to content

Commit

Permalink
Fix false negatives when using handler property in `vue/no-arrow-func…
Browse files Browse the repository at this point in the history
…tions-in-watch` rule (#1368)
  • Loading branch information
ota-meshi committed Dec 4, 2020
1 parent 3f2b669 commit b73d7c5
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 29 deletions.
20 changes: 12 additions & 8 deletions lib/rules/no-arrow-functions-in-watch.js
Expand Up @@ -28,14 +28,18 @@ module.exports = {
return
}
for (const property of watchValue.properties) {
if (
property.type === 'Property' &&
property.value.type === 'ArrowFunctionExpression'
) {
context.report({
node: property,
message: 'You should not use an arrow function to define a watcher.'
})
if (property.type !== 'Property') {
continue
}

for (const handler of utils.iterateWatchHandlerValues(property)) {
if (handler.type === 'ArrowFunctionExpression') {
context.report({
node: handler,
message:
'You should not use an arrow function to define a watcher.'
})
}
}
}
})
Expand Down
33 changes: 13 additions & 20 deletions lib/rules/no-unused-properties.js
Expand Up @@ -634,24 +634,17 @@ module.exports = {
if (watcher.type === 'object') {
const property = watcher.property
if (property.kind === 'init') {
/** @type {Expression | null} */
let handlerValueNode = null
if (property.value.type === 'ObjectExpression') {
const handler = utils.findProperty(property.value, 'handler')
if (handler) {
handlerValueNode = handler.value
}
} else {
handlerValueNode = property.value
}
if (
handlerValueNode &&
(handlerValueNode.type === 'Literal' ||
handlerValueNode.type === 'TemplateLiteral')
) {
const name = utils.getStringLiteralValue(handlerValueNode)
if (name != null) {
watcherUsedProperties.add(name)
for (const handlerValueNode of utils.iterateWatchHandlerValues(
property
)) {
if (
handlerValueNode.type === 'Literal' ||
handlerValueNode.type === 'TemplateLiteral'
) {
const name = utils.getStringLiteralValue(handlerValueNode)
if (name != null) {
watcherUsedProperties.add(name)
}
}
}
}
Expand Down Expand Up @@ -699,11 +692,11 @@ module.exports = {
if (
utils.getStaticPropertyName(parentParentProperty) !==
'computed' ||
utils.getStaticPropertyName(property) !== 'handler'
utils.getStaticPropertyName(property) !== 'get'
) {
return
}
// check { computed: { foo: { handler: (vm) => vm.prop } } }
// check { computed: { foo: { get: () => vm.prop } } }
} else {
return
}
Expand Down
30 changes: 30 additions & 0 deletions lib/utils/index.js
Expand Up @@ -1476,6 +1476,13 @@ module.exports = {
}
},

/**
* Return generator with the all handler nodes defined in the given watcher property.
* @param {Property|Expression} property
* @returns {IterableIterator<Expression>}
*/
iterateWatchHandlerValues,

/**
* Wraps composition API trace map in both 'vue' and '@vue/composition-api' imports
* @param {import('eslint-utils').TYPES.TraceMap} map
Expand Down Expand Up @@ -1990,3 +1997,26 @@ function getComponentComments(context) {
componentComments.set(context, tokens)
return tokens
}

/**
* Return generator with the all handler nodes defined in the given watcher property.
* @param {Property|Expression} property
* @returns {IterableIterator<Expression>}
*/
function* iterateWatchHandlerValues(property) {
const value = property.type === 'Property' ? property.value : property
if (value.type === 'ObjectExpression') {
const handler = findProperty(value, 'handler')
if (handler) {
yield handler.value
}
} else if (value.type === 'ArrayExpression') {
for (const element of value.elements.filter(isDef)) {
if (element.type !== 'SpreadElement') {
yield* iterateWatchHandlerValues(element)
}
}
} else {
yield value
}
}
45 changes: 45 additions & 0 deletions tests/lib/rules/no-arrow-functions-in-watch.js
Expand Up @@ -218,6 +218,51 @@ ruleTester.run('no-arrow-functions-in-watch', rule, {
line: 15
}
]
},
{
filename: 'test.vue',
code: `
export default {
watch: {
foo:{
handler: function() {},
},
bar:{
handler: () => {}
}
}
}`,
errors: [
{
message: 'You should not use an arrow function to define a watcher.',
line: 8
}
]
},
{
filename: 'test.vue',
code: `
export default {
watch: {
e: [
'handle1',
(val, oldVal) => { /* ... */ },
{
handler: (val, oldVal) => { /* ... */ },
}
],
}
}`,
errors: [
{
message: 'You should not use an arrow function to define a watcher.',
line: 6
},
{
message: 'You should not use an arrow function to define a watcher.',
line: 8
}
]
}
]
})
25 changes: 24 additions & 1 deletion tests/lib/rules/no-unused-properties.js
Expand Up @@ -263,6 +263,29 @@ tester.run('no-unused-properties', rule, {
</script>
`
},
{
filename: 'test.vue',
code: `
<script>
export default {
props: ['foo'],
watch: {
foo: [
'bar',
{
handler: 'baz'
}
],
},
methods: {
bar () {},
baz () {},
}
};
</script>
`,
options: allOptions
},

// data used as a template identifier
{
Expand Down Expand Up @@ -1114,7 +1137,7 @@ tester.run('no-unused-properties', rule, {
props: ['x'],
computed: {
y: {
handler: (vm) => vm.x * 2
get: () => this.x * 2
}
}
};
Expand Down

0 comments on commit b73d7c5

Please sign in to comment.