Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/rules/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ Rules in this category are enabled for all presets provided by eslint-plugin-vue
| [vue/valid-v-else](./valid-v-else.md) | enforce valid `v-else` directives | | :three::two::warning: |
| [vue/valid-v-for](./valid-v-for.md) | enforce valid `v-for` directives | | :three::two::warning: |
| [vue/valid-v-html](./valid-v-html.md) | enforce valid `v-html` directives | | :three::two::warning: |
| [vue/no-root-v-if](./no-root-v-if.md) | enforce valid `v-if` directives on root element | | :three::two::warning: |
| [vue/valid-v-if](./valid-v-if.md) | enforce valid `v-if` directives | | :three::two::warning: |
| [vue/valid-v-is](./valid-v-is.md) | enforce valid `v-is` directives | | :three::warning: |
| [vue/valid-v-memo](./valid-v-memo.md) | enforce valid `v-memo` directives | | :three::warning: |
Expand Down
39 changes: 39 additions & 0 deletions docs/rules/no-root-v-if.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
pageClass: rule-details
sidebarDepth: 0
title: vue/no-root-v-if
description: enforce valid `v-if` directives on root element
---
# vue/no-root-v-if

> enforce valid `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 checks whether every template root is valid.

## :book: Rule Details

This rule reports the template root in the following cases:

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

```vue
<!-- `v-if` should not be used on root element without `v-else` -->
<template>
<div v-if="foo"></div>
</template>

<template><custom-component v-if="shouldShow" /></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
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ module.exports = {
'valid-v-else': require('./rules/valid-v-else'),
'valid-v-for': require('./rules/valid-v-for'),
'valid-v-html': require('./rules/valid-v-html'),
'no-root-v-if': require('./rules/no-root-v-if'),
'valid-v-if': require('./rules/valid-v-if'),
'valid-v-is': require('./rules/valid-v-is'),
'valid-v-memo': require('./rules/valid-v-memo'),
Expand Down
70 changes: 70 additions & 0 deletions lib/rules/no-root-v-if.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* @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')

/**
* Get the number of root element directive
* @param {VNode[]} rootElements The start tag node to check.
*/
function getDirectivesLength(rootElements) {
let ifLength = 0
let elseLength = 0
let elseIfLength = 0

for (const element of rootElements) {
if (element.type === 'VElement') {
if (utils.hasDirective(element, 'if')) ifLength += 1
if (utils.hasDirective(element, 'else')) elseLength += 1
if (utils.hasDirective(element, 'else-if')) elseIfLength += 1
}
}

return {
ifLength,
elseLength,
elseIfLength
}
}

module.exports = {
meta: {
type: 'problem',
docs: {
description: 'enforce valid `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 === 0) return
const { ifLength, elseLength, elseIfLength } =
getDirectivesLength(rootElements)
if (ifLength === 1 && elseLength === 0 && elseIfLength === 0) {
context.report({
node: element,
loc: element.loc,
message:
'`v-if` should not be used on root element without `v-else`.'
})
}
}
}
}
}
116 changes: 116 additions & 0 deletions tests/lib/rules/no-root-v-if.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/**
* @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>'
}
],
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`.']
},
{
filename: 'test.vue',
code: '<template><div /><div v-if="foo" /></template>',
errors: ['`v-if` should not be used on root element without `v-else`.']
}
]
})