Skip to content

Commit a0feb9e

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

File tree

2 files changed

+110
-57
lines changed

2 files changed

+110
-57
lines changed

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

+55-30
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,42 @@
66

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

9-
/** @param expression {Expression | null} */
10-
function expressionIsRef(expression) {
11-
// @ts-ignore
12-
return expression?.callee?.name === 'ref'
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,
22+
// @ts-ignore
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(
40+
// @ts-ignore
41+
(declarator) => declarator.init?.callee?.name === 'ref'
42+
)
43+
44+
return refDeclarators.map(convertDeclaratorToScriptRef)
1345
}
1446

1547
/** @type {import("eslint").Rule.RuleModule} */
@@ -32,40 +64,33 @@ module.exports = {
3264
/** @type Set<string> */
3365
const templateRefs = new Set()
3466

35-
/**
36-
* @typedef ScriptRef
37-
* @type {{node: Expression, ref: string}}
38-
*/
39-
4067
/**
4168
* @type ScriptRef[] */
4269
const scriptRefs = []
4370

4471
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-
}
72+
utils.defineTemplateBodyVisitor(context, {
73+
'VAttribute[directive=false]'(node) {
74+
if (node.key.name === 'ref' && node.value?.value) {
75+
templateRefs.add(node.value.value)
5276
}
53-
},
54-
{
55-
VariableDeclarator(declarator) {
56-
if (!expressionIsRef(declarator.init)) {
57-
return
58-
}
77+
}
78+
}),
79+
utils.defineVueVisitor(context, {
80+
onSetupFunctionEnter(node) {
81+
// @ts-ignore
82+
const newScriptRefs = getScriptRefsFromSetupFunction(node.body.body)
5983

60-
scriptRefs.push({
61-
// @ts-ignore
62-
node: declarator.init,
63-
// @ts-ignore
64-
ref: declarator.id.name
65-
})
66-
}
84+
scriptRefs.push(...newScriptRefs)
85+
}
86+
}),
87+
utils.defineScriptSetupVisitor(context, {
88+
Program(node) {
89+
const newScriptRefs = getScriptRefsFromSetupFunction(node.body)
90+
91+
scriptRefs.push(...newScriptRefs)
6792
}
68-
),
93+
}),
6994
{
7095
'Program:exit'() {
7196
for (const templateRef of templateRefs) {

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

+55-27
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: [
@@ -266,33 +321,6 @@ tester.run('prefer-use-template-ref', rule, {
266321
}
267322
]
268323
},
269-
{
270-
filename: 'ref-in-block.vue',
271-
code: `
272-
<template>
273-
<div>
274-
<ul>
275-
<li ref="firstListItem">Morning</li>
276-
<li>Afternoon</li>
277-
<li>Evening</li>
278-
</ul>
279-
</div>
280-
</template>
281-
<script setup>
282-
import { ref } from 'vue';
283-
function getFirstListItemElement() {
284-
const firstListItem = ref();
285-
}
286-
</script>
287-
`,
288-
errors: [
289-
{
290-
messageId: 'preferUseTemplateRef',
291-
line: 14,
292-
column: 33
293-
}
294-
]
295-
},
296324
{
297325
filename: 'setup-function-only-refs.vue',
298326
code: `

0 commit comments

Comments
 (0)