Skip to content

Commit

Permalink
Feature/support for fragments (#1038)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
przemkow committed Mar 14, 2020
1 parent a72237d commit d8e728a
Show file tree
Hide file tree
Showing 9 changed files with 294 additions and 115 deletions.
1 change: 1 addition & 0 deletions docs/rules/README.md
Expand Up @@ -41,6 +41,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
| [vue/no-async-in-computed-properties](./no-async-in-computed-properties.md) | disallow asynchronous actions in computed properties | |
| [vue/no-dupe-keys](./no-dupe-keys.md) | disallow duplication of field names | |
| [vue/no-duplicate-attributes](./no-duplicate-attributes.md) | disallow duplication of attributes | |
| [vue/no-multiple-template-root](./no-multiple-template-root.md) | disallow adding multiple root nodes to the template | |
| [vue/no-parsing-error](./no-parsing-error.md) | disallow parsing errors in `<template>` | |
| [vue/no-reserved-keys](./no-reserved-keys.md) | disallow overwriting reserved keys | |
| [vue/no-shared-component-data](./no-shared-component-data.md) | enforce component's data property to be a function | :wrench: |
Expand Down
65 changes: 65 additions & 0 deletions docs/rules/no-multiple-template-root.md
@@ -0,0 +1,65 @@
---
pageClass: rule-details
sidebarDepth: 0
title: vue/no-multiple-template-root
description: disallow adding multiple root nodes to the template
---
# vue/no-multiple-template-root
> disallow adding multiple root nodes to the template
- :gear: This rule is included in all of `"plugin:vue/essential"`, `"plugin:vue/strongly-recommended"` and `"plugin:vue/recommended"`.

This rule checks whether template contains single root element valid for Vue 2.


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

```vue
<!-- The root is text -->
<template>Lorem ipsum</template>
```

</eslint-code-block>

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

```vue
<!-- There are multiple root elements -->
<template>
<div>hello</div>
<div>hello</div>
</template>
```

</eslint-code-block>

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

```vue
<!-- The root element has `v-for` directives -->
<template>
<div v-for="item in items"/>
</template>
```

</eslint-code-block>

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

```vue
<!-- The root element is `<template>` or `<slot>` -->
<template>
<slot />
</template>
```

</eslint-code-block>

## :wrench: Options

Nothing.

## :mag: Implementation

- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-multiple-template-root.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-multiple-template-root.js)
38 changes: 2 additions & 36 deletions docs/rules/valid-template-root.md
Expand Up @@ -27,42 +27,8 @@ This rule reports the template root in the following cases:
<eslint-code-block :rules="{'vue/valid-template-root': ['error']}">

```vue
<!-- The root is text -->
<template>Lorem ipsum</template>
```

</eslint-code-block>

<eslint-code-block :rules="{'vue/valid-template-root': ['error']}">

```vue
<!-- There are multiple root elements -->
<template>
<div>hello</div>
<div>hello</div>
</template>
```

</eslint-code-block>

<eslint-code-block :rules="{'vue/valid-template-root': ['error']}">

```vue
<!-- The root element has `v-for` directives -->
<template>
<div v-for="item in items"/>
</template>
```

</eslint-code-block>

<eslint-code-block :rules="{'vue/valid-template-root': ['error']}">

```vue
<!-- The root element is `<template>` or `<slot>` -->
<template>
<slot />
</template>
<!-- The root with src attribute is not empty -->
<template src="foo.html"><div></div></template>
```

</eslint-code-block>
Expand Down
1 change: 1 addition & 0 deletions lib/configs/essential.js
Expand Up @@ -9,6 +9,7 @@ module.exports = {
'vue/no-async-in-computed-properties': 'error',
'vue/no-dupe-keys': 'error',
'vue/no-duplicate-attributes': 'error',
'vue/no-multiple-template-root': 'error',
'vue/no-parsing-error': 'error',
'vue/no-reserved-keys': 'error',
'vue/no-shared-component-data': 'error',
Expand Down
1 change: 1 addition & 0 deletions lib/index.js
Expand Up @@ -47,6 +47,7 @@ module.exports = {
'no-empty-pattern': require('./rules/no-empty-pattern'),
'no-irregular-whitespace': require('./rules/no-irregular-whitespace'),
'no-multi-spaces': require('./rules/no-multi-spaces'),
'no-multiple-template-root': require('./rules/no-multiple-template-root'),
'no-parsing-error': require('./rules/no-parsing-error'),
'no-reserved-component-names': require('./rules/no-reserved-component-names'),
'no-reserved-keys': require('./rules/no-reserved-keys'),
Expand Down
98 changes: 98 additions & 0 deletions lib/rules/no-multiple-template-root.js
@@ -0,0 +1,98 @@
/**
* @fileoverview disallow adding multiple root nodes to the template
* @author Przemyslaw Falowski (@przemkow)
*/
'use strict'

// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------

const utils = require('../utils')

// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------

module.exports = {
meta: {
type: 'problem',
docs: {
description: 'disallow adding multiple root nodes to the template',
category: 'essential',
url: 'https://eslint.vuejs.org/rules/no-multiple-template-root.html'
},
fixable: null,
schema: []
},

create: function (context) {
const sourceCode = context.getSourceCode()

return {
Program (program) {
const element = program.templateBody
if (element == null) {
return
}

const rootElements = []
let extraText = null
let extraElement = null
let vIf = false
for (const child of element.children) {
if (child.type === 'VElement') {
if (rootElements.length === 0) {
rootElements.push(child)
vIf = utils.hasDirective(child, 'if')
} else if (vIf && utils.hasDirective(child, 'else-if')) {
rootElements.push(child)
} else if (vIf && utils.hasDirective(child, 'else')) {
rootElements.push(child)
vIf = false
} else {
extraElement = child
}
} else if (sourceCode.getText(child).trim() !== '') {
extraText = child
}
}

if (extraText != null) {
context.report({
node: extraText,
loc: extraText.loc,
message: 'The template root requires an element rather than texts.'
})
} else if (extraElement != null) {
context.report({
node: extraElement,
loc: extraElement.loc,
message: 'The template root requires exactly one element.'
})
} else {
for (const element of rootElements) {
const tag = element.startTag
const name = element.name

if (name === 'template' || name === 'slot') {
context.report({
node: tag,
loc: tag.loc,
message: "The template root disallows '<{{name}}>' elements.",
data: { name }
})
}
if (utils.hasDirective(element, 'for')) {
context.report({
node: tag,
loc: tag.loc,
message: "The template root disallows 'v-for' directives."
})
}
}
}
}
}
}
}
69 changes: 12 additions & 57 deletions lib/rules/valid-template-root.js
Expand Up @@ -39,72 +39,27 @@ module.exports = {

const hasSrc = utils.hasAttribute(element, 'src')
const rootElements = []
let extraText = null
let extraElement = null
let vIf = false

for (const child of element.children) {
if (child.type === 'VElement') {
if (rootElements.length === 0 && !hasSrc) {
rootElements.push(child)
vIf = utils.hasDirective(child, 'if')
} else if (vIf && utils.hasDirective(child, 'else-if')) {
rootElements.push(child)
} else if (vIf && utils.hasDirective(child, 'else')) {
rootElements.push(child)
vIf = false
} else {
extraElement = child
}
} else if (sourceCode.getText(child).trim() !== '') {
extraText = child
if (sourceCode.getText(child).trim() !== '') {
rootElements.push(child)
}
}

if (hasSrc && (extraText != null || extraElement != null)) {
context.report({
node: extraText || extraElement,
loc: (extraText || extraElement).loc,
message: "The template root with 'src' attribute is required to be empty."
})
} else if (extraText != null) {
context.report({
node: extraText,
loc: extraText.loc,
message: 'The template root requires an element rather than texts.'
})
} else if (extraElement != null) {
context.report({
node: extraElement,
loc: extraElement.loc,
message: 'The template root requires exactly one element.'
})
if (hasSrc && rootElements.length) {
for (const element of rootElements) {
context.report({
node: element,
loc: element.loc,
message: "The template root with 'src' attribute is required to be empty."
})
}
} else if (rootElements.length === 0 && !hasSrc) {
context.report({
node: element,
loc: element.loc,
message: 'The template root requires exactly one element.'
message: 'The template requires child element.'
})
} else {
for (const element of rootElements) {
const tag = element.startTag
const name = element.name

if (name === 'template' || name === 'slot') {
context.report({
node: tag,
loc: tag.loc,
message: "The template root disallows '<{{name}}>' elements.",
data: { name }
})
}
if (utils.hasDirective(element, 'for')) {
context.report({
node: tag,
loc: tag.loc,
message: "The template root disallows 'v-for' directives."
})
}
}
}
}
}
Expand Down

0 comments on commit d8e728a

Please sign in to comment.