Skip to content

Commit 684c847

Browse files
Improve the vue/no-setup-props-destructure rule (#2244)
1 parent b8814c7 commit 684c847

File tree

2 files changed

+213
-1
lines changed

2 files changed

+213
-1
lines changed

lib/rules/no-setup-props-destructure.js

+60-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ module.exports = {
2323
'Getting a value from the `props` in root scope of `{{scopeName}}` will cause the value to lose reactivity.'
2424
}
2525
},
26-
/** @param {RuleContext} context */
26+
/**
27+
* @param {RuleContext} context
28+
* @returns {RuleListener}
29+
**/
2730
create(context) {
2831
/**
2932
* @typedef {object} ScopePropsReferences
@@ -32,6 +35,10 @@ module.exports = {
3235
*/
3336
/** @type {Map<FunctionDeclaration | FunctionExpression | ArrowFunctionExpression | Program, ScopePropsReferences>} */
3437
const setupScopePropsReferenceIds = new Map()
38+
const wrapperExpressionTypes = new Set([
39+
'ArrayExpression',
40+
'ObjectExpression'
41+
])
3542

3643
/**
3744
* @param {ESNode} node
@@ -59,13 +66,22 @@ module.exports = {
5966
}
6067

6168
const rightNode = utils.skipChainExpression(right)
69+
70+
if (
71+
wrapperExpressionTypes.has(rightNode.type) &&
72+
isPropsMemberAccessed(rightNode, propsReferences)
73+
) {
74+
return report(rightNode, 'getProperty', propsReferences.scopeName)
75+
}
76+
6277
if (
6378
left.type !== 'ArrayPattern' &&
6479
left.type !== 'ObjectPattern' &&
6580
rightNode.type !== 'MemberExpression'
6681
) {
6782
return
6883
}
84+
6985
/** @type {Expression | Super} */
7086
let rightId = rightNode
7187
while (rightId.type === 'MemberExpression') {
@@ -75,6 +91,24 @@ module.exports = {
7591
report(left, 'getProperty', propsReferences.scopeName)
7692
}
7793
}
94+
95+
/**
96+
* @param {Expression} node
97+
* @param {ScopePropsReferences} propsReferences
98+
*/
99+
function isPropsMemberAccessed(node, propsReferences) {
100+
const propRefs = [...propsReferences.refs.values()]
101+
102+
return propRefs.some((props) => {
103+
const isPropsInExpressionRange = utils.inRange(node.range, props)
104+
const isPropsMemberExpression =
105+
props.parent.type === 'MemberExpression' &&
106+
props.parent.object === props
107+
108+
return isPropsInExpressionRange && isPropsMemberExpression
109+
})
110+
}
111+
78112
/**
79113
* @typedef {object} ScopeStack
80114
* @property {ScopeStack | null} upper
@@ -114,6 +148,11 @@ module.exports = {
114148
}
115149
const propsReferenceIds = new Set()
116150
for (const reference of variable.references) {
151+
// If reference is in another scope, we can't check it.
152+
if (reference.from !== context.getScope()) {
153+
continue
154+
}
155+
117156
if (!reference.isRead()) {
118157
continue
119158
}
@@ -144,6 +183,26 @@ module.exports = {
144183

145184
setupScopePropsReferenceIds.delete(node)
146185
},
186+
/**
187+
* @param {CallExpression} node
188+
*/
189+
CallExpression(node) {
190+
if (!scopeStack) {
191+
return
192+
}
193+
194+
const propsReferenceIds = setupScopePropsReferenceIds.get(
195+
scopeStack.scopeNode
196+
)
197+
198+
if (!propsReferenceIds) {
199+
return
200+
}
201+
202+
if (isPropsMemberAccessed(node, propsReferenceIds)) {
203+
report(node, 'getProperty', propsReferenceIds.scopeName)
204+
}
205+
},
147206
/**
148207
* @param {VariableDeclarator} node
149208
*/

tests/lib/rules/no-setup-props-destructure.js

+153
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,27 @@ tester.run('no-setup-props-destructure', rule, {
204204
line: 4
205205
}
206206
]
207+
},
208+
{
209+
filename: 'test.vue',
210+
code: `
211+
<script>
212+
export default {
213+
setup: (props) => {
214+
const count = computed(() => props.count)
215+
}
216+
}
217+
</script>
218+
`
219+
},
220+
{
221+
filename: 'test.vue',
222+
code: `
223+
<script setup>
224+
const props = defineProps({ count: Number })
225+
const count = computed(() => props.count)
226+
</script>
227+
`
207228
}
208229
],
209230
invalid: [
@@ -410,6 +431,18 @@ tester.run('no-setup-props-destructure', rule, {
410431
{
411432
messageId: 'getProperty',
412433
line: 7
434+
},
435+
{
436+
messageId: 'getProperty',
437+
line: 9
438+
},
439+
{
440+
messageId: 'getProperty',
441+
line: 10
442+
},
443+
{
444+
messageId: 'getProperty',
445+
line: 11
413446
}
414447
]
415448
},
@@ -524,6 +557,126 @@ tester.run('no-setup-props-destructure', rule, {
524557
line: 5
525558
}
526559
]
560+
},
561+
{
562+
filename: 'test.vue',
563+
code: `
564+
<script>
565+
export default {
566+
setup: (props) => {
567+
const count = ref(props.count)
568+
count = fn(props.count)
569+
}
570+
}
571+
</script>
572+
`,
573+
errors: [
574+
{
575+
messageId: 'getProperty',
576+
line: 5
577+
},
578+
{
579+
messageId: 'getProperty',
580+
line: 6
581+
}
582+
]
583+
},
584+
{
585+
filename: 'test.vue',
586+
code: `
587+
<script setup>
588+
const props = defineProps({ count: Number })
589+
const count = ref(props.count)
590+
count = fn(props.count)
591+
</script>
592+
`,
593+
errors: [
594+
{
595+
messageId: 'getProperty',
596+
line: 4
597+
},
598+
{
599+
messageId: 'getProperty',
600+
line: 5
601+
}
602+
]
603+
},
604+
{
605+
filename: 'test.vue',
606+
code: `
607+
<script setup>
608+
const props = defineProps({ count: Number })
609+
const newProps = ref({ count: props.count })
610+
</script>
611+
`,
612+
errors: [
613+
{
614+
messageId: 'getProperty',
615+
line: 4
616+
}
617+
]
618+
},
619+
{
620+
filename: 'test.vue',
621+
code: `
622+
<script setup>
623+
const props = defineProps({ count: Number })
624+
const counts = [props.count]
625+
</script>
626+
`,
627+
errors: [
628+
{
629+
messageId: 'getProperty',
630+
line: 4
631+
}
632+
]
633+
},
634+
{
635+
filename: 'test.vue',
636+
code: `
637+
<script setup>
638+
const props = defineProps({ count: Number })
639+
const counter = { count: props.count }
640+
</script>
641+
`,
642+
errors: [
643+
{
644+
messageId: 'getProperty',
645+
line: 4
646+
}
647+
]
648+
},
649+
{
650+
filename: 'test.vue',
651+
code: `
652+
<script setup>
653+
const props = defineProps({ count: Number })
654+
const counters = [{ count: [props.count] }]
655+
</script>
656+
`,
657+
errors: [
658+
{
659+
messageId: 'getProperty',
660+
line: 4
661+
}
662+
]
663+
},
664+
{
665+
filename: 'test.vue',
666+
code: `
667+
<script setup>
668+
const props = defineProps({ count: Number })
669+
const buildCounter = (count) => ({ count })
670+
671+
buildCounter(props.count)
672+
</script>
673+
`,
674+
errors: [
675+
{
676+
messageId: 'getProperty',
677+
line: 6
678+
}
679+
]
527680
}
528681
]
529682
})

0 commit comments

Comments
 (0)