Skip to content

Commit

Permalink
feat: Add no-empty-component-block rule (#1222)
Browse files Browse the repository at this point in the history
* feat: Add no-empty-component-block rule

* chore: run update

* feat: consider whether exist node.templateBody

* refactor: use parserServices.getDocumentFragment

* feat: consider whether exists context.parserServices.getDocumentFragment

* feat: consider whitespaces and break lines

* chore run update

* refactor: reflect PR review
  • Loading branch information
tyankatsu0105 committed Jun 27, 2020
1 parent 2957638 commit c210505
Show file tree
Hide file tree
Showing 5 changed files with 314 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/rules/README.md
Expand Up @@ -286,6 +286,7 @@ For example:
| [vue/no-bare-strings-in-template](./no-bare-strings-in-template.md) | disallow the use of bare strings in `<template>` | |
| [vue/no-boolean-default](./no-boolean-default.md) | disallow boolean defaults | :wrench: |
| [vue/no-duplicate-attr-inheritance](./no-duplicate-attr-inheritance.md) | enforce `inheritAttrs` to be set to `false` when using `v-bind="$attrs"` | |
| [vue/no-empty-component-block](./no-empty-component-block.md) | disallow the `<template>` `<script>` `<style>` block to be empty | |
| [vue/no-multiple-objects-in-class](./no-multiple-objects-in-class.md) | disallow to pass multiple objects into array to class | |
| [vue/no-potential-component-option-typo](./no-potential-component-option-typo.md) | disallow a potential typo in your component property | |
| [vue/no-reserved-component-names](./no-reserved-component-names.md) | disallow the use of reserved names in component definitions | |
Expand Down
68 changes: 68 additions & 0 deletions docs/rules/no-empty-component-block.md
@@ -0,0 +1,68 @@
---
pageClass: rule-details
sidebarDepth: 0
title: vue/no-empty-component-block
description: disallow the `<template>` `<script>` `<style>` block to be empty
---
# vue/no-empty-component-block
> disallow the `<template>` `<script>` `<style>` block to be empty
## :book: Rule Details

This rule disallows the `<template>` `<script>` `<style>` block to be empty.

This rule also checks block what has attribute `src`.
See: https://vue-loader.vuejs.org/spec.html#src-imports

<eslint-code-block :rules="{'vue/no-empty-component-block': ['error']}">

```vue
// ✓ GOOD
<template>
<p>foo</p>
</template>
<script>
console.log('foo')
</script>
<style>
p {
display: inline;
}
</style>
<template src="./template.html"></template>
<template src="./template.html" />
<script src="./script.js"></script>
<script src="./script.js" />
<style src="./style.css"></style>
<style src="./style.css" />
// ✗ BAD
<template></template>
<template />
<template src="" />
<script></script>
<script />
<script src="" />
<style></style>
<style />
<style src="" />
```

</eslint-code-block>

## :wrench: Options

Nothing.

## :mag: Implementation

- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-empty-component-block.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-empty-component-block.js)
1 change: 1 addition & 0 deletions lib/index.js
Expand Up @@ -69,6 +69,7 @@ module.exports = {
'no-dupe-keys': require('./rules/no-dupe-keys'),
'no-duplicate-attr-inheritance': require('./rules/no-duplicate-attr-inheritance'),
'no-duplicate-attributes': require('./rules/no-duplicate-attributes'),
'no-empty-component-block': require('./rules/no-empty-component-block'),
'no-empty-pattern': require('./rules/no-empty-pattern'),
'no-extra-parens': require('./rules/no-extra-parens'),
'no-irregular-whitespace': require('./rules/no-irregular-whitespace'),
Expand Down
99 changes: 99 additions & 0 deletions lib/rules/no-empty-component-block.js
@@ -0,0 +1,99 @@
/**
* @author tyankatsu <https://github.com/tyankatsu0105>
* See LICENSE file in root directory for full license.
*/
'use strict'

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

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

/**
* check whether has attribute `src`
*/
function hasAttributeSrc(componentBlock) {
const hasAttribute = componentBlock.startTag.attributes.length > 0

const hasSrc =
componentBlock.startTag.attributes.filter(
(attribute) =>
attribute.key.name === 'src' && attribute.value.value !== ''
).length > 0

return hasAttribute && hasSrc
}

/**
* check whether value under the component block is only whitespaces or break lines
*/
function isValueOnlyWhiteSpacesOrLineBreaks(componentBlock) {
return (
componentBlock.children.length === 1 &&
componentBlock.children[0].type === 'VText' &&
!componentBlock.children[0].value.trim()
)
}

module.exports = {
meta: {
type: 'suggestion',
docs: {
description:
'disallow the `<template>` `<script>` `<style>` block to be empty',
categories: undefined,
url: 'https://eslint.vuejs.org/rules/no-empty-component-block.html'
},
fixable: null,
schema: [],
messages: {
unexpected: '`<{{ blockName }}>` is empty. Empty block is not allowed.'
}
},

/**
* @param {RuleContext} context - The rule context.
* @returns {RuleListener} AST event handlers.
*/
create(context) {
if (!context.parserServices.getDocumentFragment) {
return {}
}

const componentBlocks = context.parserServices.getDocumentFragment()
.children

return {
Program(node) {
for (const componentBlock of componentBlocks) {
if (
componentBlock.name !== 'template' &&
componentBlock.name !== 'script' &&
componentBlock.name !== 'style'
)
continue

// https://vue-loader.vuejs.org/spec.html#src-imports
if (hasAttributeSrc(componentBlock)) continue

if (
isValueOnlyWhiteSpacesOrLineBreaks(componentBlock) ||
componentBlock.children.length === 0
) {
context.report({
node: componentBlock,
loc: componentBlock.loc,
messageId: 'unexpected',
data: {
blockName: componentBlock.name
}
})
}
}
}
}
}
}
145 changes: 145 additions & 0 deletions tests/lib/rules/no-empty-component-block.js
@@ -0,0 +1,145 @@
/**
* @author tyankatsu <https://github.com/tyankatsu0105>
*/
'use strict'

const RuleTester = require('eslint').RuleTester
const rule = require('../../../lib/rules/no-empty-component-block')

const tester = new RuleTester({
parser: require.resolve('vue-eslint-parser'),
parserOptions: { ecmaVersion: 2018 }
})

tester.run('no-empty-component-block', rule, {
valid: [
`<template><p>foo</p></template>`,
`<template> foobar </template>`,
`<template><p>foo</p></template><script>console.log('foo')</script>`,
`<template><p>foo</p></template><script>console.log('foo')</script><style>p{display: inline;}</style>`,
`<template src="./template.html"></template>`,
`<template src="./template.html" />`,
`<template src="./template.html"></template><script src="./script.js"></script>`,
`<template src="./template.html" /><script src="./script.js" />`,
`<template src="./template.html"></template><script src="./script.js"></script><style src="./style.css"></style>`,
`<template src="./template.html" /><script src="./script.js" /><style src="./style.css" />`
],
invalid: [
{
code: `<template></template>`,
errors: [
{
message: '`<template>` is empty. Empty block is not allowed.'
}
]
},
{
code: `<template> </template>`,
errors: [
{
message: '`<template>` is empty. Empty block is not allowed.'
}
]
},
{
code: `<template>
</template>`,
errors: [
{
message: '`<template>` is empty. Empty block is not allowed.'
}
]
},
{
code: '<template />',
errors: [
{
message: '`<template>` is empty. Empty block is not allowed.'
}
]
},
{
code: '<template src="" />',
errors: [
{
message: '`<template>` is empty. Empty block is not allowed.'
}
]
},
{
code: '<template></template><script></script>',
errors: [
{
message: '`<template>` is empty. Empty block is not allowed.'
},
{
message: '`<script>` is empty. Empty block is not allowed.'
}
]
},
{
code: '<template /><script />',
errors: [
{
message: '`<template>` is empty. Empty block is not allowed.'
},
{
message: '`<script>` is empty. Empty block is not allowed.'
}
]
},
{
code: '<template src="" /><script src="" />',
errors: [
{
message: '`<template>` is empty. Empty block is not allowed.'
},
{
message: '`<script>` is empty. Empty block is not allowed.'
}
]
},
{
code: '<template></template><script></script><style></style>',
errors: [
{
message: '`<template>` is empty. Empty block is not allowed.'
},
{
message: '`<script>` is empty. Empty block is not allowed.'
},
{
message: '`<style>` is empty. Empty block is not allowed.'
}
]
},
{
code: '<template /><script /><style />',
errors: [
{
message: '`<template>` is empty. Empty block is not allowed.'
},
{
message: '`<script>` is empty. Empty block is not allowed.'
},
{
message: '`<style>` is empty. Empty block is not allowed.'
}
]
},
{
code: '<template src="" /><script src="" /><style src="" />',
errors: [
{
message: '`<template>` is empty. Empty block is not allowed.'
},
{
message: '`<script>` is empty. Empty block is not allowed.'
},
{
message: '`<style>` is empty. Empty block is not allowed.'
}
]
}
]
})

0 comments on commit c210505

Please sign in to comment.