From 6008b54f2bb4ee3fc5d4aa40e1018b67d0374582 Mon Sep 17 00:00:00 2001 From: yosuke ota Date: Sun, 31 May 2020 11:59:20 +0900 Subject: [PATCH] Improved `vue/no-ref-as-operand` rule. - Changed `vue/no-ref-as-operand` rule to additionally track variables generated by `computed`, `toRef`, `customRef` and `shallowRef`. - Changed `vue/no-ref-as-operand` rule to report incorrect use of TemplateLiteral and MemberExpression. --- docs/rules/no-ref-as-operand.md | 3 +- lib/rules/no-ref-as-operand.js | 56 ++++++++++++--- tests/lib/rules/no-ref-as-operand.js | 103 ++++++++++++++++++++++++++- 3 files changed, 151 insertions(+), 11 deletions(-) diff --git a/docs/rules/no-ref-as-operand.md b/docs/rules/no-ref-as-operand.md index c983d669d..609a07279 100644 --- a/docs/rules/no-ref-as-operand.md +++ b/docs/rules/no-ref-as-operand.md @@ -11,7 +11,8 @@ description: disallow use of value wrapped by `ref()` (Composition API) as an op ## :book: Rule Details -This rule reports cases where a ref is used incorrectly as an operand. +This rule reports cases where a ref is used incorrectly as an operand. +You must use `.value` to access the `Ref` value. diff --git a/lib/rules/no-ref-as-operand.js b/lib/rules/no-ref-as-operand.js index 11873edf0..cfd09921c 100644 --- a/lib/rules/no-ref-as-operand.js +++ b/lib/rules/no-ref-as-operand.js @@ -4,6 +4,7 @@ */ 'use strict' const { ReferenceTracker, findVariable } = require('eslint-utils') +const utils = require('../utils') module.exports = { meta: { @@ -18,19 +19,23 @@ module.exports = { schema: [], messages: { requireDotValue: - 'Must use `.value` to read or write the value wrapped by `ref()`.' + 'Must use `.value` to read or write the value wrapped by `{{method}}()`.' } }, create(context) { const refReferenceIds = new Map() function reportIfRefWrapped(node) { - if (!refReferenceIds.has(node)) { + const data = refReferenceIds.get(node) + if (!data) { return } context.report({ node, - messageId: 'requireDotValue' + messageId: 'requireDotValue', + data: { + method: data.method + } }) } return { @@ -41,11 +46,23 @@ module.exports = { [ReferenceTracker.ESM]: true, ref: { [ReferenceTracker.CALL]: true + }, + computed: { + [ReferenceTracker.CALL]: true + }, + toRef: { + [ReferenceTracker.CALL]: true + }, + customRef: { + [ReferenceTracker.CALL]: true + }, + shallowRef: { + [ReferenceTracker.CALL]: true } } } - for (const { node } of tracker.iterateEsmReferences(traceMap)) { + for (const { node, path } of tracker.iterateEsmReferences(traceMap)) { const variableDeclarator = node.parent if ( !variableDeclarator || @@ -73,7 +90,8 @@ module.exports = { refReferenceIds.set(reference.identifier, { variableDeclarator, - variableDeclaration + variableDeclaration, + method: path[1] }) } } @@ -108,13 +126,13 @@ module.exports = { return } // Report only constants. - const info = refReferenceIds.get(node) - if (!info) { + const data = refReferenceIds.get(node) + if (!data) { return } if ( - !info.variableDeclaration || - info.variableDeclaration.kind !== 'const' + !data.variableDeclaration || + data.variableDeclaration.kind !== 'const' ) { return } @@ -126,6 +144,26 @@ module.exports = { return } reportIfRefWrapped(node) + }, + // `${refValue}` + 'TemplateLiteral>Identifier'(node) { + reportIfRefWrapped(node) + }, + // refValue.x + 'MemberExpression>Identifier'(node) { + if (node.parent.object !== node) { + return + } + const name = utils.getStaticPropertyName(node.parent) + if ( + name === 'value' || + name == null || + // WritableComputedRef + name === 'effect' + ) { + return + } + reportIfRefWrapped(node) } } } diff --git a/tests/lib/rules/no-ref-as-operand.js b/tests/lib/rules/no-ref-as-operand.js index 6eeb11b54..5d53a5571 100644 --- a/tests/lib/rules/no-ref-as-operand.js +++ b/tests/lib/rules/no-ref-as-operand.js @@ -103,7 +103,25 @@ tester.run('no-ref-as-operand', rule, { import { ref } from 'vue' const count = ref count++ - ` + `, + { + code: ` + + ` + }, + { + code: ` + + ` + } ], invalid: [ { @@ -332,6 +350,89 @@ tester.run('no-ref-as-operand', rule, { line: 9 } ] + }, + { + code: ` + + `, + errors: [ + { + message: + 'Must use `.value` to read or write the value wrapped by `ref()`.', + line: 33 + }, + { + message: + 'Must use `.value` to read or write the value wrapped by `computed()`.', + line: 34 + }, + { + message: + 'Must use `.value` to read or write the value wrapped by `toRef()`.', + line: 36 + }, + { + message: + 'Must use `.value` to read or write the value wrapped by `customRef()`.', + line: 36 + }, + { + message: + 'Must use `.value` to read or write the value wrapped by `shallowRef()`.', + line: 38 + } + ] + }, + { + code: ` + + `, + errors: [ + { + messageId: 'requireDotValue' + } + ] } ] })