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 a5fb774

Browse files
Thomasan1999FloEdelmann
andauthored
feat: add prefer-use-template-ref rule (#2554)
Co-authored-by: Flo Edelmann <git@flo-edelmann.de>
1 parent e13089e commit a5fb774

File tree

5 files changed

+485
-0
lines changed

5 files changed

+485
-0
lines changed

‎docs/rules/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ For example:
270270
| [vue/prefer-prop-type-boolean-first](./prefer-prop-type-boolean-first.md) | enforce `Boolean` comes first in component prop types | :bulb: | :warning: |
271271
| [vue/prefer-separate-static-class](./prefer-separate-static-class.md) | require static class names in template to be in a separate `class` attribute | :wrench: | :hammer: |
272272
| [vue/prefer-true-attribute-shorthand](./prefer-true-attribute-shorthand.md) | require shorthand form attribute when `v-bind` value is `true` | :bulb: | :hammer: |
273+
| [vue/prefer-use-template-ref](./prefer-use-template-ref.md) | require using `useTemplateRef` instead of `ref` for template refs | | :hammer: |
273274
| [vue/require-default-export](./require-default-export.md) | require components to be the default export | | :warning: |
274275
| [vue/require-direct-export](./require-direct-export.md) | require the component to be directly exported | | :hammer: |
275276
| [vue/require-emit-validator](./require-emit-validator.md) | require type definitions in emits | :bulb: | :hammer: |

‎docs/rules/prefer-use-template-ref.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
---
2+
pageClass: rule-details
3+
sidebarDepth: 0
4+
title: vue/prefer-use-template-ref
5+
description: require using `useTemplateRef` instead of `ref` for template refs
6+
---
7+
8+
# vue/prefer-use-template-ref
9+
10+
> require using `useTemplateRef` instead of `ref` for template refs
11+
12+
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> _**This rule has not been released yet.**_ </badge>
13+
14+
## :book: Rule Details
15+
16+
Vue 3.5 introduced a new way of obtaining template refs via
17+
the [`useTemplateRef()`](https://vuejs.org/guide/essentials/template-refs.html#accessing-the-refs) API.
18+
19+
This rule enforces using the new `useTemplateRef` function instead of `ref` for template refs.
20+
21+
<eslint-code-block :rules="{'vue/prefer-use-template-ref': ['error']}">
22+
23+
```vue
24+
<template>
25+
<button ref="submitRef">Submit</button>
26+
<button ref="closeRef">Close</button>
27+
</template>
28+
29+
<script setup>
30+
import { ref, useTemplateRef } from 'vue';
31+
32+
/* ✓ GOOD */
33+
const submitRef = useTemplateRef('submitRef');
34+
const submitButton = useTemplateRef('submitRef');
35+
const closeButton = useTemplateRef('closeRef');
36+
37+
/* ✗ BAD */
38+
const closeRef = ref();
39+
</script>
40+
```
41+
42+
</eslint-code-block>
43+
44+
This rule skips `ref` template function refs as these should be used to allow custom implementation of storing `ref`. If you prefer
45+
`useTemplateRef`, you have to change the value of the template `ref` to a string.
46+
47+
<eslint-code-block :rules="{'vue/prefer-use-template-ref': ['error']}">
48+
49+
```vue
50+
<template>
51+
<button :ref="ref => button = ref">Content</button>
52+
</template>
53+
54+
<script setup>
55+
import { ref } from 'vue';
56+
57+
/* ✓ GOOD */
58+
const button = ref();
59+
</script>
60+
```
61+
62+
</eslint-code-block>
63+
64+
## :wrench: Options
65+
66+
Nothing.
67+
68+
## :mag: Implementation
69+
70+
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/prefer-use-template-ref.js)
71+
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/prefer-use-template-ref.js)

‎lib/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ const plugin = {
208208
'prefer-separate-static-class': require('./rules/prefer-separate-static-class'),
209209
'prefer-template': require('./rules/prefer-template'),
210210
'prefer-true-attribute-shorthand': require('./rules/prefer-true-attribute-shorthand'),
211+
'prefer-use-template-ref': require('./rules/prefer-use-template-ref'),
211212
'prop-name-casing': require('./rules/prop-name-casing'),
212213
'quote-props': require('./rules/quote-props'),
213214
'require-component-is': require('./rules/require-component-is'),

‎lib/rules/prefer-use-template-ref.js

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/**
2+
* @author Thomasan1999
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
'use strict'
6+
7+
const utils = require('../utils')
8+
9+
/** @param expression {Expression | null} */
10+
function expressionIsRef(expression) {
11+
// @ts-ignore
12+
return expression?.callee?.name === 'ref'
13+
}
14+
15+
/** @type {import("eslint").Rule.RuleModule} */
16+
module.exports = {
17+
meta: {
18+
type: 'suggestion',
19+
docs: {
20+
description:
21+
'require using `useTemplateRef` instead of `ref` for template refs',
22+
categories: undefined,
23+
url: 'https://eslint.vuejs.org/rules/prefer-use-template-ref.html'
24+
},
25+
schema: [],
26+
messages: {
27+
preferUseTemplateRef: "Replace 'ref' with 'useTemplateRef'."
28+
}
29+
},
30+
/** @param {RuleContext} context */
31+
create(context) {
32+
/** @type Set<string> */
33+
const templateRefs = new Set()
34+
35+
/**
36+
* @typedef ScriptRef
37+
* @type {{node: Expression, ref: string}}
38+
*/
39+
40+
/**
41+
* @type ScriptRef[] */
42+
const scriptRefs = []
43+
44+
return utils.compositingVisitors(
45+
utils.defineTemplateBodyVisitor(
46+
context,
47+
{
48+
'VAttribute[directive=false]'(node) {
49+
if (node.key.name === 'ref' && node.value?.value) {
50+
templateRefs.add(node.value.value)
51+
}
52+
}
53+
},
54+
{
55+
VariableDeclarator(declarator) {
56+
if (!expressionIsRef(declarator.init)) {
57+
return
58+
}
59+
60+
scriptRefs.push({
61+
// @ts-ignore
62+
node: declarator.init,
63+
// @ts-ignore
64+
ref: declarator.id.name
65+
})
66+
}
67+
}
68+
),
69+
{
70+
'Program:exit'() {
71+
for (const templateRef of templateRefs) {
72+
const scriptRef = scriptRefs.find(
73+
(scriptRef) => scriptRef.ref === templateRef
74+
)
75+
76+
if (!scriptRef) {
77+
continue
78+
}
79+
80+
context.report({
81+
node: scriptRef.node,
82+
messageId: 'preferUseTemplateRef'
83+
})
84+
}
85+
}
86+
}
87+
)
88+
}
89+
}

0 commit comments

Comments
(0)

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