Skip to content

Commit d8e728a

Browse files
authored
Feature/support for fragments (#1038)
* feat: update valid-template-root to support Vue 3 requirements * feat: implement no-multiple-template-root method basing on valid-template-root implementation for Vue 2
1 parent a72237d commit d8e728a

9 files changed

+294
-115
lines changed

docs/rules/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
4141
| [vue/no-async-in-computed-properties](./no-async-in-computed-properties.md) | disallow asynchronous actions in computed properties | |
4242
| [vue/no-dupe-keys](./no-dupe-keys.md) | disallow duplication of field names | |
4343
| [vue/no-duplicate-attributes](./no-duplicate-attributes.md) | disallow duplication of attributes | |
44+
| [vue/no-multiple-template-root](./no-multiple-template-root.md) | disallow adding multiple root nodes to the template | |
4445
| [vue/no-parsing-error](./no-parsing-error.md) | disallow parsing errors in `<template>` | |
4546
| [vue/no-reserved-keys](./no-reserved-keys.md) | disallow overwriting reserved keys | |
4647
| [vue/no-shared-component-data](./no-shared-component-data.md) | enforce component's data property to be a function | :wrench: |
+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
---
2+
pageClass: rule-details
3+
sidebarDepth: 0
4+
title: vue/no-multiple-template-root
5+
description: disallow adding multiple root nodes to the template
6+
---
7+
# vue/no-multiple-template-root
8+
> disallow adding multiple root nodes to the template
9+
10+
- :gear: This rule is included in all of `"plugin:vue/essential"`, `"plugin:vue/strongly-recommended"` and `"plugin:vue/recommended"`.
11+
12+
This rule checks whether template contains single root element valid for Vue 2.
13+
14+
15+
<eslint-code-block :rules="{'vue/no-multiple-template-root': ['error']}">
16+
17+
```vue
18+
<!-- The root is text -->
19+
<template>Lorem ipsum</template>
20+
```
21+
22+
</eslint-code-block>
23+
24+
<eslint-code-block :rules="{'vue/no-multiple-template-root': ['error']}">
25+
26+
```vue
27+
<!-- There are multiple root elements -->
28+
<template>
29+
<div>hello</div>
30+
<div>hello</div>
31+
</template>
32+
```
33+
34+
</eslint-code-block>
35+
36+
<eslint-code-block :rules="{'vue/no-multiple-template-root': ['error']}">
37+
38+
```vue
39+
<!-- The root element has `v-for` directives -->
40+
<template>
41+
<div v-for="item in items"/>
42+
</template>
43+
```
44+
45+
</eslint-code-block>
46+
47+
<eslint-code-block :rules="{'vue/no-multiple-template-root': ['error']}">
48+
49+
```vue
50+
<!-- The root element is `<template>` or `<slot>` -->
51+
<template>
52+
<slot />
53+
</template>
54+
```
55+
56+
</eslint-code-block>
57+
58+
## :wrench: Options
59+
60+
Nothing.
61+
62+
## :mag: Implementation
63+
64+
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-multiple-template-root.js)
65+
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-multiple-template-root.js)

docs/rules/valid-template-root.md

+2-36
Original file line numberDiff line numberDiff line change
@@ -27,42 +27,8 @@ This rule reports the template root in the following cases:
2727
<eslint-code-block :rules="{'vue/valid-template-root': ['error']}">
2828

2929
```vue
30-
<!-- The root is text -->
31-
<template>Lorem ipsum</template>
32-
```
33-
34-
</eslint-code-block>
35-
36-
<eslint-code-block :rules="{'vue/valid-template-root': ['error']}">
37-
38-
```vue
39-
<!-- There are multiple root elements -->
40-
<template>
41-
<div>hello</div>
42-
<div>hello</div>
43-
</template>
44-
```
45-
46-
</eslint-code-block>
47-
48-
<eslint-code-block :rules="{'vue/valid-template-root': ['error']}">
49-
50-
```vue
51-
<!-- The root element has `v-for` directives -->
52-
<template>
53-
<div v-for="item in items"/>
54-
</template>
55-
```
56-
57-
</eslint-code-block>
58-
59-
<eslint-code-block :rules="{'vue/valid-template-root': ['error']}">
60-
61-
```vue
62-
<!-- The root element is `<template>` or `<slot>` -->
63-
<template>
64-
<slot />
65-
</template>
30+
<!-- The root with src attribute is not empty -->
31+
<template src="foo.html"><div></div></template>
6632
```
6733

6834
</eslint-code-block>

lib/configs/essential.js

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ module.exports = {
99
'vue/no-async-in-computed-properties': 'error',
1010
'vue/no-dupe-keys': 'error',
1111
'vue/no-duplicate-attributes': 'error',
12+
'vue/no-multiple-template-root': 'error',
1213
'vue/no-parsing-error': 'error',
1314
'vue/no-reserved-keys': 'error',
1415
'vue/no-shared-component-data': 'error',

lib/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ module.exports = {
4747
'no-empty-pattern': require('./rules/no-empty-pattern'),
4848
'no-irregular-whitespace': require('./rules/no-irregular-whitespace'),
4949
'no-multi-spaces': require('./rules/no-multi-spaces'),
50+
'no-multiple-template-root': require('./rules/no-multiple-template-root'),
5051
'no-parsing-error': require('./rules/no-parsing-error'),
5152
'no-reserved-component-names': require('./rules/no-reserved-component-names'),
5253
'no-reserved-keys': require('./rules/no-reserved-keys'),
+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/**
2+
* @fileoverview disallow adding multiple root nodes to the template
3+
* @author Przemyslaw Falowski (@przemkow)
4+
*/
5+
'use strict'
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
const utils = require('../utils')
12+
13+
// ------------------------------------------------------------------------------
14+
// Rule Definition
15+
// ------------------------------------------------------------------------------
16+
17+
module.exports = {
18+
meta: {
19+
type: 'problem',
20+
docs: {
21+
description: 'disallow adding multiple root nodes to the template',
22+
category: 'essential',
23+
url: 'https://eslint.vuejs.org/rules/no-multiple-template-root.html'
24+
},
25+
fixable: null,
26+
schema: []
27+
},
28+
29+
create: function (context) {
30+
const sourceCode = context.getSourceCode()
31+
32+
return {
33+
Program (program) {
34+
const element = program.templateBody
35+
if (element == null) {
36+
return
37+
}
38+
39+
const rootElements = []
40+
let extraText = null
41+
let extraElement = null
42+
let vIf = false
43+
for (const child of element.children) {
44+
if (child.type === 'VElement') {
45+
if (rootElements.length === 0) {
46+
rootElements.push(child)
47+
vIf = utils.hasDirective(child, 'if')
48+
} else if (vIf && utils.hasDirective(child, 'else-if')) {
49+
rootElements.push(child)
50+
} else if (vIf && utils.hasDirective(child, 'else')) {
51+
rootElements.push(child)
52+
vIf = false
53+
} else {
54+
extraElement = child
55+
}
56+
} else if (sourceCode.getText(child).trim() !== '') {
57+
extraText = child
58+
}
59+
}
60+
61+
if (extraText != null) {
62+
context.report({
63+
node: extraText,
64+
loc: extraText.loc,
65+
message: 'The template root requires an element rather than texts.'
66+
})
67+
} else if (extraElement != null) {
68+
context.report({
69+
node: extraElement,
70+
loc: extraElement.loc,
71+
message: 'The template root requires exactly one element.'
72+
})
73+
} else {
74+
for (const element of rootElements) {
75+
const tag = element.startTag
76+
const name = element.name
77+
78+
if (name === 'template' || name === 'slot') {
79+
context.report({
80+
node: tag,
81+
loc: tag.loc,
82+
message: "The template root disallows '<{{name}}>' elements.",
83+
data: { name }
84+
})
85+
}
86+
if (utils.hasDirective(element, 'for')) {
87+
context.report({
88+
node: tag,
89+
loc: tag.loc,
90+
message: "The template root disallows 'v-for' directives."
91+
})
92+
}
93+
}
94+
}
95+
}
96+
}
97+
}
98+
}

lib/rules/valid-template-root.js

+12-57
Original file line numberDiff line numberDiff line change
@@ -39,72 +39,27 @@ module.exports = {
3939

4040
const hasSrc = utils.hasAttribute(element, 'src')
4141
const rootElements = []
42-
let extraText = null
43-
let extraElement = null
44-
let vIf = false
42+
4543
for (const child of element.children) {
46-
if (child.type === 'VElement') {
47-
if (rootElements.length === 0 && !hasSrc) {
48-
rootElements.push(child)
49-
vIf = utils.hasDirective(child, 'if')
50-
} else if (vIf && utils.hasDirective(child, 'else-if')) {
51-
rootElements.push(child)
52-
} else if (vIf && utils.hasDirective(child, 'else')) {
53-
rootElements.push(child)
54-
vIf = false
55-
} else {
56-
extraElement = child
57-
}
58-
} else if (sourceCode.getText(child).trim() !== '') {
59-
extraText = child
44+
if (sourceCode.getText(child).trim() !== '') {
45+
rootElements.push(child)
6046
}
6147
}
6248

63-
if (hasSrc && (extraText != null || extraElement != null)) {
64-
context.report({
65-
node: extraText || extraElement,
66-
loc: (extraText || extraElement).loc,
67-
message: "The template root with 'src' attribute is required to be empty."
68-
})
69-
} else if (extraText != null) {
70-
context.report({
71-
node: extraText,
72-
loc: extraText.loc,
73-
message: 'The template root requires an element rather than texts.'
74-
})
75-
} else if (extraElement != null) {
76-
context.report({
77-
node: extraElement,
78-
loc: extraElement.loc,
79-
message: 'The template root requires exactly one element.'
80-
})
49+
if (hasSrc && rootElements.length) {
50+
for (const element of rootElements) {
51+
context.report({
52+
node: element,
53+
loc: element.loc,
54+
message: "The template root with 'src' attribute is required to be empty."
55+
})
56+
}
8157
} else if (rootElements.length === 0 && !hasSrc) {
8258
context.report({
8359
node: element,
8460
loc: element.loc,
85-
message: 'The template root requires exactly one element.'
61+
message: 'The template requires child element.'
8662
})
87-
} else {
88-
for (const element of rootElements) {
89-
const tag = element.startTag
90-
const name = element.name
91-
92-
if (name === 'template' || name === 'slot') {
93-
context.report({
94-
node: tag,
95-
loc: tag.loc,
96-
message: "The template root disallows '<{{name}}>' elements.",
97-
data: { name }
98-
})
99-
}
100-
if (utils.hasDirective(element, 'for')) {
101-
context.report({
102-
node: tag,
103-
loc: tag.loc,
104-
message: "The template root disallows 'v-for' directives."
105-
})
106-
}
107-
}
10863
}
10964
}
11065
}

0 commit comments

Comments
 (0)