From b516c575d3f57bf411de8369b9c753cc9f90e3c4 Mon Sep 17 00:00:00 2001 From: Miquel de Domingo Date: Thu, 10 Oct 2024 17:42:52 +0200 Subject: [PATCH 1/4] feat(html-closing-bracket-new-line): add rule --- README.md | 1 + docs/rules.md | 1 + docs/rules/html-closing-bracket-new-line.md | 52 ++++++ .../src/configs/flat/prettier.ts | 1 + .../src/configs/prettier.ts | 1 + .../eslint-plugin-svelte/src/rule-types.ts | 14 ++ .../rules/html-closing-bracket-new-line.ts | 163 ++++++++++++++++++ .../eslint-plugin-svelte/src/utils/rules.ts | 2 + .../invalid/multiline-never/_config.json | 3 + .../multiline-never/test01-errors.yaml | 8 + .../multiline-never/test01-input.svelte | 10 ++ .../multiline-never/test01-output.svelte | 8 + .../invalid/self-closing/always/_config.json | 3 + .../self-closing/always/test-errors.yaml | 8 + .../self-closing/always/test-input.svelte | 8 + .../self-closing/always/test-output.svelte | 8 + .../invalid/self-closing/never/_config.json | 3 + .../self-closing/never/test-errors.yaml | 8 + .../self-closing/never/test-input.svelte | 8 + .../self-closing/never/test-output.svelte | 5 + .../invalid/singleline-always/_config.json | 3 + .../singleline-always/test01-errors.yaml | 8 + .../singleline-always/test01-input.svelte | 3 + .../singleline-always/test01-output.svelte | 5 + .../invalid/test01-errors.yaml | 12 ++ .../invalid/test01-input.svelte | 11 ++ .../invalid/test01-output.svelte | 8 + .../invalid/test02-errors.yaml | 8 + .../invalid/test02-input.svelte | 11 ++ .../invalid/test02-output.svelte | 8 + .../valid/test01-input.svelte | 4 + .../rules/html-closing-bracket-new-line.ts | 12 ++ 32 files changed, 408 insertions(+) create mode 100644 docs/rules/html-closing-bracket-new-line.md create mode 100644 packages/eslint-plugin-svelte/src/rules/html-closing-bracket-new-line.ts create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/multiline-never/_config.json create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/multiline-never/test01-errors.yaml create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/multiline-never/test01-input.svelte create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/multiline-never/test01-output.svelte create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/self-closing/always/_config.json create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/self-closing/always/test-errors.yaml create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/self-closing/always/test-input.svelte create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/self-closing/always/test-output.svelte create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/self-closing/never/_config.json create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/self-closing/never/test-errors.yaml create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/self-closing/never/test-input.svelte create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/self-closing/never/test-output.svelte create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/singleline-always/_config.json create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/singleline-always/test01-errors.yaml create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/singleline-always/test01-input.svelte create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/singleline-always/test01-output.svelte create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/test01-errors.yaml create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/test01-input.svelte create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/test01-output.svelte create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/test02-errors.yaml create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/test02-input.svelte create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/test02-output.svelte create mode 100644 packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/valid/test01-input.svelte create mode 100644 packages/eslint-plugin-svelte/tests/src/rules/html-closing-bracket-new-line.ts diff --git a/README.md b/README.md index 610a270d6..640cf8e4d 100644 --- a/README.md +++ b/README.md @@ -440,6 +440,7 @@ These rules relate to style guidelines, and are therefore quite subjective: |:--------|:------------|:---| | [svelte/derived-has-same-inputs-outputs](https://sveltejs.github.io/eslint-plugin-svelte/rules/derived-has-same-inputs-outputs/) | derived store should use same variable names between values and callback | | | [svelte/first-attribute-linebreak](https://sveltejs.github.io/eslint-plugin-svelte/rules/first-attribute-linebreak/) | enforce the location of first attribute | :wrench: | +| [svelte/html-closing-bracket-new-line](https://sveltejs.github.io/eslint-plugin-svelte/rules/html-closing-bracket-new-line/) | require or disallow a line break before tag's closing brackets | :wrench: | | [svelte/html-closing-bracket-spacing](https://sveltejs.github.io/eslint-plugin-svelte/rules/html-closing-bracket-spacing/) | require or disallow a space before tag's closing brackets | :wrench: | | [svelte/html-quotes](https://sveltejs.github.io/eslint-plugin-svelte/rules/html-quotes/) | enforce quotes style of HTML attributes | :wrench: | | [svelte/html-self-closing](https://sveltejs.github.io/eslint-plugin-svelte/rules/html-self-closing/) | enforce self-closing style | :wrench: | diff --git a/docs/rules.md b/docs/rules.md index 7f115da5c..9a46d71b8 100644 --- a/docs/rules.md +++ b/docs/rules.md @@ -77,6 +77,7 @@ These rules relate to style guidelines, and are therefore quite subjective: | :------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------- | :------- | | [svelte/derived-has-same-inputs-outputs](./rules/derived-has-same-inputs-outputs.md) | derived store should use same variable names between values and callback | | | [svelte/first-attribute-linebreak](./rules/first-attribute-linebreak.md) | enforce the location of first attribute | :wrench: | +| [svelte/html-closing-bracket-new-line](./rules/html-closing-bracket-new-line.md) | require or disallow a line break before tag's closing brackets | :wrench: | | [svelte/html-closing-bracket-spacing](./rules/html-closing-bracket-spacing.md) | require or disallow a space before tag's closing brackets | :wrench: | | [svelte/html-quotes](./rules/html-quotes.md) | enforce quotes style of HTML attributes | :wrench: | | [svelte/html-self-closing](./rules/html-self-closing.md) | enforce self-closing style | :wrench: | diff --git a/docs/rules/html-closing-bracket-new-line.md b/docs/rules/html-closing-bracket-new-line.md new file mode 100644 index 000000000..4983b8aa6 --- /dev/null +++ b/docs/rules/html-closing-bracket-new-line.md @@ -0,0 +1,52 @@ +--- +pageClass: 'rule-details' +sidebarDepth: 0 +title: 'svelte/html-closing-bracket-new-line' +description: "require or disallow a line break before tag's closing brackets" +--- + +# svelte/html-closing-bracket-new-line + +> require or disallow a line break before tag's closing brackets + +- :exclamation: **_This rule has not been released yet._** +- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule. + +## :book: Rule Details + +This rule reports ???. + + + + + +```svelte + + + + + +``` + + + +## :wrench: Options + +```json +{ + "svelte/brackets-same-line": ["error", {}] +} +``` + +- + +## :books: Further Reading + +- + +## :mag: Implementation + +- [Rule source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/packages/eslint-plugin-svelte/src/rules/html-closing-bracket-new-line.ts) +- [Test source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/packages/eslint-plugin-svelte/tests/src/rules/html-closing-bracket-new-line.ts) diff --git a/packages/eslint-plugin-svelte/src/configs/flat/prettier.ts b/packages/eslint-plugin-svelte/src/configs/flat/prettier.ts index f383837db..a97e410c5 100644 --- a/packages/eslint-plugin-svelte/src/configs/flat/prettier.ts +++ b/packages/eslint-plugin-svelte/src/configs/flat/prettier.ts @@ -10,6 +10,7 @@ const config: Linter.Config[] = [ rules: { // eslint-plugin-svelte rules 'svelte/first-attribute-linebreak': 'off', + 'svelte/html-closing-bracket-new-line': 'off', 'svelte/html-closing-bracket-spacing': 'off', 'svelte/html-quotes': 'off', 'svelte/html-self-closing': 'off', diff --git a/packages/eslint-plugin-svelte/src/configs/prettier.ts b/packages/eslint-plugin-svelte/src/configs/prettier.ts index eb76a588c..6b4faf299 100644 --- a/packages/eslint-plugin-svelte/src/configs/prettier.ts +++ b/packages/eslint-plugin-svelte/src/configs/prettier.ts @@ -10,6 +10,7 @@ const config: Linter.LegacyConfig = { rules: { // eslint-plugin-svelte rules 'svelte/first-attribute-linebreak': 'off', + 'svelte/html-closing-bracket-new-line': 'off', 'svelte/html-closing-bracket-spacing': 'off', 'svelte/html-quotes': 'off', 'svelte/html-self-closing': 'off', diff --git a/packages/eslint-plugin-svelte/src/rule-types.ts b/packages/eslint-plugin-svelte/src/rule-types.ts index c6b683f45..be21c6b93 100644 --- a/packages/eslint-plugin-svelte/src/rule-types.ts +++ b/packages/eslint-plugin-svelte/src/rule-types.ts @@ -54,6 +54,11 @@ export interface RuleOptions { * @see https://sveltejs.github.io/eslint-plugin-svelte/rules/first-attribute-linebreak/ */ 'svelte/first-attribute-linebreak'?: Linter.RuleEntry + /** + * require or disallow a line break before tag's closing brackets + * @see https://sveltejs.github.io/eslint-plugin-svelte/rules/html-closing-bracket-new-line/ + */ + 'svelte/html-closing-bracket-new-line'?: Linter.RuleEntry /** * require or disallow a space before tag's closing brackets * @see https://sveltejs.github.io/eslint-plugin-svelte/rules/html-closing-bracket-spacing/ @@ -361,6 +366,15 @@ type SvelteFirstAttributeLinebreak = []|[{ multiline?: ("below" | "beside") singleline?: ("below" | "beside") }] +// ----- svelte/html-closing-bracket-new-line ----- +type SvelteHtmlClosingBracketNewLine = []|[{ + singleline?: ("always" | "never") + multiline?: ("always" | "never") + selfClosingTag?: { + singleline?: ("always" | "never") + multiline?: ("always" | "never") + } +}] // ----- svelte/html-closing-bracket-spacing ----- type SvelteHtmlClosingBracketSpacing = []|[{ startTag?: ("always" | "never" | "ignore") diff --git a/packages/eslint-plugin-svelte/src/rules/html-closing-bracket-new-line.ts b/packages/eslint-plugin-svelte/src/rules/html-closing-bracket-new-line.ts new file mode 100644 index 000000000..0086b5705 --- /dev/null +++ b/packages/eslint-plugin-svelte/src/rules/html-closing-bracket-new-line.ts @@ -0,0 +1,163 @@ +import type { AST } from 'svelte-eslint-parser'; +import { createRule } from '../utils'; +import { getSourceCode } from '../utils/compat'; +import type { SourceCode } from '../types'; + +type ExpectedNode = AST.SvelteStartTag | AST.SvelteEndTag; +type OptionValue = 'always' | 'never'; +type RuleOptions = { + singleline: OptionValue; + multiline: OptionValue; + selfClosingTag?: Omit; +}; + +function getPhrase(lineBreaks: number) { + switch (lineBreaks) { + case 0: { + return 'no line breaks'; + } + case 1: { + return '1 line break'; + } + default: { + return `${lineBreaks} line breaks`; + } + } +} + +function getExpectedLineBreaks( + node: ExpectedNode, + options: RuleOptions, + type: keyof Omit +) { + const isSelfClosingTag = node.type === 'SvelteStartTag' && node.selfClosing; + if (isSelfClosingTag && options.selfClosingTag && options.selfClosingTag[type]) { + return options.selfClosingTag[type] === 'always' ? 1 : 0; + } + + return options[type] === 'always' ? 1 : 0; +} + +type NodeData = { + actualLineBreaks: number; + expectedLineBreaks: number; + startToken: AST.Token; + endToken: AST.Token; +}; + +function getSelfClosingData( + sourceCode: SourceCode, + node: AST.SvelteStartTag, + options: RuleOptions +): NodeData | null { + const tokens = sourceCode.getTokens(node); + const closingToken = tokens[tokens.length - 2]; + if (closingToken.value !== '/') { + return null; + } + + const prevToken = sourceCode.getTokenBefore(closingToken)!; + const type = node.loc.start.line === prevToken.loc.end.line ? 'singleline' : 'multiline'; + + const expectedLineBreaks = getExpectedLineBreaks(node, options, type); + const actualLineBreaks = closingToken.loc.start.line - prevToken.loc.end.line; + + return { actualLineBreaks, expectedLineBreaks, startToken: prevToken, endToken: closingToken }; +} + +function getNodeData( + sourceCode: SourceCode, + node: ExpectedNode, + options: RuleOptions +): NodeData | null { + const closingToken = sourceCode.getLastToken(node); + if (closingToken.value !== '>') { + return null; + } + + const prevToken = sourceCode.getTokenBefore(closingToken)!; + const type = node.loc.start.line === prevToken.loc.end.line ? 'singleline' : 'multiline'; + + const expectedLineBreaks = getExpectedLineBreaks(node, options, type); + const actualLineBreaks = closingToken.loc.start.line - prevToken.loc.end.line; + + return { actualLineBreaks, expectedLineBreaks, startToken: prevToken, endToken: closingToken }; +} + +export default createRule('html-closing-bracket-new-line', { + meta: { + docs: { + description: "require or disallow a line break before tag's closing brackets", + category: 'Stylistic Issues', + recommended: false, + conflictWithPrettier: true + }, + schema: [ + { + type: 'object', + properties: { + singleline: { 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 + } + ], + messages: { + expectedBeforeClosingBracket: + 'Expected {{expected}} before closing bracket, but {{actual}} found.' + }, + fixable: 'code', + type: 'suggestion' + }, + create(context) { + const options: RuleOptions = context.options[0] ?? {}; + options.singleline ??= 'never'; + options.multiline ??= 'always'; + + const sourceCode = getSourceCode(context); + + return { + 'SvelteStartTag, SvelteEndTag'(node: ExpectedNode) { + const data = + node.type === 'SvelteStartTag' && node.selfClosing + ? getSelfClosingData(sourceCode, node, options) + : getNodeData(sourceCode, node, options); + if (!data) { + return; + } + + const { actualLineBreaks, expectedLineBreaks, startToken, endToken } = data; + if (actualLineBreaks !== expectedLineBreaks) { + // For SvelteEndTag, does not make sense to add a line break, so we only fix if there are extra line breaks + if (node.type === 'SvelteEndTag' && expectedLineBreaks !== 0) { + return; + } + + context.report({ + node, + loc: { start: startToken.loc.end, end: endToken.loc.start }, + messageId: 'expectedBeforeClosingBracket', + data: { + expected: getPhrase(expectedLineBreaks), + actual: getPhrase(actualLineBreaks) + }, + fix(fixer) { + const range: AST.Range = [startToken.range[1], endToken.range[0]]; + const text = '\n'.repeat(expectedLineBreaks); + return fixer.replaceTextRange(range, text); + } + }); + } + } + }; + } +}); diff --git a/packages/eslint-plugin-svelte/src/utils/rules.ts b/packages/eslint-plugin-svelte/src/utils/rules.ts index af0dd20e6..614c3ef6a 100644 --- a/packages/eslint-plugin-svelte/src/utils/rules.ts +++ b/packages/eslint-plugin-svelte/src/utils/rules.ts @@ -10,6 +10,7 @@ import derivedHasSameInputsOutputs from '../rules/derived-has-same-inputs-output import experimentalRequireSlotTypes from '../rules/experimental-require-slot-types'; import experimentalRequireStrictEvents from '../rules/experimental-require-strict-events'; import firstAttributeLinebreak from '../rules/first-attribute-linebreak'; +import htmlClosingBracketNewLine from '../rules/html-closing-bracket-new-line'; import htmlClosingBracketSpacing from '../rules/html-closing-bracket-spacing'; import htmlQuotes from '../rules/html-quotes'; import htmlSelfClosing from '../rules/html-self-closing'; @@ -75,6 +76,7 @@ export const rules = [ experimentalRequireSlotTypes, experimentalRequireStrictEvents, firstAttributeLinebreak, + htmlClosingBracketNewLine, htmlClosingBracketSpacing, htmlQuotes, htmlSelfClosing, diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/multiline-never/_config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/multiline-never/_config.json new file mode 100644 index 000000000..1c8db1860 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/multiline-never/_config.json @@ -0,0 +1,3 @@ +{ + "options": [{ "multiline": "never" }] +} diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/multiline-never/test01-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/multiline-never/test01-errors.yaml new file mode 100644 index 000000000..fc094d043 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/multiline-never/test01-errors.yaml @@ -0,0 +1,8 @@ +- message: Expected no line breaks before closing bracket, but 1 line break found. + line: 2 + column: 12 + suggestions: null +- message: Expected no line breaks before closing bracket, but 1 line break found. + line: 7 + column: 12 + suggestions: null diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/multiline-never/test01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/multiline-never/test01-input.svelte new file mode 100644 index 000000000..e3e5b5e1f --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/multiline-never/test01-input.svelte @@ -0,0 +1,10 @@ +
+
+
+Children +
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/multiline-never/test01-output.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/multiline-never/test01-output.svelte new file mode 100644 index 000000000..40b9c843e --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/multiline-never/test01-output.svelte @@ -0,0 +1,8 @@ +
+
+
+Children +
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/self-closing/always/_config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/self-closing/always/_config.json new file mode 100644 index 000000000..0d0e60c52 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/self-closing/always/_config.json @@ -0,0 +1,3 @@ +{ + "options": [{ "selfClosingTag": { "singleline": "always", "multiline": "always" } }] +} diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/self-closing/always/test-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/self-closing/always/test-errors.yaml new file mode 100644 index 000000000..1a10165e6 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/self-closing/always/test-errors.yaml @@ -0,0 +1,8 @@ +- message: Expected 1 line break before closing bracket, but no line breaks found. + line: 1 + column: 18 + suggestions: null +- message: Expected 1 line break before closing bracket, but 2 line breaks found. + line: 6 + column: 12 + suggestions: null diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/self-closing/always/test-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/self-closing/always/test-input.svelte new file mode 100644 index 000000000..b150ca599 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/self-closing/always/test-input.svelte @@ -0,0 +1,8 @@ + + + diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/self-closing/always/test-output.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/self-closing/always/test-output.svelte new file mode 100644 index 000000000..f39f85537 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/self-closing/always/test-output.svelte @@ -0,0 +1,8 @@ + + + diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/self-closing/never/_config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/self-closing/never/_config.json new file mode 100644 index 000000000..127cc8b7c --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/self-closing/never/_config.json @@ -0,0 +1,3 @@ +{ + "options": [{ "selfClosingTag": { "singleline": "never", "multiline": "never" } }] +} diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/self-closing/never/test-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/self-closing/never/test-errors.yaml new file mode 100644 index 000000000..a124b0cf2 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/self-closing/never/test-errors.yaml @@ -0,0 +1,8 @@ +- message: Expected no line breaks before closing bracket, but 1 line break found. + line: 3 + column: 12 + suggestions: null +- message: Expected no line breaks before closing bracket, but 2 line breaks found. + line: 6 + column: 12 + suggestions: null diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/self-closing/never/test-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/self-closing/never/test-input.svelte new file mode 100644 index 000000000..b150ca599 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/self-closing/never/test-input.svelte @@ -0,0 +1,8 @@ + + + diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/self-closing/never/test-output.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/self-closing/never/test-output.svelte new file mode 100644 index 000000000..580b4158f --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/self-closing/never/test-output.svelte @@ -0,0 +1,5 @@ + + + diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/singleline-always/_config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/singleline-always/_config.json new file mode 100644 index 000000000..74ea9fd71 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/singleline-always/_config.json @@ -0,0 +1,3 @@ +{ + "options": [{ "singleline": "always" }] +} diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/singleline-always/test01-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/singleline-always/test01-errors.yaml new file mode 100644 index 000000000..e7b7863fc --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/singleline-always/test01-errors.yaml @@ -0,0 +1,8 @@ +- message: Expected 1 line break before closing bracket, but no line breaks found. + line: 1 + column: 5 + suggestions: null +- message: Expected 1 line break before closing bracket, but no line breaks found. + line: 2 + column: 5 + suggestions: null diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/singleline-always/test01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/singleline-always/test01-input.svelte new file mode 100644 index 000000000..2cf49756b --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/singleline-always/test01-input.svelte @@ -0,0 +1,3 @@ +
+
+
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/singleline-always/test01-output.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/singleline-always/test01-output.svelte new file mode 100644 index 000000000..da7c82492 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/singleline-always/test01-output.svelte @@ -0,0 +1,5 @@ +
+
+
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/test01-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/test01-errors.yaml new file mode 100644 index 000000000..548d3143f --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/test01-errors.yaml @@ -0,0 +1,12 @@ +- message: Expected no line breaks before closing bracket, but 3 line breaks found. + line: 3 + column: 8 + suggestions: null +- message: Expected no line breaks before closing bracket, but 1 line break found. + line: 8 + column: 5 + suggestions: null +- message: Expected 1 line break before closing bracket, but no line breaks found. + line: 11 + column: 14 + suggestions: null diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/test01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/test01-input.svelte new file mode 100644 index 000000000..fb10f848f --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/test01-input.svelte @@ -0,0 +1,11 @@ +
+
+
+
+
+
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/test01-output.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/test01-output.svelte new file mode 100644 index 000000000..45cdbc87d --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/test01-output.svelte @@ -0,0 +1,8 @@ +
+
+
+
+
+
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/test02-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/test02-errors.yaml new file mode 100644 index 000000000..354b3d192 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/test02-errors.yaml @@ -0,0 +1,8 @@ +- message: Expected 1 line break before closing bracket, but 2 line breaks found. + line: 3 + column: 14 + suggestions: null +- message: Expected no line breaks before closing bracket, but 2 line breaks found. + line: 7 + column: 5 + suggestions: null diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/test02-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/test02-input.svelte new file mode 100644 index 000000000..ab050693c --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/test02-input.svelte @@ -0,0 +1,11 @@ + + + +
+ +
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/test02-output.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/test02-output.svelte new file mode 100644 index 000000000..f145cb7e2 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/invalid/test02-output.svelte @@ -0,0 +1,8 @@ + + + +
+ +
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/valid/test01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/valid/test01-input.svelte new file mode 100644 index 000000000..aa2be93c0 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/html-closing-bracket-new-line/valid/test01-input.svelte @@ -0,0 +1,4 @@ +
+ diff --git a/packages/eslint-plugin-svelte/tests/src/rules/html-closing-bracket-new-line.ts b/packages/eslint-plugin-svelte/tests/src/rules/html-closing-bracket-new-line.ts new file mode 100644 index 000000000..dfeba140c --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/src/rules/html-closing-bracket-new-line.ts @@ -0,0 +1,12 @@ +import { RuleTester } from '../../utils/eslint-compat'; +import rule from '../../../src/rules/html-closing-bracket-new-line'; +import { loadTestCases } from '../../utils/utils'; + +const tester = new RuleTester({ + languageOptions: { + ecmaVersion: 2020, + sourceType: 'module' + } +}); + +tester.run('html-closing-bracket-new-line', rule as any, loadTestCases('html-closing-bracket-new-line')); From 8e5baca71126f015eefcd4b678bb0a5f080a84f3 Mon Sep 17 00:00:00 2001 From: Miquel de Domingo Date: Thu, 10 Oct 2024 17:48:43 +0200 Subject: [PATCH 2/4] docs(html-closing-bracket-new-line): add docs --- docs/rules/html-closing-bracket-new-line.md | 43 ++++++++++++++++--- .../rules/html-closing-bracket-new-line.ts | 2 +- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/docs/rules/html-closing-bracket-new-line.md b/docs/rules/html-closing-bracket-new-line.md index 4983b8aa6..89e9d2757 100644 --- a/docs/rules/html-closing-bracket-new-line.md +++ b/docs/rules/html-closing-bracket-new-line.md @@ -14,7 +14,7 @@ description: "require or disallow a line break before tag's closing brackets" ## :book: Rule Details -This rule reports ???. +This rule enforces a line break (or no line break) before tag's closing brackets, which can also be configured to be enforced on self-closing tags. @@ -26,8 +26,31 @@ This rule reports ???. +
+
+ Children +
+ + + + +
+
+ Children +
+ + + ```
@@ -36,15 +59,23 @@ This rule reports ???. ```json { - "svelte/brackets-same-line": ["error", {}] + "svelte/brackets-same-line": ["error", { + "singleline": "never", // ["never", "always"] + "multiline": "always", // ["never", "always"] + "selfClosingTag": { + "singleline": "never", // ["never", "always"] + "multiline": "always" // ["never", "always"] + } + }] } ``` -- - -## :books: Further Reading +- `singleline`: (`"never"` by default) 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) Configuration for multi-line elements. It's a multi-line element if the last attribute is not on the same line of the opening bracket. +- `selfClosingTag.singleline`: Configuration for single-line self closing elements. +- `selfClosingTag.multiline`: Configuration for multi-line self closing elements. -- +The `selfClosing` is optional, and by default it will use the same configuration as `singleline` and `multiline`, respectively. ## :mag: Implementation diff --git a/packages/eslint-plugin-svelte/src/rules/html-closing-bracket-new-line.ts b/packages/eslint-plugin-svelte/src/rules/html-closing-bracket-new-line.ts index 0086b5705..20902d7ed 100644 --- a/packages/eslint-plugin-svelte/src/rules/html-closing-bracket-new-line.ts +++ b/packages/eslint-plugin-svelte/src/rules/html-closing-bracket-new-line.ts @@ -87,7 +87,7 @@ function getNodeData( export default createRule('html-closing-bracket-new-line', { meta: { docs: { - description: "require or disallow a line break before tag's closing brackets", + description: "Require or disallow a line break before tag's closing brackets", category: 'Stylistic Issues', recommended: false, conflictWithPrettier: true From e40f893a874dfc593db8ed2839a1278767dfc904 Mon Sep 17 00:00:00 2001 From: Miquel de Domingo Date: Thu, 10 Oct 2024 17:51:43 +0200 Subject: [PATCH 3/4] fix(html-closing-bracket-newline): use `jsonc` over `json` --- docs/rules/html-closing-bracket-new-line.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rules/html-closing-bracket-new-line.md b/docs/rules/html-closing-bracket-new-line.md index 89e9d2757..19f16d2ba 100644 --- a/docs/rules/html-closing-bracket-new-line.md +++ b/docs/rules/html-closing-bracket-new-line.md @@ -57,7 +57,7 @@ This rule enforces a line break (or no line break) before tag's closing brackets ## :wrench: Options -```json +```jsonc { "svelte/brackets-same-line": ["error", { "singleline": "never", // ["never", "always"] From 157d6be2890607bb509543d9517c2545e11438af Mon Sep 17 00:00:00 2001 From: Miquel de Domingo Date: Thu, 10 Oct 2024 17:56:19 +0200 Subject: [PATCH 4/4] fix(html-closing-bracket-newline): run `pnpm run update` --- README.md | 2 +- docs/rules.md | 2 +- docs/rules/html-closing-bracket-new-line.md | 26 ++++++++++++------- .../eslint-plugin-svelte/src/rule-types.ts | 2 +- 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 640cf8e4d..cef48dc14 100644 --- a/README.md +++ b/README.md @@ -440,7 +440,7 @@ These rules relate to style guidelines, and are therefore quite subjective: |:--------|:------------|:---| | [svelte/derived-has-same-inputs-outputs](https://sveltejs.github.io/eslint-plugin-svelte/rules/derived-has-same-inputs-outputs/) | derived store should use same variable names between values and callback | | | [svelte/first-attribute-linebreak](https://sveltejs.github.io/eslint-plugin-svelte/rules/first-attribute-linebreak/) | enforce the location of first attribute | :wrench: | -| [svelte/html-closing-bracket-new-line](https://sveltejs.github.io/eslint-plugin-svelte/rules/html-closing-bracket-new-line/) | require or disallow a line break before tag's closing brackets | :wrench: | +| [svelte/html-closing-bracket-new-line](https://sveltejs.github.io/eslint-plugin-svelte/rules/html-closing-bracket-new-line/) | Require or disallow a line break before tag's closing brackets | :wrench: | | [svelte/html-closing-bracket-spacing](https://sveltejs.github.io/eslint-plugin-svelte/rules/html-closing-bracket-spacing/) | require or disallow a space before tag's closing brackets | :wrench: | | [svelte/html-quotes](https://sveltejs.github.io/eslint-plugin-svelte/rules/html-quotes/) | enforce quotes style of HTML attributes | :wrench: | | [svelte/html-self-closing](https://sveltejs.github.io/eslint-plugin-svelte/rules/html-self-closing/) | enforce self-closing style | :wrench: | diff --git a/docs/rules.md b/docs/rules.md index 9a46d71b8..57d224ff6 100644 --- a/docs/rules.md +++ b/docs/rules.md @@ -77,7 +77,7 @@ These rules relate to style guidelines, and are therefore quite subjective: | :------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------- | :------- | | [svelte/derived-has-same-inputs-outputs](./rules/derived-has-same-inputs-outputs.md) | derived store should use same variable names between values and callback | | | [svelte/first-attribute-linebreak](./rules/first-attribute-linebreak.md) | enforce the location of first attribute | :wrench: | -| [svelte/html-closing-bracket-new-line](./rules/html-closing-bracket-new-line.md) | require or disallow a line break before tag's closing brackets | :wrench: | +| [svelte/html-closing-bracket-new-line](./rules/html-closing-bracket-new-line.md) | Require or disallow a line break before tag's closing brackets | :wrench: | | [svelte/html-closing-bracket-spacing](./rules/html-closing-bracket-spacing.md) | require or disallow a space before tag's closing brackets | :wrench: | | [svelte/html-quotes](./rules/html-quotes.md) | enforce quotes style of HTML attributes | :wrench: | | [svelte/html-self-closing](./rules/html-self-closing.md) | enforce self-closing style | :wrench: | diff --git a/docs/rules/html-closing-bracket-new-line.md b/docs/rules/html-closing-bracket-new-line.md index 19f16d2ba..39ed84ed4 100644 --- a/docs/rules/html-closing-bracket-new-line.md +++ b/docs/rules/html-closing-bracket-new-line.md @@ -2,12 +2,12 @@ pageClass: 'rule-details' sidebarDepth: 0 title: 'svelte/html-closing-bracket-new-line' -description: "require or disallow a line break before tag's closing brackets" +description: "Require or disallow a line break before tag's closing brackets" --- # svelte/html-closing-bracket-new-line -> require or disallow a line break before tag's closing brackets +> Require or disallow a line break before tag's closing brackets - :exclamation: **_This rule has not been released yet._** - :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule. @@ -18,6 +18,7 @@ This rule enforces a line break (or no line break) before tag's closing brackets + ```svelte @@ -42,7 +43,7 @@ This rule enforces a line break (or no line break) before tag's closing brackets
-
Children
@@ -50,23 +51,28 @@ This rule enforces a line break (or no line break) before tag's closing brackets + multiline/> ``` + +
## :wrench: Options ```jsonc { - "svelte/brackets-same-line": ["error", { - "singleline": "never", // ["never", "always"] - "multiline": "always", // ["never", "always"] - "selfClosingTag": { + "svelte/brackets-same-line": [ + "error", + { "singleline": "never", // ["never", "always"] - "multiline": "always" // ["never", "always"] + "multiline": "always", // ["never", "always"] + "selfClosingTag": { + "singleline": "never", // ["never", "always"] + "multiline": "always" // ["never", "always"] + } } - }] + ] } ``` diff --git a/packages/eslint-plugin-svelte/src/rule-types.ts b/packages/eslint-plugin-svelte/src/rule-types.ts index be21c6b93..01901d52b 100644 --- a/packages/eslint-plugin-svelte/src/rule-types.ts +++ b/packages/eslint-plugin-svelte/src/rule-types.ts @@ -55,7 +55,7 @@ export interface RuleOptions { */ 'svelte/first-attribute-linebreak'?: Linter.RuleEntry /** - * require or disallow a line break before tag's closing brackets + * Require or disallow a line break before tag's closing brackets * @see https://sveltejs.github.io/eslint-plugin-svelte/rules/html-closing-bracket-new-line/ */ 'svelte/html-closing-bracket-new-line'?: Linter.RuleEntry