Skip to content

Commit

Permalink
New: Add vue/padding-line-between-blocks rule (#1021)
Browse files Browse the repository at this point in the history
  • Loading branch information
ota-meshi committed Feb 16, 2020
1 parent 7608dea commit b394ca6
Show file tree
Hide file tree
Showing 7 changed files with 651 additions and 1 deletion.
1 change: 1 addition & 0 deletions docs/rules/README.md
Expand Up @@ -165,6 +165,7 @@ For example:
| [vue/no-static-inline-styles](./no-static-inline-styles.md) | disallow static inline `style` attributes | |
| [vue/no-unsupported-features](./no-unsupported-features.md) | disallow unsupported Vue.js syntax on the specified version | :wrench: |
| [vue/object-curly-spacing](./object-curly-spacing.md) | enforce consistent spacing inside braces | :wrench: |
| [vue/padding-line-between-blocks](./padding-line-between-blocks.md) | require or disallow padding lines between blocks | :wrench: |
| [vue/require-direct-export](./require-direct-export.md) | require the component to be directly exported | |
| [vue/require-name-property](./require-name-property.md) | require a name property in Vue components | |
| [vue/script-indent](./script-indent.md) | enforce consistent indentation in `<script>` | :wrench: |
Expand Down
139 changes: 139 additions & 0 deletions docs/rules/padding-line-between-blocks.md
@@ -0,0 +1,139 @@
---
pageClass: rule-details
sidebarDepth: 0
title: vue/padding-line-between-blocks
description: require or disallow padding lines between blocks
---
# vue/padding-line-between-blocks
> require or disallow padding lines between blocks
- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.

## :book: Rule Details

This rule requires or disallows blank lines between the given 2 blocks. Properly blank lines help developers to understand the code.

<eslint-code-block fix :rules="{'vue/padding-line-between-blocks': ['error']}">

```vue
<!-- ✓ GOOD -->
<template>
<div></div>
</template>
<script>
export default {}
</script>
<style></style>
```

</eslint-code-block>

<eslint-code-block fix :rules="{'vue/padding-line-between-blocks': ['error']}">

```vue
<!-- ✗ BAD -->
<template>
<div></div>
</template>
<script>
export default {}
</script>
<style></style>
```

</eslint-code-block>

## :wrench: Options

```json
{
"vue/padding-line-between-blocks": ["error", "always" | "never"]
}
```

- `"always"` (default) ... Requires one or more blank lines. Note it does not count lines that comments exist as blank lines.
- `"never"` ... Disallows blank lines.

### `"always"` (default)

<eslint-code-block fix :rules="{'vue/padding-line-between-blocks': ['error']}">

```vue
<!-- ✓ GOOD -->
<template>
<div></div>
</template>
<script>
export default {}
</script>
<style></style>
```

</eslint-code-block>

<eslint-code-block fix :rules="{'vue/padding-line-between-blocks': ['error']}">

```vue
<!-- ✗ BAD -->
<template>
<div></div>
</template>
<script>
export default {}
</script>
<style></style>
```

</eslint-code-block>

### `"never"`

<eslint-code-block fix :rules="{'vue/padding-line-between-blocks': ['error', 'never']}">

```vue
<!-- ✓ GOOD -->
<template>
<div></div>
</template>
<script>
export default {}
</script>
<style></style>
```

</eslint-code-block>

<eslint-code-block fix :rules="{'vue/padding-line-between-blocks': ['error', 'never']}">

```vue
<!-- ✗ BAD -->
<template>
<div></div>
</template>
<script>
export default {}
</script>
<style></style>
```

</eslint-code-block>

## :books: Further reading

- [padding-line-between-statements]
- [lines-between-class-members]

[padding-line-between-statements]: https://eslint.org/docs/rules/padding-line-between-statements
[lines-between-class-members]: https://eslint.org/docs/rules/lines-between-class-members


## :mag: Implementation

- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/padding-line-between-blocks.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/padding-line-between-blocks.js)
1 change: 1 addition & 0 deletions lib/configs/no-layout-rules.js
Expand Up @@ -25,6 +25,7 @@ module.exports = {
'vue/no-multi-spaces': 'off',
'vue/no-spaces-around-equal-signs-in-attribute': 'off',
'vue/object-curly-spacing': 'off',
'vue/padding-line-between-blocks': 'off',
'vue/script-indent': 'off',
'vue/singleline-html-element-content-newline': 'off',
'vue/space-infix-ops': 'off',
Expand Down
1 change: 1 addition & 0 deletions lib/index.js
Expand Up @@ -65,6 +65,7 @@ module.exports = {
'no-v-html': require('./rules/no-v-html'),
'object-curly-spacing': require('./rules/object-curly-spacing'),
'order-in-components': require('./rules/order-in-components'),
'padding-line-between-blocks': require('./rules/padding-line-between-blocks'),
'prop-name-casing': require('./rules/prop-name-casing'),
'require-component-is': require('./rules/require-component-is'),
'require-default-prop': require('./rules/require-default-prop'),
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/component-tags-order.js
Expand Up @@ -45,7 +45,7 @@ module.exports = {

function getTopLevelHTMLElements () {
if (documentFragment) {
return documentFragment.children
return documentFragment.children.filter(e => e.type === 'VElement')
}
return []
}
Expand Down
194 changes: 194 additions & 0 deletions lib/rules/padding-line-between-blocks.js
@@ -0,0 +1,194 @@
/**
* @fileoverview Require or disallow padding lines between blocks
* @author Yosuke Ota
*/
'use strict'
const utils = require('../utils')

/**
* Split the source code into multiple lines based on the line delimiters.
* @param {string} text Source code as a string.
* @returns {string[]} Array of source code lines.
*/
function splitLines (text) {
return text.split(/\r\n|[\r\n\u2028\u2029]/gu)
}

/**
* Check and report blocks for `never` configuration.
* This autofix removes blank lines between the given 2 blocks.
* @param {RuleContext} context The rule context to report.
* @param {VElement} prevBlock The previous block to check.
* @param {VElement} nextBlock The next block to check.
* @param {Token[]} betweenTokens The array of tokens between blocks.
* @returns {void}
* @private
*/
function verifyForNever (context, prevBlock, nextBlock, betweenTokens) {
if (prevBlock.loc.end.line === nextBlock.loc.start.line) {
// same line
return
}
const tokenOrNodes = [...betweenTokens, nextBlock]
let prev = prevBlock
const paddingLines = []
for (const tokenOrNode of tokenOrNodes) {
const numOfLineBreaks = tokenOrNode.loc.start.line - prev.loc.end.line
if (numOfLineBreaks > 1) {
paddingLines.push([prev, tokenOrNode])
}
prev = tokenOrNode
}
if (!paddingLines.length) {
return
}

context.report({
node: nextBlock,
messageId: 'never',
fix (fixer) {
return paddingLines.map(([prevToken, nextToken]) => {
const start = prevToken.range[1]
const end = nextToken.range[0]
const paddingText = context.getSourceCode().text
.slice(start, end)
const lastSpaces = splitLines(paddingText).pop()
return fixer.replaceTextRange([start, end], '\n' + lastSpaces)
})
}
})
}

/**
* Check and report blocks for `always` configuration.
* This autofix inserts a blank line between the given 2 blocks.
* @param {RuleContext} context The rule context to report.
* @param {VElement} prevBlock The previous block to check.
* @param {VElement} nextBlock The next block to check.
* @param {Token[]} betweenTokens The array of tokens between blocks.
* @returns {void}
* @private
*/
function verifyForAlways (context, prevBlock, nextBlock, betweenTokens) {
const tokenOrNodes = [...betweenTokens, nextBlock]
let prev = prevBlock
let linebreak
for (const tokenOrNode of tokenOrNodes) {
const numOfLineBreaks = tokenOrNode.loc.start.line - prev.loc.end.line
if (numOfLineBreaks > 1) {
// Already padded.
return
}
if (!linebreak && numOfLineBreaks > 0) {
linebreak = prev
}
prev = tokenOrNode
}

context.report({
node: nextBlock,
messageId: 'always',
fix (fixer) {
if (linebreak) {
return fixer.insertTextAfter(linebreak, '\n')
}
return fixer.insertTextAfter(prevBlock, '\n\n')
}
})
}

/**
* Types of blank lines.
* `never` and `always` are defined.
* Those have `verify` method to check and report statements.
* @private
*/
const PaddingTypes = {
never: { verify: verifyForNever },
always: { verify: verifyForAlways }
}

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

module.exports = {
meta: {
type: 'layout',
docs: {
description: 'require or disallow padding lines between blocks',
category: undefined,
url: 'https://eslint.vuejs.org/rules/padding-line-between-blocks.html'
},
fixable: 'whitespace',
schema: [
{
enum: Object.keys(PaddingTypes)
}
],
messages: {
never: 'Unexpected blank line before this block.',
always: 'Expected blank line before this block.'
}
},
create (context) {
const paddingType = PaddingTypes[context.options[0] || 'always']
const documentFragment = context.parserServices.getDocumentFragment && context.parserServices.getDocumentFragment()

let tokens
function getTopLevelHTMLElements () {
if (documentFragment) {
return documentFragment.children.filter(e => e.type === 'VElement')
}
return []
}

function getTokenAndCommentsBetween (prev, next) {
// When there is no <template>, tokenStore.getTokensBetween cannot be used.
if (!tokens) {
tokens = [
...documentFragment.tokens
.filter(token => token.type !== 'HTMLWhitespace'),
...documentFragment.comments
].sort((a, b) => a.range[0] > b.range[0] ? 1 : a.range[0] < b.range[0] ? -1 : 0)
}

let token = tokens.shift()

const results = []
while (token) {
if (prev.range[1] <= token.range[0]) {
if (next.range[0] <= token.range[0]) {
tokens.unshift(token)
break
} else {
results.push(token)
}
}
token = tokens.shift()
}

return results
}

return utils.defineTemplateBodyVisitor(
context,
{},
{
Program (node) {
if (utils.hasInvalidEOF(node)) {
return
}
const elements = [...getTopLevelHTMLElements()]

let prev = elements.shift()
for (const element of elements) {
const betweenTokens = getTokenAndCommentsBetween(prev, element)
paddingType.verify(context, prev, element, betweenTokens)
prev = element
}
}
}
)
}
}

0 comments on commit b394ca6

Please sign in to comment.