Skip to content

Commit a4e2a2e

Browse files
committed
fix(prefer-use-template-ref): should check only root-level variables
1 parent 4500389 commit a4e2a2e

File tree

2 files changed

+107
-31
lines changed

2 files changed

+107
-31
lines changed

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

+52-31
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,42 @@
66

77
const utils = require('../utils')
88

9-
/** @param expression {Expression | null} */
10-
function expressionIsRef(expression) {
11-
return (
9+
/**
10+
* @typedef ScriptRef
11+
* @type {{node: Expression, ref: string}}
12+
*/
13+
14+
/**
15+
* @param declarator {VariableDeclarator}
16+
* @returns {ScriptRef}
17+
* */
18+
function convertDeclaratorToScriptRef(declarator) {
19+
return {
20+
// @ts-ignore
21+
node: declarator.init,
1222
// @ts-ignore
13-
expression?.callee?.name === 'ref' ||
23+
ref: declarator.id.name
24+
}
25+
}
26+
27+
/**
28+
* @param body {(Statement | ModuleDeclaration)[]}
29+
* @returns {ScriptRef[]}
30+
* */
31+
function getScriptRefsFromSetupFunction(body) {
32+
/** @type {VariableDeclaration[]} */
33+
const variableDeclarations = body.filter(
34+
(child) => child.type === 'VariableDeclaration'
35+
)
36+
const variableDeclarators = variableDeclarations.map(
37+
(declaration) => declaration.declarations[0]
38+
)
39+
const refDeclarators = variableDeclarators.filter(
1440
// @ts-ignore
15-
expression?.callee?.name === 'shallowRef'
41+
(declarator) => declarator.init?.callee?.name === 'ref'
1642
)
43+
44+
return refDeclarators.map(convertDeclaratorToScriptRef)
1745
}
1846

1947
/** @type {import("eslint").Rule.RuleModule} */
@@ -36,40 +64,33 @@ module.exports = {
3664
/** @type Set<string> */
3765
const templateRefs = new Set()
3866

39-
/**
40-
* @typedef ScriptRef
41-
* @type {{node: Expression, ref: string}}
42-
*/
43-
4467
/**
4568
* @type ScriptRef[] */
4669
const scriptRefs = []
4770

4871
return utils.compositingVisitors(
49-
utils.defineTemplateBodyVisitor(
50-
context,
51-
{
52-
'VAttribute[directive=false]'(node) {
53-
if (node.key.name === 'ref' && node.value?.value) {
54-
templateRefs.add(node.value.value)
55-
}
72+
utils.defineTemplateBodyVisitor(context, {
73+
'VAttribute[directive=false]'(node) {
74+
if (node.key.name === 'ref' && node.value?.value) {
75+
templateRefs.add(node.value.value)
5676
}
57-
},
58-
{
59-
VariableDeclarator(declarator) {
60-
if (!expressionIsRef(declarator.init)) {
61-
return
62-
}
77+
}
78+
}),
79+
utils.defineVueVisitor(context, {
80+
onSetupFunctionEnter(node) {
81+
// @ts-ignore
82+
const newScriptRefs = getScriptRefsFromSetupFunction(node.body.body)
6383

64-
scriptRefs.push({
65-
// @ts-ignore
66-
node: declarator.init,
67-
// @ts-ignore
68-
ref: declarator.id.name
69-
})
70-
}
84+
scriptRefs.push(...newScriptRefs)
85+
}
86+
}),
87+
utils.defineScriptSetupVisitor(context, {
88+
Program(node) {
89+
const newScriptRefs = getScriptRefsFromSetupFunction(node.body)
90+
91+
scriptRefs.push(...newScriptRefs)
7192
}
72-
),
93+
}),
7394
{
7495
'Program:exit'() {
7596
for (const templateRef of templateRefs) {

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

+55
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,61 @@ tester.run('prefer-use-template-ref', rule, {
197197
const button = ref();
198198
</script>
199199
`
200+
},
201+
{
202+
filename: 'ref-in-block.vue',
203+
code: `
204+
<template>
205+
<div>
206+
<ul>
207+
<li ref="firstListItem">Morning</li>
208+
<li ref="second">Afternoon</li>
209+
<li>Evening</li>
210+
</ul>
211+
</div>
212+
</template>
213+
<script setup>
214+
import { ref } from 'vue';
215+
function getFirstListItemElement() {
216+
const firstListItem = ref();
217+
const nestedCallback = () => {
218+
const second = ref();
219+
console.log(second);
220+
}
221+
nestedCallback();
222+
}
223+
</script>
224+
`
225+
},
226+
{
227+
filename: 'ref-in-block-setup-fn.vue',
228+
code: `
229+
<template>
230+
<div>
231+
<ul>
232+
<li ref="firstListItem">Morning</li>
233+
<li ref="second">Afternoon</li>
234+
<li>Evening</li>
235+
</ul>
236+
</div>
237+
</template>
238+
<script>
239+
import { ref } from 'vue';
240+
export default {
241+
name: 'ComponentWithRefInBlock',
242+
setup() {
243+
function getFirstListItemElement() {
244+
const firstListItem = ref();
245+
const nestedCallback = () => {
246+
const second = ref();
247+
console.log(second);
248+
}
249+
nestedCallback();
250+
}
251+
}
252+
}
253+
</script>
254+
`
200255
}
201256
],
202257
invalid: [

0 commit comments

Comments
 (0)