diff --git a/docs/content/rules/sort-union-types.mdx b/docs/content/rules/sort-union-types.mdx index afa3a6df..e057dc31 100644 --- a/docs/content/rules/sort-union-types.mdx +++ b/docs/content/rules/sort-union-types.mdx @@ -165,6 +165,18 @@ type CarBrand = Each group of union types (separated by empty lines) is treated independently, and the order within each group is preserved. +### newlinesBetween + +default: `'ignore'` + +Specifies how new lines should be handled between union type groups. + +- `ignore` — Do not report errors related to new lines between union type groups. +- `always` — Enforce one new line between each group, and forbid new lines inside a group. +- `never` — No new lines are allowed in union types. + +This options is only applicable when `partitionByNewLine` is `false`. + ### groups @@ -314,8 +326,9 @@ Determines the matcher used for patterns in the `partitionByComment` option. order: 'asc', ignoreCase: true, specialCharacters: 'keep', - partitionByNewLine: false, partitionByComment: false, + partitionByNewLine: false, + newlinesBetween: 'ignore', matcher: 'minimatch', groups: [], }, @@ -342,8 +355,9 @@ Determines the matcher used for patterns in the `partitionByComment` option. order: 'asc', ignoreCase: true, specialCharacters: 'keep', - partitionByNewLine: false, partitionByComment: false, + partitionByNewLine: false, + newlinesBetween: 'ignore', matcher: 'minimatch', groups: [], }, diff --git a/rules/sort-union-types.ts b/rules/sort-union-types.ts index 4532526e..c79feaa1 100644 --- a/rules/sort-union-types.ts +++ b/rules/sort-union-types.ts @@ -1,9 +1,12 @@ import type { SortingNode } from '../typings' +import { validateNewlinesAndPartitionConfiguration } from '../utils/validate-newlines-and-partition-configuration' import { validateGroupsConfiguration } from '../utils/validate-groups-configuration' import { hasPartitionComment } from '../utils/is-partition-comment' import { sortNodesByGroups } from '../utils/sort-nodes-by-groups' import { getCommentsBefore } from '../utils/get-comments-before' +import { makeNewlinesFixes } from '../utils/make-newlines-fixes' +import { getNewlinesErrors } from '../utils/get-newlines-errors' import { createEslintRule } from '../utils/create-eslint-rule' import { getLinesBetween } from '../utils/get-lines-between' import { getGroupNumber } from '../utils/get-group-number' @@ -16,7 +19,11 @@ import { makeFixes } from '../utils/make-fixes' import { complete } from '../utils/complete' import { pairwise } from '../utils/pairwise' -type MESSAGE_ID = 'unexpectedUnionTypesGroupOrder' | 'unexpectedUnionTypesOrder' +type MESSAGE_ID = + | 'missedSpacingBetweenUnionTypes' + | 'unexpectedUnionTypesGroupOrder' + | 'extraSpacingBetweenUnionTypes' + | 'unexpectedUnionTypesOrder' type Group = | 'intersection' @@ -37,6 +44,7 @@ type Options = [ Partial<{ type: 'alphabetical' | 'line-length' | 'natural' partitionByComment: string[] | boolean | string + newlinesBetween: 'ignore' | 'always' | 'never' specialCharacters: 'remove' | 'trim' | 'keep' matcher: 'minimatch' | 'regex' groups: (Group[] | Group)[] @@ -125,6 +133,12 @@ export default createEslintRule({ 'Allows to use spaces to separate the nodes into logical groups.', type: 'boolean', }, + newlinesBetween: { + description: + 'Specifies how new lines should be handled between object types groups.', + enum: ['ignore', 'always', 'never'], + type: 'string', + }, }, additionalProperties: false, }, @@ -134,6 +148,10 @@ export default createEslintRule({ 'Expected "{{right}}" ({{rightGroup}}) to come before "{{left}}" ({{leftGroup}}).', unexpectedUnionTypesOrder: 'Expected "{{right}}" to come before "{{left}}".', + missedSpacingBetweenUnionTypes: + 'Missed spacing between "{{left}}" and "{{right}}" types.', + extraSpacingBetweenUnionTypes: + 'Extra spacing between "{{left}}" and "{{right}}" types.', }, }, defaultOptions: [ @@ -159,6 +177,7 @@ export default createEslintRule({ order: 'asc', groups: [], matcher: 'minimatch', + newlinesBetween: 'ignore', partitionByNewLine: false, partitionByComment: false, } as const) @@ -182,6 +201,7 @@ export default createEslintRule({ ], [], ) + validateNewlinesAndPartitionConfiguration(options) let sourceCode = getSourceCode(context) let partitionComment = options.partitionByComment @@ -277,17 +297,41 @@ export default createEslintRule({ for (let nodes of formattedMembers) { let sortedNodes = sortNodesByGroups(nodes, options) + pairwise(nodes, (left, right) => { + let leftNum = getGroupNumber(options.groups, left) + let rightNum = getGroupNumber(options.groups, right) + let indexOfLeft = sortedNodes.indexOf(left) let indexOfRight = sortedNodes.indexOf(right) + + let messageIds: MESSAGE_ID[] = [] + if (indexOfLeft > indexOfRight) { - let leftNum = getGroupNumber(options.groups, left) - let rightNum = getGroupNumber(options.groups, right) + messageIds.push( + leftNum !== rightNum + ? 'unexpectedUnionTypesGroupOrder' + : 'unexpectedUnionTypesOrder', + ) + } + + messageIds = [ + ...messageIds, + ...getNewlinesErrors({ + left, + leftNum, + right, + rightNum, + sourceCode, + missedSpacingError: 'missedSpacingBetweenUnionTypes', + extraSpacingError: 'extraSpacingBetweenUnionTypes', + options, + }), + ] + + for (let messageId of messageIds) { context.report({ - messageId: - leftNum !== rightNum - ? 'unexpectedUnionTypesGroupOrder' - : 'unexpectedUnionTypesOrder', + messageId, data: { left: toSingleLine(left.name), leftGroup: left.group, @@ -295,8 +339,16 @@ export default createEslintRule({ rightGroup: right.group, }, node: right.node, - fix: fixer => - makeFixes(fixer, nodes, sortedNodes, sourceCode, options), + fix: fixer => [ + ...makeFixes(fixer, nodes, sortedNodes, sourceCode, options), + ...makeNewlinesFixes( + fixer, + nodes, + sortedNodes, + sourceCode, + options, + ), + ], }) } })