From db8717f54cbfaf468f0d506db2fafa843e4a17b7 Mon Sep 17 00:00:00 2001 From: "David R. Myers" Date: Thu, 20 Jul 2023 18:06:45 -0400 Subject: [PATCH 1/3] Improve the vue/no-setup-props-destructure rule --- lib/rules/no-setup-props-destructure.js | 35 ++++++++- tests/lib/rules/no-setup-props-destructure.js | 76 +++++++++++++++++++ 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/lib/rules/no-setup-props-destructure.js b/lib/rules/no-setup-props-destructure.js index f89d9996e..f6368783e 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 @@ -59,6 +62,30 @@ module.exports = { } const rightNode = utils.skipChainExpression(right) + + if (rightNode.type === 'CallExpression') { + const propRefs = [...propsReferences.refs.values()] + const isPropsMemberAccessed = propRefs.some((props) => { + const isPropsInCallExpression = utils.inRange(rightNode.range, props) + + if (isPropsInCallExpression) { + const isPropMemberExpression = + props.parent.type === 'MemberExpression' && + props.parent.object === props + + if (isPropMemberExpression) { + return true + } + } + + return false + }) + + if (isPropsMemberAccessed) { + return report(left, 'getProperty', propsReferences.scopeName) + } + } + if ( left.type !== 'ArrayPattern' && left.type !== 'ObjectPattern' && @@ -66,6 +93,7 @@ module.exports = { ) { return } + /** @type {Expression | Super} */ let rightId = rightNode while (rightId.type === 'MemberExpression') { @@ -114,6 +142,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 } diff --git a/tests/lib/rules/no-setup-props-destructure.js b/tests/lib/rules/no-setup-props-destructure.js index 5e84a565e..f4c1307f6 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,49 @@ 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 + } + ] } ] }) From ce36f1811422aa5d600e647292f76f5eeb8b1464 Mon Sep 17 00:00:00 2001 From: "David R. Myers" Date: Thu, 20 Jul 2023 23:43:06 -0400 Subject: [PATCH 2/3] Check array/object expressions too --- lib/rules/no-setup-props-destructure.js | 7 ++- tests/lib/rules/no-setup-props-destructure.js | 60 +++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/lib/rules/no-setup-props-destructure.js b/lib/rules/no-setup-props-destructure.js index f6368783e..1170267e7 100644 --- a/lib/rules/no-setup-props-destructure.js +++ b/lib/rules/no-setup-props-destructure.js @@ -35,6 +35,11 @@ module.exports = { */ /** @type {Map} */ const setupScopePropsReferenceIds = new Map() + const outerExpressionTypes = new Set([ + 'ArrayExpression', + 'CallExpression', + 'ObjectExpression' + ]) /** * @param {ESNode} node @@ -63,7 +68,7 @@ module.exports = { const rightNode = utils.skipChainExpression(right) - if (rightNode.type === 'CallExpression') { + if (outerExpressionTypes.has(rightNode.type)) { const propRefs = [...propsReferences.refs.values()] const isPropsMemberAccessed = propRefs.some((props) => { const isPropsInCallExpression = utils.inRange(rightNode.range, props) diff --git a/tests/lib/rules/no-setup-props-destructure.js b/tests/lib/rules/no-setup-props-destructure.js index f4c1307f6..de2048350 100644 --- a/tests/lib/rules/no-setup-props-destructure.js +++ b/tests/lib/rules/no-setup-props-destructure.js @@ -600,6 +600,66 @@ tester.run('no-setup-props-destructure', rule, { 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 + } + ] } ] }) From cf2fcc9b823f490e6b1e18df4ae1c520df202e31 Mon Sep 17 00:00:00 2001 From: "David R. Myers" Date: Thu, 27 Jul 2023 16:02:21 -0400 Subject: [PATCH 3/3] Report on standalone function calls too --- lib/rules/no-setup-props-destructure.js | 67 ++++++++++++------- tests/lib/rules/no-setup-props-destructure.js | 17 +++++ 2 files changed, 61 insertions(+), 23 deletions(-) diff --git a/lib/rules/no-setup-props-destructure.js b/lib/rules/no-setup-props-destructure.js index 1170267e7..653a0b449 100644 --- a/lib/rules/no-setup-props-destructure.js +++ b/lib/rules/no-setup-props-destructure.js @@ -35,9 +35,8 @@ module.exports = { */ /** @type {Map} */ const setupScopePropsReferenceIds = new Map() - const outerExpressionTypes = new Set([ + const wrapperExpressionTypes = new Set([ 'ArrayExpression', - 'CallExpression', 'ObjectExpression' ]) @@ -68,27 +67,11 @@ module.exports = { const rightNode = utils.skipChainExpression(right) - if (outerExpressionTypes.has(rightNode.type)) { - const propRefs = [...propsReferences.refs.values()] - const isPropsMemberAccessed = propRefs.some((props) => { - const isPropsInCallExpression = utils.inRange(rightNode.range, props) - - if (isPropsInCallExpression) { - const isPropMemberExpression = - props.parent.type === 'MemberExpression' && - props.parent.object === props - - if (isPropMemberExpression) { - return true - } - } - - return false - }) - - if (isPropsMemberAccessed) { - return report(left, 'getProperty', propsReferences.scopeName) - } + if ( + wrapperExpressionTypes.has(rightNode.type) && + isPropsMemberAccessed(rightNode, propsReferences) + ) { + return report(rightNode, 'getProperty', propsReferences.scopeName) } if ( @@ -108,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 @@ -182,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 de2048350..5d9a704d1 100644 --- a/tests/lib/rules/no-setup-props-destructure.js +++ b/tests/lib/rules/no-setup-props-destructure.js @@ -660,6 +660,23 @@ tester.run('no-setup-props-destructure', rule, { line: 4 } ] + }, + { + filename: 'test.vue', + code: ` + + `, + errors: [ + { + messageId: 'getProperty', + line: 6 + } + ] } ] })