Skip to content

Commit 6812e2b

Browse files
hugop95azat-io
authored andcommittedNov 19, 2024
feat(sort-imports)!: add partition by new line and partition by comment options
1 parent 9ac3188 commit 6812e2b

File tree

4 files changed

+292
-117
lines changed

4 files changed

+292
-117
lines changed
 

‎docs/content/rules/sort-classes.mdx

+4
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,8 @@ Example:
694694
ignoreCase: true,
695695
specialCharacters: 'keep',
696696
partitionByComment: false,
697+
partitionByNewLine: false,
698+
newlinesBetween: 'ignore',
697699
groups: [
698700
'index-signature',
699701
['static-property', 'static-accessor-property'],
@@ -744,6 +746,8 @@ Example:
744746
ignoreCase: true,
745747
specialCharacters: 'keep',
746748
partitionByComment: false,
749+
partitionByNewLine: false,
750+
newlinesBetween: 'ignore',
747751
groups: [
748752
'index-signature',
749753
['static-property', 'static-accessor-property'],

‎docs/content/rules/sort-imports.mdx

+30
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,30 @@ Specifies whether side effect imports should be sorted. By default, sorting side
183183
- `true` — Sort side effect imports.
184184
- `false` — Do not sort side effect imports.
185185

186+
### partitionByComment
187+
188+
<sub>default: `false`</sub>
189+
190+
Allows you to use comments to separate imports into logical groups.
191+
192+
- `true` — All comments will be treated as delimiters, creating partitions.
193+
- `false` — Comments will not be used as delimiters.
194+
- `string` — A regexp pattern to specify which comments should act as delimiters.
195+
- `string[]` — An array of regexp patterns to specify which comments should act as delimiters.
196+
197+
### partitionByNewLine
198+
199+
<sub>default: `false`</sub>
200+
201+
When `true`, the rule will not sort imports if there is an empty line between them. This can be useful for keeping logically separated groups of members in their defined order.
202+
203+
```ts
204+
import { b1, b2 } from 'b'
205+
206+
import { a } from 'a'
207+
import { c } from 'c'
208+
```
209+
186210
### newlinesBetween
187211

188212
<sub>default: `'always'`</sub>
@@ -193,6 +217,8 @@ Specifies how new lines should be handled between import groups.
193217
- `always` — Enforce one new line between each group, and forbid new lines inside a group.
194218
- `never` — No new lines are allowed in the entire import section.
195219

220+
This options is only applicable when `partitionByNewLine` is `false`.
221+
196222
### maxLineLength
197223

198224
<sub>default: `undefined`</sub>
@@ -375,6 +401,8 @@ Specifies which environment’s built-in modules should be recognized. If you ar
375401
ignoreCase: true,
376402
specialCharacters: 'keep',
377403
internalPattern: ['^~/.+'],
404+
partitionByComment: false,
405+
partitionByNewLine: false,
378406
newlinesBetween: 'always',
379407
maxLineLength: undefined,
380408
groups: [
@@ -414,6 +442,8 @@ Specifies which environment’s built-in modules should be recognized. If you ar
414442
ignoreCase: true,
415443
specialCharacters: 'keep',
416444
internalPattern: ['^~/.+'],
445+
partitionByComment: false,
446+
partitionByNewLine: false,
417447
newlinesBetween: 'always',
418448
maxLineLength: undefined,
419449
groups: [

‎rules/sort-imports.ts

+45-12
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,27 @@ import { builtinModules } from 'node:module'
55
import type { SortingNode } from '../typings'
66

77
import {
8+
partitionByCommentJsonSchema,
9+
partitionByNewLineJsonSchema,
810
specialCharactersJsonSchema,
911
ignoreCaseJsonSchema,
1012
localesJsonSchema,
1113
groupsJsonSchema,
1214
orderJsonSchema,
1315
typeJsonSchema,
1416
} from '../utils/common-json-schemas'
17+
import { validateNewlinesAndPartitionConfiguration } from '../utils/validate-newlines-and-partition-configuration'
1518
import { validateGroupsConfiguration } from '../utils/validate-groups-configuration'
1619
import { readClosestTsConfigByPath } from '../utils/read-closest-ts-config-by-path'
1720
import { getOptionsWithCleanGroups } from '../utils/get-options-with-clean-groups'
1821
import { getTypescriptImport } from '../utils/get-typescript-import'
22+
import { hasPartitionComment } from '../utils/is-partition-comment'
1923
import { sortNodesByGroups } from '../utils/sort-nodes-by-groups'
2024
import { getCommentsBefore } from '../utils/get-comments-before'
2125
import { makeNewlinesFixes } from '../utils/make-newlines-fixes'
2226
import { getNewlinesErrors } from '../utils/get-newlines-errors'
2327
import { createEslintRule } from '../utils/create-eslint-rule'
28+
import { getLinesBetween } from '../utils/get-lines-between'
2429
import { getGroupNumber } from '../utils/get-group-number'
2530
import { getSourceCode } from '../utils/get-source-code'
2631
import { rangeToDiff } from '../utils/range-to-diff'
@@ -65,11 +70,13 @@ export type Options<T extends string[]> = [
6570
type?: { [key in T[number]]: string[] | string }
6671
}
6772
type: 'alphabetical' | 'line-length' | 'natural'
73+
partitionByComment: string[] | boolean | string
6874
newlinesBetween: 'ignore' | 'always' | 'never'
6975
specialCharacters: 'remove' | 'trim' | 'keep'
7076
locales: NonNullable<Intl.LocalesArgument>
7177
groups: (Group<T>[] | Group<T>)[]
7278
environment: 'node' | 'bun'
79+
partitionByNewLine: boolean
7380
internalPattern: string[]
7481
sortSideEffects: boolean
7582
tsconfigRootDir?: string
@@ -113,6 +120,12 @@ export default createEslintRule<Options<string[]>, MESSAGE_ID>({
113120
'Controls whether side-effect imports should be sorted.',
114121
type: 'boolean',
115122
},
123+
partitionByComment: {
124+
...partitionByCommentJsonSchema,
125+
description:
126+
'Allows you to use comments to separate the interface properties into logical groups.',
127+
},
128+
partitionByNewLine: partitionByNewLineJsonSchema,
116129
newlinesBetween: {
117130
description:
118131
'Specifies how new lines should be handled between import groups.',
@@ -197,6 +210,8 @@ export default createEslintRule<Options<string[]>, MESSAGE_ID>({
197210
},
198211
defaultOptions: [
199212
{
213+
partitionByComment: false,
214+
partitionByNewLine: false,
200215
type: 'alphabetical',
201216
order: 'asc',
202217
ignoreCase: true,
@@ -237,6 +252,8 @@ export default createEslintRule<Options<string[]>, MESSAGE_ID>({
237252
'object',
238253
'unknown',
239254
],
255+
partitionByComment: false,
256+
partitionByNewLine: false,
240257
customGroups: { type: {}, value: {} },
241258
internalPattern: ['^~/.*'],
242259
newlinesBetween: 'always',
@@ -277,6 +294,7 @@ export default createEslintRule<Options<string[]>, MESSAGE_ID>({
277294
...Object.keys(options.customGroups.value ?? {}),
278295
],
279296
)
297+
validateNewlinesAndPartitionConfiguration(options)
280298

281299
let tsConfigOutput = options.tsconfigRootDir
282300
? readClosestTsConfigByPath({
@@ -575,24 +593,33 @@ export default createEslintRule<Options<string[]>, MESSAGE_ID>({
575593
left: SortImportsSortingNode,
576594
right: SortImportsSortingNode,
577595
): boolean =>
578-
getCommentsBefore(right.node, sourceCode).length > 0 ||
579596
!!sourceCode.getTokensBetween(left.node, right.node, {
580597
includeComments: false,
581598
}).length
582599

583-
let splitNodes: SortImportsSortingNode[][] = [[]]
584-
585-
for (let node of nodes) {
586-
let lastNode = splitNodes.at(-1)?.at(-1)
587-
588-
if (lastNode && hasContentBetweenNodes(lastNode, node)) {
589-
splitNodes.push([node])
590-
} else {
591-
splitNodes.at(-1)!.push(node)
600+
let formattedMembers: SortImportsSortingNode[][] = [[]]
601+
for (let sortingNode of nodes) {
602+
let lastSortingNode = formattedMembers.at(-1)?.at(-1)
603+
604+
if (
605+
(options.partitionByComment &&
606+
hasPartitionComment(
607+
options.partitionByComment,
608+
getCommentsBefore(sortingNode.node, sourceCode),
609+
)) ||
610+
(options.partitionByNewLine &&
611+
lastSortingNode &&
612+
getLinesBetween(sourceCode, lastSortingNode, sortingNode)) ||
613+
(lastSortingNode &&
614+
hasContentBetweenNodes(lastSortingNode, sortingNode))
615+
) {
616+
formattedMembers.push([])
592617
}
618+
619+
formattedMembers.at(-1)!.push(sortingNode)
593620
}
594621

595-
for (let nodeList of splitNodes) {
622+
for (let nodeList of formattedMembers) {
596623
let sortedNodes = sortNodesByGroups(nodeList, options, {
597624
isNodeIgnored: node => node.isIgnored,
598625
getGroupCompareOptions: groupNumber => {
@@ -645,7 +672,13 @@ export default createEslintRule<Options<string[]>, MESSAGE_ID>({
645672
},
646673
node: right.node,
647674
fix: fixer => [
648-
...makeFixes(fixer, nodeList, sortedNodes, sourceCode),
675+
...makeFixes(
676+
fixer,
677+
nodeList,
678+
sortedNodes,
679+
sourceCode,
680+
options,
681+
),
649682
...makeNewlinesFixes(
650683
fixer,
651684
nodeList,

0 commit comments

Comments
 (0)
Please sign in to comment.