Skip to content

Commit

Permalink
Add selfClosingTag option to vue/html-closing-bracket-newline (#2346
Browse files Browse the repository at this point in the history
)

Co-authored-by: Mussin Benarbia <mussin.benarbia@gmail.com>
  • Loading branch information
mussinbenarbia and Mussin Benarbia authored Dec 27, 2023
1 parent 0ac61d9 commit 591c7af
Show file tree
Hide file tree
Showing 3 changed files with 230 additions and 12 deletions.
52 changes: 42 additions & 10 deletions docs/rules/html-closing-bracket-newline.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ title: vue/html-closing-bracket-newline
description: require or disallow a line break before tag's closing brackets
since: v4.1.0
---

# vue/html-closing-bracket-newline

> require or disallow a line break before tag's closing brackets
Expand Down Expand Up @@ -58,19 +59,31 @@ This rule aims to warn the right angle brackets which are at the location other

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

- `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"` (default) ... disallow line breaks before the closing bracket.
- `"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.
- `"always"` (default) ... require one line break before the closing bracket.
- `singleline` (`"never"` by default) ... 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.
- `multiline` (`"always"` by default) ... the configuration for multiline elements. It's a multiline element if the last attribute is not on the same line of the opening bracket.
- `selfClosingTag.singleline` ... the configuration for single-line self closing elements.
- `selfClosingTag.multiline` ... the configuration for multiline self closing elements.

Every option can be set to one of the following values:

- `"always"` ... require one line break before the closing bracket.
- `"never"` ... disallow line breaks before the closing bracket.

If `selfClosingTag` is not specified, the `singleline` and `multiline` options are inherited for self-closing tags.

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

Expand All @@ -95,6 +108,25 @@ Plus, you can use [`vue/html-indent`](./html-indent.md) rule to enforce indent-l

</eslint-code-block>

### `"selfClosingTag": { "multiline": "always" }`

<eslint-code-block fix :rules="{'vue/html-closing-bracket-newline': ['error', { 'selfClosingTag': {'multiline': 'always'} }]}">

```vue
<template>
<!-- ✓ GOOD -->
<MyComponent
:foo="foo"
/>
<!-- ✗ BAD -->
<MyComponent
:foo="foo" />
</template>
```

</eslint-code-block>

## :rocket: Version

This rule was introduced in eslint-plugin-vue v4.1.0
Expand Down
54 changes: 52 additions & 2 deletions lib/rules/html-closing-bracket-newline.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,45 @@ function getPhrase(lineBreaks) {
}
}

/**
* @typedef LineBreakBehavior
* @type {('always'|'never')}
*/

/**
* @typedef LineType
* @type {('singleline'|'multiline')}
*/

/**
* @typedef RuleOptions
* @type {object}
* @property {LineBreakBehavior} singleline - The behavior for single line tags.
* @property {LineBreakBehavior} multiline - The behavior for multiline tags.
* @property {object} selfClosingTag
* @property {LineBreakBehavior} selfClosingTag.singleline - The behavior for single line self closing tags.
* @property {LineBreakBehavior} selfClosingTag.multiline - The behavior for multiline self closing tags.
*/

/**
* @param {VStartTag | VEndTag} node - The node representing a start or end tag.
* @param {RuleOptions} options - The options for line breaks.
* @param {LineType} type - The type of line break.
* @returns {number} - The expected line breaks.
*/
function getExpectedLineBreaks(node, options, type) {
const isSelfClosingTag = node.type === 'VStartTag' && node.selfClosing
if (
isSelfClosingTag &&
options.selfClosingTag &&
options.selfClosingTag[type]
) {
return options.selfClosingTag[type] === 'always' ? 1 : 0
}

return options[type] === 'always' ? 1 : 0
}

module.exports = {
meta: {
type: 'layout',
Expand All @@ -39,7 +78,16 @@ module.exports = {
type: 'object',
properties: {
singleline: { enum: ['always', 'never'] },
multiline: { enum: ['always', 'never'] }
multiline: { enum: ['always', 'never'] },
selfClosingTag: {
type: 'object',
properties: {
singleline: { enum: ['always', 'never'] },
multiline: { enum: ['always', 'never'] }
},
additionalProperties: false,
minProperties: 1
}
},
additionalProperties: false
}
Expand Down Expand Up @@ -80,7 +128,9 @@ module.exports = {
node.loc.start.line === prevToken.loc.end.line
? 'singleline'
: 'multiline'
const expectedLineBreaks = options[type] === 'always' ? 1 : 0

const expectedLineBreaks = getExpectedLineBreaks(node, options, type)

const actualLineBreaks =
closingBracketToken.loc.start.line - prevToken.loc.end.line

Expand Down
136 changes: 136 additions & 0 deletions tests/lib/rules/html-closing-bracket-newline.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,42 @@ tester.run('html-closing-bracket-newline', rule, {
}
]
},
{
code: `
<template>
<MyComp
:foo="foo"
/>
<MyComp :foo="foo" />
</template>
`,
options: [
{
selfClosingTag: {
singleline: 'never',
multiline: 'always'
}
}
]
},
{
code: `
<template>
<MyComp :foo="foo"
/>
<MyComp
:foo="foo" />
</template>
`,
options: [
{
selfClosingTag: {
singleline: 'always',
multiline: 'never'
}
}
]
},

// Ignore if no closing brackets
`
Expand Down Expand Up @@ -479,6 +515,106 @@ tester.run('html-closing-bracket-newline', rule, {
endColumn: 23
}
]
},
{
code: `
<template>
<MyComp
/>
</template>
`,
output: `
<template>
<MyComp/>
</template>
`,
options: [
{
selfClosingTag: {
singleline: 'never',
multiline: 'always'
}
}
],
errors: [
'Expected no line breaks before closing bracket, but 1 line break found.'
]
},
{
code: `
<template>
<MyComp
:foo="foo"/>
</template>
`,
output: `
<template>
<MyComp
:foo="foo"
/>
</template>
`,
options: [
{
selfClosingTag: {
singleline: 'never',
multiline: 'always'
}
}
],
errors: [
'Expected 1 line break before closing bracket, but no line breaks found.'
]
},
{
code: `
<template>
<MyComp :foo="foo"/>
</template>
`,
output: `
<template>
<MyComp :foo="foo"
/>
</template>
`,
options: [
{
selfClosingTag: {
singleline: 'always',
multiline: 'never'
}
}
],
errors: [
'Expected 1 line break before closing bracket, but no line breaks found.'
]
},
{
code: `
<template>
<MyComp
:foo="foo"
/>
</template>
`,
output: `
<template>
<MyComp
:foo="foo"/>
</template>
`,
options: [
{
selfClosingTag: {
singleline: 'always',
multiline: 'never'
}
}
],
errors: [
'Expected no line breaks before closing bracket, but 1 line break found.'
]
}
]
})

0 comments on commit 591c7af

Please sign in to comment.