Skip to content

Commit

Permalink
Add no-deprecated-router-link-tag-prop rule (#1663)
Browse files Browse the repository at this point in the history
* 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
csordasmarton and ota-meshi committed Oct 20, 2021
1 parent 68b184a commit b28867d
Show file tree
Hide file tree
Showing 5 changed files with 508 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/rules/README.md
Expand Up @@ -302,6 +302,7 @@ For example:
| [vue/no-bare-strings-in-template](./no-bare-strings-in-template.md) | disallow the use of bare strings in `<template>` | |
| [vue/no-boolean-default](./no-boolean-default.md) | disallow boolean defaults | :wrench: |
| [vue/no-computed-properties-in-data](./no-computed-properties-in-data.md) | disallow accessing computed properties in `data`. | |
| [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+) | |
| [vue/no-deprecated-v-is](./no-deprecated-v-is.md) | disallow deprecated `v-is` directive (in Vue.js 3.1.0+) | :wrench: |
| [vue/no-duplicate-attr-inheritance](./no-duplicate-attr-inheritance.md) | enforce `inheritAttrs` to be set to `false` when using `v-bind="$attrs"` | |
| [vue/no-empty-component-block](./no-empty-component-block.md) | disallow the `<template>` `<script>` `<style>` block to be empty | |
Expand Down
91 changes: 91 additions & 0 deletions docs/rules/no-deprecated-router-link-tag-prop.md
@@ -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)
1 change: 1 addition & 0 deletions lib/index.js
Expand Up @@ -71,6 +71,7 @@ module.exports = {
'no-deprecated-html-element-is': require('./rules/no-deprecated-html-element-is'),
'no-deprecated-inline-template': require('./rules/no-deprecated-inline-template'),
'no-deprecated-props-default-this': require('./rules/no-deprecated-props-default-this'),
'no-deprecated-router-link-tag-prop': require('./rules/no-deprecated-router-link-tag-prop'),
'no-deprecated-scope-attribute': require('./rules/no-deprecated-scope-attribute'),
'no-deprecated-slot-attribute': require('./rules/no-deprecated-slot-attribute'),
'no-deprecated-slot-scope-attribute': require('./rules/no-deprecated-slot-scope-attribute'),
Expand Down
103 changes: 103 additions & 0 deletions lib/rules/no-deprecated-router-link-tag-prop.js
@@ -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
}
})
}
}
})
}
}

0 comments on commit b28867d

Please sign in to comment.