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 238de1b

Browse files
Add vue/no-unused-refs rule (#1474)
* Add `vue/no-unused-refs` rule * update doc
1 parent 084bfe3 commit 238de1b

File tree

5 files changed

+641
-0
lines changed

5 files changed

+641
-0
lines changed

‎docs/rules/README.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ For example:
300300
| [vue/no-boolean-default](./no-boolean-default.md) | disallow boolean defaults | :wrench: |
301301
| [vue/no-duplicate-attr-inheritance](./no-duplicate-attr-inheritance.md) | enforce `inheritAttrs` to be set to `false` when using `v-bind="$attrs"` | |
302302
| [vue/no-empty-component-block](./no-empty-component-block.md) | disallow the `<template>` `<script>` `<style>` block to be empty | |
303+
| [vue/no-invalid-model-keys](./no-invalid-model-keys.md) | require valid keys in model option | |
303304
| [vue/no-multiple-objects-in-class](./no-multiple-objects-in-class.md) | disallow to pass multiple objects into array to class | |
304305
| [vue/no-potential-component-option-typo](./no-potential-component-option-typo.md) | disallow a potential typo in your component property | |
305306
| [vue/no-reserved-component-names](./no-reserved-component-names.md) | disallow the use of reserved names in component definitions | |
@@ -315,6 +316,7 @@ For example:
315316
| [vue/no-unregistered-components](./no-unregistered-components.md) | disallow using components that are not registered inside templates | |
316317
| [vue/no-unsupported-features](./no-unsupported-features.md) | disallow unsupported Vue.js syntax on the specified version | :wrench: |
317318
| [vue/no-unused-properties](./no-unused-properties.md) | disallow unused properties | |
319+
| [vue/no-unused-refs](./no-unused-refs.md) | disallow unused refs | |
318320
| [vue/no-useless-mustaches](./no-useless-mustaches.md) | disallow unnecessary mustache interpolations | :wrench: |
319321
| [vue/no-useless-v-bind](./no-useless-v-bind.md) | disallow unnecessary `v-bind` directives | :wrench: |
320322
| [vue/padding-line-between-blocks](./padding-line-between-blocks.md) | require or disallow padding lines between blocks | :wrench: |

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

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
---
2+
pageClass: rule-details
3+
sidebarDepth: 0
4+
title: vue/no-unused-refs
5+
description: disallow unused refs
6+
---
7+
# vue/no-unused-refs
8+
9+
> disallow unused refs
10+
11+
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge>
12+
13+
## :book: Rule Details
14+
15+
This rule is aimed at eliminating unused refs.
16+
This rule reports refs that are defined using the `ref` attribute in `<template>` but are not used via `$refs`.
17+
18+
::: warning Note
19+
This rule cannot be checked for use in other components (e.g. `mixins`, Access via `$refs.x.$refs`).
20+
:::
21+
22+
<eslint-code-block :rules="{'vue/no-unused-refs': ['error']}">
23+
24+
```vue
25+
<template>
26+
<!-- ✓ GOOD -->
27+
<input ref="foo" />
28+
29+
<!-- ✗ BAD (`bar` is not used) -->
30+
<input ref="bar" />
31+
</template>
32+
<script>
33+
export default {
34+
mounted() {
35+
this.$refs.foo.value = 'foo'
36+
}
37+
}
38+
</script>
39+
```
40+
41+
</eslint-code-block>
42+
43+
## :wrench: Options
44+
45+
Nothing.
46+
47+
## :mag: Implementation
48+
49+
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-unused-refs.js)
50+
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-unused-refs.js)

‎lib/index.js‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ module.exports = {
117117
'no-unsupported-features': require('./rules/no-unsupported-features'),
118118
'no-unused-components': require('./rules/no-unused-components'),
119119
'no-unused-properties': require('./rules/no-unused-properties'),
120+
'no-unused-refs': require('./rules/no-unused-refs'),
120121
'no-unused-vars': require('./rules/no-unused-vars'),
121122
'no-use-v-if-with-v-for': require('./rules/no-use-v-if-with-v-for'),
122123
'no-useless-concat': require('./rules/no-useless-concat'),

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

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
/**
2+
* @fileoverview Disallow unused refs.
3+
* @author Yosuke Ota
4+
*/
5+
'use strict'
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
const utils = require('../utils')
12+
13+
// ------------------------------------------------------------------------------
14+
// Helpers
15+
// ------------------------------------------------------------------------------
16+
17+
/**
18+
* Extract names from references objects.
19+
* @param {VReference[]} references
20+
*/
21+
function getReferences(references) {
22+
return references.filter((ref) => ref.variable == null).map((ref) => ref.id)
23+
}
24+
25+
// ------------------------------------------------------------------------------
26+
// Rule Definition
27+
// ------------------------------------------------------------------------------
28+
29+
module.exports = {
30+
meta: {
31+
type: 'suggestion',
32+
docs: {
33+
description: 'disallow unused refs',
34+
categories: undefined,
35+
url: 'https://eslint.vuejs.org/rules/no-unused-refs.html'
36+
},
37+
fixable: null,
38+
schema: [],
39+
messages: {
40+
unused: "'{{name}}' is defined as ref, but never used."
41+
}
42+
},
43+
/** @param {RuleContext} context */
44+
create(context) {
45+
/** @type {Set<string>} */
46+
const usedRefs = new Set()
47+
48+
/** @type {VLiteral[]} */
49+
const defineRefs = []
50+
let hasUnknown = false
51+
52+
/**
53+
* Report all unused refs.
54+
*/
55+
function reportUnusedRefs() {
56+
for (const defineRef of defineRefs) {
57+
if (usedRefs.has(defineRef.value)) {
58+
continue
59+
}
60+
61+
context.report({
62+
node: defineRef,
63+
messageId: 'unused',
64+
data: {
65+
name: defineRef.value
66+
}
67+
})
68+
}
69+
}
70+
71+
/**
72+
* Extract the use ref names for ObjectPattern.
73+
* @param {ObjectPattern} node
74+
* @returns {void}
75+
*/
76+
function extractUsedForObjectPattern(node) {
77+
for (const prop of node.properties) {
78+
if (prop.type === 'Property') {
79+
const name = utils.getStaticPropertyName(prop)
80+
if (name) {
81+
usedRefs.add(name)
82+
} else {
83+
hasUnknown = true
84+
return
85+
}
86+
} else {
87+
hasUnknown = true
88+
return
89+
}
90+
}
91+
}
92+
/**
93+
* Extract the use ref names.
94+
* @param {Identifier | MemberExpression} refsNode
95+
* @returns {void}
96+
*/
97+
function extractUsedForPattern(refsNode) {
98+
/** @type {Identifier | MemberExpression | ChainExpression} */
99+
let node = refsNode
100+
while (node.parent.type === 'ChainExpression') {
101+
node = node.parent
102+
}
103+
const parent = node.parent
104+
if (parent.type === 'AssignmentExpression') {
105+
if (parent.right === node) {
106+
if (parent.left.type === 'ObjectPattern') {
107+
// `({foo} = $refs)`
108+
extractUsedForObjectPattern(parent.left)
109+
} else if (parent.left.type === 'Identifier') {
110+
// `foo = $refs`
111+
hasUnknown = true
112+
}
113+
}
114+
} else if (parent.type === 'VariableDeclarator') {
115+
if (parent.init === node) {
116+
if (parent.id.type === 'ObjectPattern') {
117+
// `const {foo} = $refs`
118+
extractUsedForObjectPattern(parent.id)
119+
} else if (parent.id.type === 'Identifier') {
120+
// `const foo = $refs`
121+
hasUnknown = true
122+
}
123+
}
124+
} else if (parent.type === 'MemberExpression') {
125+
if (parent.object === node) {
126+
// `$refs.foo`
127+
const name = utils.getStaticPropertyName(parent)
128+
if (name) {
129+
usedRefs.add(name)
130+
} else {
131+
hasUnknown = true
132+
}
133+
}
134+
} else if (parent.type === 'CallExpression') {
135+
const argIndex = parent.arguments.indexOf(node)
136+
if (argIndex > -1) {
137+
// `foo($refs)`
138+
hasUnknown = true
139+
}
140+
} else if (
141+
parent.type === 'ForInStatement' ||
142+
parent.type === 'ReturnStatement'
143+
) {
144+
hasUnknown = true
145+
}
146+
}
147+
148+
return utils.defineTemplateBodyVisitor(
149+
context,
150+
{
151+
/**
152+
* @param {VExpressionContainer} node
153+
*/
154+
VExpressionContainer(node) {
155+
if (hasUnknown) {
156+
return
157+
}
158+
for (const id of getReferences(node.references)) {
159+
if (id.name !== '$refs') {
160+
continue
161+
}
162+
extractUsedForPattern(id)
163+
}
164+
},
165+
/**
166+
* @param {VAttribute} node
167+
*/
168+
'VAttribute[directive=false]'(node) {
169+
if (hasUnknown) {
170+
return
171+
}
172+
if (node.key.name === 'ref' && node.value != null) {
173+
defineRefs.push(node.value)
174+
}
175+
},
176+
"VElement[parent.type!='VElement']:exit"() {
177+
if (hasUnknown) {
178+
return
179+
}
180+
reportUnusedRefs()
181+
}
182+
},
183+
{
184+
Identifier(id) {
185+
if (hasUnknown) {
186+
return
187+
}
188+
if (id.name !== '$refs') {
189+
return
190+
}
191+
/** @type {Identifier | MemberExpression} */
192+
let refsNode = id
193+
if (id.parent.type === 'MemberExpression') {
194+
if (id.parent.property === id) {
195+
// `this.$refs.foo`
196+
refsNode = id.parent
197+
}
198+
}
199+
extractUsedForPattern(refsNode)
200+
}
201+
}
202+
)
203+
}
204+
}

0 commit comments

Comments
(0)

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