diff --git a/docs/rules/html-closing-bracket-newline.md b/docs/rules/html-closing-bracket-newline.md index ac6c505d8..f47d4ac7b 100644 --- a/docs/rules/html-closing-bracket-newline.md +++ b/docs/rules/html-closing-bracket-newline.md @@ -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 @@ -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. @@ -95,6 +108,25 @@ Plus, you can use [`vue/html-indent`](./html-indent.md) rule to enforce indent-l +### `"selfClosingTag": { "multiline": "always" }` + + + +```vue + +``` + + + ## :rocket: Version This rule was introduced in eslint-plugin-vue v4.1.0 diff --git a/lib/rules/html-closing-bracket-newline.js b/lib/rules/html-closing-bracket-newline.js index c353c35d3..58f7fc8e8 100644 --- a/lib/rules/html-closing-bracket-newline.js +++ b/lib/rules/html-closing-bracket-newline.js @@ -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', @@ -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 } @@ -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 diff --git a/tests/lib/rules/html-closing-bracket-newline.js b/tests/lib/rules/html-closing-bracket-newline.js index 523b2d53a..3a032ba86 100644 --- a/tests/lib/rules/html-closing-bracket-newline.js +++ b/tests/lib/rules/html-closing-bracket-newline.js @@ -116,6 +116,42 @@ tester.run('html-closing-bracket-newline', rule, { } ] }, + { + code: ` + + `, + options: [ + { + selfClosingTag: { + singleline: 'never', + multiline: 'always' + } + } + ] + }, + { + code: ` + + `, + options: [ + { + selfClosingTag: { + singleline: 'always', + multiline: 'never' + } + } + ] + }, // Ignore if no closing brackets ` @@ -479,6 +515,106 @@ tester.run('html-closing-bracket-newline', rule, { endColumn: 23 } ] + }, + { + code: ` + + `, + output: ` + + `, + options: [ + { + selfClosingTag: { + singleline: 'never', + multiline: 'always' + } + } + ], + errors: [ + 'Expected no line breaks before closing bracket, but 1 line break found.' + ] + }, + { + code: ` + + `, + output: ` + + `, + options: [ + { + selfClosingTag: { + singleline: 'never', + multiline: 'always' + } + } + ], + errors: [ + 'Expected 1 line break before closing bracket, but no line breaks found.' + ] + }, + { + code: ` + + `, + output: ` + + `, + options: [ + { + selfClosingTag: { + singleline: 'always', + multiline: 'never' + } + } + ], + errors: [ + 'Expected 1 line break before closing bracket, but no line breaks found.' + ] + }, + { + code: ` + + `, + output: ` + + `, + options: [ + { + selfClosingTag: { + singleline: 'always', + multiline: 'never' + } + } + ], + errors: [ + 'Expected no line breaks before closing bracket, but 1 line break found.' + ] } ] })