Skip to content

Commit

Permalink
New: add vue/no-unused-properties rule (#631)
Browse files Browse the repository at this point in the history
  • Loading branch information
Michaela Robosova authored and ota-meshi committed May 19, 2020
1 parent d191481 commit fb6a5b6
Show file tree
Hide file tree
Showing 3 changed files with 942 additions and 0 deletions.
129 changes: 129 additions & 0 deletions docs/rules/no-unused-properties.md
@@ -0,0 +1,129 @@
---
pageClass: rule-details
sidebarDepth: 0
title: vue/no-unused-properties
description: disallow unused properties, data and computed properties
---
# vue/no-unused-properties
> disallow unused properties, data and computed properties
## :book: Rule Details

This rule disallows any unused properties, data and computed properties.

```vue
/* ✓ GOOD */
<template>
<div>{{ count }}</div>
</template>
<script>
export default {
props: ['count']
}
</script>
```

```vue
/* ✗ BAD (`count` property not used) */
<template>
<div>{{ cnt }}</div>
</template>
<script>
export default {
props: ['count']
}
</script>
```

```vue
/* ✓ GOOD */
<script>
export default {
data() {
return {
count: null
}
},
created() {
this.count = 2
}
}
</script>
```

```vue
/* ✓ BAD (`count` data not used) */
<script>
export default {
data() {
return {
count: null
}
},
created() {
this.cnt = 2
}
}
</script>
```

```vue
/* ✓ GOOD */
<template>
<p>{{ reversedMessage }}</p>
</template>
<script>
export default {
data() {
return {
message: 'Hello'
}
},
computed: {
reversedMessage() {
return this.message.split('').reverse().join('')
}
}
}
</script>
```

```vue
/* ✓ BAD (`reversedMessage` computed property not used) */
<template>
<p>{{ message }}</p>
</template>
<script>
export default {
data() {
return {
message: 'Hello'
}
},
computed: {
reversedMessage() {
return this.message.split('').reverse().join('')
}
}
}
</script>
```

## :wrench: Options

None.

## :mag: Implementation

- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-unused-properties.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-unused-properties.js)
167 changes: 167 additions & 0 deletions lib/rules/no-unused-properties.js
@@ -0,0 +1,167 @@
/**
* @fileoverview Disallow unused properties, data and computed properties.
* @author Learning Equality
*/
'use strict'

// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------

const remove = require('lodash/remove')
const utils = require('../utils')

// ------------------------------------------------------------------------------
// Constants
// ------------------------------------------------------------------------------

const GROUP_PROPERTY = 'props'
const GROUP_DATA = 'data'
const GROUP_COMPUTED_PROPERTY = 'computed'
const GROUP_WATCHER = 'watch'

const PROPERTY_LABEL = {
[GROUP_PROPERTY]: 'property',
[GROUP_DATA]: 'data',
[GROUP_COMPUTED_PROPERTY]: 'computed property'
}

// ------------------------------------------------------------------------------
// Helpers
// ------------------------------------------------------------------------------

/**
* Extract names from references objects.
*/
const getReferencesNames = references => {
if (!references || !references.length) {
return []
}

return references.map(reference => {
if (!reference.id || !reference.id.name) {
return
}

return reference.id.name
})
}

/**
* Report all unused properties.
*/
const reportUnusedProperties = (context, properties) => {
if (!properties || !properties.length) {
return
}

properties.forEach(property => {
context.report({
node: property.node,
message: `Unused ${PROPERTY_LABEL[property.groupName]} found: "${property.name}"`
})
})
}

// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------

module.exports = {
meta: {
type: 'suggestion',
docs: {
description: 'disallow unused properties, data and computed properties',
category: undefined,
url: 'https://eslint.vuejs.org/rules/no-unused-properties.html'
},
fixable: null,
schema: []
},

create (context) {
let hasTemplate
let rootTemplateEnd
let unusedProperties = []
const thisExpressionsVariablesNames = []

const initialize = {
Program (node) {
if (context.parserServices.getTemplateBodyTokenStore == null) {
context.report({
loc: { line: 1, column: 0 },
message:
'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.'
})
return
}

hasTemplate = Boolean(node.templateBody)
}
}

const scriptVisitor = Object.assign(
{},
{
'MemberExpression[object.type="ThisExpression"][property.type="Identifier"][property.name]' (
node
) {
thisExpressionsVariablesNames.push(node.property.name)
}
},
utils.executeOnVue(context, obj => {
unusedProperties = Array.from(
utils.iterateProperties(obj, new Set([GROUP_PROPERTY, GROUP_DATA, GROUP_COMPUTED_PROPERTY]))
)

const watchers = Array.from(utils.iterateProperties(obj, new Set([GROUP_WATCHER])))
const watchersNames = watchers.map(watcher => watcher.name)

remove(unusedProperties, property => {
return (
thisExpressionsVariablesNames.includes(property.name) ||
watchersNames.includes(property.name)
)
})

if (!hasTemplate && unusedProperties.length) {
reportUnusedProperties(context, unusedProperties)
}
})
)

const templateVisitor = {
'VExpressionContainer[expression!=null][references]' (node) {
const referencesNames = getReferencesNames(node.references)

remove(unusedProperties, property => {
return referencesNames.includes(property.name)
})
},
// save root template end location - just a helper to be used
// for a decision if a parser reached the end of the root template
"VElement[name='template']" (node) {
if (rootTemplateEnd) {
return
}

rootTemplateEnd = node.loc.end
},
"VElement[name='template']:exit" (node) {
if (node.loc.end !== rootTemplateEnd) {
return
}

if (unusedProperties.length) {
reportUnusedProperties(context, unusedProperties)
}
}
}

return Object.assign(
{},
initialize,
utils.defineTemplateBodyVisitor(context, templateVisitor, scriptVisitor)
)
}
}

0 comments on commit fb6a5b6

Please sign in to comment.