From c741b59dd9d051f7db0ab3475b5c85d46263b2ab Mon Sep 17 00:00:00 2001 From: Vida Xie Date: Thu, 27 Nov 2025 23:09:38 +0800 Subject: [PATCH 1/7] refactor(prefer-use-template-ref): optimize array processing with flatMap --- lib/rules/prefer-use-template-ref.js | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/lib/rules/prefer-use-template-ref.js b/lib/rules/prefer-use-template-ref.js index eb8b8c06d..ae3347150 100644 --- a/lib/rules/prefer-use-template-ref.js +++ b/lib/rules/prefer-use-template-ref.js @@ -29,19 +29,20 @@ function convertDeclaratorToScriptRef(declarator) { * @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 - ['ref', 'shallowRef'].includes(declarator.init?.callee?.name) - ) + return body.flatMap((child) => { + if (child.type === 'VariableDeclaration') { + const declarator = child.declarations[0] + + if ( + declarator.init?.type === 'CallExpression' && + declarator.init.callee?.type === 'Identifier' && + ['ref', 'shallowRef'].includes(declarator.init?.callee?.name) + ) + return [convertDeclaratorToScriptRef(declarator)] + } - return refDeclarators.map(convertDeclaratorToScriptRef) + return [] + }) } /** @type {import("eslint").Rule.RuleModule} */ From 3f4795c6a6d97bf4de0f0a03ac02e993a2160bd6 Mon Sep 17 00:00:00 2001 From: Vida Xie Date: Thu, 27 Nov 2025 23:20:45 +0800 Subject: [PATCH 2/7] refactor: restrict type check --- lib/rules/prefer-use-template-ref.js | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/lib/rules/prefer-use-template-ref.js b/lib/rules/prefer-use-template-ref.js index ae3347150..db4736885 100644 --- a/lib/rules/prefer-use-template-ref.js +++ b/lib/rules/prefer-use-template-ref.js @@ -11,19 +11,6 @@ const utils = require('../utils') * @type {{node: Expression, ref: string}} */ -/** - * @param declarator {VariableDeclarator} - * @returns {ScriptRef} - * */ -function convertDeclaratorToScriptRef(declarator) { - return { - // @ts-ignore - node: declarator.init, - // @ts-ignore - ref: declarator.id.name - } -} - /** * @param body {(Statement | ModuleDeclaration)[]} * @returns {ScriptRef[]} @@ -36,9 +23,13 @@ function getScriptRefsFromSetupFunction(body) { if ( declarator.init?.type === 'CallExpression' && declarator.init.callee?.type === 'Identifier' && - ['ref', 'shallowRef'].includes(declarator.init?.callee?.name) + declarator.id.type === 'Identifier' && + ['ref', 'shallowRef'].includes(declarator.init.callee.name) ) - return [convertDeclaratorToScriptRef(declarator)] + return { + node: declarator.init, + ref: declarator.id.name + } } return [] From ebff8d9780d6a43e4ee52550a6bbe10de928e3c3 Mon Sep 17 00:00:00 2001 From: Vida Xie Date: Thu, 27 Nov 2025 23:37:56 +0800 Subject: [PATCH 3/7] refactor: generate Map --- lib/rules/prefer-use-template-ref.js | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/lib/rules/prefer-use-template-ref.js b/lib/rules/prefer-use-template-ref.js index db4736885..37bfec2d1 100644 --- a/lib/rules/prefer-use-template-ref.js +++ b/lib/rules/prefer-use-template-ref.js @@ -8,7 +8,7 @@ const utils = require('../utils') /** * @typedef ScriptRef - * @type {{node: Expression, ref: string}} + * @type {[string, CallExpression]} */ /** @@ -26,10 +26,7 @@ function getScriptRefsFromSetupFunction(body) { declarator.id.type === 'Identifier' && ['ref', 'shallowRef'].includes(declarator.init.callee.name) ) - return { - node: declarator.init, - ref: declarator.id.name - } + return [[declarator.id.name, declarator.init]] } return [] @@ -86,21 +83,21 @@ module.exports = { }), { 'Program:exit'() { + const scriptRefsMap = new Map(scriptRefs) + for (const templateRef of templateRefs) { - const scriptRef = scriptRefs.find( - (scriptRef) => scriptRef.ref === templateRef - ) + const scriptRef = scriptRefsMap.get(templateRef) if (!scriptRef) { continue } context.report({ - node: scriptRef.node, + node: scriptRef, messageId: 'preferUseTemplateRef', data: { - // @ts-ignore - name: scriptRef.node?.callee?.name + // @ts-expect-error `scriptRef.callee` is `Identifier` + name: scriptRef.callee.name } }) } From cb02d044a36a6c9233b9ada0da03977e3e6a546b Mon Sep 17 00:00:00 2001 From: Vida Xie Date: Thu, 27 Nov 2025 23:51:23 +0800 Subject: [PATCH 4/7] feat(prefer-template-ref): support auto-fix --- lib/rules/prefer-use-template-ref.js | 7 ++ tests/lib/rules/prefer-use-template-ref.js | 133 +++++++++++++++++++-- 2 files changed, 131 insertions(+), 9 deletions(-) diff --git a/lib/rules/prefer-use-template-ref.js b/lib/rules/prefer-use-template-ref.js index 37bfec2d1..581c18038 100644 --- a/lib/rules/prefer-use-template-ref.js +++ b/lib/rules/prefer-use-template-ref.js @@ -43,6 +43,7 @@ module.exports = { categories: undefined, url: 'https://eslint.vuejs.org/rules/prefer-use-template-ref.html' }, + fixable: 'code', schema: [], messages: { preferUseTemplateRef: "Replace '{{name}}' with 'useTemplateRef'." @@ -98,6 +99,12 @@ module.exports = { data: { // @ts-expect-error `scriptRef.callee` is `Identifier` name: scriptRef.callee.name + }, + fix(fixer) { + return fixer.replaceText( + scriptRef, + `useTemplateRef('${templateRef}')` + ) } }) } diff --git a/tests/lib/rules/prefer-use-template-ref.js b/tests/lib/rules/prefer-use-template-ref.js index afc6d6a11..cf59f3917 100644 --- a/tests/lib/rules/prefer-use-template-ref.js +++ b/tests/lib/rules/prefer-use-template-ref.js @@ -311,6 +311,15 @@ tester.run('prefer-use-template-ref', rule, { const root = ref(); `, + output: ` + + + `, errors: [ { messageId: 'preferUseTemplateRef', @@ -318,7 +327,9 @@ tester.run('prefer-use-template-ref', rule, { name: 'ref' }, line: 7, - column: 22 + column: 22, + endLine: 7, + endColumn: 27 } ] }, @@ -335,6 +346,17 @@ tester.run('prefer-use-template-ref', rule, { const link = ref(); `, + output: ` + + + `, errors: [ { messageId: 'preferUseTemplateRef', @@ -342,7 +364,9 @@ tester.run('prefer-use-template-ref', rule, { name: 'ref' }, line: 9, - column: 22 + column: 22, + endLine: 9, + endColumn: 27 } ] }, @@ -359,6 +383,17 @@ tester.run('prefer-use-template-ref', rule, { const link = ref(); `, + output: ` + + + `, errors: [ { messageId: 'preferUseTemplateRef', @@ -366,7 +401,9 @@ tester.run('prefer-use-template-ref', rule, { name: 'ref' }, line: 8, - column: 25 + column: 25, + endLine: 8, + endColumn: 30 }, { messageId: 'preferUseTemplateRef', @@ -374,7 +411,9 @@ tester.run('prefer-use-template-ref', rule, { name: 'ref' }, line: 9, - column: 22 + column: 22, + endLine: 9, + endColumn: 27 } ] }, @@ -396,6 +435,22 @@ tester.run('prefer-use-template-ref', rule, { } `, + output: ` + + + `, errors: [ { messageId: 'preferUseTemplateRef', @@ -403,7 +458,9 @@ tester.run('prefer-use-template-ref', rule, { name: 'ref' }, line: 12, - column: 28 + column: 28, + endLine: 12, + endColumn: 33 } ] }, @@ -418,6 +475,15 @@ tester.run('prefer-use-template-ref', rule, { const root = shallowRef(); `, + output: ` + + + `, errors: [ { messageId: 'preferUseTemplateRef', @@ -425,7 +491,9 @@ tester.run('prefer-use-template-ref', rule, { name: 'shallowRef' }, line: 7, - column: 22 + column: 22, + endLine: 7, + endColumn: 34 } ] }, @@ -444,6 +512,19 @@ tester.run('prefer-use-template-ref', rule, { } `, + output: ` + + + `, errors: [ { messageId: 'preferUseTemplateRef', @@ -451,7 +532,9 @@ tester.run('prefer-use-template-ref', rule, { name: 'ref' }, line: 9, - column: 28 + column: 28, + endLine: 9, + endColumn: 33 } ] }, @@ -467,6 +550,20 @@ tester.run('prefer-use-template-ref', rule, { const root = ref() + + `, + output: ` + + + + @@ -478,7 +575,9 @@ tester.run('prefer-use-template-ref', rule, { name: 'ref' }, line: 8, - column: 20 + column: 20, + endLine: 8, + endColumn: 25 } ] }, @@ -498,6 +597,20 @@ tester.run('prefer-use-template-ref', rule, { const root = ref() `, + output: ` + + + + + + `, errors: [ { messageId: 'preferUseTemplateRef', @@ -505,7 +618,9 @@ tester.run('prefer-use-template-ref', rule, { name: 'ref' }, line: 12, - column: 20 + column: 20, + endLine: 12, + endColumn: 25 } ] } From 3487873cffe4d67fc4747692a12975f12e2159d1 Mon Sep 17 00:00:00 2001 From: Vida Xie Date: Fri, 28 Nov 2025 00:09:12 +0800 Subject: [PATCH 5/7] chore: run update --- docs/rules/index.md | 2 +- docs/rules/prefer-use-template-ref.md | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/rules/index.md b/docs/rules/index.md index 3209c157d..fe8850555 100644 --- a/docs/rules/index.md +++ b/docs/rules/index.md @@ -274,7 +274,7 @@ For example: | [vue/prefer-prop-type-boolean-first] | enforce `Boolean` comes first in component prop types | :bulb: | :warning: | | [vue/prefer-separate-static-class] | require static class names in template to be in a separate `class` attribute | :wrench: | :hammer: | | [vue/prefer-true-attribute-shorthand] | require shorthand form attribute when `v-bind` value is `true` | :bulb: | :hammer: | -| [vue/prefer-use-template-ref] | require using `useTemplateRef` instead of `ref`/`shallowRef` for template refs | | :hammer: | +| [vue/prefer-use-template-ref] | require using `useTemplateRef` instead of `ref`/`shallowRef` for template refs | :wrench: | :hammer: | | [vue/require-default-export] | require components to be the default export | | :warning: | | [vue/require-direct-export] | require the component to be directly exported | | :hammer: | | [vue/require-emit-validator] | require type definitions in emits | :bulb: | :hammer: | diff --git a/docs/rules/prefer-use-template-ref.md b/docs/rules/prefer-use-template-ref.md index 1b1b40385..cfa9a7a88 100644 --- a/docs/rules/prefer-use-template-ref.md +++ b/docs/rules/prefer-use-template-ref.md @@ -10,6 +10,8 @@ since: v9.31.0 > require using `useTemplateRef` instead of `ref`/`shallowRef` for template refs +- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix-problems) can automatically fix some of the problems reported by this rule. + ## :book: Rule Details Vue 3.5 introduced a new way of obtaining template refs via @@ -17,7 +19,7 @@ the [`useTemplateRef()`](https://vuejs.org/guide/essentials/template-refs.html#a This rule enforces using the new `useTemplateRef` function instead of `ref`/`shallowRef` for template refs. - + ```vue