Skip to content

Commit d8033f7

Browse files
committed
feat(sort-jsx-props): adds partitionByNewLine
1 parent a95d3fa commit d8033f7

File tree

2 files changed

+61
-36
lines changed

2 files changed

+61
-36
lines changed

docs/content/rules/sort-jsx-props.mdx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,12 @@ Allows you to specify names or patterns for JSX elements that should be ignored
200200

201201
You can specify their names or a regexp pattern to ignore, for example: `'^Table.+'` to ignore all object types whose names begin with the word Table.
202202

203+
### partitionByNewLine
204+
205+
<sub>default: `false`</sub>
206+
207+
When `true`, the rule will not sort members if there is an empty line between them. This can be useful for keeping logically separated groups of members in their defined order.
208+
203209
### groups
204210

205211
<sub>
@@ -299,6 +305,7 @@ Custom group matching takes precedence over predefined group matching.
299305
ignoreCase: true,
300306
specialCharacters: 'keep',
301307
ignorePattern: [],
308+
partitionByNewLine: false,
302309
newlinesBetween: 'ignore',
303310
groups: [],
304311
customGroups: {},
@@ -327,6 +334,7 @@ Custom group matching takes precedence over predefined group matching.
327334
ignoreCase: true,
328335
specialCharacters: 'keep',
329336
ignorePattern: [],
337+
partitionByNewLine: false,
330338
newlinesBetween: 'ignore',
331339
groups: [],
332340
customGroups: {},

rules/sort-jsx-props.ts

Lines changed: 53 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { TSESTree } from '@typescript-eslint/types'
33
import type { SortingNode } from '../types/sorting-node'
44

55
import {
6+
partitionByNewLineJsonSchema,
67
specialCharactersJsonSchema,
78
newlinesBetweenJsonSchema,
89
customGroupsJsonSchema,
@@ -13,6 +14,7 @@ import {
1314
groupsJsonSchema,
1415
orderJsonSchema,
1516
} from '../utils/common-json-schemas'
17+
import { validateNewlinesAndPartitionConfiguration } from '../utils/validate-newlines-and-partition-configuration'
1618
import { validateCustomSortConfiguration } from '../utils/validate-custom-sort-configuration'
1719
import { validateGroupsConfiguration } from '../utils/validate-groups-configuration'
1820
import { getEslintDisabledLines } from '../utils/get-eslint-disabled-lines'
@@ -22,6 +24,7 @@ import { sortNodesByGroups } from '../utils/sort-nodes-by-groups'
2224
import { getNewlinesErrors } from '../utils/get-newlines-errors'
2325
import { makeNewlinesFixes } from '../utils/make-newlines-fixes'
2426
import { createEslintRule } from '../utils/create-eslint-rule'
27+
import { getLinesBetween } from '../utils/get-lines-between'
2528
import { getGroupNumber } from '../utils/get-group-number'
2629
import { getSourceCode } from '../utils/get-source-code'
2730
import { rangeToDiff } from '../utils/range-to-diff'
@@ -45,6 +48,7 @@ type Options<T extends string[]> = [
4548
newlinesBetween: 'ignore' | 'always' | 'never'
4649
specialCharacters: 'remove' | 'trim' | 'keep'
4750
locales: NonNullable<Intl.LocalesArgument>
51+
partitionByNewLine: boolean
4852
ignorePattern: string[]
4953
order: 'desc' | 'asc'
5054
ignoreCase: boolean
@@ -67,6 +71,7 @@ type Group<T extends string[]> =
6771
let defaultOptions: Required<Options<string[]>[0]> = {
6872
specialCharacters: 'keep',
6973
newlinesBetween: 'ignore',
74+
partitionByNewLine: false,
7075
type: 'alphabetical',
7176
ignorePattern: [],
7277
ignoreCase: true,
@@ -92,6 +97,7 @@ export default createEslintRule<Options<string[]>, MESSAGE_ID>({
9297
['multiline', 'shorthand', 'unknown'],
9398
Object.keys(options.customGroups),
9499
)
100+
validateNewlinesAndPartitionConfiguration(options)
95101

96102
let sourceCode = getSourceCode(context)
97103

@@ -111,50 +117,60 @@ export default createEslintRule<Options<string[]>, MESSAGE_ID>({
111117
sourceCode,
112118
})
113119

114-
let parts: SortingNode[][] = node.openingElement.attributes.reduce(
115-
(
116-
accumulator: SortingNode[][],
117-
attribute: TSESTree.JSXSpreadAttribute | TSESTree.JSXAttribute,
118-
) => {
119-
if (attribute.type === 'JSXSpreadAttribute') {
120-
accumulator.push([])
121-
return accumulator
122-
}
120+
let formattedMembers: SortingNode[][] =
121+
node.openingElement.attributes.reduce(
122+
(
123+
accumulator: SortingNode[][],
124+
attribute: TSESTree.JSXSpreadAttribute | TSESTree.JSXAttribute,
125+
) => {
126+
if (attribute.type === 'JSXSpreadAttribute') {
127+
accumulator.push([])
128+
return accumulator
129+
}
123130

124-
let name =
125-
attribute.name.type === 'JSXNamespacedName'
126-
? `${attribute.name.namespace.name}:${attribute.name.name.name}`
127-
: attribute.name.name
131+
let name =
132+
attribute.name.type === 'JSXNamespacedName'
133+
? `${attribute.name.namespace.name}:${attribute.name.name.name}`
134+
: attribute.name.name
128135

129-
let { setCustomGroups, defineGroup, getGroup } = useGroups(options)
136+
let { setCustomGroups, defineGroup, getGroup } = useGroups(options)
130137

131-
setCustomGroups(options.customGroups, name)
138+
setCustomGroups(options.customGroups, name)
132139

133-
if (attribute.value === null) {
134-
defineGroup('shorthand')
135-
} else if (attribute.loc.start.line !== attribute.loc.end.line) {
136-
defineGroup('multiline')
137-
}
140+
if (attribute.value === null) {
141+
defineGroup('shorthand')
142+
} else if (attribute.loc.start.line !== attribute.loc.end.line) {
143+
defineGroup('multiline')
144+
}
138145

139-
let jsxNode: SortingNode = {
140-
isEslintDisabled: isNodeEslintDisabled(
141-
attribute,
142-
eslintDisabledLines,
143-
),
144-
size: rangeToDiff(attribute, sourceCode),
145-
group: getGroup(),
146-
node: attribute,
147-
name,
148-
}
146+
let sortingNode: SortingNode = {
147+
isEslintDisabled: isNodeEslintDisabled(
148+
attribute,
149+
eslintDisabledLines,
150+
),
151+
size: rangeToDiff(attribute, sourceCode),
152+
group: getGroup(),
153+
node: attribute,
154+
name,
155+
}
149156

150-
accumulator.at(-1)!.push(jsxNode)
157+
let lastSortingNode = accumulator.at(-1)?.at(-1)
158+
if (
159+
options.partitionByNewLine &&
160+
lastSortingNode &&
161+
getLinesBetween(sourceCode, lastSortingNode, sortingNode)
162+
) {
163+
accumulator.push([])
164+
}
151165

152-
return accumulator
153-
},
154-
[[]],
155-
)
166+
accumulator.at(-1)!.push(sortingNode)
167+
168+
return accumulator
169+
},
170+
[[]],
171+
)
156172

157-
for (let nodes of parts) {
173+
for (let nodes of formattedMembers) {
158174
let sortNodesExcludingEslintDisabled = (
159175
ignoreEslintDisabledNodes: boolean,
160176
): SortingNode[] =>
@@ -245,6 +261,7 @@ export default createEslintRule<Options<string[]>, MESSAGE_ID>({
245261
},
246262
type: 'array',
247263
},
264+
partitionByNewLine: partitionByNewLineJsonSchema,
248265
specialCharacters: specialCharactersJsonSchema,
249266
newlinesBetween: newlinesBetweenJsonSchema,
250267
customGroups: customGroupsJsonSchema,

0 commit comments

Comments
 (0)