Skip to content

Commit 1a0bd29

Browse files
songpengyuanperrysongFloEdelmann
authored
Add vue/no-root-v-if rule (#2138)
Co-authored-by: perrysong <[email protected]> Co-authored-by: Flo Edelmann <[email protected]>
1 parent 3cbb1b3 commit 1a0bd29

File tree

5 files changed

+200
-0
lines changed

5 files changed

+200
-0
lines changed

docs/rules/index.md

+1
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ For example:
240240
| [vue/no-restricted-props](./no-restricted-props.md) | disallow specific props | :bulb: | :hammer: |
241241
| [vue/no-restricted-static-attribute](./no-restricted-static-attribute.md) | disallow specific attribute | | :hammer: |
242242
| [vue/no-restricted-v-bind](./no-restricted-v-bind.md) | disallow specific argument in `v-bind` | | :hammer: |
243+
| [vue/no-root-v-if](./no-root-v-if.md) | disallow `v-if` directives on root element | | :hammer: |
243244
| [vue/no-static-inline-styles](./no-static-inline-styles.md) | disallow static inline `style` attributes | | :hammer: |
244245
| [vue/no-template-target-blank](./no-template-target-blank.md) | disallow target="_blank" attribute without rel="noopener noreferrer" | :bulb: | :warning: |
245246
| [vue/no-this-in-before-route-enter](./no-this-in-before-route-enter.md) | disallow `this` usage in a `beforeRouteEnter` method | | :warning: |

docs/rules/no-root-v-if.md

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

lib/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ module.exports = {
124124
'no-restricted-static-attribute': require('./rules/no-restricted-static-attribute'),
125125
'no-restricted-syntax': require('./rules/no-restricted-syntax'),
126126
'no-restricted-v-bind': require('./rules/no-restricted-v-bind'),
127+
'no-root-v-if': require('./rules/no-root-v-if'),
127128
'no-setup-props-destructure': require('./rules/no-setup-props-destructure'),
128129
'no-shared-component-data': require('./rules/no-shared-component-data'),
129130
'no-side-effects-in-computed-properties': require('./rules/no-side-effects-in-computed-properties'),

lib/rules/no-root-v-if.js

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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+
module.exports = {
11+
meta: {
12+
type: 'suggestion',
13+
docs: {
14+
description: 'disallow `v-if` directives on root element',
15+
categories: undefined,
16+
url: 'https://eslint.vuejs.org/rules/no-root-v-if.html'
17+
},
18+
fixable: null,
19+
schema: []
20+
},
21+
/** @param {RuleContext} context */
22+
create(context) {
23+
return {
24+
/** @param {Program} program */
25+
Program(program) {
26+
const element = program.templateBody
27+
if (element == null) {
28+
return
29+
}
30+
31+
const rootElements = element.children.filter(utils.isVElement)
32+
if (
33+
rootElements.length === 1 &&
34+
utils.hasDirective(rootElements[0], 'if')
35+
) {
36+
context.report({
37+
node: element,
38+
loc: element.loc,
39+
message:
40+
'`v-if` should not be used on root element without `v-else`.'
41+
})
42+
}
43+
}
44+
}
45+
}
46+
}

tests/lib/rules/no-root-v-if.js

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
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/no-root-v-if')
10+
11+
const tester = new RuleTester({
12+
parser: require.resolve('vue-eslint-parser'),
13+
parserOptions: { ecmaVersion: 2015 }
14+
})
15+
16+
tester.run('no-root-v-if', 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+
filename: 'test.vue',
100+
code: '<template><div /><div v-if="foo" /></template>'
101+
}
102+
],
103+
invalid: [
104+
{
105+
filename: 'test.vue',
106+
code: '<template><custom-component v-if="foo"></custom-component></template>',
107+
errors: ['`v-if` should not be used on root element without `v-else`.']
108+
},
109+
{
110+
filename: 'test.vue',
111+
code: '<template><div v-if="foo"></div></template>',
112+
errors: ['`v-if` should not be used on root element without `v-else`.']
113+
}
114+
]
115+
})

0 commit comments

Comments
 (0)