Skip to content

Commit a0dd38d

Browse files
author
Michaela Robosova
committed
New: add vue/no-unused-properties rule (vuejs#631)
1 parent c3111fe commit a0dd38d

File tree

4 files changed

+943
-0
lines changed

4 files changed

+943
-0
lines changed

docs/rules/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ For example:
155155
| [vue/no-deprecated-scope-attribute](./no-deprecated-scope-attribute.md) | disallow deprecated `scope` attribute (in Vue.js 2.5.0+) | :wrench: |
156156
| [vue/no-empty-pattern](./no-empty-pattern.md) | disallow empty destructuring patterns | |
157157
| [vue/no-restricted-syntax](./no-restricted-syntax.md) | disallow specified syntax | |
158+
| [vue/no-unused-properties](./no-unused-properties.md) | disallow unused properties, data and computed properties | |
158159
| [vue/object-curly-spacing](./object-curly-spacing.md) | enforce consistent spacing inside braces | :wrench: |
159160
| [vue/require-direct-export](./require-direct-export.md) | require the component to be directly exported | |
160161
| [vue/script-indent](./script-indent.md) | enforce consistent indentation in `<script>` | :wrench: |

docs/rules/no-unused-properties.md

+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
---
2+
pageClass: rule-details
3+
sidebarDepth: 0
4+
title: vue/no-unused-properties
5+
description: disallow unused properties, data and computed properties
6+
---
7+
# vue/no-unused-properties
8+
> disallow unused properties, data and computed properties
9+
10+
## :book: Rule Details
11+
12+
This rule disallows any unused properties, data and computed properties.
13+
14+
```vue
15+
/* ✓ GOOD */
16+
17+
<template>
18+
<div>{{ count }}</div>
19+
</template>
20+
21+
<script>
22+
export default {
23+
props: ['count']
24+
}
25+
</script>
26+
```
27+
28+
```vue
29+
/* ✗ BAD (`count` property not used) */
30+
31+
<template>
32+
<div>{{ cnt }}</div>
33+
</template>
34+
35+
<script>
36+
export default {
37+
props: ['count']
38+
}
39+
</script>
40+
```
41+
42+
```vue
43+
/* ✓ GOOD */
44+
45+
<script>
46+
export default {
47+
data() {
48+
return {
49+
count: null
50+
}
51+
},
52+
created() {
53+
this.count = 2
54+
}
55+
}
56+
</script>
57+
```
58+
59+
```vue
60+
/* ✓ BAD (`count` data not used) */
61+
62+
<script>
63+
export default {
64+
data() {
65+
return {
66+
count: null
67+
}
68+
},
69+
created() {
70+
this.cnt = 2
71+
}
72+
}
73+
</script>
74+
```
75+
76+
```vue
77+
/* ✓ GOOD */
78+
79+
<template>
80+
<p>{{ reversedMessage }}</p>
81+
</template>
82+
83+
<script>
84+
export default {
85+
data() {
86+
return {
87+
message: 'Hello'
88+
}
89+
},
90+
computed: {
91+
reversedMessage() {
92+
return this.message.split('').reverse().join('')
93+
}
94+
}
95+
}
96+
</script>
97+
```
98+
99+
```vue
100+
/* ✓ BAD (`reversedMessage` computed property not used) */
101+
102+
<template>
103+
<p>{{ message }}</p>
104+
</template>
105+
106+
<script>
107+
export default {
108+
data() {
109+
return {
110+
message: 'Hello'
111+
}
112+
},
113+
computed: {
114+
reversedMessage() {
115+
return this.message.split('').reverse().join('')
116+
}
117+
}
118+
}
119+
</script>
120+
```
121+
122+
## :wrench: Options
123+
124+
None.
125+
126+
## :mag: Implementation
127+
128+
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-unused-properties.js)
129+
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-unused-properties.js)

lib/rules/no-unused-properties.js

+167
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
/**
2+
* @fileoverview Disallow unused properties, data and computed properties.
3+
* @author Learning Equality
4+
*/
5+
'use strict'
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
const remove = require('lodash/remove')
12+
const utils = require('../utils')
13+
14+
// ------------------------------------------------------------------------------
15+
// Constants
16+
// ------------------------------------------------------------------------------
17+
18+
const GROUP_PROPERTY = 'props'
19+
const GROUP_DATA = 'data'
20+
const GROUP_COMPUTED_PROPERTY = 'computed'
21+
const GROUP_WATCHER = 'watch'
22+
23+
const PROPERTY_LABEL = {
24+
[GROUP_PROPERTY]: 'property',
25+
[GROUP_DATA]: 'data',
26+
[GROUP_COMPUTED_PROPERTY]: 'computed property'
27+
}
28+
29+
// ------------------------------------------------------------------------------
30+
// Helpers
31+
// ------------------------------------------------------------------------------
32+
33+
/**
34+
* Extract names from references objects.
35+
*/
36+
const getReferencesNames = references => {
37+
if (!references || !references.length) {
38+
return []
39+
}
40+
41+
return references.map(reference => {
42+
if (!reference.id || !reference.id.name) {
43+
return
44+
}
45+
46+
return reference.id.name
47+
})
48+
}
49+
50+
/**
51+
* Report all unused properties.
52+
*/
53+
const reportUnusedProperties = (context, properties) => {
54+
if (!properties || !properties.length) {
55+
return
56+
}
57+
58+
properties.forEach(property => {
59+
context.report({
60+
node: property.node,
61+
message: `Unused ${PROPERTY_LABEL[property.groupName]} found: "${property.name}"`
62+
})
63+
})
64+
}
65+
66+
// ------------------------------------------------------------------------------
67+
// Rule Definition
68+
// ------------------------------------------------------------------------------
69+
70+
module.exports = {
71+
meta: {
72+
type: 'suggestion',
73+
docs: {
74+
description: 'disallow unused properties, data and computed properties',
75+
category: undefined,
76+
url: 'https://eslint.vuejs.org/rules/no-unused-properties.html'
77+
},
78+
fixable: null,
79+
schema: []
80+
},
81+
82+
create (context) {
83+
let hasTemplate
84+
let rootTemplateEnd
85+
let unusedProperties = []
86+
const thisExpressionsVariablesNames = []
87+
88+
const initialize = {
89+
Program (node) {
90+
if (context.parserServices.getTemplateBodyTokenStore == null) {
91+
context.report({
92+
loc: { line: 1, column: 0 },
93+
message:
94+
'Use the latest vue-eslint-parser. See also https://vuejs.github.io/eslint-plugin-vue/user-guide/#what-is-the-use-the-latest-vue-eslint-parser-error.'
95+
})
96+
return
97+
}
98+
99+
hasTemplate = Boolean(node.templateBody)
100+
}
101+
}
102+
103+
const scriptVisitor = Object.assign(
104+
{},
105+
{
106+
'MemberExpression[object.type="ThisExpression"][property.type="Identifier"][property.name]' (
107+
node
108+
) {
109+
thisExpressionsVariablesNames.push(node.property.name)
110+
}
111+
},
112+
utils.executeOnVue(context, obj => {
113+
unusedProperties = Array.from(
114+
utils.iterateProperties(obj, new Set([GROUP_PROPERTY, GROUP_DATA, GROUP_COMPUTED_PROPERTY]))
115+
)
116+
117+
const watchers = Array.from(utils.iterateProperties(obj, new Set([GROUP_WATCHER])))
118+
const watchersNames = watchers.map(watcher => watcher.name)
119+
120+
remove(unusedProperties, property => {
121+
return (
122+
thisExpressionsVariablesNames.includes(property.name) ||
123+
watchersNames.includes(property.name)
124+
)
125+
})
126+
127+
if (!hasTemplate && unusedProperties.length) {
128+
reportUnusedProperties(context, unusedProperties)
129+
}
130+
})
131+
)
132+
133+
const templateVisitor = {
134+
'VExpressionContainer[expression!=null][references]' (node) {
135+
const referencesNames = getReferencesNames(node.references)
136+
137+
remove(unusedProperties, property => {
138+
return referencesNames.includes(property.name)
139+
})
140+
},
141+
// save root template end location - just a helper to be used
142+
// for a decision if a parser reached the end of the root template
143+
"VElement[name='template']" (node) {
144+
if (rootTemplateEnd) {
145+
return
146+
}
147+
148+
rootTemplateEnd = node.loc.end
149+
},
150+
"VElement[name='template']:exit" (node) {
151+
if (node.loc.end !== rootTemplateEnd) {
152+
return
153+
}
154+
155+
if (unusedProperties.length) {
156+
reportUnusedProperties(context, unusedProperties)
157+
}
158+
}
159+
}
160+
161+
return Object.assign(
162+
{},
163+
initialize,
164+
utils.defineTemplateBodyVisitor(context, templateVisitor, scriptVisitor)
165+
)
166+
}
167+
}

0 commit comments

Comments
 (0)