Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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‎

Lines changed: 60 additions & 1 deletion
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‎

Lines changed: 153 additions & 0 deletions
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)

AltStyle によって変換されたページ (->オリジナル) /