Skip to content

Commit

Permalink
Add no-deprecated-router-link-tag-prop rule
Browse files Browse the repository at this point in the history
  • Loading branch information
csordasmarton committed Oct 18, 2021
1 parent 7bca4d3 commit be4e0e8
Show file tree
Hide file tree
Showing 5 changed files with 311 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/rules/README.md
Expand Up @@ -301,6 +301,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
72 changes: 72 additions & 0 deletions docs/rules/no-deprecated-router-link-tag-prop.md
@@ -0,0 +1,72 @@
---
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>
<RouterLink to="/">
<div>Home</div>
</RouterLink>
<NuxtLink tag="div" to="/">Home</NuxtLink>
<!-- ✗ BAD -->
<RouterLink tag="div" to="/">Home</RouterLink>
<RouterLink :tag="someVariable" to="/">Home</RouterLink>
</template>
```

</eslint-code-block>

## :wrench: Options

```json
{
"vue/no-deprecated-router-link-tag-prop": ["error", {
"components": ['RouterLink', 'NuxtLink']
}]
}
```

### `{ "components": ['RouterLink', 'NuxtLink'] }`

<eslint-code-block :rules="{'vue/no-deprecated-router-link-tag-prop': ['error', {'components': ['RouterLink', 'NuxtLink']}]}">

```vue
<template>
<!-- ✗ BAD -->
<NuxtLink tag="div" to="/">Home</NuxtLink>
<NuxtLink :tag="someVariable" to="/">Home</NuxtLink>
<RouterLink tag="div" to="/">Home</RouterLink>
<RouterLink :tag="someVariable" to="/">Home</RouterLink>
</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 @@ -70,6 +70,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
84 changes: 84 additions & 0 deletions lib/rules/no-deprecated-router-link-tag-prop.js
@@ -0,0 +1,84 @@
/**
* @author Marton Csordas
* See LICENSE file in root directory for full license.
*/
'use strict'

// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------

const utils = require('../utils')

// ------------------------------------------------------------------------------
// 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'
},
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) {
let components = ['RouterLink']
if (context.options[0] && context.options[0].components) {
components = context.options[0].components
}

return utils.defineTemplateBodyVisitor(context, {
"VElement"(node) {
if (!components.includes(node.rawName)) return

const attributes = node.startTag.attributes
attributes.forEach(attr => {
/** @type VIdentifier | null */
let tagAttr = null;

if (attr.key.type === 'VIdentifier') {
tagAttr = attr.key;
} else if (attr.directive && attr.key.type === 'VDirectiveKey') {
const arg = attr.key.argument
if (arg && arg.type === 'VIdentifier') {
tagAttr = arg
}
}

if (tagAttr && tagAttr.name === 'tag') {
context.report({
node: tagAttr,
messageId: 'deprecated',
data: {
element: node.rawName
}
})
}
})
}
})
}
}
153 changes: 153 additions & 0 deletions tests/lib/rules/no-deprecated-router-link-tag-prop.js
@@ -0,0 +1,153 @@
/**
* @author Marton Csordas
* See LICENSE file in root directory for full license.
*/
'use strict'

const RuleTester = require('eslint').RuleTester
const rule = require('../../../lib/rules/no-deprecated-router-link-tag-prop')

const tester = new RuleTester({
parser: require.resolve('vue-eslint-parser'),
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module'
}
})

tester.run('no-deprecated-router-link-tag-prop', rule, {
valid: [
{
filename: 'test.vue',
code: `
<template>
<RouterLink to="/">Home</RouterLink>
</template>
`
},
{
filename: 'test.vue',
code: `
<template>
<RouterLink to="/">
<div>Home</div>
</RouterLink>
</template>
`
},
{
filename: 'test.vue',
code: `
<template>
<NuxtLink to="/">Home</NuxtLink>
</template>
`
},
{
filename: 'test.vue',
code: `
<template>
<NuxtLink to="/">
<div>Home</div>
</NuxtLink>
</template>
`
},
],
invalid: [
{
filename: 'test.vue',
code: `
<template>
<RouterLink tag="div" to="/">Home</RouterLink>
</template>
`,
errors: [
{
message: "'tag' property on 'RouterLink' component is deprecated. Use scoped slots instead.",
line: 3,
column: 21
},
]
},
{
filename: 'test.vue',
code: `
<template>
<RouterLink tag="div" to="/">Home</RouterLink>
</template>
`,
options: [{ components: ['RouterLink'] }],
errors: [
{
message: "'tag' property on 'RouterLink' component is deprecated. Use scoped slots instead.",
line: 3,
column: 21
},
]
},
{
filename: 'test.vue',
code: `
<template>
<RouterLink :tag="someVariable" to="/">Home</RouterLink>
</template>
`,
errors: [
{
message: "'tag' property on 'RouterLink' component is deprecated. Use scoped slots instead.",
line: 3,
column: 22
},
]
},
{
filename: 'test.vue',
code: `
<template>
<RouterLink :tag="someVariable" to="/">Home</RouterLink>
</template>
`,
options: [{ components: ['RouterLink'] }],
errors: [
{
message: "'tag' property on 'RouterLink' component is deprecated. Use scoped slots instead.",
line: 3,
column: 22
},
]
},
{
filename: 'test.vue',
code: `
<template>
<NuxtLink tag="div" to="/">Home</NuxtLink>
</template>
`,
options: [{ components: ['NuxtLink'] }],
errors: [
{
message: "'tag' property on 'NuxtLink' component is deprecated. Use scoped slots instead.",
line: 3,
column: 19
},
]
},
{
filename: 'test.vue',
code: `
<template>
<NuxtLink :tag="someVariable" to="/">Home</NuxtLink>
</template>
`,
options: [{ components: ['NuxtLink'] }],
errors: [
{
message: "'tag' property on 'NuxtLink' component is deprecated. Use scoped slots instead.",
line: 3,
column: 20
},
]
},
]
})

0 comments on commit be4e0e8

Please sign in to comment.