Skip to content

Commit 8fb41c6

Browse files
authored
refactor: reduce duplication in sort-union-types and sort-intersection-types
1 parent 0df348f commit 8fb41c6

File tree

2 files changed

+284
-536
lines changed

2 files changed

+284
-536
lines changed

rules/sort-intersection-types.ts

Lines changed: 21 additions & 306 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,17 @@
1-
import type { SortingNode } from '../types/sorting-node'
1+
import type { Options as SortUnionTypesOptions } from './sort-union-types'
22

3-
import {
4-
partitionByCommentJsonSchema,
5-
partitionByNewLineJsonSchema,
6-
specialCharactersJsonSchema,
7-
newlinesBetweenJsonSchema,
8-
ignoreCaseJsonSchema,
9-
buildTypeJsonSchema,
10-
alphabetJsonSchema,
11-
localesJsonSchema,
12-
groupsJsonSchema,
13-
orderJsonSchema,
14-
} from '../utils/common-json-schemas'
15-
import { validateNewlinesAndPartitionConfiguration } from '../utils/validate-newlines-and-partition-configuration'
16-
import { validateCustomSortConfiguration } from '../utils/validate-custom-sort-configuration'
17-
import { validateGroupsConfiguration } from '../utils/validate-groups-configuration'
18-
import { getEslintDisabledLines } from '../utils/get-eslint-disabled-lines'
19-
import { isNodeEslintDisabled } from '../utils/is-node-eslint-disabled'
20-
import { hasPartitionComment } from '../utils/has-partition-comment'
21-
import { createNodeIndexMap } from '../utils/create-node-index-map'
22-
import { sortNodesByGroups } from '../utils/sort-nodes-by-groups'
23-
import { getCommentsBefore } from '../utils/get-comments-before'
24-
import { makeNewlinesFixes } from '../utils/make-newlines-fixes'
25-
import { getNewlinesErrors } from '../utils/get-newlines-errors'
3+
import { sortUnionOrIntersectionTypes } from './sort-union-types'
264
import { createEslintRule } from '../utils/create-eslint-rule'
27-
import { getLinesBetween } from '../utils/get-lines-between'
28-
import { getGroupNumber } from '../utils/get-group-number'
29-
import { getSourceCode } from '../utils/get-source-code'
30-
import { toSingleLine } from '../utils/to-single-line'
31-
import { rangeToDiff } from '../utils/range-to-diff'
32-
import { getSettings } from '../utils/get-settings'
33-
import { useGroups } from '../utils/use-groups'
34-
import { makeFixes } from '../utils/make-fixes'
35-
import { complete } from '../utils/complete'
36-
import { pairwise } from '../utils/pairwise'
37-
38-
type Options = [
39-
Partial<{
40-
partitionByComment:
41-
| {
42-
block?: string[] | boolean | string
43-
line?: string[] | boolean | string
44-
}
45-
| string[]
46-
| boolean
47-
| string
48-
groups: (
49-
| { newlinesBetween: 'ignore' | 'always' | 'never' }
50-
| Group[]
51-
| Group
52-
)[]
53-
type: 'alphabetical' | 'line-length' | 'natural' | 'custom'
54-
newlinesBetween: 'ignore' | 'always' | 'never'
55-
specialCharacters: 'remove' | 'trim' | 'keep'
56-
locales: NonNullable<Intl.LocalesArgument>
57-
partitionByNewLine: boolean
58-
order: 'desc' | 'asc'
59-
ignoreCase: boolean
60-
alphabet: string
61-
}>,
62-
]
63-
64-
type Group =
65-
| 'intersection'
66-
| 'conditional'
67-
| 'function'
68-
| 'operator'
69-
| 'keyword'
70-
| 'literal'
71-
| 'nullish'
72-
| 'unknown'
73-
| 'import'
74-
| 'object'
75-
| 'named'
76-
| 'tuple'
77-
| 'union'
5+
import { jsonSchema } from './sort-union-types'
786

797
type MESSAGE_ID =
808
| 'missedSpacingBetweenIntersectionTypes'
819
| 'unexpectedIntersectionTypesGroupOrder'
8210
| 'extraSpacingBetweenIntersectionTypes'
8311
| 'unexpectedIntersectionTypesOrder'
8412

13+
type Options = SortUnionTypesOptions
14+
8515
let defaultOptions: Required<Options[0]> = {
8616
specialCharacters: 'keep',
8717
newlinesBetween: 'ignore',
@@ -96,238 +26,7 @@ let defaultOptions: Required<Options[0]> = {
9626
}
9727

9828
export default createEslintRule<Options, MESSAGE_ID>({
99-
create: context => ({
100-
TSIntersectionType: node => {
101-
let settings = getSettings(context.settings)
102-
103-
let options = complete(context.options.at(0), settings, defaultOptions)
104-
validateCustomSortConfiguration(options)
105-
validateGroupsConfiguration(
106-
options.groups,
107-
[
108-
'intersection',
109-
'conditional',
110-
'function',
111-
'operator',
112-
'keyword',
113-
'literal',
114-
'nullish',
115-
'unknown',
116-
'import',
117-
'object',
118-
'named',
119-
'tuple',
120-
'union',
121-
],
122-
[],
123-
)
124-
validateNewlinesAndPartitionConfiguration(options)
125-
126-
let sourceCode = getSourceCode(context)
127-
let eslintDisabledLines = getEslintDisabledLines({
128-
ruleName: context.id,
129-
sourceCode,
130-
})
131-
132-
let formattedMembers: SortingNode[][] = node.types.reduce(
133-
(accumulator: SortingNode[][], type) => {
134-
let { defineGroup, getGroup } = useGroups(options)
135-
136-
switch (type.type) {
137-
case 'TSTemplateLiteralType':
138-
case 'TSLiteralType':
139-
defineGroup('literal')
140-
break
141-
case 'TSIndexedAccessType':
142-
case 'TSTypeReference':
143-
case 'TSQualifiedName':
144-
case 'TSArrayType':
145-
case 'TSInferType':
146-
defineGroup('named')
147-
break
148-
case 'TSIntersectionType':
149-
defineGroup('intersection')
150-
break
151-
case 'TSUndefinedKeyword':
152-
case 'TSNullKeyword':
153-
case 'TSVoidKeyword':
154-
defineGroup('nullish')
155-
break
156-
case 'TSConditionalType':
157-
defineGroup('conditional')
158-
break
159-
case 'TSConstructorType':
160-
case 'TSFunctionType':
161-
defineGroup('function')
162-
break
163-
case 'TSBooleanKeyword':
164-
case 'TSUnknownKeyword':
165-
case 'TSBigIntKeyword':
166-
case 'TSNumberKeyword':
167-
case 'TSObjectKeyword':
168-
case 'TSStringKeyword':
169-
case 'TSSymbolKeyword':
170-
case 'TSNeverKeyword':
171-
case 'TSAnyKeyword':
172-
case 'TSThisType':
173-
defineGroup('keyword')
174-
break
175-
case 'TSTypeOperator':
176-
case 'TSTypeQuery':
177-
defineGroup('operator')
178-
break
179-
case 'TSTypeLiteral':
180-
case 'TSMappedType':
181-
defineGroup('object')
182-
break
183-
case 'TSImportType':
184-
defineGroup('import')
185-
break
186-
case 'TSTupleType':
187-
defineGroup('tuple')
188-
break
189-
case 'TSUnionType':
190-
defineGroup('union')
191-
break
192-
}
193-
194-
let lastGroup = accumulator.at(-1)
195-
let lastSortingNode = lastGroup?.at(-1)
196-
let sortingNode: SortingNode = {
197-
isEslintDisabled: isNodeEslintDisabled(type, eslintDisabledLines),
198-
size: rangeToDiff(type, sourceCode),
199-
name: sourceCode.getText(type),
200-
group: getGroup(),
201-
node: type,
202-
}
203-
if (
204-
hasPartitionComment({
205-
comments: getCommentsBefore({
206-
tokenValueToIgnoreBefore: '&',
207-
node: type,
208-
sourceCode,
209-
}),
210-
partitionByComment: options.partitionByComment,
211-
}) ||
212-
(options.partitionByNewLine &&
213-
lastSortingNode &&
214-
getLinesBetween(sourceCode, lastSortingNode, sortingNode))
215-
) {
216-
lastGroup = []
217-
accumulator.push(lastGroup)
218-
}
219-
220-
lastGroup?.push(sortingNode)
221-
222-
return accumulator
223-
},
224-
[[]],
225-
)
226-
227-
for (let nodes of formattedMembers) {
228-
let sortNodesExcludingEslintDisabled = (
229-
ignoreEslintDisabledNodes: boolean,
230-
): SortingNode[] =>
231-
sortNodesByGroups(nodes, options, { ignoreEslintDisabledNodes })
232-
233-
let sortedNodes = sortNodesExcludingEslintDisabled(false)
234-
let sortedNodesExcludingEslintDisabled =
235-
sortNodesExcludingEslintDisabled(true)
236-
237-
let nodeIndexMap = createNodeIndexMap(sortedNodes)
238-
239-
pairwise(nodes, (left, right) => {
240-
let leftNumber = getGroupNumber(options.groups, left)
241-
let rightNumber = getGroupNumber(options.groups, right)
242-
243-
let leftIndex = nodeIndexMap.get(left)!
244-
let rightIndex = nodeIndexMap.get(right)!
245-
246-
let indexOfRightExcludingEslintDisabled =
247-
sortedNodesExcludingEslintDisabled.indexOf(right)
248-
249-
let messageIds: MESSAGE_ID[] = []
250-
251-
if (
252-
leftIndex > rightIndex ||
253-
leftIndex >= indexOfRightExcludingEslintDisabled
254-
) {
255-
messageIds.push(
256-
leftNumber === rightNumber
257-
? 'unexpectedIntersectionTypesOrder'
258-
: 'unexpectedIntersectionTypesGroupOrder',
259-
)
260-
}
261-
262-
messageIds = [
263-
...messageIds,
264-
...getNewlinesErrors({
265-
missedSpacingError: 'missedSpacingBetweenIntersectionTypes',
266-
extraSpacingError: 'extraSpacingBetweenIntersectionTypes',
267-
rightNum: rightNumber,
268-
leftNum: leftNumber,
269-
sourceCode,
270-
options,
271-
right,
272-
left,
273-
}),
274-
]
275-
276-
for (let messageId of messageIds) {
277-
context.report({
278-
fix: fixer => [
279-
...makeFixes({
280-
sortedNodes: sortedNodesExcludingEslintDisabled,
281-
sourceCode,
282-
options,
283-
fixer,
284-
nodes,
285-
}),
286-
...makeNewlinesFixes({
287-
sortedNodes: sortedNodesExcludingEslintDisabled,
288-
sourceCode,
289-
options,
290-
fixer,
291-
nodes,
292-
}),
293-
],
294-
data: {
295-
right: toSingleLine(right.name),
296-
left: toSingleLine(left.name),
297-
rightGroup: right.group,
298-
leftGroup: left.group,
299-
},
300-
node: right.node,
301-
messageId,
302-
})
303-
}
304-
})
305-
}
306-
},
307-
}),
30829
meta: {
309-
schema: [
310-
{
311-
properties: {
312-
partitionByComment: {
313-
...partitionByCommentJsonSchema,
314-
description:
315-
'Allows you to use comments to separate the intersection types members into logical groups.',
316-
},
317-
partitionByNewLine: partitionByNewLineJsonSchema,
318-
specialCharacters: specialCharactersJsonSchema,
319-
newlinesBetween: newlinesBetweenJsonSchema,
320-
ignoreCase: ignoreCaseJsonSchema,
321-
alphabet: alphabetJsonSchema,
322-
type: buildTypeJsonSchema(),
323-
locales: localesJsonSchema,
324-
groups: groupsJsonSchema,
325-
order: orderJsonSchema,
326-
},
327-
additionalProperties: false,
328-
type: 'object',
329-
},
330-
],
33130
messages: {
33231
unexpectedIntersectionTypesGroupOrder:
33332
'Expected "{{right}}" ({{rightGroup}}) to come before "{{left}}" ({{leftGroup}}).',
@@ -343,9 +42,25 @@ export default createEslintRule<Options, MESSAGE_ID>({
34342
description: 'Enforce sorted intersection types.',
34443
recommended: true,
34544
},
45+
schema: [jsonSchema],
34646
type: 'suggestion',
34747
fixable: 'code',
34848
},
49+
create: context => ({
50+
TSIntersectionType: node => {
51+
sortUnionOrIntersectionTypes({
52+
availableMessageIds: {
53+
missedSpacingBetweenMembers: 'missedSpacingBetweenIntersectionTypes',
54+
extraSpacingBetweenMembers: 'extraSpacingBetweenIntersectionTypes',
55+
unexpectedGroupOrder: 'unexpectedIntersectionTypesGroupOrder',
56+
unexpectedOrder: 'unexpectedIntersectionTypesOrder',
57+
},
58+
tokenValueToIgnoreBefore: '&',
59+
context,
60+
node,
61+
})
62+
},
63+
}),
34964
defaultOptions: [defaultOptions],
35065
name: 'sort-intersection-types',
35166
})

0 commit comments

Comments
 (0)