Skip to content

Commit

Permalink
Add vue/no-root-v-if rule (#2138)
Browse files Browse the repository at this point in the history
Co-authored-by: perrysong <perrysong@xiaoman.cn>
Co-authored-by: Flo Edelmann <git@flo-edelmann.de>
  • Loading branch information
3 people committed May 10, 2023
1 parent 3cbb1b3 commit 1a0bd29
Show file tree
Hide file tree
Showing 5 changed files with 200 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/rules/index.md
Expand Up @@ -240,6 +240,7 @@ For example:
| [vue/no-restricted-props](./no-restricted-props.md) | disallow specific props | :bulb: | :hammer: |
| [vue/no-restricted-static-attribute](./no-restricted-static-attribute.md) | disallow specific attribute | | :hammer: |
| [vue/no-restricted-v-bind](./no-restricted-v-bind.md) | disallow specific argument in `v-bind` | | :hammer: |
| [vue/no-root-v-if](./no-root-v-if.md) | disallow `v-if` directives on root element | | :hammer: |
| [vue/no-static-inline-styles](./no-static-inline-styles.md) | disallow static inline `style` attributes | | :hammer: |
| [vue/no-template-target-blank](./no-template-target-blank.md) | disallow target="_blank" attribute without rel="noopener noreferrer" | :bulb: | :warning: |
| [vue/no-this-in-before-route-enter](./no-this-in-before-route-enter.md) | disallow `this` usage in a `beforeRouteEnter` method | | :warning: |
Expand Down
37 changes: 37 additions & 0 deletions docs/rules/no-root-v-if.md
@@ -0,0 +1,37 @@
---
pageClass: rule-details
sidebarDepth: 0
title: vue/no-root-v-if
description: disallow `v-if` directives on root element
---

# vue/no-root-v-if

> disallow `v-if` directives on root element
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge>

This rule reports template roots with `v-if`. Rendering of the whole component could be made conditional in the parent component (with a `v-if` there) instead.

## :book: Rule Details

This rule reports the template root in the following cases:

<eslint-code-block :rules="{'vue/no-root-v-if': ['error']}">

```vue
<template>
<div v-if="foo"></div>
</template>
```

</eslint-code-block>

## :wrench: Options

Nothing.

## :mag: Implementation

- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-root-v-if.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-root-v-if.js)
1 change: 1 addition & 0 deletions lib/index.js
Expand Up @@ -124,6 +124,7 @@ module.exports = {
'no-restricted-static-attribute': require('./rules/no-restricted-static-attribute'),
'no-restricted-syntax': require('./rules/no-restricted-syntax'),
'no-restricted-v-bind': require('./rules/no-restricted-v-bind'),
'no-root-v-if': require('./rules/no-root-v-if'),
'no-setup-props-destructure': require('./rules/no-setup-props-destructure'),
'no-shared-component-data': require('./rules/no-shared-component-data'),
'no-side-effects-in-computed-properties': require('./rules/no-side-effects-in-computed-properties'),
Expand Down
46 changes: 46 additions & 0 deletions lib/rules/no-root-v-if.js
@@ -0,0 +1,46 @@
/**
* @author Perry Song
* @copyright 2023 Perry Song. All rights reserved.
* See LICENSE file in root directory for full license.
*/
'use strict'

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

module.exports = {
meta: {
type: 'suggestion',
docs: {
description: 'disallow `v-if` directives on root element',
categories: undefined,
url: 'https://eslint.vuejs.org/rules/no-root-v-if.html'
},
fixable: null,
schema: []
},
/** @param {RuleContext} context */
create(context) {
return {
/** @param {Program} program */
Program(program) {
const element = program.templateBody
if (element == null) {
return
}

const rootElements = element.children.filter(utils.isVElement)
if (
rootElements.length === 1 &&
utils.hasDirective(rootElements[0], 'if')
) {
context.report({
node: element,
loc: element.loc,
message:
'`v-if` should not be used on root element without `v-else`.'
})
}
}
}
}
}
115 changes: 115 additions & 0 deletions tests/lib/rules/no-root-v-if.js
@@ -0,0 +1,115 @@
/**
* @author Perry Song
* @copyright 2023 Perry Song. All rights reserved.
* See LICENSE file in root directory for full license.
*/
'use strict'

const RuleTester = require('eslint').RuleTester
const rule = require('../../../lib/rules/no-root-v-if')

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

tester.run('no-root-v-if', rule, {
valid: [
{
filename: 'test.vue',
code: ''
},
{
filename: 'test.vue',
code: '<template><div>abc</div></template>'
},
{
filename: 'test.vue',
code: '<template>\n <div>abc</div>\n</template>'
},
{
filename: 'test.vue',
code: '<template>\n <!-- comment -->\n <div>abc</div>\n</template>'
},
{
filename: 'test.vue',
code: '<template>\n <!-- comment -->\n <div v-if="foo">abc</div>\n <div v-else>abc</div>\n</template>'
},
{
filename: 'test.vue',
code: '<template>\n <!-- comment -->\n <div v-if="foo">abc</div>\n <div v-else-if="bar">abc</div>\n <div v-else>abc</div>\n</template>'
},
{
filename: 'test.vue',
code: `<template>\n <c1 v-if="1" />\n <c2 v-else-if="1" />\n <c3 v-else />\n</template>`
},
{
filename: 'test.vue',
code: '<template><div v-if="foo"></div><div v-else-if="bar"></div></template>'
},
{
filename: 'test.vue',
code: '<template src="foo.html"></template>'
},
{
filename: 'test.vue',
code: '<template><div><textarea/>test</div></template>'
},
{
filename: 'test.vue',
code: '<template><table><custom-thead></custom-thead></table></template>'
},
{
filename: 'test.vue',
code: '<template><div></div><div></div></template>'
},
{
filename: 'test.vue',
code: '<template>\n <div></div>\n <div></div>\n</template>'
},
{
filename: 'test.vue',
code: '<template>{{a b c}}</template>'
},
{
filename: 'test.vue',
code: '<template><div></div>aaaaaa</template>'
},
{
filename: 'test.vue',
code: '<template>aaaaaa<div></div></template>'
},
{
filename: 'test.vue',
code: '<template><div v-for="x in list"></div></template>'
},
{
filename: 'test.vue',
code: '<template><slot></slot></template>'
},
{
filename: 'test.vue',
code: '<template><template></template></template>'
},
{
filename: 'test.vue',
code: '<template> <div v-if="mode === \'a\'"></div><div v-if="mode === \'b\'"></div></template>'
},
{
filename: 'test.vue',
code: '<template><div /><div v-if="foo" /></template>'
}
],
invalid: [
{
filename: 'test.vue',
code: '<template><custom-component v-if="foo"></custom-component></template>',
errors: ['`v-if` should not be used on root element without `v-else`.']
},
{
filename: 'test.vue',
code: '<template><div v-if="foo"></div></template>',
errors: ['`v-if` should not be used on root element without `v-else`.']
}
]
})

0 comments on commit 1a0bd29

Please sign in to comment.