Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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>
- Loading branch information
1 parent
68b184a
commit b28867d
Showing
5 changed files
with
508 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
--- | ||
pageClass: rule-details | ||
sidebarDepth: 0 | ||
title: vue/no-deprecated-router-link-tag-prop | ||
description: disallow using deprecated `tag` property on `RouterLink` (in Vue.js 3.0.0+) | ||
--- | ||
# vue/no-deprecated-router-link-tag-prop | ||
|
||
> disallow using deprecated `tag` property on `RouterLink` (in Vue.js 3.0.0+) | ||
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge> | ||
|
||
## :book: Rule Details | ||
|
||
This rule reports deprecated the `tag` attribute on `RouterLink` elements (removed in Vue.js v3.0.0+). | ||
|
||
<eslint-code-block :rules="{'vue/no-deprecated-router-link-tag-prop': ['error']}"> | ||
|
||
```vue | ||
<template> | ||
<!-- ✓ GOOD --> | ||
<RouterLink to="/">Home</RouterLink> | ||
<router-link to="/">Home</router-link> | ||
<RouterLink to="/"> | ||
<div>Home</div> | ||
</RouterLink> | ||
<router-link to="/"> | ||
<div>Home</div> | ||
</router-link> | ||
<NuxtLink tag="div" to="/">Home</NuxtLink> | ||
<nuxt-link tag="div" to="/">Home</nuxt-link> | ||
<!-- ✗ BAD --> | ||
<RouterLink tag="div" to="/">Home</RouterLink> | ||
<router-link tag="div" to="/">Home</router-link> | ||
<RouterLink :tag="someVariable" to="/">Home</RouterLink> | ||
<router-link :tag="someVariable" to="/">Home</router-link> | ||
</template> | ||
``` | ||
|
||
</eslint-code-block> | ||
|
||
## :wrench: Options | ||
|
||
```json | ||
{ | ||
"vue/no-deprecated-router-link-tag-prop": ["error", { | ||
"components": ['RouterLink'] | ||
}] | ||
} | ||
``` | ||
|
||
- `components` (`string[]`) ... Component names which will be checked with the `tag` attribute. default `['RouterLink']`. | ||
|
||
Note: this rule will check both `kebab-case` and `PascalCase` versions of the | ||
given component names. | ||
|
||
### `{ "components": ['RouterLink', 'NuxtLink'] }` | ||
|
||
<eslint-code-block :rules="{'vue/no-deprecated-router-link-tag-prop': ['error', {'components': ['RouterLink', 'NuxtLink']}]}"> | ||
|
||
```vue | ||
<template> | ||
<!-- ✗ BAD --> | ||
<RouterLink tag="div" to="/">Home</RouterLink> | ||
<router-link tag="div" to="/">Home</router-link> | ||
<RouterLink :tag="someVariable" to="/">Home</RouterLink> | ||
<router-link :tag="someVariable" to="/">Home</router-link> | ||
<NuxtLink tag="div" to="/">Home</NuxtLink> | ||
<nuxt-link tag="div" to="/">Home</nuxt-link> | ||
<NuxtLink :tag="someVariable" to="/">Home</NuxtLink> | ||
<nuxt-link :tag="someVariable" to="/">Home</nuxt-link> | ||
</template> | ||
``` | ||
|
||
</eslint-code-block> | ||
|
||
## :books: Further Reading | ||
|
||
- [Vue RFCs - 0021-router-link-scoped-slot](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0021-router-link-scoped-slot.md) | ||
|
||
## :mag: Implementation | ||
|
||
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-deprecated-router-link-tag-prop.js) | ||
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-deprecated-router-link-tag-prop.js) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
/** | ||
* @author Marton Csordas | ||
* See LICENSE file in root directory for full license. | ||
*/ | ||
'use strict' | ||
|
||
// ------------------------------------------------------------------------------ | ||
// Requirements | ||
// ------------------------------------------------------------------------------ | ||
|
||
const utils = require('../utils') | ||
const casing = require('../utils/casing') | ||
|
||
// -------------------------------------------------------------------------- | ||
// Helpers | ||
// -------------------------------------------------------------------------- | ||
|
||
/** @param {RuleContext} context */ | ||
function getComponentNames(context) { | ||
let components = ['RouterLink'] | ||
|
||
if (context.options[0] && context.options[0].components) { | ||
components = context.options[0].components | ||
} | ||
|
||
return components.reduce((prev, curr) => { | ||
prev.add(casing.kebabCase(curr)) | ||
prev.add(casing.pascalCase(curr)) | ||
return prev | ||
}, new Set()) | ||
} | ||
|
||
// ------------------------------------------------------------------------------ | ||
// Rule Definition | ||
// ------------------------------------------------------------------------------ | ||
|
||
module.exports = { | ||
meta: { | ||
type: 'problem', | ||
docs: { | ||
description: | ||
'disallow using deprecated `tag` property on `RouterLink` (in Vue.js 3.0.0+)', | ||
categories: undefined, | ||
url: 'https://eslint.vuejs.org/rules/no-deprecated-router-link-tag-prop.html' | ||
}, | ||
fixable: null, | ||
schema: [ | ||
{ | ||
type: 'object', | ||
properties: { | ||
components: { | ||
type: 'array', | ||
items: { | ||
type: 'string' | ||
}, | ||
uniqueItems: true, | ||
minItems: 1 | ||
} | ||
} | ||
} | ||
], | ||
messages: { | ||
deprecated: | ||
"'tag' property on '{{element}}' component is deprecated. Use scoped slots instead." | ||
} | ||
}, | ||
/** @param {RuleContext} context */ | ||
create(context) { | ||
const components = getComponentNames(context) | ||
|
||
return utils.defineTemplateBodyVisitor(context, { | ||
VElement(node) { | ||
if (!components.has(node.rawName)) return | ||
|
||
/** @type VIdentifier | null */ | ||
let tagKey = null | ||
|
||
const tagAttr = utils.getAttribute(node, 'tag') | ||
if (tagAttr) { | ||
tagKey = tagAttr.key | ||
} else { | ||
const directive = utils.getDirective(node, 'bind', 'tag') | ||
if (directive) { | ||
const arg = directive.key.argument | ||
if (arg && arg.type === 'VIdentifier') { | ||
tagKey = arg | ||
} | ||
} | ||
} | ||
|
||
if (tagKey) { | ||
context.report({ | ||
node: tagKey, | ||
messageId: 'deprecated', | ||
data: { | ||
element: node.rawName | ||
} | ||
}) | ||
} | ||
} | ||
}) | ||
} | ||
} |
Oops, something went wrong.