Skip to content

Commit

Permalink
feat: add partition by comment and partition by new line in sort-name…
Browse files Browse the repository at this point in the history
…d-imports
  • Loading branch information
hugop95 authored Sep 22, 2024
1 parent 4a0931b commit 41e18b9
Show file tree
Hide file tree
Showing 3 changed files with 329 additions and 29 deletions.
42 changes: 42 additions & 0 deletions docs/content/rules/sort-named-imports.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,44 @@ Allows you to group named imports by their kind, determining whether value impor
- `values-first` — Group all value imports before type imports.
- `types-first` — Group all type imports before value imports.

### partitionByComment

<sub>default: `false`</sub>

Allows you to use comments to separate the members of named imports into logical groups. This can help in organizing and maintaining large enums by creating partitions within the enum based on comments.

- `true` — All comments will be treated as delimiters, creating partitions.
- `false` — Comments will not be used as delimiters.
- `string` — A glob pattern to specify which comments should act as delimiters.
- `string[]` — A list of glob patterns to specify which comments should act as delimiters.

### partitionByNewLine

<sub>default: `false`</sub>

When `true`, the rule will not sort the members of named imports if there is an empty line between them. This can be useful for keeping logically separated groups of members in their defined order.

```ts
import {
// Group 1
Drone,
Keyboard,
Mouse,
Smartphone,

// Group 2
Laptop,
Monitor,
Smartwatch,
Tablet,

// Group 3
Headphones,
Router,
} from './devices'
```


## Usage

<CodeTabs
Expand All @@ -168,6 +206,8 @@ Allows you to group named imports by their kind, determining whether value impor
ignoreAlias: false,
ignoreCase: true,
groupKind: 'mixed',
partitionByNewLine: false,
partitionByComment: false,
},
],
},
Expand All @@ -193,6 +233,8 @@ Allows you to group named imports by their kind, determining whether value impor
ignoreAlias: false,
ignoreCase: true,
groupKind: 'mixed',
partitionByNewLine: false,
partitionByComment: false,
},
],
},
Expand Down
112 changes: 83 additions & 29 deletions rules/sort-named-imports.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import type { SortingNode } from '../typings'

import { hasPartitionComment } from '../utils/is-partition-comment'
import { getCommentsBefore } from '../utils/get-comments-before'
import { createEslintRule } from '../utils/create-eslint-rule'
import { getLinesBetween } from '../utils/get-lines-between'
import { getGroupNumber } from '../utils/get-group-number'
import { getSourceCode } from '../utils/get-source-code'
import { rangeToDiff } from '../utils/range-to-diff'
Expand All @@ -18,6 +21,8 @@ type Options = [
Partial<{
groupKind: 'values-first' | 'types-first' | 'mixed'
type: 'alphabetical' | 'line-length' | 'natural'
partitionByComment: string[] | boolean | string
partitionByNewLine: boolean
order: 'desc' | 'asc'
ignoreAlias: boolean
ignoreCase: boolean
Expand Down Expand Up @@ -61,6 +66,29 @@ export default createEslintRule<Options, MESSAGE_ID>({
enum: ['mixed', 'values-first', 'types-first'],
type: 'string',
},
partitionByComment: {
description:
'Allows you to use comments to separate the named imports members into logical groups.',
anyOf: [
{
type: 'array',
items: {
type: 'string',
},
},
{
type: 'boolean',
},
{
type: 'string',
},
],
},
partitionByNewLine: {
description:
'Allows to use spaces to separate the nodes into logical groups.',
type: 'boolean',
},
},
additionalProperties: false,
},
Expand All @@ -76,6 +104,8 @@ export default createEslintRule<Options, MESSAGE_ID>({
order: 'asc',
ignoreAlias: false,
ignoreCase: true,
partitionByNewLine: false,
partitionByComment: false,
groupKind: 'mixed',
},
],
Expand All @@ -93,12 +123,16 @@ export default createEslintRule<Options, MESSAGE_ID>({
ignoreAlias: false,
groupKind: 'mixed',
ignoreCase: true,
partitionByNewLine: false,
partitionByComment: false,
order: 'asc',
} as const)

let sourceCode = getSourceCode(context)
let partitionComment = options.partitionByComment

let nodes: SortingNode[] = specifiers.map(specifier => {
let formattedMembers: SortingNode[][] = [[]]
for (let specifier of specifiers) {
let group: undefined | 'value' | 'type'
let { name } = specifier.local

Expand All @@ -115,47 +149,67 @@ export default createEslintRule<Options, MESSAGE_ID>({
group = 'value'
}

return {
let lastSortingNode = formattedMembers.at(-1)?.at(-1)
let sortingNode: SortingNode = {
size: rangeToDiff(specifier.range),
node: specifier,
group,
name,
}
})

if (
(partitionComment &&
hasPartitionComment(
partitionComment,
getCommentsBefore(specifier, sourceCode),
)) ||
(options.partitionByNewLine &&
lastSortingNode &&
getLinesBetween(sourceCode, lastSortingNode, sortingNode))
) {
formattedMembers.push([])
}

formattedMembers.at(-1)!.push(sortingNode)
}

let shouldGroupByKind = options.groupKind !== 'mixed'
let groupKindOrder =
options.groupKind === 'values-first'
? ['value', 'type']
: ['type', 'value']

pairwise(nodes, (left, right) => {
let leftNum = getGroupNumber(groupKindOrder, left)
let rightNum = getGroupNumber(groupKindOrder, right)
for (let nodes of formattedMembers) {
pairwise(nodes, (left, right) => {
let leftNum = getGroupNumber(groupKindOrder, left)
let rightNum = getGroupNumber(groupKindOrder, right)
if (
(shouldGroupByKind && leftNum > rightNum) ||
((!shouldGroupByKind || leftNum === rightNum) &&
isPositive(compare(left, right, options)))
) {
let sortedNodes = shouldGroupByKind
? groupKindOrder
.map(group => nodes.filter(n => n.group === group))
.map(groupedNodes => sortNodes(groupedNodes, options))
.flat()
: sortNodes(nodes, options)

if (
(shouldGroupByKind && leftNum > rightNum) ||
((!shouldGroupByKind || leftNum === rightNum) &&
isPositive(compare(left, right, options)))
) {
let sortedNodes = shouldGroupByKind
? groupKindOrder
.map(group => nodes.filter(n => n.group === group))
.map(groupedNodes => sortNodes(groupedNodes, options))
.flat()
: sortNodes(nodes, options)

context.report({
messageId: 'unexpectedNamedImportsOrder',
data: {
left: left.name,
right: right.name,
},
node: right.node,
fix: fixer => makeFixes(fixer, nodes, sortedNodes, sourceCode),
})
}
})
context.report({
messageId: 'unexpectedNamedImportsOrder',
data: {
left: left.name,
right: right.name,
},
node: right.node,
fix: fixer =>
makeFixes(fixer, nodes, sortedNodes, sourceCode, {
partitionComment,
}),
})
}
})
}
}
},
}),
Expand Down
Loading

0 comments on commit 41e18b9

Please sign in to comment.