Skip to content

Commit 7422a0e

Browse files
authored
Rename vue/no-ref-object-destructure to vue/no-ref-object-reactivity-loss (#2269)
1 parent 4112be5 commit 7422a0e

7 files changed

+692
-160
lines changed

docs/rules/index.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ For example:
231231
| [vue/no-empty-component-block](./no-empty-component-block.md) | disallow the `<template>` `<script>` `<style>` block to be empty | | :hammer: |
232232
| [vue/no-multiple-objects-in-class](./no-multiple-objects-in-class.md) | disallow to pass multiple objects into array to class | | :hammer: |
233233
| [vue/no-potential-component-option-typo](./no-potential-component-option-typo.md) | disallow a potential typo in your component property | :bulb: | :hammer: |
234-
| [vue/no-ref-object-destructure](./no-ref-object-destructure.md) | disallow destructuring of ref objects that can lead to loss of reactivity | | :warning: |
234+
| [vue/no-ref-object-reactivity-loss](./no-ref-object-reactivity-loss.md) | disallow usages of ref objects that can lead to loss of reactivity | | :warning: |
235235
| [vue/no-required-prop-with-default](./no-required-prop-with-default.md) | enforce props with default values to be optional | :wrench::bulb: | :warning: |
236236
| [vue/no-restricted-block](./no-restricted-block.md) | disallow specific block | | :hammer: |
237237
| [vue/no-restricted-call-after-await](./no-restricted-call-after-await.md) | disallow asynchronously called restricted methods | | :hammer: |
@@ -339,6 +339,7 @@ The following rules extend the rules provided by ESLint itself and apply them to
339339
|:--------|:------------|
340340
| [vue/component-tags-order](./component-tags-order.md) | [vue/block-order](./block-order.md) |
341341
| [vue/no-invalid-model-keys](./no-invalid-model-keys.md) | [vue/valid-model-definition](./valid-model-definition.md) |
342+
| [vue/no-ref-object-destructure](./no-ref-object-destructure.md) | [vue/no-ref-object-reactivity-loss](./no-ref-object-reactivity-loss.md) |
342343
| [vue/no-setup-props-destructure](./no-setup-props-destructure.md) | [vue/no-setup-props-reactivity-loss](./no-setup-props-reactivity-loss.md) |
343344
| [vue/script-setup-uses-vars](./script-setup-uses-vars.md) | (no replacement) |
344345
| [vue/v-on-function-call](./v-on-function-call.md) | [vue/v-on-handler-style](./v-on-handler-style.md) |

docs/rules/no-ref-object-destructure.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
pageClass: rule-details
33
sidebarDepth: 0
44
title: vue/no-ref-object-destructure
5-
description: disallow destructuring of ref objects that can lead to loss of reactivity
5+
description: disallow usages of ref objects that can lead to loss of reactivity
66
since: v9.5.0
77
---
88
# vue/no-ref-object-destructure
99

10-
> disallow destructuring of ref objects that can lead to loss of reactivity
10+
> disallow usages of ref objects that can lead to loss of reactivity
11+
12+
- :no_entry_sign: This rule was **deprecated** and replaced by [vue/no-ref-object-reactivity-loss](no-ref-object-reactivity-loss.md) rule.
1113

1214
## :book: Rule Details
1315

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
---
2+
pageClass: rule-details
3+
sidebarDepth: 0
4+
title: vue/no-ref-object-reactivity-loss
5+
description: disallow usages of ref objects that can lead to loss of reactivity
6+
---
7+
# vue/no-ref-object-reactivity-loss
8+
9+
> disallow usages of ref objects that can lead to loss of reactivity
10+
11+
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge>
12+
13+
## :book: Rule Details
14+
15+
This rule reports the usages of ref objects causing the value to lose reactivity.
16+
17+
<eslint-code-block :rules="{'vue/no-ref-object-reactivity-loss': ['error']}" language="javascript" filename="example.js" >
18+
19+
```js
20+
import { ref } from 'vue'
21+
const count = ref(0)
22+
const v1 = count.value /* ✗ BAD */
23+
const { value: v2 } = count /* ✗ BAD */
24+
const v3 = computed(() => count.value /* ✓ GOOD */)
25+
const v4 = fn(count.value) /* ✗ BAD */
26+
const v5 = fn(count) /* ✓ GOOD */
27+
const v6 = computed(() => fn(count.value) /* ✓ GOOD */)
28+
```
29+
30+
</eslint-code-block>
31+
32+
This rule also supports Reactivity Transform, but Reactivity Transform is an experimental feature and may have false positives due to future Vue changes.
33+
See the [RFC](https://github.com/vuejs/rfcs/pull/420) for more information on Reactivity Transform.
34+
35+
<eslint-code-block :rules="{'vue/no-ref-object-reactivity-loss': ['error']}" language="javascript" filename="example.js" >
36+
37+
```js
38+
const count = $ref(0)
39+
const v1 = count /* ✗ BAD */
40+
const v2 = $computed(() => count /* ✓ GOOD */)
41+
const v3 = fn(count) /* ✗ BAD */
42+
const v4 = fn($$(count)) /* ✓ GOOD */
43+
const v5 = $computed(() => fn(count) /* ✓ GOOD */)
44+
```
45+
46+
</eslint-code-block>
47+
48+
## :wrench: Options
49+
50+
Nothing.
51+
52+
## :mag: Implementation
53+
54+
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-ref-object-reactivity-loss.js)
55+
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-ref-object-reactivity-loss.js)

lib/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ module.exports = {
114114
'no-potential-component-option-typo': require('./rules/no-potential-component-option-typo'),
115115
'no-ref-as-operand': require('./rules/no-ref-as-operand'),
116116
'no-ref-object-destructure': require('./rules/no-ref-object-destructure'),
117+
'no-ref-object-reactivity-loss': require('./rules/no-ref-object-reactivity-loss'),
117118
'no-required-prop-with-default': require('./rules/no-required-prop-with-default'),
118119
'no-reserved-component-names': require('./rules/no-reserved-component-names'),
119120
'no-reserved-keys': require('./rules/no-reserved-keys'),
+10-157
Original file line numberDiff line numberDiff line change
@@ -1,171 +1,24 @@
11
/**
2-
* @author Yosuke Ota <https://github.com/ota-meshi>
2+
* @author Yosuke Ota
33
* See LICENSE file in root directory for full license.
44
*/
55
'use strict'
6-
7-
const utils = require('../utils')
8-
const {
9-
extractRefObjectReferences,
10-
extractReactiveVariableReferences
11-
} = require('../utils/ref-object-references')
12-
13-
/**
14-
* @typedef {import('../utils/ref-object-references').RefObjectReferences} RefObjectReferences
15-
* @typedef {import('../utils/ref-object-references').RefObjectReference} RefObjectReference
16-
*/
17-
18-
/**
19-
* Checks whether writing assigns a value to the given pattern.
20-
* @param {Pattern | AssignmentProperty | Property} node
21-
* @returns {boolean}
22-
*/
23-
function isUpdate(node) {
24-
const parent = node.parent
25-
if (parent.type === 'UpdateExpression' && parent.argument === node) {
26-
// e.g. `pattern++`
27-
return true
28-
}
29-
if (parent.type === 'AssignmentExpression' && parent.left === node) {
30-
// e.g. `pattern = 42`
31-
return true
32-
}
33-
if (
34-
(parent.type === 'Property' && parent.value === node) ||
35-
parent.type === 'ArrayPattern' ||
36-
(parent.type === 'ObjectPattern' &&
37-
parent.properties.includes(/** @type {any} */ (node))) ||
38-
(parent.type === 'AssignmentPattern' && parent.left === node) ||
39-
parent.type === 'RestElement' ||
40-
(parent.type === 'MemberExpression' && parent.object === node)
41-
) {
42-
return isUpdate(parent)
43-
}
44-
return false
45-
}
6+
const baseRule = require('./no-ref-object-reactivity-loss')
467

478
module.exports = {
9+
// eslint-disable-next-line eslint-plugin/require-meta-schema, eslint-plugin/prefer-message-ids, internal/no-invalid-meta, eslint-plugin/require-meta-type -- inherit schema from base rule
4810
meta: {
49-
type: 'problem',
11+
...baseRule.meta,
12+
// eslint-disable-next-line eslint-plugin/require-meta-docs-description, internal/no-invalid-meta-docs-categories, eslint-plugin/meta-property-ordering
5013
docs: {
51-
description:
52-
'disallow destructuring of ref objects that can lead to loss of reactivity',
53-
categories: undefined,
14+
...baseRule.meta.docs,
5415
url: 'https://eslint.vuejs.org/rules/no-ref-object-destructure.html'
5516
},
56-
fixable: null,
57-
schema: [],
58-
messages: {
59-
getValueInSameScope:
60-
'Getting a value from the ref object in the same scope will cause the value to lose reactivity.',
61-
getReactiveVariableInSameScope:
62-
'Getting a reactive variable in the same scope will cause the value to lose reactivity.'
63-
}
17+
deprecated: true,
18+
replacedBy: ['no-ref-object-reactivity-loss']
6419
},
65-
/**
66-
* @param {RuleContext} context
67-
* @returns {RuleListener}
68-
*/
20+
/** @param {RuleContext} context */
6921
create(context) {
70-
/**
71-
* @typedef {object} ScopeStack
72-
* @property {ScopeStack | null} upper
73-
* @property {Program | FunctionExpression | FunctionDeclaration | ArrowFunctionExpression} node
74-
*/
75-
/** @type {ScopeStack} */
76-
let scopeStack = { upper: null, node: context.getSourceCode().ast }
77-
/** @type {Map<CallExpression, ScopeStack>} */
78-
const scopes = new Map()
79-
80-
const refObjectReferences = extractRefObjectReferences(context)
81-
const reactiveVariableReferences =
82-
extractReactiveVariableReferences(context)
83-
84-
/**
85-
* Verify the given ref object value. `refObj = ref(); refObj.value;`
86-
* @param {Expression | Super | ObjectPattern} node
87-
*/
88-
function verifyRefObjectValue(node) {
89-
const ref = refObjectReferences.get(node)
90-
if (!ref) {
91-
return
92-
}
93-
if (scopes.get(ref.define) !== scopeStack) {
94-
// Not in the same scope
95-
return
96-
}
97-
98-
context.report({
99-
node,
100-
messageId: 'getValueInSameScope'
101-
})
102-
}
103-
104-
/**
105-
* Verify the given reactive variable. `refVal = $ref(); refVal;`
106-
* @param {Identifier} node
107-
*/
108-
function verifyReactiveVariable(node) {
109-
const ref = reactiveVariableReferences.get(node)
110-
if (!ref || ref.escape) {
111-
return
112-
}
113-
if (scopes.get(ref.define) !== scopeStack) {
114-
// Not in the same scope
115-
return
116-
}
117-
118-
context.report({
119-
node,
120-
messageId: 'getReactiveVariableInSameScope'
121-
})
122-
}
123-
124-
return {
125-
':function'(node) {
126-
scopeStack = { upper: scopeStack, node }
127-
},
128-
':function:exit'() {
129-
scopeStack = scopeStack.upper || scopeStack
130-
},
131-
CallExpression(node) {
132-
scopes.set(node, scopeStack)
133-
},
134-
/**
135-
* Check for `refObj.value`.
136-
*/
137-
'MemberExpression:exit'(node) {
138-
if (isUpdate(node)) {
139-
// e.g. `refObj.value = 42`, `refObj.value++`
140-
return
141-
}
142-
const name = utils.getStaticPropertyName(node)
143-
if (name !== 'value') {
144-
return
145-
}
146-
verifyRefObjectValue(node.object)
147-
},
148-
/**
149-
* Check for `{value} = refObj`.
150-
*/
151-
'ObjectPattern:exit'(node) {
152-
const prop = utils.findAssignmentProperty(node, 'value')
153-
if (!prop) {
154-
return
155-
}
156-
verifyRefObjectValue(node)
157-
},
158-
/**
159-
* Check for reactive variable`.
160-
* @param {Identifier} node
161-
*/
162-
'Identifier:exit'(node) {
163-
if (isUpdate(node)) {
164-
// e.g. `reactiveVariable = 42`, `reactiveVariable++`
165-
return
166-
}
167-
verifyReactiveVariable(node)
168-
}
169-
}
22+
return baseRule.create(context)
17023
}
17124
}

0 commit comments

Comments
 (0)