Skip to content

[New] Add html-closing-bracket-newline (fixes #169) #190

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

Merged
merged 2 commits into from
Jan 2, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,13 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
| | [order-in-components](./docs/rules/order-in-components.md) | enforce order of properties in components |
| | [this-in-template](./docs/rules/this-in-template.md) | enforce usage of `this` in template |


### Uncategorized

| | Rule ID | Description |
|:---|:--------|:------------|
| :wrench: | [html-closing-bracket-newline](./docs/rules/html-closing-bracket-newline.md) | require or disallow a line break before tag's closing brackets |

<!--RULES_TABLE_END-->

## :couple: FAQ
Expand Down
95 changes: 95 additions & 0 deletions docs/rules/html-closing-bracket-newline.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# require or disallow a line break before tag's closing brackets (html-closing-bracket-newline)

- :wrench: The `--fix` option on the [command line](http://eslint.org/docs/user-guide/command-line-interface#fix) can automatically fix some of the problems reported by this rule.

People have own preference about the location of closing brackets.
This rule enforces a line break (or no line break) before tag's closing brackets.

```html
<div
id="foo"
class="bar"> <!-- On the same line with the last attribute. -->
</div>

<div
id="foo"
class="bar"
> <!-- On the next line. -->
</div>
```

## Rule Details

```json
{
"html-closing-bracket-newline": ["error", {
"singleline": "never",
"multiline": "never"
}]
}
```

- `singleline` ... the configuration for single-line elements. It's a single-line element if the element does not have attributes or the last attribute is on the same line as the opening bracket.
- `"never"` ... disallow line breaks before the closing bracket. This is the default.
- `"always"` ... require one line break before the closing bracket.
- `multiline` ... the configuration for multiline elements. It's a multiline element if the last attribute is not on the same line of the opening bracket.
- `"never"` ... disallow line breaks before the closing bracket. This is the default.
- `"always"` ... require one line break before the closing bracket.

Plus, you can use [`vue/html-indent`](./html-indent.md) rule to enforce indent-level of the closing brackets.

:-1: Examples of **incorrect** code for this rule:

```html
/*eslint html-closing-bracket-newline: "error"*/

<div id="foo" class="bar"
>
<div
id="foo"
class="bar"
>
<div
id="foo"
class="bar"
>
```

:+1: Examples of **correct** code for this rule:

```html
/*eslint html-closing-bracket-newline: "error"*/

<div id="foo" class="bar">
<div
id="foo"
class="bar">
```

:-1: Examples of **incorrect** code for `{ "multiline": "always" }`:

```html
/*eslint html-closing-bracket-newline: ["error", { multiline: always }]*/

<div id="foo" class="bar"
>
<div
id="foo"
class="bar">
```

:+1: Examples of **correct** code for `{ "multiline": "always" }`:

```html
/*eslint html-closing-bracket-newline: ["error", { multiline: always }]*/

<div id="foo" class="bar">
<div
id="foo"
class="bar"
>
<div
id="foo"
class="bar"
>
```
92 changes: 92 additions & 0 deletions lib/rules/html-closing-bracket-newline.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/**
* @author Toru Nagashima
* @copyright 2016 Toru Nagashima. All rights reserved.
* See LICENSE file in root directory for full license.
*/
'use strict'

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

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

// ------------------------------------------------------------------------------
// Helpers
// ------------------------------------------------------------------------------

function getPhrase (lineBreaks) {
switch (lineBreaks) {
case 0: return 'no line breaks'
case 1: return '1 line break'
default: return `${lineBreaks} line breaks`
}
}

/**
* Creates AST event handlers for html-closing-bracket-newline.
*
* @param {RuleContext} context - The rule context.
* @returns {object} AST event handlers.
*/
function create (context) {
const options = context.options[0] || {}
const template = context.parserServices.getTemplateBodyTokenStore && context.parserServices.getTemplateBodyTokenStore()

return utils.defineTemplateBodyVisitor(context, {
'VStartTag, VEndTag' (node) {
const closingBracketToken = template.getLastToken(node)
if (closingBracketToken.type !== 'HTMLSelfClosingTagClose' && closingBracketToken.type !== 'HTMLTagClose') {
return
}

const prevToken = template.getTokenBefore(closingBracketToken)
const type = (node.loc.start.line === prevToken.loc.end.line) ? 'singleline' : 'multiline'
const expectedLineBreaks = (options[type] === 'always') ? 1 : 0
const actualLineBreaks = (closingBracketToken.loc.start.line - prevToken.loc.end.line)

if (actualLineBreaks !== expectedLineBreaks) {
context.report({
node,
loc: {
start: prevToken.loc.end,
end: closingBracketToken.loc.start
},
message: 'Expected {{expected}} before closing bracket, but {{actual}} found.',
data: {
expected: getPhrase(expectedLineBreaks),
actual: getPhrase(actualLineBreaks)
},
fix (fixer) {
const range = [prevToken.range[1], closingBracketToken.range[0]]
const text = '\n'.repeat(expectedLineBreaks)
return fixer.replaceTextRange(range, text)
}
})
}
}
})
}

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

module.exports = {
create,
meta: {
docs: {
description: "require or disallow a line break before tag's closing brackets",
category: undefined
},
fixable: 'whitespace',
schema: [{
type: 'object',
properties: {
'singleline': { enum: ['always', 'never'] },
'multiline': { enum: ['always', 'never'] }
},
additionalProperties: false
}]
}
}
Loading