Repository: intlify/eslint-plugin-vue-i18n
Pull request #580 (2024-12-06 07:06:28)

feat: add `no-deprecated-v-t` rule
## background
In vue-i18n v11, `v-t` custom directive is going to deprecated.
vue-i18n has already published docs for breaking changes.
https://vue-i18n.intlify.dev/guide/migration/breaking11.html#deprecate-custom-directive-v-t

This rule supports migration.

cloudflare-workers-and-pages[bot]: ## Deploying eslint-plugin-vue-i18n with &nbsp;<a href="https://pages.dev"><img alt="Cloudflare Pages" src="https://user-images.githubusercontent.com/23264/106598434-9e719e00-654f-11eb-9e59-6167043cfa01.png" width="16"></a> &nbsp;Cloudflare Pages

<table><tr><td><strong>Latest commit:</strong> </td><td>
<code>7e88518</code>
</td></tr>
<tr><td><strong>Status:</strong></td><td>⚡️&nbsp; Build in progress...</td></tr>
</table>

[View logs](https://dash.cloudflare.com/?to=/:account/pages/view/eslint-plugin-vue-i18n/775dd06b-66f5-42ae-b138-b3a24269027c)


Patch:
diff --git a/.changeset/chilled-colts-destroy.md b/.changeset/chilled-colts-destroy.md
new file mode 100644
index 0000000..57a61ae
--- /dev/null
+++ b/.changeset/chilled-colts-destroy.md
@@ -0,0 +1,5 @@
+---
+"@intlify/eslint-plugin-vue-i18n": minor
+---
+
+feat: add `no-deprecated-v-t` rule
diff --git a/docs/rules/index.md b/docs/rules/index.md
index 93c5d88..8ae409c 100644
--- a/docs/rules/index.md
+++ b/docs/rules/index.md
@@ -13,6 +13,7 @@
 | [@intlify/vue-i18n/<wbr>no-deprecated-i18n-places-prop](./no-deprecated-i18n-places-prop.html) | disallow using deprecated `places` prop (Removed in Vue I18n 9.0.0+) | :star: |
 | [@intlify/vue-i18n/<wbr>no-deprecated-modulo-syntax](./no-deprecated-modulo-syntax.html) | enforce modulo interpolation to be named interpolation | :star::black_nib: |
 | [@intlify/vue-i18n/<wbr>no-deprecated-tc](./no-deprecated-tc.html) | disallow using deprecated `tc` or `$tc` (Deprecated in Vue I18n 10.0.0, removed fully in Vue I18n 11.0.0) | :star: |
+| [@intlify/vue-i18n/<wbr>no-deprecated-v-t](./no-deprecated-v-t.html) | disallow using deprecated `v-t` custom directive (Deprecated in Vue I18n 11.0.0, removed fully in Vue I18n 12.0.0) | :star: |
 | [@intlify/vue-i18n/<wbr>no-html-messages](./no-html-messages.html) | disallow use HTML localization messages | :star: |
 | [@intlify/vue-i18n/<wbr>no-i18n-t-path-prop](./no-i18n-t-path-prop.html) | disallow using `path` prop with `<i18n-t>` | :star::black_nib: |
 | [@intlify/vue-i18n/<wbr>no-missing-keys](./no-missing-keys.html) | disallow missing locale message key at localization methods | :star: |
diff --git a/docs/rules/no-deprecated-v-t.md b/docs/rules/no-deprecated-v-t.md
new file mode 100644
index 0000000..af57694
--- /dev/null
+++ b/docs/rules/no-deprecated-v-t.md
@@ -0,0 +1,66 @@
+---
+title: '@intlify/vue-i18n/no-deprecated-v-t'
+description: disallow using deprecated `v-t` custom directive (Deprecated in Vue I18n 11.0.0, removed fully in Vue I18n 12.0.0)
+since: v3.0.0
+---
+
+# @intlify/vue-i18n/no-deprecated-v-t
+
+> disallow using deprecated `v-t` custom directive (Deprecated in Vue I18n 11.0.0, removed fully in Vue I18n 12.0.0)
+
+- :star: The `"extends": "plugin:@intlify/vue-i18n/recommended"` or `*.configs["flat/recommended"]` property in a configuration file enables this rule.
+
+If you are migrating from Vue I18n v10 to v11, `v-t` custom direcitve should be replaced with `t` or `$t`.
+
+## :book: Rule Details
+
+This rule reports use of deprecated `v-t` custom directive (Deprecated in Vue I18n 11.0.0, removed fully in Vue I18n 12.0.0)
+
+:-1: Examples of **incorrect** code for this rule:
+
+<eslint-code-block>
+
+<!-- eslint-skip -->
+
+```vue
+<script>
+/* eslint @intlify/vue-i18n/no-deprecated-v-t: 'error' */
+</script>
+<template>
+  <!-- ✗ BAD -->
+  <p v-t="'banana'"></p>
+</template>
+```
+
+</eslint-code-block>
+
+:+1: Examples of **correct** code for this rule:
+
+<eslint-code-block>
+
+<!-- eslint-skip -->
+
+```vue
+<script>
+/* eslint @intlify/vue-i18n/no-deprecated-v-t: 'error' */
+</script>
+<template>
+  <!-- ✓ GOOD -->
+  <p>{{ $t('banana') }}</p>
+</template>
+```
+
+</eslint-code-block>
+
+## :books: Further reading
+
+- [Vue I18n > Breaking Changes in v11 - Deprecate Custom Directive `v-t`](https://vue-i18n.intlify.dev/guide/migration/breaking11.html#deprecate-custom-directive-v-t)
+
+## :rocket: Version
+
+This rule was introduced in `@intlify/eslint-plugin-vue-i18n` v3.0.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/intlify/eslint-plugin-vue-i18n/blob/master/lib/rules/no-deprecated-v-t.ts)
+- [Test source](https://github.com/intlify/eslint-plugin-vue-i18n/tree/master/tests/lib/rules/no-deprecated-v-t.ts)
diff --git a/lib/configs/flat/recommended.ts b/lib/configs/flat/recommended.ts
index f1e538a..280d1f9 100644
--- a/lib/configs/flat/recommended.ts
+++ b/lib/configs/flat/recommended.ts
@@ -24,6 +24,7 @@ export = [
       '@intlify/vue-i18n/no-deprecated-i18n-places-prop': 'warn',
       '@intlify/vue-i18n/no-deprecated-modulo-syntax': 'warn',
       '@intlify/vue-i18n/no-deprecated-tc': 'warn',
+      '@intlify/vue-i18n/no-deprecated-v-t': 'warn',
       '@intlify/vue-i18n/no-html-messages': 'warn',
       '@intlify/vue-i18n/no-i18n-t-path-prop': 'warn',
       '@intlify/vue-i18n/no-missing-keys': 'warn',
diff --git a/lib/configs/recommended.ts b/lib/configs/recommended.ts
index ababfea..f56b0d2 100644
--- a/lib/configs/recommended.ts
+++ b/lib/configs/recommended.ts
@@ -18,6 +18,7 @@ export = {
     '@intlify/vue-i18n/no-deprecated-i18n-places-prop': 'warn',
     '@intlify/vue-i18n/no-deprecated-modulo-syntax': 'warn',
     '@intlify/vue-i18n/no-deprecated-tc': 'warn',
+    '@intlify/vue-i18n/no-deprecated-v-t': 'warn',
     '@intlify/vue-i18n/no-html-messages': 'warn',
     '@intlify/vue-i18n/no-i18n-t-path-prop': 'warn',
     '@intlify/vue-i18n/no-missing-keys': 'warn',
diff --git a/lib/index.ts b/lib/index.ts
index 3bad70a..75ef61b 100644
--- a/lib/index.ts
+++ b/lib/index.ts
@@ -12,6 +12,7 @@ import noDeprecatedI18nPlaceAttr from './rules/no-deprecated-i18n-place-attr'
 import noDeprecatedI18nPlacesProp from './rules/no-deprecated-i18n-places-prop'
 import noDeprecatedModuloSyntax from './rules/no-deprecated-modulo-syntax'
 import noDeprecatedTc from './rules/no-deprecated-tc'
+import noDeprecatedVT from './rules/no-deprecated-v-t'
 import noDuplicateKeysInLocale from './rules/no-duplicate-keys-in-locale'
 import noDynamicKeys from './rules/no-dynamic-keys'
 import noHtmlMessages from './rules/no-html-messages'
@@ -45,6 +46,7 @@ export = {
     'no-deprecated-i18n-places-prop': noDeprecatedI18nPlacesProp,
     'no-deprecated-modulo-syntax': noDeprecatedModuloSyntax,
     'no-deprecated-tc': noDeprecatedTc,
+    'no-deprecated-v-t': noDeprecatedVT,
     'no-duplicate-keys-in-locale': noDuplicateKeysInLocale,
     'no-dynamic-keys': noDynamicKeys,
     'no-html-messages': noHtmlMessages,
diff --git a/lib/rules/no-deprecated-v-t.ts b/lib/rules/no-deprecated-v-t.ts
new file mode 100644
index 0000000..f34fae9
--- /dev/null
+++ b/lib/rules/no-deprecated-v-t.ts
@@ -0,0 +1,43 @@
+/**
+ * @author kazuya kawaguchi (a.k.a. kazupon)
+ */
+import { defineTemplateBodyVisitor } from '../utils/index'
+import { createRule } from '../utils/rule'
+
+import type { RuleContext, RuleListener } from '../types'
+import type { AST as VAST } from 'vue-eslint-parser'
+
+function checkDirective(context: RuleContext, node: VAST.VDirective) {
+  context.report({
+    node,
+    message: `'v-t' custom directive is used, but it is deprecated. Use 't' or '$t' instead.`
+  })
+}
+
+function create(context: RuleContext): RuleListener {
+  return defineTemplateBodyVisitor(context, {
+    "VAttribute[directive=true][key.name='t']"(node: VAST.VDirective) {
+      checkDirective(context, node)
+    },
+
+    "VAttribute[directive=true][key.name.name='t']"(node: VAST.VDirective) {
+      checkDirective(context, node)
+    }
+  })
+}
+
+export = createRule({
+  meta: {
+    type: 'problem',
+    docs: {
+      description:
+        'disallow using deprecated `v-t` custom directive (Deprecated in Vue I18n 11.0.0, removed fully in Vue I18n 12.0.0)',
+      category: 'Recommended',
+      url: 'https://eslint-plugin-vue-i18n.intlify.dev/rules/no-deprecated-v-t.html',
+      recommended: false
+    },
+    fixable: null,
+    schema: []
+  },
+  create
+})


Test patch:
diff --git a/tests/lib/rules/no-deprecated-v-t.ts b/tests/lib/rules/no-deprecated-v-t.ts
new file mode 100644
index 0000000..b27d396
--- /dev/null
+++ b/tests/lib/rules/no-deprecated-v-t.ts
@@ -0,0 +1,30 @@
+/**
+ * @author kazuya kawaguchi (a.k.a. kazupon)
+ */
+import { RuleTester } from '../eslint-compat'
+import rule from '../../../lib/rules/no-deprecated-v-t'
+import * as vueParser from 'vue-eslint-parser'
+
+const tester = new RuleTester({
+  languageOptions: {
+    parser: vueParser,
+    ecmaVersion: 2020,
+    sourceType: 'module'
+  }
+})
+
+tester.run('no-deprecated-v-t', rule as never, {
+  valid: [
+    {
+      code: `<template><p v-html="$t('hello')"></p></template>`
+    }
+  ],
+  invalid: [
+    {
+      code: `<template><p v-t="'banana'"></p></template>`,
+      errors: [
+        `'v-t' custom directive is used, but it is deprecated. Use 't' or '$t' instead.`
+      ]
+    }
+  ]
+})