Skip to content

Commit cdd3ac9

Browse files
perrysongsongpengyuan
perrysong
authored andcommitted
Added valid-v-if-template-root rule.
1 parent 9184384 commit cdd3ac9

8 files changed

+228
-1
lines changed

docs/rules/index.md

+1
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ Rules in this category are enabled for all presets provided by eslint-plugin-vue
118118
| [vue/valid-v-else](./valid-v-else.md) | enforce valid `v-else` directives | | :three::two::warning: |
119119
| [vue/valid-v-for](./valid-v-for.md) | enforce valid `v-for` directives | | :three::two::warning: |
120120
| [vue/valid-v-html](./valid-v-html.md) | enforce valid `v-html` directives | | :three::two::warning: |
121+
| [vue/valid-v-if-template-root](./valid-v-if-template-root.md) | enforce valid `v-if` directives on root element | | :three::two::warning: |
121122
| [vue/valid-v-if](./valid-v-if.md) | enforce valid `v-if` directives | | :three::two::warning: |
122123
| [vue/valid-v-is](./valid-v-is.md) | enforce valid `v-is` directives | | :three::warning: |
123124
| [vue/valid-v-memo](./valid-v-memo.md) | enforce valid `v-memo` directives | | :three::warning: |

docs/rules/valid-template-root.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,4 @@ This rule was introduced in eslint-plugin-vue v3.11.0
4646
## :mag: Implementation
4747

4848
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/valid-template-root.js)
49-
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/valid-template-root.js)
49+
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/valid-template-root.js)
+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
pageClass: rule-details
3+
sidebarDepth: 0
4+
title: vue/valid-v-if-template-root
5+
description: enforce valid `v-if` directives on root element
6+
---
7+
# vue/valid-v-if-template-root
8+
9+
> enforce valid `v-if` directives on root element
10+
11+
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge>
12+
- :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"`.
13+
14+
This rule checks whether every template root is valid.
15+
16+
## :book: Rule Details
17+
18+
This rule reports the template root in the following cases:
19+
20+
<eslint-code-block :rules="{'vue/valid-v-if-template-root': ['error']}">
21+
22+
```vue
23+
<!-- `v-if` should not be used on root element without `v-else` -->
24+
<template>
25+
<div v-if="foo"></div>
26+
</template>
27+
28+
<template><custom-component v-if="shouldShow" /></template>
29+
```
30+
31+
</eslint-code-block>
32+
33+
## :wrench: Options
34+
35+
Nothing.
36+
37+
## :mag: Implementation
38+
39+
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/valid-v-if-template-root.js)
40+
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/valid-v-if-template-root.js)

lib/configs/essential.js

+1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ module.exports = {
6262
'vue/valid-v-else': 'error',
6363
'vue/valid-v-for': 'error',
6464
'vue/valid-v-html': 'error',
65+
'vue/valid-v-if-template-root': 'error',
6566
'vue/valid-v-if': 'error',
6667
'vue/valid-v-model': 'error',
6768
'vue/valid-v-on': 'error',

lib/configs/vue3-essential.js

+1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ module.exports = {
7777
'vue/valid-v-else': 'error',
7878
'vue/valid-v-for': 'error',
7979
'vue/valid-v-html': 'error',
80+
'vue/valid-v-if-template-root': 'error',
8081
'vue/valid-v-if': 'error',
8182
'vue/valid-v-is': 'error',
8283
'vue/valid-v-memo': 'error',

lib/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ module.exports = {
220220
'valid-v-else': require('./rules/valid-v-else'),
221221
'valid-v-for': require('./rules/valid-v-for'),
222222
'valid-v-html': require('./rules/valid-v-html'),
223+
'valid-v-if-template-root': require('./rules/valid-v-if-template-root'),
223224
'valid-v-if': require('./rules/valid-v-if'),
224225
'valid-v-is': require('./rules/valid-v-is'),
225226
'valid-v-memo': require('./rules/valid-v-memo'),

lib/rules/valid-v-if-template-root.js

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/**
2+
* @author Perry Song
3+
* @copyright 2023 Perry Song. All rights reserved.
4+
* See LICENSE file in root directory for full license.
5+
*/
6+
'use strict'
7+
8+
const utils = require('../utils')
9+
10+
/**
11+
* Get the number of root element directive
12+
* @param {VNode[]} rootElements The start tag node to check.
13+
* @param {string} directiveName The directive name to check.
14+
*/
15+
function getDirectiveLength(rootElements, directiveName) {
16+
if (!directiveName) return 0
17+
return rootElements.filter(
18+
(element) =>
19+
element.type === 'VElement' && utils.hasDirective(element, directiveName)
20+
).length
21+
}
22+
23+
module.exports = {
24+
meta: {
25+
type: 'problem',
26+
docs: {
27+
description: 'enforce valid `v-if` directives on root element',
28+
categories: ['vue3-essential', 'essential'],
29+
url: 'https://eslint.vuejs.org/rules/valid-v-if-template-root.html'
30+
},
31+
fixable: null,
32+
schema: []
33+
},
34+
/** @param {RuleContext} context */
35+
create(context) {
36+
const sourceCode = context.getSourceCode()
37+
38+
return {
39+
/** @param {Program} program */
40+
Program(program) {
41+
const element = program.templateBody
42+
if (element == null) {
43+
return
44+
}
45+
46+
const rootElements = []
47+
for (const child of element.children) {
48+
if (sourceCode.getText(child).trim() !== '') {
49+
rootElements.push(child)
50+
}
51+
}
52+
53+
if (rootElements.length === 0) return
54+
const hasRootVIfLength = getDirectiveLength(rootElements, 'if')
55+
const hasRootVElseLength = getDirectiveLength(rootElements, 'else')
56+
const hasRootVElseIfLength = getDirectiveLength(rootElements, 'else-if')
57+
if (
58+
hasRootVIfLength === 1 &&
59+
hasRootVElseLength === 0 &&
60+
hasRootVElseIfLength === 0
61+
) {
62+
context.report({
63+
node: element,
64+
loc: element.loc,
65+
message:
66+
'`v-if` should not be used on root element without `v-else`.'
67+
})
68+
}
69+
}
70+
}
71+
}
72+
}
+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/**
2+
* @author Perry Song
3+
* @copyright 2023 Perry Song. All rights reserved.
4+
* See LICENSE file in root directory for full license.
5+
*/
6+
'use strict'
7+
8+
const RuleTester = require('eslint').RuleTester
9+
const rule = require('../../../lib/rules/valid-v-if-template-root')
10+
11+
const tester = new RuleTester({
12+
parser: require.resolve('vue-eslint-parser'),
13+
parserOptions: { ecmaVersion: 2015 }
14+
})
15+
16+
tester.run('valid-v-if-template-root', rule, {
17+
valid: [
18+
{
19+
filename: 'test.vue',
20+
code: ''
21+
},
22+
{
23+
filename: 'test.vue',
24+
code: '<template><div>abc</div></template>'
25+
},
26+
{
27+
filename: 'test.vue',
28+
code: '<template>\n <div>abc</div>\n</template>'
29+
},
30+
{
31+
filename: 'test.vue',
32+
code: '<template>\n <!-- comment -->\n <div>abc</div>\n</template>'
33+
},
34+
{
35+
filename: 'test.vue',
36+
code: '<template>\n <!-- comment -->\n <div v-if="foo">abc</div>\n <div v-else>abc</div>\n</template>'
37+
},
38+
{
39+
filename: 'test.vue',
40+
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>'
41+
},
42+
{
43+
filename: 'test.vue',
44+
code: `<template>\n <c1 v-if="1" />\n <c2 v-else-if="1" />\n <c3 v-else />\n</template>`
45+
},
46+
{
47+
filename: 'test.vue',
48+
code: '<template><div v-if="foo"></div><div v-else-if="bar"></div></template>'
49+
},
50+
{
51+
filename: 'test.vue',
52+
code: '<template src="foo.html"></template>'
53+
},
54+
{
55+
filename: 'test.vue',
56+
code: '<template><div><textarea/>test</div></template>'
57+
},
58+
{
59+
filename: 'test.vue',
60+
code: '<template><table><custom-thead></custom-thead></table></template>'
61+
},
62+
{
63+
filename: 'test.vue',
64+
code: '<template><div></div><div></div></template>'
65+
},
66+
{
67+
filename: 'test.vue',
68+
code: '<template>\n <div></div>\n <div></div>\n</template>'
69+
},
70+
{
71+
filename: 'test.vue',
72+
code: '<template>{{a b c}}</template>'
73+
},
74+
{
75+
filename: 'test.vue',
76+
code: '<template><div></div>aaaaaa</template>'
77+
},
78+
{
79+
filename: 'test.vue',
80+
code: '<template>aaaaaa<div></div></template>'
81+
},
82+
{
83+
filename: 'test.vue',
84+
code: '<template><div v-for="x in list"></div></template>'
85+
},
86+
{
87+
filename: 'test.vue',
88+
code: '<template><slot></slot></template>'
89+
},
90+
{
91+
filename: 'test.vue',
92+
code: '<template><template></template></template>'
93+
},
94+
{
95+
filename: 'test.vue',
96+
code: '<template> <div v-if="mode === \'a\'"></div><div v-if="mode === \'b\'"></div></template>'
97+
}
98+
],
99+
invalid: [
100+
{
101+
filename: 'test.vue',
102+
code: '<template><custom-component v-if="foo"></custom-component></template>',
103+
errors: ['`v-if` should not be used on root element without `v-else`.']
104+
},
105+
{
106+
filename: 'test.vue',
107+
code: '<template><div v-if="foo"></div></template>',
108+
errors: ['`v-if` should not be used on root element without `v-else`.']
109+
}
110+
]
111+
})

0 commit comments

Comments
 (0)