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

Appearance settings

Commit 31a38a9

Browse filesBrowse files
dsl101David Lomasota-meshi
authored
Add option to show warnings when props could be accessed via this (#2101)
Co-authored-by: David Lomas <david.lomas@knowinnovation.com> Co-authored-by: Yosuke Ota <otameshiyo23@gmail.com>
1 parent 6c32bf5 commit 31a38a9
Copy full SHA for 31a38a9

File tree

Expand file treeCollapse file tree

4 files changed

+270
-10
lines changed
Filter options
Expand file treeCollapse file tree

4 files changed

+270
-10
lines changed

‎docs/rules/no-unused-properties.md

Copy file name to clipboardExpand all lines: docs/rules/no-unused-properties.md
+69-2Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ since: v7.0.0
1414
This rule is aimed at eliminating unused properties.
1515

1616
::: warning Note
17-
This rule cannot be checked for use in other components (e.g. `mixins`, Property access via `$refs`) and use in places where the scope cannot be determined.
17+
This rule cannot check for use of properties by other components (e.g. `mixins`, property access via `$refs`) and use in places where the scope cannot be determined. Some access to properties might be implied, for example accessing data or computed via a variable such as `this[varName]`. In this case, the default is to assume all properties, methods, etc. are 'used'. See the `unreferencedOptions` for a more strict interpretation of 'use' in these cases.
1818
:::
1919

2020
<eslint-code-block :rules="{'vue/no-unused-properties': ['error']}">
@@ -56,7 +56,8 @@ This rule cannot be checked for use in other components (e.g. `mixins`, Property
5656
"vue/no-unused-properties": ["error", {
5757
"groups": ["props"],
5858
"deepData": false,
59-
"ignorePublicMembers": false
59+
"ignorePublicMembers": false,
60+
"unreferencedOptions": []
6061
}]
6162
}
6263
```
@@ -69,6 +70,7 @@ This rule cannot be checked for use in other components (e.g. `mixins`, Property
6970
- `"setup"`
7071
- `deepData` (`boolean`) If `true`, the object of the property defined in `data` will be searched deeply. Default is `false`. Include `"data"` in `groups` to use this option.
7172
- `ignorePublicMembers` (`boolean`) If `true`, members marked with a [JSDoc `/** @public */` tag](https://jsdoc.app/tags-public.html) will be ignored. Default is `false`.
73+
- `unreferencedOptions` (`string[]`) Array of access methods that should be interpreted as leaving properties unreferenced. Currently, two such methods are available: `unknownMemberAsUnreferenced`, and `returnAsUnreferenced`. See examples below.
7274

7375
### `"groups": ["props", "data"]`
7476

@@ -218,6 +220,71 @@ This rule cannot be checked for use in other components (e.g. `mixins`, Property
218220

219221
</eslint-code-block>
220222

223+
### `{ "groups": ["computed"], "unreferencedOptions": ["unknownMemberAsUnreferenced"] }`
224+
225+
<eslint-code-block :rules="{'vue/no-unused-properties': ['error', {groups: ['computed'], unreferencedOptions: ['unknownMemberAsUnreferenced']}]}">
226+
227+
```vue
228+
<template>
229+
230+
</template>
231+
<script>
232+
export default {
233+
computed: {
234+
one () {
235+
return 1
236+
},
237+
two () {
238+
return 2
239+
}
240+
},
241+
methods: {
242+
handler () {
243+
/* ✓ GOOD - explicit access to computed */
244+
const a = this.one
245+
const i = 'two'
246+
/* ✗ BAD - unknown access via a variable, two will be reported as unreferenced */
247+
return this[i]
248+
},
249+
}
250+
}
251+
</script>
252+
```
253+
254+
</eslint-code-block>
255+
256+
### `{ "groups": ["computed"], "unreferencedOptions": ["returnAsUnreferenced"] }`
257+
258+
<eslint-code-block :rules="{'vue/no-unused-properties': ['error', {groups: ['computed'], unreferencedOptions: ['returnAsUnreferenced']}]}">
259+
260+
```vue
261+
<template>
262+
263+
</template>
264+
<script>
265+
export default {
266+
computed: {
267+
one () {
268+
return 1
269+
},
270+
two () {
271+
return 2
272+
}
273+
},
274+
methods: {
275+
handler () {
276+
/* ✓ GOOD - explicit access to computed */
277+
const a = this.one
278+
/* ✗ BAD - any property could be accessed by returning `this`, but two will still be reported as unreferenced */
279+
return this
280+
},
281+
}
282+
}
283+
</script>
284+
```
285+
286+
</eslint-code-block>
287+
221288
## :rocket: Version
222289

223290
This rule was introduced in eslint-plugin-vue v7.0.0

‎lib/rules/no-unused-properties.js

Copy file name to clipboardExpand all lines: lib/rules/no-unused-properties.js
+22-2Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ const GROUP_SETUP = 'setup'
5555
const GROUP_WATCHER = 'watch'
5656
const GROUP_EXPOSE = 'expose'
5757

58+
const UNREFERENCED_UNKNOWN_MEMBER = 'unknownMemberAsUnreferenced'
59+
const UNREFERENCED_RETURN = 'returnAsUnreferenced'
60+
5861
const PROPERTY_LABEL = {
5962
props: 'property',
6063
data: 'data',
@@ -206,7 +209,15 @@ module.exports = {
206209
uniqueItems: true
207210
},
208211
deepData: { type: 'boolean' },
209-
ignorePublicMembers: { type: 'boolean' }
212+
ignorePublicMembers: { type: 'boolean' },
213+
unreferencedOptions: {
214+
type: 'array',
215+
items: {
216+
enum: [UNREFERENCED_UNKNOWN_MEMBER, UNREFERENCED_RETURN]
217+
},
218+
additionalItems: false,
219+
uniqueItems: true
220+
}
210221
},
211222
additionalProperties: false
212223
}
@@ -221,8 +232,17 @@ module.exports = {
221232
const groups = new Set(options.groups || [GROUP_PROPERTY])
222233
const deepData = Boolean(options.deepData)
223234
const ignorePublicMembers = Boolean(options.ignorePublicMembers)
235+
const unreferencedOptions = new Set(options.unreferencedOptions || [])
224236

225-
const propertyReferenceExtractor = definePropertyReferenceExtractor(context)
237+
const propertyReferenceExtractor = definePropertyReferenceExtractor(
238+
context,
239+
{
240+
unknownMemberAsUnreferenced: unreferencedOptions.has(
241+
UNREFERENCED_UNKNOWN_MEMBER
242+
),
243+
returnAsUnreferenced: unreferencedOptions.has(UNREFERENCED_RETURN)
244+
}
245+
)
226246

227247
/** @type {TemplatePropertiesContainer} */
228248
const templatePropertiesContainer = {

‎lib/utils/property-references.js

Copy file name to clipboardExpand all lines: lib/utils/property-references.js
+20-5Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,10 @@ module.exports = {
9292
/**
9393
* @param {RuleContext} context The rule context.
9494
*/
95-
function definePropertyReferenceExtractor(context) {
95+
function definePropertyReferenceExtractor(
96+
context,
97+
{ unknownMemberAsUnreferenced = false, returnAsUnreferenced = false } = {}
98+
) {
9699
/** @type {Map<Expression, IPropertyReferences>} */
97100
const cacheForExpression = new Map()
98101
/** @type {Map<Pattern, IPropertyReferences>} */
@@ -314,9 +317,15 @@ function definePropertyReferenceExtractor(context) {
314317
if (parent.object === node) {
315318
// `arg.foo`
316319
const name = utils.getStaticPropertyName(parent)
317-
return name
318-
? new PropertyReferencesForMember(parent, name, withInTemplate)
319-
: ANY
320+
if (name) {
321+
return new PropertyReferencesForMember(
322+
parent,
323+
name,
324+
withInTemplate
325+
)
326+
} else {
327+
return unknownMemberAsUnreferenced ? NEVER : ANY
328+
}
320329
}
321330
return NEVER
322331
}
@@ -331,12 +340,18 @@ function definePropertyReferenceExtractor(context) {
331340
return extractFromExpression(parent, withInTemplate)
332341
}
333342
case 'ArrowFunctionExpression':
334-
case 'ReturnStatement':
335343
case 'VExpressionContainer':
336344
case 'Property':
337345
case 'ArrayExpression': {
338346
return maybeExternalUsed(parent) ? ANY : NEVER
339347
}
348+
case 'ReturnStatement': {
349+
if (returnAsUnreferenced) {
350+
return NEVER
351+
} else {
352+
return maybeExternalUsed(parent) ? ANY : NEVER
353+
}
354+
}
340355
}
341356
return NEVER
342357
}

‎tests/lib/rules/no-unused-properties.js

Copy file name to clipboardExpand all lines: tests/lib/rules/no-unused-properties.js
+159-1Lines changed: 159 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,33 @@ const allOptions = [
2121
]
2222
const deepDataOptions = [{ groups: ['data'], deepData: true }]
2323

24+
const unreferencedOptions = {
25+
// Report errors when accessing via unknown property, e.g. this[varName]
26+
unknownMemberAsUnreferenced: [
27+
{
28+
groups: ['computed'],
29+
unreferencedOptions: ['unknownMemberAsUnreferenced']
30+
}
31+
],
32+
// Report errors when returning this
33+
returnAsUnreferenced: [
34+
{
35+
groups: ['computed'],
36+
unreferencedOptions: ['returnAsUnreferenced']
37+
}
38+
],
39+
// Report all
40+
all: [
41+
{
42+
groups: ['computed'],
43+
unreferencedOptions: [
44+
'unknownMemberAsUnreferenced',
45+
'returnAsUnreferenced'
46+
]
47+
}
48+
]
49+
}
50+
2451
tester.run('no-unused-properties', rule, {
2552
valid: [
2653
// a property used in a script expression
@@ -1699,7 +1726,6 @@ tester.run('no-unused-properties', rule, {
16991726
</script>`
17001727
}
17011728
],
1702-
17031729
invalid: [
17041730
// unused property
17051731
{
@@ -2803,6 +2829,138 @@ tester.run('no-unused-properties', rule, {
28032829
line: 10
28042830
}
28052831
]
2832+
},
2833+
2834+
// unreferencedOptions: unknownMemberAsUnreferenced
2835+
{
2836+
filename: 'test.vue',
2837+
code: `
2838+
<script>
2839+
export default {
2840+
computed: {
2841+
one () {
2842+
return 1
2843+
},
2844+
two () {
2845+
return 2
2846+
}
2847+
},
2848+
methods: {
2849+
handler () {
2850+
const a = this.one
2851+
const i = 'two'
2852+
return this[i]
2853+
},
2854+
}
2855+
}
2856+
</script>`,
2857+
options: unreferencedOptions.unknownMemberAsUnreferenced,
2858+
errors: [
2859+
{
2860+
message: "'two' of computed property found, but never used.",
2861+
line: 8
2862+
}
2863+
]
2864+
},
2865+
// unreferencedOptions: returnAsUnreferenced
2866+
{
2867+
filename: 'test.vue',
2868+
code: `
2869+
<script>
2870+
export default {
2871+
computed: {
2872+
one () {
2873+
return 1
2874+
},
2875+
two () {
2876+
return 2
2877+
}
2878+
},
2879+
methods: {
2880+
handler () {
2881+
const a = this.one
2882+
return this
2883+
},
2884+
}
2885+
}
2886+
</script>`,
2887+
options: unreferencedOptions.returnAsUnreferenced,
2888+
errors: [
2889+
{
2890+
message: "'two' of computed property found, but never used.",
2891+
line: 8
2892+
}
2893+
]
2894+
},
2895+
// unreferencedOptions: returnAsUnreferenced via variable with deepData
2896+
{
2897+
filename: 'test.vue',
2898+
code: `
2899+
<script>
2900+
export default {
2901+
data () {
2902+
return {
2903+
foo: {
2904+
bar: 1
2905+
},
2906+
baz: 2
2907+
}
2908+
},
2909+
methods: {
2910+
handler () {
2911+
const vm = this
2912+
console.log(vm.baz)
2913+
return vm.foo
2914+
},
2915+
}
2916+
}
2917+
</script>
2918+
`,
2919+
options: [
2920+
{
2921+
groups: ['data'],
2922+
unreferencedOptions: ['returnAsUnreferenced'],
2923+
deepData: true
2924+
}
2925+
],
2926+
errors: [
2927+
{
2928+
message: "'foo.bar' of data found, but never used.",
2929+
line: 7
2930+
}
2931+
]
2932+
},
2933+
// unreferencedOptions: all
2934+
{
2935+
filename: 'test.vue',
2936+
code: `
2937+
<script>
2938+
export default {
2939+
computed: {
2940+
one () {
2941+
return 1
2942+
},
2943+
two () {
2944+
return 2
2945+
}
2946+
},
2947+
methods: {
2948+
handler () {
2949+
const a = this.one
2950+
const i = 'two'
2951+
const b = this[i]
2952+
return this
2953+
},
2954+
}
2955+
}
2956+
</script>`,
2957+
options: unreferencedOptions.all,
2958+
errors: [
2959+
{
2960+
message: "'two' of computed property found, but never used.",
2961+
line: 8
2962+
}
2963+
]
28062964
}
28072965
]
28082966
})

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.