Skip to content

Commit b28867d

Browse files
Add no-deprecated-router-link-tag-prop rule (#1663)
* Add `no-deprecated-router-link-tag-prop` rule * Fix pylint error at no-deprecated-router-link-tag-prop * Fix review comments for no-deprecated-router-link-tag-prop - Use helper functions from utils to get attributes for nodes. - Add more test cases. - Handle both kebab-case and PascalCase version of components. * Update no-deprecated-router-link-tag-prop.md Co-authored-by: Yosuke Ota <otameshiyo23@gmail.com>
1 parent 68b184a commit b28867d

File tree

5 files changed

+508
-0
lines changed

5 files changed

+508
-0
lines changed

docs/rules/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ For example:
302302
| [vue/no-bare-strings-in-template](./no-bare-strings-in-template.md) | disallow the use of bare strings in `<template>` | |
303303
| [vue/no-boolean-default](./no-boolean-default.md) | disallow boolean defaults | :wrench: |
304304
| [vue/no-computed-properties-in-data](./no-computed-properties-in-data.md) | disallow accessing computed properties in `data`. | |
305+
| [vue/no-deprecated-router-link-tag-prop](./no-deprecated-router-link-tag-prop.md) | disallow using deprecated `tag` property on `RouterLink` (in Vue.js 3.0.0+) | |
305306
| [vue/no-deprecated-v-is](./no-deprecated-v-is.md) | disallow deprecated `v-is` directive (in Vue.js 3.1.0+) | :wrench: |
306307
| [vue/no-duplicate-attr-inheritance](./no-duplicate-attr-inheritance.md) | enforce `inheritAttrs` to be set to `false` when using `v-bind="$attrs"` | |
307308
| [vue/no-empty-component-block](./no-empty-component-block.md) | disallow the `<template>` `<script>` `<style>` block to be empty | |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
---
2+
pageClass: rule-details
3+
sidebarDepth: 0
4+
title: vue/no-deprecated-router-link-tag-prop
5+
description: disallow using deprecated `tag` property on `RouterLink` (in Vue.js 3.0.0+)
6+
---
7+
# vue/no-deprecated-router-link-tag-prop
8+
9+
> disallow using deprecated `tag` property on `RouterLink` (in Vue.js 3.0.0+)
10+
11+
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge>
12+
13+
## :book: Rule Details
14+
15+
This rule reports deprecated the `tag` attribute on `RouterLink` elements (removed in Vue.js v3.0.0+).
16+
17+
<eslint-code-block :rules="{'vue/no-deprecated-router-link-tag-prop': ['error']}">
18+
19+
```vue
20+
<template>
21+
<!-- ✓ GOOD -->
22+
<RouterLink to="/">Home</RouterLink>
23+
<router-link to="/">Home</router-link>
24+
25+
<RouterLink to="/">
26+
<div>Home</div>
27+
</RouterLink>
28+
29+
<router-link to="/">
30+
<div>Home</div>
31+
</router-link>
32+
33+
<NuxtLink tag="div" to="/">Home</NuxtLink>
34+
<nuxt-link tag="div" to="/">Home</nuxt-link>
35+
36+
<!-- ✗ BAD -->
37+
<RouterLink tag="div" to="/">Home</RouterLink>
38+
<router-link tag="div" to="/">Home</router-link>
39+
<RouterLink :tag="someVariable" to="/">Home</RouterLink>
40+
<router-link :tag="someVariable" to="/">Home</router-link>
41+
</template>
42+
```
43+
44+
</eslint-code-block>
45+
46+
## :wrench: Options
47+
48+
```json
49+
{
50+
"vue/no-deprecated-router-link-tag-prop": ["error", {
51+
"components": ['RouterLink']
52+
}]
53+
}
54+
```
55+
56+
- `components` (`string[]`) ... Component names which will be checked with the `tag` attribute. default `['RouterLink']`.
57+
58+
Note: this rule will check both `kebab-case` and `PascalCase` versions of the
59+
given component names.
60+
61+
### `{ "components": ['RouterLink', 'NuxtLink'] }`
62+
63+
<eslint-code-block :rules="{'vue/no-deprecated-router-link-tag-prop': ['error', {'components': ['RouterLink', 'NuxtLink']}]}">
64+
65+
```vue
66+
<template>
67+
<!-- ✗ BAD -->
68+
<RouterLink tag="div" to="/">Home</RouterLink>
69+
<router-link tag="div" to="/">Home</router-link>
70+
71+
<RouterLink :tag="someVariable" to="/">Home</RouterLink>
72+
<router-link :tag="someVariable" to="/">Home</router-link>
73+
74+
<NuxtLink tag="div" to="/">Home</NuxtLink>
75+
<nuxt-link tag="div" to="/">Home</nuxt-link>
76+
77+
<NuxtLink :tag="someVariable" to="/">Home</NuxtLink>
78+
<nuxt-link :tag="someVariable" to="/">Home</nuxt-link>
79+
</template>
80+
```
81+
82+
</eslint-code-block>
83+
84+
## :books: Further Reading
85+
86+
- [Vue RFCs - 0021-router-link-scoped-slot](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0021-router-link-scoped-slot.md)
87+
88+
## :mag: Implementation
89+
90+
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-deprecated-router-link-tag-prop.js)
91+
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-deprecated-router-link-tag-prop.js)

lib/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ module.exports = {
7171
'no-deprecated-html-element-is': require('./rules/no-deprecated-html-element-is'),
7272
'no-deprecated-inline-template': require('./rules/no-deprecated-inline-template'),
7373
'no-deprecated-props-default-this': require('./rules/no-deprecated-props-default-this'),
74+
'no-deprecated-router-link-tag-prop': require('./rules/no-deprecated-router-link-tag-prop'),
7475
'no-deprecated-scope-attribute': require('./rules/no-deprecated-scope-attribute'),
7576
'no-deprecated-slot-attribute': require('./rules/no-deprecated-slot-attribute'),
7677
'no-deprecated-slot-scope-attribute': require('./rules/no-deprecated-slot-scope-attribute'),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/**
2+
* @author Marton Csordas
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
'use strict'
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
const utils = require('../utils')
12+
const casing = require('../utils/casing')
13+
14+
// --------------------------------------------------------------------------
15+
// Helpers
16+
// --------------------------------------------------------------------------
17+
18+
/** @param {RuleContext} context */
19+
function getComponentNames(context) {
20+
let components = ['RouterLink']
21+
22+
if (context.options[0] && context.options[0].components) {
23+
components = context.options[0].components
24+
}
25+
26+
return components.reduce((prev, curr) => {
27+
prev.add(casing.kebabCase(curr))
28+
prev.add(casing.pascalCase(curr))
29+
return prev
30+
}, new Set())
31+
}
32+
33+
// ------------------------------------------------------------------------------
34+
// Rule Definition
35+
// ------------------------------------------------------------------------------
36+
37+
module.exports = {
38+
meta: {
39+
type: 'problem',
40+
docs: {
41+
description:
42+
'disallow using deprecated `tag` property on `RouterLink` (in Vue.js 3.0.0+)',
43+
categories: undefined,
44+
url: 'https://eslint.vuejs.org/rules/no-deprecated-router-link-tag-prop.html'
45+
},
46+
fixable: null,
47+
schema: [
48+
{
49+
type: 'object',
50+
properties: {
51+
components: {
52+
type: 'array',
53+
items: {
54+
type: 'string'
55+
},
56+
uniqueItems: true,
57+
minItems: 1
58+
}
59+
}
60+
}
61+
],
62+
messages: {
63+
deprecated:
64+
"'tag' property on '{{element}}' component is deprecated. Use scoped slots instead."
65+
}
66+
},
67+
/** @param {RuleContext} context */
68+
create(context) {
69+
const components = getComponentNames(context)
70+
71+
return utils.defineTemplateBodyVisitor(context, {
72+
VElement(node) {
73+
if (!components.has(node.rawName)) return
74+
75+
/** @type VIdentifier | null */
76+
let tagKey = null
77+
78+
const tagAttr = utils.getAttribute(node, 'tag')
79+
if (tagAttr) {
80+
tagKey = tagAttr.key
81+
} else {
82+
const directive = utils.getDirective(node, 'bind', 'tag')
83+
if (directive) {
84+
const arg = directive.key.argument
85+
if (arg && arg.type === 'VIdentifier') {
86+
tagKey = arg
87+
}
88+
}
89+
}
90+
91+
if (tagKey) {
92+
context.report({
93+
node: tagKey,
94+
messageId: 'deprecated',
95+
data: {
96+
element: node.rawName
97+
}
98+
})
99+
}
100+
}
101+
})
102+
}
103+
}

0 commit comments

Comments
 (0)