From 8441eee80ae78e109763d3cfc7451d68eb2ef53f Mon Sep 17 00:00:00 2001 From: Mussin Benarbia Date: Tue, 28 Nov 2023 07:12:13 -0800 Subject: [PATCH 1/7] feat: handle self closing tags in html-closing-bracket-newline --- lib/rules/html-closing-bracket-newline.js | 23 ++- .../lib/rules/html-closing-bracket-newline.js | 133 ++++++++++++++++++ 2 files changed, 154 insertions(+), 2 deletions(-) diff --git a/lib/rules/html-closing-bracket-newline.js b/lib/rules/html-closing-bracket-newline.js index e1a746e42..c3f1ba5d6 100644 --- a/lib/rules/html-closing-bracket-newline.js +++ b/lib/rules/html-closing-bracket-newline.js @@ -24,6 +24,19 @@ function getPhrase(lineBreaks) { } } +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 +52,11 @@ module.exports = { type: 'object', properties: { singleline: { enum: ['always', 'never'] }, - multiline: { enum: ['always', 'never'] } + multiline: { enum: ['always', 'never'] }, + selfClosingTag: { + singleline: { enum: ['always', 'never'] }, + multiline: { enum: ['always', 'never'] } + } }, additionalProperties: false } @@ -79,7 +96,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..46efd82c3 100644 --- a/tests/lib/rules/html-closing-bracket-newline.js +++ b/tests/lib/rules/html-closing-bracket-newline.js @@ -116,6 +116,39 @@ 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 +512,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.' + ] } ] }) From 26104c9328731eb16f4c1865cda15f82886765dc Mon Sep 17 00:00:00 2001 From: Mussin Benarbia Date: Wed, 6 Dec 2023 21:03:09 +0900 Subject: [PATCH 2/7] fix: wrong schema format --- lib/rules/html-closing-bracket-newline.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/rules/html-closing-bracket-newline.js b/lib/rules/html-closing-bracket-newline.js index 25fc4366a..9954dde85 100644 --- a/lib/rules/html-closing-bracket-newline.js +++ b/lib/rules/html-closing-bracket-newline.js @@ -54,8 +54,13 @@ module.exports = { singleline: { enum: ['always', 'never'] }, multiline: { enum: ['always', 'never'] }, selfClosingTag: { - singleline: { enum: ['always', 'never'] }, - multiline: { enum: ['always', 'never'] } + type: 'object', + properties: { + singleline: { enum: ['always', 'never'] }, + multiline: { enum: ['always', 'never'] } + }, + additionalProperties: false, + minProperties: 1 } }, additionalProperties: false From 58f17d6c99f6620e12a4506f35ecaffc5074bb75 Mon Sep 17 00:00:00 2001 From: Mussin Benarbia Date: Wed, 6 Dec 2023 21:03:43 +0900 Subject: [PATCH 3/7] refactor: document helper function types with JSDoc --- lib/rules/html-closing-bracket-newline.js | 27 +++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/lib/rules/html-closing-bracket-newline.js b/lib/rules/html-closing-bracket-newline.js index 9954dde85..8547fc810 100644 --- a/lib/rules/html-closing-bracket-newline.js +++ b/lib/rules/html-closing-bracket-newline.js @@ -24,6 +24,33 @@ 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 ( From 03e5674846a50312266ae612262cede0ccb673e5 Mon Sep 17 00:00:00 2001 From: Mussin Benarbia Date: Thu, 7 Dec 2023 21:34:23 +0900 Subject: [PATCH 4/7] style: remove newline between JSDoc and function --- lib/rules/html-closing-bracket-newline.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/rules/html-closing-bracket-newline.js b/lib/rules/html-closing-bracket-newline.js index 8547fc810..58f7fc8e8 100644 --- a/lib/rules/html-closing-bracket-newline.js +++ b/lib/rules/html-closing-bracket-newline.js @@ -50,7 +50,6 @@ function getPhrase(lineBreaks) { * @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 ( From 0a30ce877248ee3e873fe8074c3765a7f0d6573d Mon Sep 17 00:00:00 2001 From: Mussin Benarbia Date: Thu, 7 Dec 2023 21:37:06 +0900 Subject: [PATCH 5/7] test: improve test cases --- tests/lib/rules/html-closing-bracket-newline.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/lib/rules/html-closing-bracket-newline.js b/tests/lib/rules/html-closing-bracket-newline.js index 46efd82c3..3a032ba86 100644 --- a/tests/lib/rules/html-closing-bracket-newline.js +++ b/tests/lib/rules/html-closing-bracket-newline.js @@ -122,6 +122,7 @@ tester.run('html-closing-bracket-newline', rule, { + `, options: [ @@ -138,6 +139,8 @@ tester.run('html-closing-bracket-newline', rule, { `, options: [ From fe00ed691000103dd44ec2e5a018f052ce8df940 Mon Sep 17 00:00:00 2001 From: Mussin Benarbia Date: Thu, 7 Dec 2023 22:56:13 +0900 Subject: [PATCH 6/7] docs: update documentation to reflect new option --- docs/rules/html-closing-bracket-newline.md | 50 +++++++++++++++++----- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/docs/rules/html-closing-bracket-newline.md b/docs/rules/html-closing-bracket-newline.md index ac6c505d8..0cdaecaed 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,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. @@ -95,6 +106,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 From a9ee9906bffe5c3da35890ba61f9b52bcdd95616 Mon Sep 17 00:00:00 2001 From: Mussin Benarbia Date: Thu, 7 Dec 2023 23:36:15 +0900 Subject: [PATCH 7/7] docs: improve clarity of option This commit also adds a missing whitespace --- docs/rules/html-closing-bracket-newline.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/rules/html-closing-bracket-newline.md b/docs/rules/html-closing-bracket-newline.md index 0cdaecaed..f47d4ac7b 100644 --- a/docs/rules/html-closing-bracket-newline.md +++ b/docs/rules/html-closing-bracket-newline.md @@ -83,6 +83,8 @@ 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. ### `"multiline": "never"` @@ -106,7 +108,7 @@ Plus, you can use [`vue/html-indent`](./html-indent.md) rule to enforce indent-l -### `"selfClosingTag": { "multiline": "always"}` +### `"selfClosingTag": { "multiline": "always" }`