Skip to content
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

Add selfClosingTag option to vue/html-closing-bracket-newline #2346

Merged
merged 8 commits into from
Dec 27, 2023
Merged
Show file tree
Hide file tree
Changes from 7 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
50 changes: 40 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,29 @@ 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.

Plus, you can use [`vue/html-indent`](./html-indent.md) rule to enforce indent-level of the closing brackets.
FloEdelmann marked this conversation as resolved.
Show resolved Hide resolved

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

</eslint-code-block>

### `"selfClosingTag": { "multiline": "always"}`
FloEdelmann marked this conversation as resolved.
Show resolved Hide resolved

<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) {
FloEdelmann marked this conversation as resolved.
Show resolved Hide resolved
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.'
]
}
]
})