Skip to content

Conversation

@hugop95
Copy link
Contributor

@hugop95 hugop95 commented Nov 23, 2025

Description

Our current sorting system relies on the following:

The problem

nodeValueGetter and fallbackSortNodeValueGetter are now lacking for future use cases.

With #593, we would like to be able to add a fallback sort which logic is not convenient to represent with just those two properties:

The two following imports are equal with the alphabetical and natural sorts in this case:

  • import { a } from "a".
  • import type { Type } from "a".

In order to be able to place the type import first with the current options we have, we would need to make sure that the first import's name comes before the second one.

This can be achieved with a nodeValueGetter that transforms the import's name to aa and the second to ab, but this isn't really clean.

Solution

Rather than allowing rules to pass nodeValueGetter and fallbackSortNodeValueGetter, allow them to directly pass comparison functions. Those comparison functions can be computed from options.

For most rules, the comparison functions to pass are the default ones (alphabetical sorting, natural sorting, etc).

For rules that allow sorting by value (sort-enums for example), we simply compute an alphabetical/natural sorting function that takes the node's value field instead of name, and pass those to sort-nodes/sort-nodes-by-groups.

For #593, we simply need to create and pass a comparison function that takes two imports, and gives priority to the one with type.

How it looks

🧑‍💻 The default comparison functions passed
export type ComparatorByOptionsComputer<
  S extends CommonOptions,
  T extends SortingNode,
> = (options: S) => Comparator<T>

export type Comparator<T extends SortingNode> = (a: T, b: T) => number

type Options = Pick<
  CommonOptions,
  | 'specialCharacters'
  | 'fallbackSort'
  | 'ignoreCase'
  | 'alphabet'
  | 'locales'
  | 'order'
  | 'type'
>

export let defaultComparatorByOptionsComputer: ComparatorByOptionsComputer<
  Options,
  SortingNode
> = options => {
  switch (options.type) {
    case 'alphabetical':
      return (a, b) => compareAlphabetically(a.name, b.name, options)
    case 'line-length':
      return (a, b) => compareByLineLength(a, b, options)
    case 'unsorted':
      return unsortedComparator
    case 'natural':
      return (a, b) => computeNaturalSort(a.name, b.name, options)
    case 'custom':
      return (a, b) => compareByCustomSort(a.name, b.name, options)
    default:
      throw new UnreachableCaseError(options.type)
  }
}
🧑‍💻 The comparison functions for `sort-enums` (which allows sorting by value)
export function buildComparatorByOptionsComputer(
  isNumericEnum: boolean,
): ComparatorByOptionsComputer<
  Required<Options[number]>,
  SortEnumsSortingNode
> {
  return options => {
    switch (options.sortByValue) {
      case 'ifNumericEnum':
        if (isNumericEnum) {
          return byNumericValueComparatorComputer(options)
        }
        return defaultComparatorByOptionsComputer(options)
      case 'always':
        if (isNumericEnum) {
          return byNumericValueComparatorComputer(options)
        }
        return byNonNumericValueComparatorComputer(options)
      case 'never':
        return defaultComparatorByOptionsComputer(options)
      default: {
        throw new UnreachableCaseError(options.sortByValue)
      }
    }
  }
}

let byNonNumericValueComparatorComputer: ComparatorByOptionsComputer<
  Required<Options[number]>,
  SortEnumsSortingNode
> = options => {
  switch (options.type) {
    case 'alphabetical':
      return (a, b) =>
        compareAlphabetically(
          a.value?.toString() ?? '',
          b.value?.toString() ?? '',
          options,
        )
    case 'line-length':
      return (a, b) => compareByLineLength(a, b, options)
    case 'unsorted':
      return unsortedComparator
    case 'natural':
      return (a, b) =>
        computeNaturalSort(
          a.value?.toString() ?? '',
          b.value?.toString() ?? '',
          options,
        )
    case 'custom':
      return (a, b) =>
        compareByCustomSort(a.value ?? '', b.value ?? '', options)
    default:
      throw new UnreachableCaseError(options.type)
  }
}

let byNumericValueComparatorComputer: ComparatorByOptionsComputer<
  Required<Options[number]>,
  SortEnumsSortingNode
> = options => {
  switch (options.type) {
    case 'alphabetical':
    case 'line-length':
    case 'natural':
    case 'custom':
      return (a, b) =>
        computeNaturalSort(
          a.numericValue!.toString(),
          b.numericValue!.toString(),
          options,
        )
    case 'unsorted':
      return unsortedComparator
    default:
      throw new UnreachableCaseError(options.type)
  }
}

What is the purpose of this pull request?

  • Other

@coderabbitai
Copy link

coderabbitai bot commented Nov 23, 2025

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@hugop95 hugop95 changed the title POC refactor: improve sorting override framework Nov 23, 2025
@hugop95 hugop95 force-pushed the refactor/sorting branch 10 times, most recently from 92c97c3 to 9130c26 Compare November 23, 2025 13:33
@hugop95 hugop95 mentioned this pull request Nov 23, 2025
@hugop95 hugop95 mentioned this pull request Nov 24, 2025
1 task
@hugop95 hugop95 force-pushed the refactor/sorting branch 3 times, most recently from 37f2776 to 9cbace4 Compare November 24, 2025 21:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant