123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171 |
- /**
- * @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)
- }
- }
- }
- }
|