Skip to content

Commit c210505

Browse files
feat: Add no-empty-component-block rule (#1222)
* 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
1 parent 2957638 commit c210505

File tree

5 files changed

+314
-0
lines changed

5 files changed

+314
-0
lines changed

Diff for: docs/rules/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ For example:
286286
| [vue/no-bare-strings-in-template](./no-bare-strings-in-template.md) | disallow the use of bare strings in `<template>` | |
287287
| [vue/no-boolean-default](./no-boolean-default.md) | disallow boolean defaults | :wrench: |
288288
| [vue/no-duplicate-attr-inheritance](./no-duplicate-attr-inheritance.md) | enforce `inheritAttrs` to be set to `false` when using `v-bind="$attrs"` | |
289+
| [vue/no-empty-component-block](./no-empty-component-block.md) | disallow the `<template>` `<script>` `<style>` block to be empty | |
289290
| [vue/no-multiple-objects-in-class](./no-multiple-objects-in-class.md) | disallow to pass multiple objects into array to class | |
290291
| [vue/no-potential-component-option-typo](./no-potential-component-option-typo.md) | disallow a potential typo in your component property | |
291292
| [vue/no-reserved-component-names](./no-reserved-component-names.md) | disallow the use of reserved names in component definitions | |

Diff for: docs/rules/no-empty-component-block.md

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
---
2+
pageClass: rule-details
3+
sidebarDepth: 0
4+
title: vue/no-empty-component-block
5+
description: disallow the `<template>` `<script>` `<style>` block to be empty
6+
---
7+
# vue/no-empty-component-block
8+
> disallow the `<template>` `<script>` `<style>` block to be empty
9+
10+
## :book: Rule Details
11+
12+
This rule disallows the `<template>` `<script>` `<style>` block to be empty.
13+
14+
This rule also checks block what has attribute `src`.
15+
See: https://vue-loader.vuejs.org/spec.html#src-imports
16+
17+
<eslint-code-block :rules="{'vue/no-empty-component-block': ['error']}">
18+
19+
```vue
20+
// ✓ GOOD
21+
<template>
22+
<p>foo</p>
23+
</template>
24+
25+
<script>
26+
console.log('foo')
27+
</script>
28+
29+
<style>
30+
p {
31+
display: inline;
32+
}
33+
</style>
34+
35+
<template src="./template.html"></template>
36+
<template src="./template.html" />
37+
38+
<script src="./script.js"></script>
39+
<script src="./script.js" />
40+
41+
<style src="./style.css"></style>
42+
<style src="./style.css" />
43+
44+
45+
// ✗ BAD
46+
<template></template>
47+
<template />
48+
<template src="" />
49+
50+
<script></script>
51+
<script />
52+
<script src="" />
53+
54+
<style></style>
55+
<style />
56+
<style src="" />
57+
```
58+
59+
</eslint-code-block>
60+
61+
## :wrench: Options
62+
63+
Nothing.
64+
65+
## :mag: Implementation
66+
67+
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-empty-component-block.js)
68+
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-empty-component-block.js)

Diff for: lib/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ module.exports = {
6969
'no-dupe-keys': require('./rules/no-dupe-keys'),
7070
'no-duplicate-attr-inheritance': require('./rules/no-duplicate-attr-inheritance'),
7171
'no-duplicate-attributes': require('./rules/no-duplicate-attributes'),
72+
'no-empty-component-block': require('./rules/no-empty-component-block'),
7273
'no-empty-pattern': require('./rules/no-empty-pattern'),
7374
'no-extra-parens': require('./rules/no-extra-parens'),
7475
'no-irregular-whitespace': require('./rules/no-irregular-whitespace'),

Diff for: lib/rules/no-empty-component-block.js

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/**
2+
* @author tyankatsu <https://github.com/tyankatsu0105>
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
'use strict'
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
// ------------------------------------------------------------------------------
12+
// Rule Definition
13+
// ------------------------------------------------------------------------------
14+
15+
/**
16+
* check whether has attribute `src`
17+
*/
18+
function hasAttributeSrc(componentBlock) {
19+
const hasAttribute = componentBlock.startTag.attributes.length > 0
20+
21+
const hasSrc =
22+
componentBlock.startTag.attributes.filter(
23+
(attribute) =>
24+
attribute.key.name === 'src' && attribute.value.value !== ''
25+
).length > 0
26+
27+
return hasAttribute && hasSrc
28+
}
29+
30+
/**
31+
* check whether value under the component block is only whitespaces or break lines
32+
*/
33+
function isValueOnlyWhiteSpacesOrLineBreaks(componentBlock) {
34+
return (
35+
componentBlock.children.length === 1 &&
36+
componentBlock.children[0].type === 'VText' &&
37+
!componentBlock.children[0].value.trim()
38+
)
39+
}
40+
41+
module.exports = {
42+
meta: {
43+
type: 'suggestion',
44+
docs: {
45+
description:
46+
'disallow the `<template>` `<script>` `<style>` block to be empty',
47+
categories: undefined,
48+
url: 'https://eslint.vuejs.org/rules/no-empty-component-block.html'
49+
},
50+
fixable: null,
51+
schema: [],
52+
messages: {
53+
unexpected: '`<{{ blockName }}>` is empty. Empty block is not allowed.'
54+
}
55+
},
56+
57+
/**
58+
* @param {RuleContext} context - The rule context.
59+
* @returns {RuleListener} AST event handlers.
60+
*/
61+
create(context) {
62+
if (!context.parserServices.getDocumentFragment) {
63+
return {}
64+
}
65+
66+
const componentBlocks = context.parserServices.getDocumentFragment()
67+
.children
68+
69+
return {
70+
Program(node) {
71+
for (const componentBlock of componentBlocks) {
72+
if (
73+
componentBlock.name !== 'template' &&
74+
componentBlock.name !== 'script' &&
75+
componentBlock.name !== 'style'
76+
)
77+
continue
78+
79+
// https://vue-loader.vuejs.org/spec.html#src-imports
80+
if (hasAttributeSrc(componentBlock)) continue
81+
82+
if (
83+
isValueOnlyWhiteSpacesOrLineBreaks(componentBlock) ||
84+
componentBlock.children.length === 0
85+
) {
86+
context.report({
87+
node: componentBlock,
88+
loc: componentBlock.loc,
89+
messageId: 'unexpected',
90+
data: {
91+
blockName: componentBlock.name
92+
}
93+
})
94+
}
95+
}
96+
}
97+
}
98+
}
99+
}

Diff for: tests/lib/rules/no-empty-component-block.js

+145
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/**
2+
* @author tyankatsu <https://github.com/tyankatsu0105>
3+
*/
4+
'use strict'
5+
6+
const RuleTester = require('eslint').RuleTester
7+
const rule = require('../../../lib/rules/no-empty-component-block')
8+
9+
const tester = new RuleTester({
10+
parser: require.resolve('vue-eslint-parser'),
11+
parserOptions: { ecmaVersion: 2018 }
12+
})
13+
14+
tester.run('no-empty-component-block', rule, {
15+
valid: [
16+
`<template><p>foo</p></template>`,
17+
`<template> foobar </template>`,
18+
`<template><p>foo</p></template><script>console.log('foo')</script>`,
19+
`<template><p>foo</p></template><script>console.log('foo')</script><style>p{display: inline;}</style>`,
20+
`<template src="./template.html"></template>`,
21+
`<template src="./template.html" />`,
22+
`<template src="./template.html"></template><script src="./script.js"></script>`,
23+
`<template src="./template.html" /><script src="./script.js" />`,
24+
`<template src="./template.html"></template><script src="./script.js"></script><style src="./style.css"></style>`,
25+
`<template src="./template.html" /><script src="./script.js" /><style src="./style.css" />`
26+
],
27+
invalid: [
28+
{
29+
code: `<template></template>`,
30+
errors: [
31+
{
32+
message: '`<template>` is empty. Empty block is not allowed.'
33+
}
34+
]
35+
},
36+
{
37+
code: `<template> </template>`,
38+
errors: [
39+
{
40+
message: '`<template>` is empty. Empty block is not allowed.'
41+
}
42+
]
43+
},
44+
{
45+
code: `<template>
46+
</template>`,
47+
errors: [
48+
{
49+
message: '`<template>` is empty. Empty block is not allowed.'
50+
}
51+
]
52+
},
53+
{
54+
code: '<template />',
55+
errors: [
56+
{
57+
message: '`<template>` is empty. Empty block is not allowed.'
58+
}
59+
]
60+
},
61+
{
62+
code: '<template src="" />',
63+
errors: [
64+
{
65+
message: '`<template>` is empty. Empty block is not allowed.'
66+
}
67+
]
68+
},
69+
{
70+
code: '<template></template><script></script>',
71+
errors: [
72+
{
73+
message: '`<template>` is empty. Empty block is not allowed.'
74+
},
75+
{
76+
message: '`<script>` is empty. Empty block is not allowed.'
77+
}
78+
]
79+
},
80+
{
81+
code: '<template /><script />',
82+
errors: [
83+
{
84+
message: '`<template>` is empty. Empty block is not allowed.'
85+
},
86+
{
87+
message: '`<script>` is empty. Empty block is not allowed.'
88+
}
89+
]
90+
},
91+
{
92+
code: '<template src="" /><script src="" />',
93+
errors: [
94+
{
95+
message: '`<template>` is empty. Empty block is not allowed.'
96+
},
97+
{
98+
message: '`<script>` is empty. Empty block is not allowed.'
99+
}
100+
]
101+
},
102+
{
103+
code: '<template></template><script></script><style></style>',
104+
errors: [
105+
{
106+
message: '`<template>` is empty. Empty block is not allowed.'
107+
},
108+
{
109+
message: '`<script>` is empty. Empty block is not allowed.'
110+
},
111+
{
112+
message: '`<style>` is empty. Empty block is not allowed.'
113+
}
114+
]
115+
},
116+
{
117+
code: '<template /><script /><style />',
118+
errors: [
119+
{
120+
message: '`<template>` is empty. Empty block is not allowed.'
121+
},
122+
{
123+
message: '`<script>` is empty. Empty block is not allowed.'
124+
},
125+
{
126+
message: '`<style>` is empty. Empty block is not allowed.'
127+
}
128+
]
129+
},
130+
{
131+
code: '<template src="" /><script src="" /><style src="" />',
132+
errors: [
133+
{
134+
message: '`<template>` is empty. Empty block is not allowed.'
135+
},
136+
{
137+
message: '`<script>` is empty. Empty block is not allowed.'
138+
},
139+
{
140+
message: '`<style>` is empty. Empty block is not allowed.'
141+
}
142+
]
143+
}
144+
]
145+
})

0 commit comments

Comments
 (0)