Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 591c7af

Browse filesBrowse files
mussinbenarbiaMussin Benarbia
and
Mussin Benarbia
authored
Add selfClosingTag option to vue/html-closing-bracket-newline (#2346)
Co-authored-by: Mussin Benarbia <mussin.benarbia@gmail.com>
1 parent 0ac61d9 commit 591c7af
Copy full SHA for 591c7af

File tree

3 files changed

+230
-12
lines changed
Filter options

3 files changed

+230
-12
lines changed

‎docs/rules/html-closing-bracket-newline.md

Copy file name to clipboardExpand all lines: docs/rules/html-closing-bracket-newline.md
+42-10Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ title: vue/html-closing-bracket-newline
55
description: require or disallow a line break before tag's closing brackets
66
since: v4.1.0
77
---
8+
89
# vue/html-closing-bracket-newline
910

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

5960
```json
6061
{
61-
"vue/html-closing-bracket-newline": ["error", {
62-
"singleline": "never",
63-
"multiline": "always"
64-
}]
62+
"vue/html-closing-bracket-newline": [
63+
"error",
64+
{
65+
"singleline": "never",
66+
"multiline": "always",
67+
"selfClosingTag": {
68+
"singleline": "never",
69+
"multiline": "always"
70+
}
71+
}
72+
]
6573
}
6674
```
6775

68-
- `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.
69-
- `"never"` (default) ... disallow line breaks before the closing bracket.
70-
- `"always"` ... require one line break before the closing bracket.
71-
- `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.
72-
- `"never"` ... disallow line breaks before the closing bracket.
73-
- `"always"` (default) ... require one line break before the closing bracket.
76+
- `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.
77+
- `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.
78+
- `selfClosingTag.singleline` ... the configuration for single-line self closing elements.
79+
- `selfClosingTag.multiline` ... the configuration for multiline self closing elements.
80+
81+
Every option can be set to one of the following values:
82+
83+
- `"always"` ... require one line break before the closing bracket.
84+
- `"never"` ... disallow line breaks before the closing bracket.
85+
86+
If `selfClosingTag` is not specified, the `singleline` and `multiline` options are inherited for self-closing tags.
7487

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

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

96109
</eslint-code-block>
97110

111+
### `"selfClosingTag": { "multiline": "always" }`
112+
113+
<eslint-code-block fix :rules="{'vue/html-closing-bracket-newline': ['error', { 'selfClosingTag': {'multiline': 'always'} }]}">
114+
115+
```vue
116+
<template>
117+
<!-- ✓ GOOD -->
118+
<MyComponent
119+
:foo="foo"
120+
/>
121+
122+
<!-- ✗ BAD -->
123+
<MyComponent
124+
:foo="foo" />
125+
</template>
126+
```
127+
128+
</eslint-code-block>
129+
98130
## :rocket: Version
99131

100132
This rule was introduced in eslint-plugin-vue v4.1.0

‎lib/rules/html-closing-bracket-newline.js

Copy file name to clipboardExpand all lines: lib/rules/html-closing-bracket-newline.js
+52-2Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,45 @@ function getPhrase(lineBreaks) {
2424
}
2525
}
2626

27+
/**
28+
* @typedef LineBreakBehavior
29+
* @type {('always'|'never')}
30+
*/
31+
32+
/**
33+
* @typedef LineType
34+
* @type {('singleline'|'multiline')}
35+
*/
36+
37+
/**
38+
* @typedef RuleOptions
39+
* @type {object}
40+
* @property {LineBreakBehavior} singleline - The behavior for single line tags.
41+
* @property {LineBreakBehavior} multiline - The behavior for multiline tags.
42+
* @property {object} selfClosingTag
43+
* @property {LineBreakBehavior} selfClosingTag.singleline - The behavior for single line self closing tags.
44+
* @property {LineBreakBehavior} selfClosingTag.multiline - The behavior for multiline self closing tags.
45+
*/
46+
47+
/**
48+
* @param {VStartTag | VEndTag} node - The node representing a start or end tag.
49+
* @param {RuleOptions} options - The options for line breaks.
50+
* @param {LineType} type - The type of line break.
51+
* @returns {number} - The expected line breaks.
52+
*/
53+
function getExpectedLineBreaks(node, options, type) {
54+
const isSelfClosingTag = node.type === 'VStartTag' && node.selfClosing
55+
if (
56+
isSelfClosingTag &&
57+
options.selfClosingTag &&
58+
options.selfClosingTag[type]
59+
) {
60+
return options.selfClosingTag[type] === 'always' ? 1 : 0
61+
}
62+
63+
return options[type] === 'always' ? 1 : 0
64+
}
65+
2766
module.exports = {
2867
meta: {
2968
type: 'layout',
@@ -39,7 +78,16 @@ module.exports = {
3978
type: 'object',
4079
properties: {
4180
singleline: { enum: ['always', 'never'] },
42-
multiline: { enum: ['always', 'never'] }
81+
multiline: { enum: ['always', 'never'] },
82+
selfClosingTag: {
83+
type: 'object',
84+
properties: {
85+
singleline: { enum: ['always', 'never'] },
86+
multiline: { enum: ['always', 'never'] }
87+
},
88+
additionalProperties: false,
89+
minProperties: 1
90+
}
4391
},
4492
additionalProperties: false
4593
}
@@ -80,7 +128,9 @@ module.exports = {
80128
node.loc.start.line === prevToken.loc.end.line
81129
? 'singleline'
82130
: 'multiline'
83-
const expectedLineBreaks = options[type] === 'always' ? 1 : 0
131+
132+
const expectedLineBreaks = getExpectedLineBreaks(node, options, type)
133+
84134
const actualLineBreaks =
85135
closingBracketToken.loc.start.line - prevToken.loc.end.line
86136

‎tests/lib/rules/html-closing-bracket-newline.js

Copy file name to clipboardExpand all lines: tests/lib/rules/html-closing-bracket-newline.js
+136Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,42 @@ tester.run('html-closing-bracket-newline', rule, {
116116
}
117117
]
118118
},
119+
{
120+
code: `
121+
<template>
122+
<MyComp
123+
:foo="foo"
124+
/>
125+
<MyComp :foo="foo" />
126+
</template>
127+
`,
128+
options: [
129+
{
130+
selfClosingTag: {
131+
singleline: 'never',
132+
multiline: 'always'
133+
}
134+
}
135+
]
136+
},
137+
{
138+
code: `
139+
<template>
140+
<MyComp :foo="foo"
141+
/>
142+
<MyComp
143+
:foo="foo" />
144+
</template>
145+
`,
146+
options: [
147+
{
148+
selfClosingTag: {
149+
singleline: 'always',
150+
multiline: 'never'
151+
}
152+
}
153+
]
154+
},
119155

120156
// Ignore if no closing brackets
121157
`
@@ -479,6 +515,106 @@ tester.run('html-closing-bracket-newline', rule, {
479515
endColumn: 23
480516
}
481517
]
518+
},
519+
{
520+
code: `
521+
<template>
522+
<MyComp
523+
/>
524+
</template>
525+
`,
526+
output: `
527+
<template>
528+
<MyComp/>
529+
</template>
530+
`,
531+
options: [
532+
{
533+
selfClosingTag: {
534+
singleline: 'never',
535+
multiline: 'always'
536+
}
537+
}
538+
],
539+
errors: [
540+
'Expected no line breaks before closing bracket, but 1 line break found.'
541+
]
542+
},
543+
{
544+
code: `
545+
<template>
546+
<MyComp
547+
:foo="foo"/>
548+
</template>
549+
`,
550+
output: `
551+
<template>
552+
<MyComp
553+
:foo="foo"
554+
/>
555+
</template>
556+
`,
557+
options: [
558+
{
559+
selfClosingTag: {
560+
singleline: 'never',
561+
multiline: 'always'
562+
}
563+
}
564+
],
565+
errors: [
566+
'Expected 1 line break before closing bracket, but no line breaks found.'
567+
]
568+
},
569+
{
570+
code: `
571+
<template>
572+
<MyComp :foo="foo"/>
573+
</template>
574+
`,
575+
output: `
576+
<template>
577+
<MyComp :foo="foo"
578+
/>
579+
</template>
580+
`,
581+
options: [
582+
{
583+
selfClosingTag: {
584+
singleline: 'always',
585+
multiline: 'never'
586+
}
587+
}
588+
],
589+
errors: [
590+
'Expected 1 line break before closing bracket, but no line breaks found.'
591+
]
592+
},
593+
{
594+
code: `
595+
<template>
596+
<MyComp
597+
:foo="foo"
598+
/>
599+
</template>
600+
`,
601+
output: `
602+
<template>
603+
<MyComp
604+
:foo="foo"/>
605+
</template>
606+
`,
607+
options: [
608+
{
609+
selfClosingTag: {
610+
singleline: 'always',
611+
multiline: 'never'
612+
}
613+
}
614+
],
615+
errors: [
616+
'Expected no line breaks before closing bracket, but 1 line break found.'
617+
]
482618
}
483619
]
484620
})

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.