Skip to content

Commit e665b7c

Browse files
authored
Fixed false negatives for props in template in vue/no-unused-properties (#2435)
1 parent a4be0fc commit e665b7c

File tree

2 files changed

+148
-6
lines changed

2 files changed

+148
-6
lines changed

Diff for: lib/rules/no-unused-properties.js

+27-6
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,8 @@ module.exports = {
233233
const deepData = Boolean(options.deepData)
234234
const ignorePublicMembers = Boolean(options.ignorePublicMembers)
235235
const unreferencedOptions = new Set(options.unreferencedOptions || [])
236+
/** @type {null | Pattern} */
237+
let propsReferencePattern = null
236238

237239
const propertyReferenceExtractor = definePropertyReferenceExtractor(
238240
context,
@@ -332,8 +334,9 @@ module.exports = {
332334

333335
for (const property of container.properties) {
334336
if (
335-
property.groupName === 'props' &&
336-
propertyReferencesForProps.hasProperty(property.name)
337+
(property.groupName === 'props' &&
338+
propertyReferencesForProps.hasProperty(property.name)) ||
339+
propertyReferences.hasProperty('$props')
337340
) {
338341
// used props
339342
continue
@@ -369,6 +372,7 @@ module.exports = {
369372
}
370373
continue
371374
}
375+
372376
context.report({
373377
node: property.node,
374378
messageId: 'unused',
@@ -450,9 +454,9 @@ module.exports = {
450454
return
451455
}
452456

453-
const pattern = target.parent.id
457+
propsReferencePattern = target.parent.id
454458
const propertyReferences =
455-
propertyReferenceExtractor.extractFromPattern(pattern)
459+
propertyReferenceExtractor.extractFromPattern(propsReferencePattern)
456460
container.propertyReferencesForProps.push(propertyReferences)
457461
},
458462
onDefineModelEnter(node, model) {
@@ -709,9 +713,26 @@ module.exports = {
709713
* @param {VExpressionContainer} node
710714
*/
711715
VExpressionContainer(node) {
712-
templatePropertiesContainer.propertyReferences.push(
716+
const property =
713717
propertyReferenceExtractor.extractFromVExpressionContainer(node)
714-
)
718+
719+
templatePropertiesContainer.propertyReferences.push(property)
720+
721+
if (!propsReferencePattern) {
722+
return
723+
}
724+
725+
// props.prop in template
726+
for (const key of property.allProperties().keys()) {
727+
if (
728+
propsReferencePattern.type === 'Identifier' &&
729+
propsReferencePattern.name === key
730+
) {
731+
templatePropertiesContainer.propertyReferences.push(
732+
property.getNest(key)
733+
)
734+
}
735+
}
715736
},
716737
/**
717738
* @param {VAttribute} node

Diff for: tests/lib/rules/no-unused-properties.js

+121
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,20 @@ tester.run('no-unused-properties', rule, {
718718
</script>
719719
`
720720
},
721+
// a property used as a template $props expression
722+
{
723+
filename: 'test.vue',
724+
code: `
725+
<template>
726+
<div>{{ $props }}</div>
727+
</template>
728+
<script>
729+
export default {
730+
props: ['count']
731+
}
732+
</script>
733+
`
734+
},
721735

722736
// properties used in a template expression
723737
{
@@ -910,6 +924,20 @@ tester.run('no-unused-properties', rule, {
910924
</script>
911925
`
912926
},
927+
// a property used in v-on as $props expression
928+
{
929+
filename: 'test.vue',
930+
code: `
931+
<template>
932+
<button @click="alert($props)" />
933+
</template>
934+
<script>
935+
export default {
936+
props: ['count']
937+
};
938+
</script>
939+
`
940+
},
913941

914942
// data used in a script expression
915943
{
@@ -2452,6 +2480,48 @@ tester.run('no-unused-properties', rule, {
24522480
{{ foo }}
24532481
</template>
24542482
`
2483+
},
2484+
2485+
// props.prop in template
2486+
{
2487+
filename: 'test.vue',
2488+
code: `
2489+
<template>
2490+
{{props}}
2491+
</template>
2492+
<script setup>
2493+
const props = defineProps(['a', 'b', 'c'])
2494+
</script>`
2495+
},
2496+
{
2497+
filename: 'test.vue',
2498+
code: `
2499+
<template>
2500+
{{props.a}}
2501+
</template>
2502+
<script setup>
2503+
const props = defineProps(['a'])
2504+
</script>`
2505+
},
2506+
{
2507+
filename: 'test.vue',
2508+
code: `
2509+
<template>
2510+
{{foo.a}}
2511+
</template>
2512+
<script setup>
2513+
const foo = defineProps(['a'])
2514+
</script>`
2515+
},
2516+
{
2517+
code: `
2518+
<script setup lang="ts">
2519+
const props = defineProps<{ foo: string, bar: string }>()
2520+
</script>
2521+
<template>
2522+
{{ props.foo }}{{ bar }}
2523+
</template>`,
2524+
...getTypeScriptFixtureTestOptions()
24552525
}
24562526
],
24572527
invalid: [
@@ -4619,6 +4689,57 @@ tester.run('no-unused-properties', rule, {
46194689
line: 6
46204690
}
46214691
]
4692+
},
4693+
4694+
// a property used as a template $props member expression
4695+
{
4696+
filename: 'test.vue',
4697+
code: `
4698+
<template>
4699+
<div>{{ $props.foo }}</div>
4700+
</template>
4701+
<script>
4702+
export default {
4703+
props: ['foo', 'bar']
4704+
}
4705+
</script>
4706+
`,
4707+
errors: ["'bar' of property found, but never used."]
4708+
},
4709+
4710+
// props.prop in template
4711+
{
4712+
filename: 'test.vue',
4713+
code: `
4714+
<template>
4715+
{{props.a}}
4716+
</template>
4717+
<script setup>
4718+
const props = defineProps(['a', 'b'])
4719+
</script>`,
4720+
errors: ["'b' of property found, but never used."]
4721+
},
4722+
{
4723+
filename: 'test.vue',
4724+
code: `
4725+
<template>
4726+
{{foo.a}}
4727+
</template>
4728+
<script setup>
4729+
const foo = defineProps(['a', 'b'])
4730+
</script>`,
4731+
errors: ["'b' of property found, but never used."]
4732+
},
4733+
{
4734+
code: `
4735+
<script setup lang="ts">
4736+
const props = defineProps<{ foo: string, bar: string, baz: string }>()
4737+
</script>
4738+
<template>
4739+
{{ props.foo }}{{ bar }}
4740+
</template>`,
4741+
errors: ["'baz' of property found, but never used."],
4742+
...getTypeScriptFixtureTestOptions()
46224743
}
46234744
]
46244745
})

0 commit comments

Comments
 (0)