Repository: vuejs/eslint-plugin-vue
Pull request #2269 (2023-08-05 02:52:23)

Rename `vue/no-ref-object-destructure` to `vue/no-ref-object-reactivity-loss`
related to #2268 


Patch:
diff --git a/docs/rules/index.md b/docs/rules/index.md
index d5525a78..224cddc7 100644
--- a/docs/rules/index.md
+++ b/docs/rules/index.md
@@ -231,7 +231,7 @@ For example:
 | [vue/no-empty-component-block](./no-empty-component-block.md) | disallow the `<template>` `<script>` `<style>` block to be empty |  | :hammer: |
 | [vue/no-multiple-objects-in-class](./no-multiple-objects-in-class.md) | disallow to pass multiple objects into array to class |  | :hammer: |
 | [vue/no-potential-component-option-typo](./no-potential-component-option-typo.md) | disallow a potential typo in your component property | :bulb: | :hammer: |
-| [vue/no-ref-object-destructure](./no-ref-object-destructure.md) | disallow destructuring of ref objects that can lead to loss of reactivity |  | :warning: |
+| [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: |
 | [vue/no-required-prop-with-default](./no-required-prop-with-default.md) | enforce props with default values to be optional | :wrench::bulb: | :warning: |
 | [vue/no-restricted-block](./no-restricted-block.md) | disallow specific block |  | :hammer: |
 | [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
 |:--------|:------------|
 | [vue/component-tags-order](./component-tags-order.md) | [vue/block-order](./block-order.md) |
 | [vue/no-invalid-model-keys](./no-invalid-model-keys.md) | [vue/valid-model-definition](./valid-model-definition.md) |
+| [vue/no-ref-object-destructure](./no-ref-object-destructure.md) | [vue/no-ref-object-reactivity-loss](./no-ref-object-reactivity-loss.md) |
 | [vue/no-setup-props-destructure](./no-setup-props-destructure.md) | [vue/no-setup-props-reactivity-loss](./no-setup-props-reactivity-loss.md) |
 | [vue/script-setup-uses-vars](./script-setup-uses-vars.md) | (no replacement) |
 | [vue/v-on-function-call](./v-on-function-call.md) | [vue/v-on-handler-style](./v-on-handler-style.md) |
diff --git a/docs/rules/no-ref-object-destructure.md b/docs/rules/no-ref-object-destructure.md
index a3b9d775..fd3534c6 100644
--- a/docs/rules/no-ref-object-destructure.md
+++ b/docs/rules/no-ref-object-destructure.md
@@ -2,12 +2,14 @@
 pageClass: rule-details
 sidebarDepth: 0
 title: vue/no-ref-object-destructure
-description: disallow destructuring of ref objects that can lead to loss of reactivity
+description: disallow usages of ref objects that can lead to loss of reactivity
 since: v9.5.0
 ---
 # vue/no-ref-object-destructure
 
-> disallow destructuring of ref objects that can lead to loss of reactivity
+> disallow usages of ref objects that can lead to loss of reactivity
+
+- :no_entry_sign: This rule was **deprecated** and replaced by [vue/no-ref-object-reactivity-loss](no-ref-object-reactivity-loss.md) rule.
 
 ## :book: Rule Details
 
diff --git a/docs/rules/no-ref-object-reactivity-loss.md b/docs/rules/no-ref-object-reactivity-loss.md
new file mode 100644
index 00000000..22bca127
--- /dev/null
+++ b/docs/rules/no-ref-object-reactivity-loss.md
@@ -0,0 +1,55 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-ref-object-reactivity-loss
+description: disallow usages of ref objects that can lead to loss of reactivity
+---
+# vue/no-ref-object-reactivity-loss
+
+> disallow usages of ref objects that can lead to loss of reactivity
+
+- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge>
+
+## :book: Rule Details
+
+This rule reports the usages of ref objects causing the value to lose reactivity.
+
+<eslint-code-block :rules="{'vue/no-ref-object-reactivity-loss': ['error']}" language="javascript" filename="example.js" >
+
+```js
+import { ref } from 'vue'
+const count = ref(0)
+const v1 = count.value /* ✗ BAD */
+const { value: v2 } = count /* ✗ BAD */
+const v3 = computed(() => count.value /* ✓ GOOD */)
+const v4 = fn(count.value) /* ✗ BAD */
+const v5 = fn(count) /* ✓ GOOD */
+const v6 = computed(() => fn(count.value) /* ✓ GOOD */)
+```
+
+</eslint-code-block>
+
+This rule also supports Reactivity Transform, but Reactivity Transform is an experimental feature and may have false positives due to future Vue changes.  
+See the [RFC](https://github.com/vuejs/rfcs/pull/420) for more information on Reactivity Transform.
+
+<eslint-code-block :rules="{'vue/no-ref-object-reactivity-loss': ['error']}" language="javascript" filename="example.js" >
+
+```js
+const count = $ref(0)
+const v1 = count /* ✗ BAD */
+const v2 = $computed(() => count /* ✓ GOOD */)
+const v3 = fn(count) /* ✗ BAD */
+const v4 = fn($$(count)) /* ✓ GOOD */
+const v5 = $computed(() => fn(count) /* ✓ GOOD */)
+```
+
+</eslint-code-block>
+
+## :wrench: Options
+
+Nothing.
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-ref-object-reactivity-loss.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-ref-object-reactivity-loss.js)
diff --git a/lib/index.js b/lib/index.js
index d69a03f4..edbe34a2 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -114,6 +114,7 @@ module.exports = {
     'no-potential-component-option-typo': require('./rules/no-potential-component-option-typo'),
     'no-ref-as-operand': require('./rules/no-ref-as-operand'),
     'no-ref-object-destructure': require('./rules/no-ref-object-destructure'),
+    'no-ref-object-reactivity-loss': require('./rules/no-ref-object-reactivity-loss'),
     'no-required-prop-with-default': require('./rules/no-required-prop-with-default'),
     'no-reserved-component-names': require('./rules/no-reserved-component-names'),
     'no-reserved-keys': require('./rules/no-reserved-keys'),
diff --git a/lib/rules/no-ref-object-destructure.js b/lib/rules/no-ref-object-destructure.js
index 859a3cb1..221c20b3 100644
--- a/lib/rules/no-ref-object-destructure.js
+++ b/lib/rules/no-ref-object-destructure.js
@@ -1,171 +1,24 @@
 /**
- * @author Yosuke Ota <https://github.com/ota-meshi>
+ * @author Yosuke Ota
  * See LICENSE file in root directory for full license.
  */
 'use strict'
-
-const utils = require('../utils')
-const {
-  extractRefObjectReferences,
-  extractReactiveVariableReferences
-} = require('../utils/ref-object-references')
-
-/**
- * @typedef {import('../utils/ref-object-references').RefObjectReferences} RefObjectReferences
- * @typedef {import('../utils/ref-object-references').RefObjectReference} RefObjectReference
- */
-
-/**
- * Checks whether writing assigns a value to the given pattern.
- * @param {Pattern | AssignmentProperty | Property} node
- * @returns {boolean}
- */
-function isUpdate(node) {
-  const parent = node.parent
-  if (parent.type === 'UpdateExpression' && parent.argument === node) {
-    // e.g. `pattern++`
-    return true
-  }
-  if (parent.type === 'AssignmentExpression' && parent.left === node) {
-    // e.g. `pattern = 42`
-    return true
-  }
-  if (
-    (parent.type === 'Property' && parent.value === node) ||
-    parent.type === 'ArrayPattern' ||
-    (parent.type === 'ObjectPattern' &&
-      parent.properties.includes(/** @type {any} */ (node))) ||
-    (parent.type === 'AssignmentPattern' && parent.left === node) ||
-    parent.type === 'RestElement' ||
-    (parent.type === 'MemberExpression' && parent.object === node)
-  ) {
-    return isUpdate(parent)
-  }
-  return false
-}
+const baseRule = require('./no-ref-object-reactivity-loss')
 
 module.exports = {
+  // 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
   meta: {
-    type: 'problem',
+    ...baseRule.meta,
+    // eslint-disable-next-line eslint-plugin/require-meta-docs-description, internal/no-invalid-meta-docs-categories, eslint-plugin/meta-property-ordering
     docs: {
-      description:
-        'disallow destructuring of ref objects that can lead to loss of reactivity',
-      categories: undefined,
+      ...baseRule.meta.docs,
       url: 'https://eslint.vuejs.org/rules/no-ref-object-destructure.html'
     },
-    fixable: null,
-    schema: [],
-    messages: {
-      getValueInSameScope:
-        'Getting a value from the ref object in the same scope will cause the value to lose reactivity.',
-      getReactiveVariableInSameScope:
-        'Getting a reactive variable in the same scope will cause the value to lose reactivity.'
-    }
+    deprecated: true,
+    replacedBy: ['no-ref-object-reactivity-loss']
   },
-  /**
-   * @param {RuleContext} context
-   * @returns {RuleListener}
-   */
+  /** @param {RuleContext} context */
   create(context) {
-    /**
-     * @typedef {object} ScopeStack
-     * @property {ScopeStack | null} upper
-     * @property {Program | FunctionExpression | FunctionDeclaration | ArrowFunctionExpression} node
-     */
-    /** @type {ScopeStack} */
-    let scopeStack = { upper: null, node: context.getSourceCode().ast }
-    /** @type {Map<CallExpression, ScopeStack>} */
-    const scopes = new Map()
-
-    const refObjectReferences = extractRefObjectReferences(context)
-    const reactiveVariableReferences =
-      extractReactiveVariableReferences(context)
-
-    /**
-     * Verify the given ref object value. `refObj = ref(); refObj.value;`
-     * @param {Expression | Super | ObjectPattern} node
-     */
-    function verifyRefObjectValue(node) {
-      const ref = refObjectReferences.get(node)
-      if (!ref) {
-        return
-      }
-      if (scopes.get(ref.define) !== scopeStack) {
-        // Not in the same scope
-        return
-      }
-
-      context.report({
-        node,
-        messageId: 'getValueInSameScope'
-      })
-    }
-
-    /**
-     * Verify the given reactive variable. `refVal = $ref(); refVal;`
-     * @param {Identifier} node
-     */
-    function verifyReactiveVariable(node) {
-      const ref = reactiveVariableReferences.get(node)
-      if (!ref || ref.escape) {
-        return
-      }
-      if (scopes.get(ref.define) !== scopeStack) {
-        // Not in the same scope
-        return
-      }
-
-      context.report({
-        node,
-        messageId: 'getReactiveVariableInSameScope'
-      })
-    }
-
-    return {
-      ':function'(node) {
-        scopeStack = { upper: scopeStack, node }
-      },
-      ':function:exit'() {
-        scopeStack = scopeStack.upper || scopeStack
-      },
-      CallExpression(node) {
-        scopes.set(node, scopeStack)
-      },
-      /**
-       * Check for `refObj.value`.
-       */
-      'MemberExpression:exit'(node) {
-        if (isUpdate(node)) {
-          // e.g. `refObj.value = 42`, `refObj.value++`
-          return
-        }
-        const name = utils.getStaticPropertyName(node)
-        if (name !== 'value') {
-          return
-        }
-        verifyRefObjectValue(node.object)
-      },
-      /**
-       * Check for `{value} = refObj`.
-       */
-      'ObjectPattern:exit'(node) {
-        const prop = utils.findAssignmentProperty(node, 'value')
-        if (!prop) {
-          return
-        }
-        verifyRefObjectValue(node)
-      },
-      /**
-       * Check for reactive variable`.
-       * @param {Identifier} node
-       */
-      'Identifier:exit'(node) {
-        if (isUpdate(node)) {
-          // e.g. `reactiveVariable = 42`, `reactiveVariable++`
-          return
-        }
-        verifyReactiveVariable(node)
-      }
-    }
+    return baseRule.create(context)
   }
 }
diff --git a/lib/rules/no-ref-object-reactivity-loss.js b/lib/rules/no-ref-object-reactivity-loss.js
new file mode 100644
index 00000000..8f61804b
--- /dev/null
+++ b/lib/rules/no-ref-object-reactivity-loss.js
@@ -0,0 +1,171 @@
+/**
+ * @author Yosuke Ota <https://github.com/ota-meshi>
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+const utils = require('../utils')
+const {
+  extractRefObjectReferences,
+  extractReactiveVariableReferences
+} = require('../utils/ref-object-references')
+
+/**
+ * @typedef {import('../utils/ref-object-references').RefObjectReferences} RefObjectReferences
+ * @typedef {import('../utils/ref-object-references').RefObjectReference} RefObjectReference
+ */
+
+/**
+ * Checks whether writing assigns a value to the given pattern.
+ * @param {Pattern | AssignmentProperty | Property} node
+ * @returns {boolean}
+ */
+function isUpdate(node) {
+  const parent = node.parent
+  if (parent.type === 'UpdateExpression' && parent.argument === node) {
+    // e.g. `pattern++`
+    return true
+  }
+  if (parent.type === 'AssignmentExpression' && parent.left === node) {
+    // e.g. `pattern = 42`
+    return true
+  }
+  if (
+    (parent.type === 'Property' && parent.value === node) ||
+    parent.type === 'ArrayPattern' ||
+    (parent.type === 'ObjectPattern' &&
+      parent.properties.includes(/** @type {any} */ (node))) ||
+    (parent.type === 'AssignmentPattern' && parent.left === node) ||
+    parent.type === 'RestElement' ||
+    (parent.type === 'MemberExpression' && parent.object === node)
+  ) {
+    return isUpdate(parent)
+  }
+  return false
+}
+
+module.exports = {
+  meta: {
+    type: 'problem',
+    docs: {
+      description:
+        'disallow usages of ref objects that can lead to loss of reactivity',
+      categories: undefined,
+      url: 'https://eslint.vuejs.org/rules/no-ref-object-reactivity-loss.html'
+    },
+    fixable: null,
+    schema: [],
+    messages: {
+      getValueInSameScope:
+        'Getting a value from the ref object in the same scope will cause the value to lose reactivity.',
+      getReactiveVariableInSameScope:
+        'Getting a reactive variable in the same scope will cause the value to lose reactivity.'
+    }
+  },
+  /**
+   * @param {RuleContext} context
+   * @returns {RuleListener}
+   */
+  create(context) {
+    /**
+     * @typedef {object} ScopeStack
+     * @property {ScopeStack | null} upper
+     * @property {Program | FunctionExpression | FunctionDeclaration | ArrowFunctionExpression} node
+     */
+    /** @type {ScopeStack} */
+    let scopeStack = { upper: null, node: context.getSourceCode().ast }
+    /** @type {Map<CallExpression, ScopeStack>} */
+    const scopes = new Map()
+
+    const refObjectReferences = extractRefObjectReferences(context)
+    const reactiveVariableReferences =
+      extractReactiveVariableReferences(context)
+
+    /**
+     * Verify the given ref object value. `refObj = ref(); refObj.value;`
+     * @param {Expression | Super | ObjectPattern} node
+     */
+    function verifyRefObjectValue(node) {
+      const ref = refObjectReferences.get(node)
+      if (!ref) {
+        return
+      }
+      if (scopes.get(ref.define) !== scopeStack) {
+        // Not in the same scope
+        return
+      }
+
+      context.report({
+        node,
+        messageId: 'getValueInSameScope'
+      })
+    }
+
+    /**
+     * Verify the given reactive variable. `refVal = $ref(); refVal;`
+     * @param {Identifier} node
+     */
+    function verifyReactiveVariable(node) {
+      const ref = reactiveVariableReferences.get(node)
+      if (!ref || ref.escape) {
+        return
+      }
+      if (scopes.get(ref.define) !== scopeStack) {
+        // Not in the same scope
+        return
+      }
+
+      context.report({
+        node,
+        messageId: 'getReactiveVariableInSameScope'
+      })
+    }
+
+    return {
+      ':function'(node) {
+        scopeStack = { upper: scopeStack, node }
+      },
+      ':function:exit'() {
+        scopeStack = scopeStack.upper || scopeStack
+      },
+      CallExpression(node) {
+        scopes.set(node, scopeStack)
+      },
+      /**
+       * Check for `refObj.value`.
+       */
+      'MemberExpression:exit'(node) {
+        if (isUpdate(node)) {
+          // e.g. `refObj.value = 42`, `refObj.value++`
+          return
+        }
+        const name = utils.getStaticPropertyName(node)
+        if (name !== 'value') {
+          return
+        }
+        verifyRefObjectValue(node.object)
+      },
+      /**
+       * Check for `{value} = refObj`.
+       */
+      'ObjectPattern:exit'(node) {
+        const prop = utils.findAssignmentProperty(node, 'value')
+        if (!prop) {
+          return
+        }
+        verifyRefObjectValue(node)
+      },
+      /**
+       * Check for reactive variable`.
+       * @param {Identifier} node
+       */
+      'Identifier:exit'(node) {
+        if (isUpdate(node)) {
+          // e.g. `reactiveVariable = 42`, `reactiveVariable++`
+          return
+        }
+        verifyReactiveVariable(node)
+      }
+    }
+  }
+}


Test patch:
diff --git a/tests/lib/rules/no-ref-object-reactivity-loss.js b/tests/lib/rules/no-ref-object-reactivity-loss.js
new file mode 100644
index 00000000..a40f8619
--- /dev/null
+++ b/tests/lib/rules/no-ref-object-reactivity-loss.js
@@ -0,0 +1,449 @@
+/**
+ * @author Yosuke Ota <https://github.com/ota-meshi>
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+const RuleTester = require('eslint').RuleTester
+const rule = require('../../../lib/rules/no-ref-object-reactivity-loss')
+
+const tester = new RuleTester({
+  parserOptions: {
+    ecmaVersion: 2020,
+    sourceType: 'module'
+  }
+})
+
+tester.run('no-ref-object-reactivity-loss', rule, {
+  valid: [
+    `
+    import { ref } from 'vue'
+    const count = ref(0)
+    const value1 = computed(() => count.value)
+    const value2 = fn(count)
+    const value3 = computed(() => fn(count.value))
+    `,
+    `
+    import { toRefs } from 'vue'
+    const { count } = toRefs(foo)
+    const value1 = computed(() => count.value)
+    const value2 = fn(count)
+    const value3 = computed(() => fn(count.value))
+    `,
+    `
+    import { toRefs } from 'vue'
+    const refs = toRefs(foo)
+    const value1 = computed(() => refs.count.value)
+    const value2 = fn(refs.count)
+    const value3 = computed(() => fn(refs.count.value))
+    `,
+    `
+    import { ref } from 'vue'
+    const count = ref(0)
+    count.value = 42
+    `,
+    `
+    import { ref } from 'vue'
+    const count = ref(0)
+    count.value++
+    `,
+    `
+    import { ref } from 'vue'
+    const count = ref(0)
+    ;( { foo: count.value } = bar )
+    `,
+    `
+    import { ref, computed, shallowRef, customRef, toRef } from 'vue'
+    const r = ref(0)
+    const c = computed(() => r.value)
+    const sr = shallowRef({ count: 1 })
+    const cr = customRef((track, trigger) => {
+      return {
+        get() { return sr.value },
+        set(newValue) { sr.value = newValue }
+      }
+    })
+    const tr = toRef(sr, 'count')
+    function fn() {
+      console.log(c.value, cr.value, tr.value)
+    }
+    `,
+    // unknown
+    `
+    import { ref } from 'vue'
+    const [a] = ref(0)
+    foo.bar = ref(0)
+    unknown.value
+    `,
+    `
+    import { ref } from 'vue'
+    let foo = ref(0)
+    foo = foo
+    `,
+    // Reactivity Transform
+    `
+    const count = $ref(0)
+    const value1 = computed(() => count)
+    const value2 = fn($$(count))
+    const value3 = computed(() => fn(count))
+    `,
+    `
+    const count = $(foo)
+    const value1 = computed(() => count)
+    const value2 = fn($$(count))
+    const value3 = computed(() => fn(count))
+    `,
+    `
+    const { count } = $(foo)
+    const value1 = computed(() => count)
+    const value2 = fn($$(count))
+    const value3 = computed(() => fn(count))
+    `,
+    `
+    let count = $ref(0)
+    count = 42
+    `,
+    `
+    let count = $ref(0)
+    count++
+    `,
+    `
+    let count = $ref(0)
+    ;( { foo: count } = bar )
+    `,
+    `
+    const { v1, v2, v3, v4 } = $(foo)
+    fn($$({ v1, a: [v2], a: [...v3], ...v4}))
+    `,
+    `
+    let r = $ref(0)
+    let c = $computed(() => r)
+    let sr = $shallowRef({ count: 1 })
+    let cr = $customRef((track, trigger) => {
+      return {
+        get() { return sr },
+        set(newValue) { sr = newValue }
+      }
+    })
+    let tr = $toRef($$(sr), 'count')
+    function fn() {
+      console.log(
+        r.value,
+        c.value,
+        sr.value,
+        cr.value,
+        tr.value
+      )
+    }
+    `
+  ],
+  invalid: [
+    {
+      code: `
+      import { ref } from 'vue'
+      const count = ref(0)
+      const value1 = count.value
+      const { value: value2 } = count
+      const value3 = fn(count.value)
+      const { value: value4 = 42 } = count
+      if (foo) {
+        const value1 = count.value
+      }
+      `,
+      errors: [
+        {
+          message:
+            'Getting a value from the ref object in the same scope will cause the value to lose reactivity.',
+          line: 4
+        },
+        {
+          message:
+            'Getting a value from the ref object in the same scope will cause the value to lose reactivity.',
+          line: 5
+        },
+        {
+          message:
+            'Getting a value from the ref object in the same scope will cause the value to lose reactivity.',
+          line: 6
+        },
+        {
+          message:
+            'Getting a value from the ref object in the same scope will cause the value to lose reactivity.',
+          line: 7
+        },
+        {
+          message:
+            'Getting a value from the ref object in the same scope will cause the value to lose reactivity.',
+          line: 9
+        }
+      ]
+    },
+    {
+      code: `
+      import { toRefs } from 'vue'
+      const { count } = toRefs(foo)
+      const value1 = count.value
+      const { value: value2 } = count
+      const value3 = fn(count.value)
+      `,
+      errors: [
+        {
+          message:
+            'Getting a value from the ref object in the same scope will cause the value to lose reactivity.',
+          line: 4
+        },
+        {
+          message:
+            'Getting a value from the ref object in the same scope will cause the value to lose reactivity.',
+          line: 5
+        },
+        {
+          message:
+            'Getting a value from the ref object in the same scope will cause the value to lose reactivity.',
+          line: 6
+        }
+      ]
+    },
+    {
+      code: `
+      import { toRefs } from 'vue'
+      const refs = toRefs(foo)
+      const value1 = refs.count.value
+      const { value: value2 } = refs.count
+      const value3 = fn(refs.count.value)
+      `,
+      errors: [
+        {
+          message:
+            'Getting a value from the ref object in the same scope will cause the value to lose reactivity.',
+          line: 4
+        },
+        {
+          message:
+            'Getting a value from the ref object in the same scope will cause the value to lose reactivity.',
+          line: 5
+        },
+        {
+          message:
+            'Getting a value from the ref object in the same scope will cause the value to lose reactivity.',
+          line: 6
+        }
+      ]
+    },
+    {
+      code: `
+      import { ref } from 'vue'
+      const count = ref(0).value
+      `,
+      errors: [
+        {
+          message:
+            'Getting a value from the ref object in the same scope will cause the value to lose reactivity.',
+          line: 3
+        }
+      ]
+    },
+    {
+      code: `
+      import { toRefs } from 'vue'
+      const refs = toRefs(foo)
+      const { foo = 42 } = refs
+      const v = foo.value
+      `,
+      errors: [
+        {
+          message:
+            'Getting a value from the ref object in the same scope will cause the value to lose reactivity.',
+          line: 5
+        }
+      ]
+    },
+    {
+      code: `
+      import { ref, computed, shallowRef, customRef, toRef } from 'vue'
+      const r = ref(0)
+      const c = computed(() => r.value)
+      const sr = shallowRef({ count: 1 })
+      const cr = customRef((track, trigger) => {
+        return {
+          get() { return sr.value },
+          set(newValue) { sr.value = newValue }
+        }
+      })
+      const tr = toRef(sr, 'count')
+
+      console.log(
+        r.value,
+        c.value,
+        sr.value,
+        cr.value,
+        tr.value
+      )
+      `,
+      errors: [
+        {
+          message:
+            'Getting a value from the ref object in the same scope will cause the value to lose reactivity.',
+          line: 15
+        },
+        {
+          message:
+            'Getting a value from the ref object in the same scope will cause the value to lose reactivity.',
+          line: 16
+        },
+        {
+          message:
+            'Getting a value from the ref object in the same scope will cause the value to lose reactivity.',
+          line: 17
+        },
+        {
+          message:
+            'Getting a value from the ref object in the same scope will cause the value to lose reactivity.',
+          line: 18
+        },
+        {
+          message:
+            'Getting a value from the ref object in the same scope will cause the value to lose reactivity.',
+          line: 19
+        }
+      ]
+    },
+    // Reactivity Transform
+    {
+      code: `
+      const count = $ref(0)
+      const value1 = count
+      const value2 = fn(count)
+      `,
+      errors: [
+        {
+          message:
+            'Getting a reactive variable in the same scope will cause the value to lose reactivity.',
+          line: 3
+        },
+        {
+          message:
+            'Getting a reactive variable in the same scope will cause the value to lose reactivity.',
+          line: 4
+        }
+      ]
+    },
+    {
+      code: `
+      const count = $(foo)
+      const value1 = count
+      const value2 = fn(count)
+      `,
+      errors: [
+        {
+          message:
+            'Getting a reactive variable in the same scope will cause the value to lose reactivity.',
+          line: 3
+        },
+        {
+          message:
+            'Getting a reactive variable in the same scope will cause the value to lose reactivity.',
+          line: 4
+        }
+      ]
+    },
+    {
+      code: `
+      const { count } = $(foo)
+      const value1 = count
+      const value2 = fn(count)
+      `,
+      errors: [
+        {
+          message:
+            'Getting a reactive variable in the same scope will cause the value to lose reactivity.',
+          line: 3
+        },
+        {
+          message:
+            'Getting a reactive variable in the same scope will cause the value to lose reactivity.',
+          line: 4
+        }
+      ]
+    },
+    {
+      code: `
+      const { v1, a: [v2 = 42], b: [...v3], ...v4 } = $(foo)
+      const value1 = v1
+      const value2 = v2
+      const value3 = v3
+      const value4 = v4
+      `,
+      errors: [
+        {
+          message:
+            'Getting a reactive variable in the same scope will cause the value to lose reactivity.',
+          line: 3
+        },
+        {
+          message:
+            'Getting a reactive variable in the same scope will cause the value to lose reactivity.',
+          line: 4
+        },
+        {
+          message:
+            'Getting a reactive variable in the same scope will cause the value to lose reactivity.',
+          line: 5
+        },
+        {
+          message:
+            'Getting a reactive variable in the same scope will cause the value to lose reactivity.',
+          line: 6
+        }
+      ]
+    },
+    {
+      code: `
+      let r = $ref(0)
+      let c = $computed(() => r)
+      let sr = $shallowRef({ count: 1 })
+      let cr = $customRef((track, trigger) => {
+        return {
+          get() { return sr },
+          set(newValue) { sr = newValue }
+        }
+      })
+      let tr = $toRef($$(sr), 'count')
+      console.log(
+        r.value,
+        c.value,
+        sr.value,
+        cr.value,
+        tr.value
+      )
+      `,
+      errors: [
+        {
+          message:
+            'Getting a reactive variable in the same scope will cause the value to lose reactivity.',
+          line: 13
+        },
+        {
+          message:
+            'Getting a reactive variable in the same scope will cause the value to lose reactivity.',
+          line: 14
+        },
+        {
+          message:
+            'Getting a reactive variable in the same scope will cause the value to lose reactivity.',
+          line: 15
+        },
+        {
+          message:
+            'Getting a reactive variable in the same scope will cause the value to lose reactivity.',
+          line: 16
+        },
+        {
+          message:
+            'Getting a reactive variable in the same scope will cause the value to lose reactivity.',
+          line: 17
+        }
+      ]
+    }
+  ]
+})