Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 1a0bd29

Browse files
songpengyuanperrysongFloEdelmann
authored
Add vue/no-root-v-if rule (#2138)
Co-authored-by: perrysong <perrysong@xiaoman.cn> Co-authored-by: Flo Edelmann <git@flo-edelmann.de>
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 37 additions & 0 deletions
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 46 additions & 0 deletions
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

Lines changed: 115 additions & 0 deletions
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)

AltStyle によって変換されたページ (->オリジナル) /