Skip to content

Commit

Permalink
Add new vue/no-use-v-else-with-v-for (#2224)
Browse files Browse the repository at this point in the history
Co-authored-by: Yosuke Ota <otameshiyo23@gmail.com>
  • Loading branch information
FloEdelmann and ota-meshi committed Jul 2, 2023
1 parent 2aa9ac4 commit d1f1eea
Show file tree
Hide file tree
Showing 7 changed files with 206 additions and 4 deletions.
3 changes: 2 additions & 1 deletion docs/rules/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ Rules in this category are enabled for all presets provided by eslint-plugin-vue
| [vue/no-unused-components](./no-unused-components.md) | disallow registering components that are not used inside templates | | :three::two::hammer: |
| [vue/no-unused-vars](./no-unused-vars.md) | disallow unused variable definitions of v-for directives or scope attributes | :bulb: | :three::two::hammer: |
| [vue/no-use-computed-property-like-method](./no-use-computed-property-like-method.md) | disallow use computed property like method | | :three::two::warning: |
| [vue/no-use-v-if-with-v-for](./no-use-v-if-with-v-for.md) | disallow use v-if on the same element as v-for | | :three::two::hammer: |
| [vue/no-use-v-if-with-v-for](./no-use-v-if-with-v-for.md) | disallow using `v-if` on the same element as `v-for` | | :three::two::hammer: |
| [vue/no-useless-template-attributes](./no-useless-template-attributes.md) | disallow useless attribute on `<template>` | | :three::two::warning: |
| [vue/no-v-for-template-key-on-child](./no-v-for-template-key-on-child.md) | disallow key of `<template v-for>` placed on child elements | | :three::warning: |
| [vue/no-v-for-template-key](./no-v-for-template-key.md) | disallow `key` attribute on `<template v-for>` | | :two::warning: |
Expand Down Expand Up @@ -251,6 +251,7 @@ For example:
| [vue/no-unsupported-features](./no-unsupported-features.md) | disallow unsupported Vue.js syntax on the specified version | :wrench: | :hammer: |
| [vue/no-unused-properties](./no-unused-properties.md) | disallow unused properties | | :hammer: |
| [vue/no-unused-refs](./no-unused-refs.md) | disallow unused refs | | :hammer: |
| [vue/no-use-v-else-with-v-for](./no-use-v-else-with-v-for.md) | disallow using `v-else-if`/`v-else` on the same element as `v-for` | | :hammer: |
| [vue/no-useless-mustaches](./no-useless-mustaches.md) | disallow unnecessary mustache interpolations | :wrench: | :hammer: |
| [vue/no-useless-v-bind](./no-useless-v-bind.md) | disallow unnecessary `v-bind` directives | :wrench: | :hammer: |
| [vue/no-v-text](./no-v-text.md) | disallow use of v-text | | :hammer: |
Expand Down
54 changes: 54 additions & 0 deletions docs/rules/no-use-v-else-with-v-for.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
---
pageClass: rule-details
sidebarDepth: 0
title: vue/no-use-v-else-with-v-for
description: disallow using `v-else-if`/`v-else` on the same element as `v-for`
---
# vue/no-use-v-else-with-v-for

> disallow using `v-else-if`/`v-else` on the same element as `v-for`
- :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 elements that have both `v-else-if`/`v-else` and `v-for` directives. That is valid in Vue (`v-else-if`/`v-else` will take precedence), but is confusing to read.

<eslint-code-block :rules="{'vue/no-use-v-else-with-v-for': ['error']}">

```vue
<template>
<!-- ✓ GOOD -->
<div v-if="foo">foo</div>
<template v-else-if="bar">
<div v-for="x in xs">{{ x }}</div>
</template>
<template v-else>
<div v-for="x in xs">{{ x }}</div>
</template>
<!-- ✗ BAD -->
<div v-if="foo">foo</div>
<div v-else-if="bar" v-for="x in xs">{{ x }}</div>
<div v-else v-for="x in xs">{{ x }}</div>
</template>
```

</eslint-code-block>

## :wrench: Options

Nothing.

## :mute: When Not To Use It

If you don't find using `v-else-if`/`v-else` together with `v-for` confusing to read, you can safely disable this rule.

## :couple: Related Rules

- [vue/no-use-v-if-with-v-for](./no-use-v-if-with-v-for.md)

## :mag: Implementation

- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-use-v-else-with-v-for.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-use-v-else-with-v-for.js)
8 changes: 6 additions & 2 deletions docs/rules/no-use-v-if-with-v-for.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
pageClass: rule-details
sidebarDepth: 0
title: vue/no-use-v-if-with-v-for
description: disallow use v-if on the same element as v-for
description: disallow using `v-if` on the same element as `v-for`
since: v4.6.0
---
# vue/no-use-v-if-with-v-for

> disallow use v-if on the same element as v-for
> disallow using `v-if` on the same element as `v-for`
- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/essential"`, `"plugin:vue/vue3-strongly-recommended"`, `"plugin:vue/strongly-recommended"`, `"plugin:vue/vue3-recommended"` and `"plugin:vue/recommended"`.

Expand Down Expand Up @@ -88,6 +88,10 @@ There are two common cases where this can be tempting:

</eslint-code-block>

## :couple: Related Rules

- [vue/no-use-v-else-with-v-for](./no-use-v-else-with-v-for.md)

## :books: Further Reading

- [Style guide - Avoid v-if with v-for](https://vuejs.org/style-guide/rules-essential.html#avoid-v-if-with-v-for)
Expand Down
1 change: 1 addition & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ module.exports = {
'no-unused-refs': require('./rules/no-unused-refs'),
'no-unused-vars': require('./rules/no-unused-vars'),
'no-use-computed-property-like-method': require('./rules/no-use-computed-property-like-method'),
'no-use-v-else-with-v-for': require('./rules/no-use-v-else-with-v-for'),
'no-use-v-if-with-v-for': require('./rules/no-use-v-if-with-v-for'),
'no-useless-concat': require('./rules/no-useless-concat'),
'no-useless-mustaches': require('./rules/no-useless-mustaches'),
Expand Down
44 changes: 44 additions & 0 deletions lib/rules/no-use-v-else-with-v-for.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
'use strict'

const { defineTemplateBodyVisitor, hasDirective } = require('../utils')

module.exports = {
meta: {
type: 'suggestion',
docs: {
description:
'disallow using `v-else-if`/`v-else` on the same element as `v-for`',
categories: null,
url: 'https://eslint.vuejs.org/rules/no-use-v-else-with-v-for.html'
},
fixable: null,
schema: [],
messages: {
unexpectedDirectiveWithVFor:
'Unexpected `{{ directiveName }}` and `v-for` on the same element. Move `{{ directiveName }}` to a wrapper element instead.'
}
},
/** @param {RuleContext} context */
create(context) {
return defineTemplateBodyVisitor(context, {
/** @param {VDirective} node */
"VAttribute[directive=true][key.name.name='for']"(node) {
const element = node.parent.parent

if (hasDirective(element, 'else-if')) {
context.report({
node: element,
messageId: 'unexpectedDirectiveWithVFor',
data: { directiveName: 'v-else-if' }
})
} else if (hasDirective(element, 'else')) {
context.report({
node: element,
messageId: 'unexpectedDirectiveWithVFor',
data: { directiveName: 'v-else' }
})
}
}
})
}
}
2 changes: 1 addition & 1 deletion lib/rules/no-use-v-if-with-v-for.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ module.exports = {
meta: {
type: 'suggestion',
docs: {
description: 'disallow use v-if on the same element as v-for',
description: 'disallow using `v-if` on the same element as `v-for`',
categories: ['vue3-essential', 'essential'],
url: 'https://eslint.vuejs.org/rules/no-use-v-if-with-v-for.html'
},
Expand Down
98 changes: 98 additions & 0 deletions tests/lib/rules/no-use-v-else-with-v-for.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
'use strict'

const RuleTester = require('eslint').RuleTester
const rule = require('../../../lib/rules/no-use-v-else-with-v-for')

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

tester.run('no-use-v-else-with-v-for', rule, {
valid: [
{
// caught by `vue/no-use-v-if-with-v-for`
filename: 'test.vue',
code: `
<template>
<div v-if="foo" v-for="x in xs">{{ x }}</div>
</template>
`
},
{
// `v-if`/`v-else-if`/`v-else` only
filename: 'test.vue',
code: `
<template>
<div v-if="foo">{{ x }}</div>
<div v-else-if="foo">{{ x }}</div>
<div v-else="foo">{{ x }}</div>
</template>
`
},
{
// `v-for` only
filename: 'test.vue',
code: `
<template>
<div v-for="x in xs">{{ x }}</div>
</template>
`
},
{
// `v-else-if`/`v-else` in template + `v-for`
filename: 'test.vue',
code: `
<template>
<div v-if="foo">foo</div>
<template v-else-if="bar">
<div v-for="x in xs">{{ x }}</div>
</template>
<template v-else>
<div v-for="x in xs">{{ x }}</div>
</template>
</template>
`
}
],
invalid: [
{
filename: 'test.vue',
code: `
<template>
<div v-if="foo">foo</div>
<div v-else v-for="x in xs">{{ x }}</div>
</template>
`,
errors: [
{
message:
'Unexpected `v-else` and `v-for` on the same element. Move `v-else` to a wrapper element instead.',
line: 4,
endLine: 4,
column: 11,
endColumn: 52
}
]
},
{
filename: 'test.vue',
code: `
<template>
<div v-if="foo">foo</div>
<div v-else-if="bar" v-for="x in xs">{{ x }}</div>
</template>
`,
errors: [
{
message:
'Unexpected `v-else-if` and `v-for` on the same element. Move `v-else-if` to a wrapper element instead.',
line: 4,
endLine: 4,
column: 11,
endColumn: 61
}
]
}
]
})

0 comments on commit d1f1eea

Please sign in to comment.