Skip to content

Commit

Permalink
fix(prefer-use-template-ref): only check root-level variables (#2612)
Browse files Browse the repository at this point in the history
  • Loading branch information
Thomasan1999 authored Nov 27, 2024
1 parent 39b353a commit fdfffd6
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 61 deletions.
83 changes: 52 additions & 31 deletions lib/rules/prefer-use-template-ref.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,42 @@

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

/** @param expression {Expression | null} */
function expressionIsRef(expression) {
return (
/**
* @typedef ScriptRef
* @type {{node: Expression, ref: string}}
*/

/**
* @param declarator {VariableDeclarator}
* @returns {ScriptRef}
* */
function convertDeclaratorToScriptRef(declarator) {
return {
// @ts-ignore
node: declarator.init,
// @ts-ignore
expression?.callee?.name === 'ref' ||
ref: declarator.id.name
}
}

/**
* @param body {(Statement | ModuleDeclaration)[]}
* @returns {ScriptRef[]}
* */
function getScriptRefsFromSetupFunction(body) {
/** @type {VariableDeclaration[]} */
const variableDeclarations = body.filter(
(child) => child.type === 'VariableDeclaration'
)
const variableDeclarators = variableDeclarations.map(
(declaration) => declaration.declarations[0]
)
const refDeclarators = variableDeclarators.filter((declarator) =>
// @ts-ignore
expression?.callee?.name === 'shallowRef'
['ref', 'shallowRef'].includes(declarator.init?.callee?.name)
)

return refDeclarators.map(convertDeclaratorToScriptRef)
}

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

/**
* @typedef ScriptRef
* @type {{node: Expression, ref: string}}
*/

/**
* @type ScriptRef[] */
const scriptRefs = []

return utils.compositingVisitors(
utils.defineTemplateBodyVisitor(
context,
{
'VAttribute[directive=false]'(node) {
if (node.key.name === 'ref' && node.value?.value) {
templateRefs.add(node.value.value)
}
utils.defineTemplateBodyVisitor(context, {
'VAttribute[directive=false]'(node) {
if (node.key.name === 'ref' && node.value?.value) {
templateRefs.add(node.value.value)
}
},
{
VariableDeclarator(declarator) {
if (!expressionIsRef(declarator.init)) {
return
}
}
}),
utils.defineVueVisitor(context, {
onSetupFunctionEnter(node) {
// @ts-ignore
const newScriptRefs = getScriptRefsFromSetupFunction(node.body.body)

scriptRefs.push({
// @ts-ignore
node: declarator.init,
// @ts-ignore
ref: declarator.id.name
})
}
scriptRefs.push(...newScriptRefs)
}
}),
utils.defineScriptSetupVisitor(context, {
Program(node) {
const newScriptRefs = getScriptRefsFromSetupFunction(node.body)

scriptRefs.push(...newScriptRefs)
}
),
}),
{
'Program:exit'() {
for (const templateRef of templateRefs) {
Expand Down
85 changes: 55 additions & 30 deletions tests/lib/rules/prefer-use-template-ref.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,61 @@ tester.run('prefer-use-template-ref', rule, {
const button = ref();
</script>
`
},
{
filename: 'ref-in-block.vue',
code: `
<template>
<div>
<ul>
<li ref="firstListItem">Morning</li>
<li ref="second">Afternoon</li>
<li>Evening</li>
</ul>
</div>
</template>
<script setup>
import { ref, shallowRef } from 'vue';
function getFirstListItemElement() {
const firstListItem = ref();
const nestedCallback = () => {
const second = shallowRef();
console.log(second);
}
nestedCallback();
}
</script>
`
},
{
filename: 'ref-in-block-setup-fn.vue',
code: `
<template>
<div>
<ul>
<li ref="firstListItem">Morning</li>
<li ref="second">Afternoon</li>
<li>Evening</li>
</ul>
</div>
</template>
<script>
import { ref, shallowRef } from 'vue';
export default {
name: 'ComponentWithRefInBlock',
setup() {
function getFirstListItemElement() {
const firstListItem = shallowRef();
const nestedCallback = () => {
const second = ref();
console.log(second);
}
nestedCallback();
}
}
}
</script>
`
}
],
invalid: [
Expand Down Expand Up @@ -278,36 +333,6 @@ tester.run('prefer-use-template-ref', rule, {
}
]
},
{
filename: 'ref-in-block.vue',
code: `
<template>
<div>
<ul>
<li ref="firstListItem">Morning</li>
<li>Afternoon</li>
<li>Evening</li>
</ul>
</div>
</template>
<script setup>
import { ref } from 'vue';
function getFirstListItemElement() {
const firstListItem = ref();
}
</script>
`,
errors: [
{
messageId: 'preferUseTemplateRef',
data: {
name: 'ref'
},
line: 14,
column: 33
}
]
},
{
filename: 'setup-function-only-refs.vue',
code: `
Expand Down

0 comments on commit fdfffd6

Please sign in to comment.