-
-
Notifications
You must be signed in to change notification settings - Fork 666
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Add no-empty-component-block rule #1222
Changes from 6 commits
bd706cd
7f2c386
e588bdf
4ab0ba8
b845da5
8022b32
4e94cdf
3f1b9d4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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' && | ||
/^(\s|\n)+$/.test(componentBlock.children[0].value) | ||
) | ||
} | ||
|
||
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' | ||
) | ||
return | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just noticed. Use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
|
||
// https://vue-loader.vuejs.org/spec.html#src-imports | ||
if (hasAttributeSrc(componentBlock)) return | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
|
||
if ( | ||
isValueOnlyWhiteSpacesOrLineBreaks(componentBlock) || | ||
componentBlock.children.length === 0 | ||
) { | ||
context.report({ | ||
node: componentBlock, | ||
loc: componentBlock.loc, | ||
messageId: 'unexpected', | ||
data: { | ||
blockName: componentBlock.name | ||
} | ||
}) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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>', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you add testcases that contains whitespaces such as line breaks? It probably won't report because it contains VText. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wow. Good point! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added some invalid cases. |
||
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.' | ||
} | ||
] | ||
} | ||
] | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if this regex is cover all cases...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe using
/^\s*$/
will also work.However, I'm not good at using regular expressions, so I often do the following.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems nice.
I reflected this code.