diff --git a/lib/rules/no-setup-props-destructure.js b/lib/rules/no-setup-props-destructure.js index f89d9996e..653a0b449 100644 --- a/lib/rules/no-setup-props-destructure.js +++ b/lib/rules/no-setup-props-destructure.js @@ -23,7 +23,10 @@ module.exports = { 'Getting a value from the `props` in root scope of `{{scopeName}}` will cause the value to lose reactivity.' } }, - /** @param {RuleContext} context */ + /** + * @param {RuleContext} context + * @returns {RuleListener} + **/ create(context) { /** * @typedef {object} ScopePropsReferences @@ -32,6 +35,10 @@ module.exports = { */ /** @type {Map} */ const setupScopePropsReferenceIds = new Map() + const wrapperExpressionTypes = new Set([ + 'ArrayExpression', + 'ObjectExpression' + ]) /** * @param {ESNode} node @@ -59,6 +66,14 @@ module.exports = { } const rightNode = utils.skipChainExpression(right) + + if ( + wrapperExpressionTypes.has(rightNode.type) && + isPropsMemberAccessed(rightNode, propsReferences) + ) { + return report(rightNode, 'getProperty', propsReferences.scopeName) + } + if ( left.type !== 'ArrayPattern' && left.type !== 'ObjectPattern' && @@ -66,6 +81,7 @@ module.exports = { ) { return } + /** @type {Expression | Super} */ let rightId = rightNode while (rightId.type === 'MemberExpression') { @@ -75,6 +91,24 @@ module.exports = { report(left, 'getProperty', propsReferences.scopeName) } } + + /** + * @param {Expression} node + * @param {ScopePropsReferences} propsReferences + */ + function isPropsMemberAccessed(node, propsReferences) { + const propRefs = [...propsReferences.refs.values()] + + return propRefs.some((props) => { + const isPropsInExpressionRange = utils.inRange(node.range, props) + const isPropsMemberExpression = + props.parent.type === 'MemberExpression' && + props.parent.object === props + + return isPropsInExpressionRange && isPropsMemberExpression + }) + } + /** * @typedef {object} ScopeStack * @property {ScopeStack | null} upper @@ -114,6 +148,11 @@ module.exports = { } const propsReferenceIds = new Set() for (const reference of variable.references) { + // If reference is in another scope, we can't check it. + if (reference.from !== context.getScope()) { + continue + } + if (!reference.isRead()) { continue } @@ -144,6 +183,26 @@ module.exports = { setupScopePropsReferenceIds.delete(node) }, + /** + * @param {CallExpression} node + */ + CallExpression(node) { + if (!scopeStack) { + return + } + + const propsReferenceIds = setupScopePropsReferenceIds.get( + scopeStack.scopeNode + ) + + if (!propsReferenceIds) { + return + } + + if (isPropsMemberAccessed(node, propsReferenceIds)) { + report(node, 'getProperty', propsReferenceIds.scopeName) + } + }, /** * @param {VariableDeclarator} node */ diff --git a/tests/lib/rules/no-setup-props-destructure.js b/tests/lib/rules/no-setup-props-destructure.js index 5e84a565e..5d9a704d1 100644 --- a/tests/lib/rules/no-setup-props-destructure.js +++ b/tests/lib/rules/no-setup-props-destructure.js @@ -204,6 +204,27 @@ tester.run('no-setup-props-destructure', rule, { line: 4 } ] + }, + { + filename: 'test.vue', + code: ` + + ` + }, + { + filename: 'test.vue', + code: ` + + ` } ], invalid: [ @@ -410,6 +431,18 @@ tester.run('no-setup-props-destructure', rule, { { messageId: 'getProperty', line: 7 + }, + { + messageId: 'getProperty', + line: 9 + }, + { + messageId: 'getProperty', + line: 10 + }, + { + messageId: 'getProperty', + line: 11 } ] }, @@ -524,6 +557,126 @@ tester.run('no-setup-props-destructure', rule, { line: 5 } ] + }, + { + filename: 'test.vue', + code: ` + + `, + errors: [ + { + messageId: 'getProperty', + line: 5 + }, + { + messageId: 'getProperty', + line: 6 + } + ] + }, + { + filename: 'test.vue', + code: ` + + `, + errors: [ + { + messageId: 'getProperty', + line: 4 + }, + { + messageId: 'getProperty', + line: 5 + } + ] + }, + { + filename: 'test.vue', + code: ` + + `, + errors: [ + { + messageId: 'getProperty', + line: 4 + } + ] + }, + { + filename: 'test.vue', + code: ` + + `, + errors: [ + { + messageId: 'getProperty', + line: 4 + } + ] + }, + { + filename: 'test.vue', + code: ` + + `, + errors: [ + { + messageId: 'getProperty', + line: 4 + } + ] + }, + { + filename: 'test.vue', + code: ` + + `, + errors: [ + { + messageId: 'getProperty', + line: 4 + } + ] + }, + { + filename: 'test.vue', + code: ` + + `, + errors: [ + { + messageId: 'getProperty', + line: 6 + } + ] } ] })