Repository: azat-io/eslint-plugin-perfectionist
Pull request #404 (2024-12-01 00:28:42)

feat: adds custom sort type through custom alphabet
Related to #384.

## Description

The recent removal of the previous `natural-compare-lite` library for the new `natural-orderby` has raised the question of trying to provide users with a way to enter their own custom sort.

This PR offers a way to resolve it.

## Constraints

Here is the main constraint regarding this feature:

ESLint configs must be **serializable**, so allowing users to enter a **sorting function** will not work. The alternative is to let users pass an **ordered list of characters** (that we wil be calling **alphabet**) representing which characters are considered more important than others.

Example:
`aAbBcCdDeEfF...` 

An alphabet can't obviously be restrained to 26 letters, however, and it would be interesting to give a way for users to simply build a custom alphabet.

As such, the PR will be built around **2** main features:

- Allowing users to enter their custom ordered alphabet.
- Giving users a simple way for users to **build** that custom alphabet.

# Feature 1: Allowing users to enter their custom ordered alphabet

This requires:
- Adding an additional `type` on top of the existing `alphabetical`, `natural` and `line-length` ones: `custom`.
- Adding an option for all rules: `alphabet: string`, representing the ordered list of characters of the user's alphabet.

Important notes:
- As mentioned above, an alphabet is more than just letters, numbers and some special characters. By following the [Unicode standard](https://en.wikipedia.org/wiki/Plane_(Unicode)), we can say that a complete alphabet should have **~300000** characters, even though likely 99.999% of characters used in a codebase could be regrouped in less than a couple of hundred characters. More information on this in the **Giving a simple way for users to build that custom alphabet** section.
- ⚠️ Ensure that no performance issue is noticed with large alphabets.
- Generating large alphabets is not something that can be done in the Legacy ESLint JSON format, so this feature is mostly targeting users using javascript-based configs. 
- It is very likely that users would enter an alphabet common to all rules, so there should be a way to enter that alphabet in common ESLint `settings`.
- Characters that are not in an alphabet will have the lowest priority by default (highest if the order is `desc`).

# Feature 2: Giving a simple way for users to build that custom alphabet.

This feature would be much handier if we gave users a way to easily build customizable alphabets. a `Alphabet` class has been created for that purpose. It has multiple utility functions to quickly build an alphabet.

## Alphabet size

`Alphabet.generateRecommendedAlphabet()` will generate ~196000 characters and should very certainly cover all use cases. It will only take into account the Unicode Planes 0 and 1.

`Alphabet.generateCompleteAlphabet()` will generate ~450000 characters. It will take into account the Unicode Plane 0, 1, 2, and 3.

I think it would be interesting in the future to find a way to generate an alphabet with just a couple thousand characters that would still be enough for 99.99% of users. 

## Examples

### Example 1: Rebuild the old `natural-lite` sort

```ts
import eslintPluginPerfectionist from 'eslint-plugin-perfectionist';
import naturalCompare from 'natural-compare-lite';

const alphabet = eslintPluginPerfectionist.Alphabet
  .generateRecommendedAlphabet()
  .sortBy((a, b) => naturalCompare(a, b))
  .getCharacters();
```

### Example 2: custom `localeCompare` sort

```ts
const alphabet = eslintPluginPerfectionist.Alphabet
  .generateRecommendedAlphabet()
  .sortByLocaleCompare('en-US')
  .prioritizeCase('uppercase') // Will ensure that each character that has a uppercase different than lowercase will have its uppercase first
  .reverse() // `desc` sort order
  .placeCharacterBefore({ characterBefore: '-', characterAfter: '/' }) // `-` should always be more important than `/` 
  .getCharacters();
```

### Example 4: Extremely simple custom `natural` (new library) sort

```ts
const alphabet = eslintPluginPerfectionist.Alphabet
  .generateFrom('abcdefgh...z') // I don't care about other characters, don't consider them when sorting!
  .sortByNaturalSort('en-US')
  .getCharacters();
```

## List of utility functions

All those functions are available in the `Alphabet` class.

### Alphabet generators

#### - `static generateRecommendedAlphabet(): Alphabet`

Generates an alphabet containing relevant characters from the Unicode standard (~196000 characters). Uses the Unicode planes 0 and 1.

#### - `static generateCompleteAlphabet(): Alphabet`

Generates an alphabet containing all characters from the Unicode standard except for irrelevant Unicode planes (~450000 characters). Uses the Unicode planes 0, 1, 2 and 3.

#### - `static generateFrom(values: string[] | string): Alphabet`

Generates an alphabet from the given characters.

### Sorters

#### - `sortByCharCodeAt(): this`

Sorts the alphabet by the character code point.

#### - `sortByNaturalSort(locale?: string): this`

Sorts the alphabet by the natural order of the characters, using `natural-orderby`.

#### - `sortByLocaleCompare(locales?: Intl.LocalesArgument): this`

Sorts the alphabet by the locale order of the characters.

#### - `sortBy(sortingFunction: (characterA: string, characterB: string) => number): this`

Sorts the alphabet by the sorting function provided.

### Adding/removing characters

#### - `pushCharacters(values: string[] | string): this`

Adds specific characters to the end of the alphabet.

#### - `removeCharacters(values: string[] | string): this`

Removes specific characters from the alphabet.

#### - `removeUnicodeRange({ start: number, end: number }): this`

Removes specific characters from the alphabet by their range.

### Other utility functions

#### - `prioritizeCase(casePriority: 'lowercase' | 'uppercase'): this`

For each character with a lower and upper case, permutes the two cases so that the alphabet is ordered by the case priority entered.

```ts
Alphabet.generateFrom('aAbBcdCD')
  .prioritizeCase('uppercase') // Returns 'AaBbCDcd'
```

#### - `placeCharacterBefore({ characterBefore: string; characterAfter: string }): this`

Places a specific character right before another character in the alphabet.

```ts
Alphabet.generateFrom('ab-cd/ef')
.placeCharacterBefore({ characterBefore: '/', characterAfter: '-' }) // Returns 'ab/-cdef'
```

#### - `placeCharacterAfter({ characterBefore: string; characterAfter: string }): this`

Places a specific character right after another character in the alphabet.

```ts
Alphabet.generateFrom('ab-cd/ef')
.placeCharacterAfter({ characterBefore: '/', characterAfter: '-' }) // Returns 'abcd/-ef'
```

#### - `placeAllWithCaseBeforeAllWithOtherCase(caseToComeFirst: 'uppercase' | 'lowercase'): this`

Permutes characters with cases so that all characters with the entered case are put before all the characters with the other case.

#### - `getCharacters(): string`

Retrieves the characters from the alphabet. Called at the very end.

## Limits

It would be nice for users to be able to do
```ts
import { Alphabet } from 'eslint-plugin-perfectionist';
```

Unfortunately, we currently use a `default export` in the plugin, and we can't add a named export `Alphabetical` on top of that. The solution is to add an `Alphabet` field to that default export. Not as clean, but it will work.

Feel free to add comments or suggest changes to these specifications, the code added is WIP.

### What is the purpose of this pull request?

- [x] New Feature

hugop95: Hi @OlivierZal @azat-io 👋

Here is a proposal to add a way for users to enter their own custom sort. Don't hesitate to say what you think of the idea!

This branch should be fully buildable, and you should be able to use `type: custom` and `alphabet: <YOUR_ALPHABET>` should you want to try it out.

@sthenault Pinging you as well in case you are interested. 🙂
azat-io: Wow. That looks very impressive. It's a cool feature. I'll look into the code tomorrow. 👍 
OlivierZal: @hugop95, that's huge and awesome!
OlivierZal: @hugop95, given the nature, power and usefulness of the `Alphabet` class, couldn't it be a separate module imported as a dependency?

It's probably easier to add it in perfectionist's utils for a POC and test purposes, but maybe you should consider since it's not directly about sorting. 
hugop95: @OlivierZal I think that this is something that can possibly come up with time. It will be easier to adapt the class internally to fit the needs of users who use it for the purpose of `perfectionist` for now. 🙂
OlivierZal: Sure, that was my guess and it's important the class doesn't miss its target. 
sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=azat-io_eslint-plugin-perfectionist&pullRequest=404) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [24 New issues](https://sonarcloud.io/project/issues?id=azat-io_eslint-plugin-perfectionist&pullRequest=404&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=azat-io_eslint-plugin-perfectionist&pullRequest=404&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=azat-io_eslint-plugin-perfectionist&pullRequest=404&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=azat-io_eslint-plugin-perfectionist&pullRequest=404&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [1.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=azat-io_eslint-plugin-perfectionist&pullRequest=404&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=azat-io_eslint-plugin-perfectionist&pullRequest=404)


hugop95: @azat-io @OlivierZal I am done with this, feel free to give feedback or test it out!

Don't hesitate to ask if you have any question.
OlivierZal: Maybe I need to find some time to play with it and appreciate how handy the use of `Alphabet` concretely is, but the code looks great.
azat-io: I want to take a few days to test it too.
azat-io: Do you want `Alphabet` to be capitalized?
hugop95: @azat-io 

> Do you want Alphabet to be capitalized?

This is a class, so in my opinion it should be to make it clear for users.
OlivierZal: Hi @azat-io, I had a last look: I think the code is great and the feature is awesome. It’s a go for me!

Patch:
diff --git a/docs/content/configs/recommended-custom.mdx b/docs/content/configs/recommended-custom.mdx
new file mode 100644
index 0000000..0214801
--- /dev/null
+++ b/docs/content/configs/recommended-custom.mdx
@@ -0,0 +1,199 @@
+---
+title: recommended-custom
+description: Learn more about the recommended custom ESLint Plugin Perfectionist configuration for sorting and organizing your code. Take customization to new levels while maintaining a consistent coding style with this setup
+shortDescription: All plugin rules with your own custom sorting
+keywords:
+  - eslint
+  - recommended custom config
+  - eslint configuration
+  - coding standards
+  - code quality
+  - javascript linting
+  - custom sorting
+  - eslint-plugin-perfectionist
+---
+
+import CodeTabs from '../../components/CodeTabs.svelte'
+import { dedent } from 'ts-dedent'
+
+Configuration for the `eslint-plugin-perfectionist` plugin, which provides all plugin rules with your predefined custom ordered alphabet.
+
+This configuration allows you to define your own custom order for sorting elements in your codebase as you truly desire.
+
+## When to Use
+
+Each rule in `eslint-plugin-perfectionist` offers a lot of options that should suit most use cases.
+
+If this is not enough, you may define your own alphabet and use the `recommended-custom` configuration to enforce a consistent custom order across various data structures in your codebase.
+
+Use this configuration to precisely tune how elements should be sorted while keeping readability and maintainability to their highest levels.
+
+## Usage
+
+You must provide an `alphabet` option in the `perfectionist` settings object or for each rule individually.
+This option should be a string that represents an ordered alphabet.
+
+Example: `01234564789abcdef...`
+
+Use the `Alphabet` utility class from `eslint-plugin-perfectionist/alphabet` to quickly generate a custom alphabet.
+
+<CodeTabs
+  code={[
+    {
+      source: dedent`
+        // eslint.config.js
+        import { Alphabet } from 'eslint-plugin-perfectionist/alphabet'
+        import perfectionist from 'eslint-plugin-perfectionist'
+        import naturalCompare from 'natural-compare-lite';
+
+        const myCustomAlphabet = Alphabet
+          .generateRecommendedAlphabet()
+          .sortingBy((a, b) => naturalCompare(a, b))
+          .getCharacters();
+
+        export default [
+          {
+            ...perfectionist.configs['recommended-custom'],
+            settings: {
+              perfectionist: {
+                  alphabet: myCustomAlphabet
+              }
+            }
+          }
+        ]
+      `,
+      name: 'Flat Config',
+      value: 'flat',
+    },
+    {
+      source: dedent`
+        // .eslintrc.js
+        import { Alphabet } from 'eslint-plugin-perfectionist/alphabet'
+        import perfectionist from 'eslint-plugin-perfectionist'
+        import naturalCompare from 'natural-compare-lite';
+
+        const myCustomAlphabet = Alphabet
+          .generateRecommendedAlphabet()
+          .sortingBy((a, b) => naturalCompare(a, b))
+          .getCharacters();
+
+        module.exports = {
+          extends: [
+            'plugin:perfectionist/recommended-custom-legacy',
+          ],
+          settings: {
+            perfectionist: {
+                alphabet: myCustomAlphabet
+            }
+          }
+        }
+      `,
+      name: 'Legacy Config',
+      value: 'legacy',
+    },
+]}
+type="config-type"
+client:load
+lang="tsx"
+/>
+
+## Alphabet class
+
+The `Alphabet` class from `eslint-plugin-perfectionist/alphabet` provides a set of methods to generate and manipulate alphabets.
+
+### Static generators
+
+#### - `static generateCompleteAlphabet(): Alphabet`
+
+Generates an alphabet containing all characters from the Unicode standard except for irrelevant [Unicode planes](https://en.wikipedia.org/wiki/Plane_(Unicode)).
+Contains the Unicode planes 0, 1, 2 and 3.
+
+#### - `static generateRecommendedAlphabet(): Alphabet`
+
+Generates an alphabet containing relevant characters from the Unicode standard. Contains the [Unicode planes](https://en.wikipedia.org/wiki/Plane_(Unicode)) 0 and 1.
+
+#### - `static generateFrom(values: string[] | string): Alphabet`
+
+Generates an alphabet from the given characters.
+
+### Adding/Removing characters
+
+#### - `pushCharacters(values: string[] | string): this`
+
+Adds specific characters to the end of the alphabet.
+
+#### - `removeCharacters(values: string[] | string): this`
+
+Removes specific characters from the alphabet.
+
+#### - `removeUnicodeRange({ start: number; end: number }): this`
+
+Removes specific characters from the alphabet by their range
+
+### Sorters
+
+#### - `sortByLocaleCompare(locales?: Intl.LocalesArgument): this`
+
+Sorts the alphabet by the locale order of the characters.
+
+#### - `sortByNaturalSort(locale?: string): this`
+
+Sorts the alphabet by the natural order of the characters using [natural-orderby](https://github.com/yobacca/natural-orderby).
+
+#### - `sortByCharCodeAt(): this`
+
+Sorts the alphabet by the character code point.
+
+#### - `sortBy(sortingFunction: (characterA: string, characterB: string) => number): this`
+
+Sorts the alphabet by the sorting function provided
+
+#### - `reverse(): this`
+
+Reverses the alphabet.
+
+### Other methods
+
+#### - `prioritizeCase(casePriority: 'lowercase' | 'uppercase'): this`
+
+For each character with a lower and upper case, permutes the two cases so that the alphabet is ordered by the case priority entered.
+
+```ts
+Alphabet.generateFrom('aAbBcdCD')
+.prioritizeCase('uppercase')
+// Returns 'AaBbCDcd'
+````
+
+#### - `placeAllWithCaseBeforeAllWithOtherCase(caseToComeFirst: 'uppercase' | 'lowercase'): this`
+
+Permutes characters with cases so that all characters with the entered case are put before the other characters.
+
+```ts
+Alphabet.generateFrom('aAbBcCdD')
+.placeAllWithCaseBeforeAllWithOtherCase('lowercase')
+// Returns 'abcdABCD'
+````
+
+#### - `placeCharacterBefore({ characterBefore: string; characterAfter: string }): this`
+
+Places a specific character right before another character in the alphabet.
+
+```ts
+Alphabet.generateFrom('ab-cd/')
+.placeCharacterBefore({ characterBefore: '/', characterAfter: '-' })
+// Returns 'ab/-cd'
+```
+
+#### - `placeCharacterAfter({ characterBefore: string; characterAfter: string }): this`
+
+Places a specific character right after another character in the alphabet.
+
+```ts
+Alphabet.generateFrom('ab-cd/')
+.placeCharacterAfter({ characterBefore: '/', characterAfter: '-' })
+// Returns 'abcd/-'
+```
+
+#### - `getCharacters(): string`
+
+Retrieves the characters from the alphabet.
diff --git a/docs/content/guide/getting-started.mdx b/docs/content/guide/getting-started.mdx
index c10fbe0..3f8e528 100644
--- a/docs/content/guide/getting-started.mdx
+++ b/docs/content/guide/getting-started.mdx
@@ -140,8 +140,9 @@ The highest priority is given to the settings of a particular rule. Next comes t
 
 In settings you can set the following options:
 
-- `type` — The type of sorting. Possible values are `'alphabetical'`, `'natural'` and `'line-length'`.
+- `type` — The type of sorting. Possible values are `'alphabetical'`, `'natural'`, `'line-length'` and `custom`.
 - `order` — The order of sorting. Possible values are `'asc'` and `'desc'`.
+- `alphabet` — The custom alphabet to use when `type` is `custom`.
 - `ignoreCase` — Ignore case when sorting.
 - `ignorePattern` — Ignore sorting for elements that match the pattern.
 - `specialCharacters` — Control whether special characters should be kept, trimmed or removed before sorting. Values can be `'keep'`, `'trim'` or `'remove'`.
diff --git a/docs/content/rules/sort-array-includes.mdx b/docs/content/rules/sort-array-includes.mdx
index 48434a3..c0a3f16 100644
--- a/docs/content/rules/sort-array-includes.mdx
+++ b/docs/content/rules/sort-array-includes.mdx
@@ -128,6 +128,7 @@ Specifies the sorting method.
 - `'alphabetical'` — Sort items alphabetically (e.g., “a” < “b” < “c”) using [localeCompare](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare).
 - `'natural'` — Sort items in a [natural](https://github.com/yobacca/natural-orderby) order (e.g., “item2” < “item10”).
 - `'line-length'` — Sort items by the length of the code line (shorter lines first).
+- `'custom'` — Sort items using the alphabet entered in the [`alphabet`](#alphabet) option.
 
 ### order
 
@@ -138,6 +139,16 @@ Determines whether the sorted items should be in ascending or descending order.
 - `'asc'` — Sort items in ascending order (A to Z, 1 to 9).
 - `'desc'` — Sort items in descending order (Z to A, 9 to 1).
 
+### alphabet
+
+<sub>default: `''`</sub>
+
+Only used when the [`type`](#type) option is set to `'custom'`. Specifies the custom alphabet to use when sorting.
+
+Use the `Alphabet` utility class from `eslint-plugin-perfectionist/alphabet` to quickly generate a custom alphabet.
+
+Example: `0123456789abcdef...`
+
 ### ignoreCase
 
 <sub>default: `true`</sub>
diff --git a/docs/content/rules/sort-classes.mdx b/docs/content/rules/sort-classes.mdx
index 5065aae..e3da41d 100644
--- a/docs/content/rules/sort-classes.mdx
+++ b/docs/content/rules/sort-classes.mdx
@@ -163,6 +163,7 @@ Specifies the sorting method.
 - `'alphabetical'` — Sort items alphabetically (e.g., “a” < “b” < “c”) using [localeCompare](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare).
 - `'natural'` — Sort items in a [natural](https://github.com/yobacca/natural-orderby) order (e.g., “item2” < “item10”).
 - `'line-length'` — Sort items by the length of the code line (shorter lines first).
+- `'custom'` — Sort items using the alphabet entered in the [`alphabet`](#alphabet) option.
 
 ### order
 
@@ -173,6 +174,16 @@ Determines whether the sorted items should be in ascending or descending order.
 - `'asc'` — Sort items in ascending order (A to Z, 1 to 9).
 - `'desc'` — Sort items in descending order (Z to A, 9 to 1).
 
+### alphabet
+
+<sub>default: `''`</sub>
+
+Only used when the [`type`](#type) option is set to `'custom'`. Specifies the custom alphabet to use when sorting.
+
+Use the `Alphabet` utility class from `eslint-plugin-perfectionist/alphabet` to quickly generate a custom alphabet.
+
+Example: `0123456789abcdef...`
+
 ### ignoreCase
 
 <sub>default: `true`</sub>
diff --git a/docs/content/rules/sort-decorators.mdx b/docs/content/rules/sort-decorators.mdx
index f8519e5..62e3163 100644
--- a/docs/content/rules/sort-decorators.mdx
+++ b/docs/content/rules/sort-decorators.mdx
@@ -127,6 +127,7 @@ Specifies the sorting method.
 - `'alphabetical'` — Sort items alphabetically (e.g., “a” < “b” < “c”) using [localeCompare](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare).
 - `'natural'` — Sort items in a [natural](https://github.com/yobacca/natural-orderby) order (e.g., “item2” < “item10”).
 - `'line-length'` — Sort items by the length of the code line (shorter lines first).
+- `'custom'` — Sort items using the alphabet entered in the [`alphabet`](#alphabet) option.
 
 ### order
 
@@ -137,6 +138,16 @@ Determines whether the sorted items should be in ascending or descending order.
 - `'asc'` — Sort items in ascending order (A to Z, 1 to 9).
 - `'desc'` — Sort items in descending order (Z to A, 9 to 1).
 
+### alphabet
+
+<sub>default: `''`</sub>
+
+Only used when the [`type`](#type) option is set to `'custom'`. Specifies the custom alphabet to use when sorting.
+
+Use the `Alphabet` utility class from `eslint-plugin-perfectionist/alphabet` to quickly generate a custom alphabet.
+
+Example: `0123456789abcdef...`
+
 ### ignoreCase
 
 <sub>default: `true`</sub>
diff --git a/docs/content/rules/sort-enums.mdx b/docs/content/rules/sort-enums.mdx
index d8b5883..de6cd85 100644
--- a/docs/content/rules/sort-enums.mdx
+++ b/docs/content/rules/sort-enums.mdx
@@ -93,6 +93,7 @@ Specifies the sorting method.
 - `'alphabetical'` — Sort items alphabetically (e.g., “a” < “b” < “c”) using [localeCompare](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare).
 - `'natural'` — Sort items in a [natural](https://github.com/yobacca/natural-orderby) order (e.g., “item2” < “item10”).
 - `'line-length'` — Sort items by the length of the code line (shorter lines first).
+- `'custom'` — Sort items using the alphabet entered in the [`alphabet`](#alphabet) option.
 
 ### order
 
@@ -103,6 +104,16 @@ Determines whether the sorted items should be in ascending or descending order.
 - `'asc'` — Sort items in ascending order (A to Z, 1 to 9).
 - `'desc'` — Sort items in descending order (Z to A, 9 to 1).
 
+### alphabet
+
+<sub>default: `''`</sub>
+
+Only used when the [`type`](#type) option is set to `'custom'`. Specifies the custom alphabet to use when sorting.
+
+Use the `Alphabet` utility class from `eslint-plugin-perfectionist/alphabet` to quickly generate a custom alphabet.
+
+Example: `0123456789abcdef...`
+
 ### ignoreCase
 
 <sub>default: `true`</sub>
diff --git a/docs/content/rules/sort-exports.mdx b/docs/content/rules/sort-exports.mdx
index 12b1953..6968ade 100644
--- a/docs/content/rules/sort-exports.mdx
+++ b/docs/content/rules/sort-exports.mdx
@@ -87,6 +87,7 @@ Specifies the sorting method.
 - `'alphabetical'` — Sort items alphabetically (e.g., “a” < “b” < “c”) using [localeCompare](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare).
 - `'natural'` — Sort items in a [natural](https://github.com/yobacca/natural-orderby) order (e.g., “item2” < “item10”).
 - `'line-length'` — Sort items by the length of the code line (shorter lines first).
+- `'custom'` — Sort items using the alphabet entered in the [`alphabet`](#alphabet) option.
 
 ### order
 
@@ -97,6 +98,16 @@ Determines whether the sorted items should be in ascending or descending order.
 - `'asc'` — Sort items in ascending order (A to Z, 1 to 9).
 - `'desc'` — Sort items in descending order (Z to A, 9 to 1).
 
+### alphabet
+
+<sub>default: `''`</sub>
+
+Only used when the [`type`](#type) option is set to `'custom'`. Specifies the custom alphabet to use when sorting.
+
+Use the `Alphabet` utility class from `eslint-plugin-perfectionist/alphabet` to quickly generate a custom alphabet.
+
+Example: `0123456789abcdef...`
+
 ### ignoreCase
 
 <sub>default: `true`</sub>
diff --git a/docs/content/rules/sort-heritage-clauses.mdx b/docs/content/rules/sort-heritage-clauses.mdx
index 5a0ab52..2ef7e77 100644
--- a/docs/content/rules/sort-heritage-clauses.mdx
+++ b/docs/content/rules/sort-heritage-clauses.mdx
@@ -70,6 +70,7 @@ Specifies the sorting method.
 - `'alphabetical'` — Sort items alphabetically (e.g., “a” < “b” < “c”) using [localeCompare](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare).
 - `'natural'` — Sort items in a [natural](https://github.com/yobacca/natural-orderby) order (e.g., “item2” < “item10”).
 - `'line-length'` — Sort items by the length of the code line (shorter lines first).
+- `'custom'` — Sort items using the alphabet entered in the [`alphabet`](#alphabet) option.
 
 ### order
 
@@ -80,6 +81,16 @@ Determines whether the sorted items should be in ascending or descending order.
 - `'asc'` — Sort items in ascending order (A to Z, 1 to 9).
 - `'desc'` — Sort items in descending order (Z to A, 9 to 1).
 
+### alphabet
+
+<sub>default: `''`</sub>
+
+Only used when the [`type`](#type) option is set to `'custom'`. Specifies the custom alphabet to use when sorting.
+
+Use the `Alphabet` utility class from `eslint-plugin-perfectionist/alphabet` to quickly generate a custom alphabet.
+
+Example: `0123456789abcdef...`
+
 ### ignoreCase
 
 <sub>default: `true`</sub>
diff --git a/docs/content/rules/sort-imports.mdx b/docs/content/rules/sort-imports.mdx
index 057a458..320f1e1 100644
--- a/docs/content/rules/sort-imports.mdx
+++ b/docs/content/rules/sort-imports.mdx
@@ -126,6 +126,7 @@ Specifies the sorting method.
 - `'alphabetical'` — Sort items alphabetically (e.g., “a” < “b” < “c”) using [localeCompare](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare).
 - `'natural'` — Sort items in a [natural](https://github.com/yobacca/natural-orderby) order (e.g., “item2” < “item10”).
 - `'line-length'` — Sort items by the length of the code line (shorter lines first).
+- `'custom'` — Sort items using the alphabet entered in the [`alphabet`](#alphabet) option.
 
 ### order
 
@@ -136,6 +137,16 @@ Determines whether the sorted items should be in ascending or descending order.
 - `'asc'` — Sort items in ascending order (A to Z, 1 to 9).
 - `'desc'` — Sort items in descending order (Z to A, 9 to 1).
 
+### alphabet
+
+<sub>default: `''`</sub>
+
+Only used when the [`type`](#type) option is set to `'custom'`. Specifies the custom alphabet to use when sorting.
+
+Use the `Alphabet` utility class from `eslint-plugin-perfectionist/alphabet` to quickly generate a custom alphabet.
+
+Example: `0123456789abcdef...`
+
 ### ignoreCase
 
 <sub>default: `true`</sub>
diff --git a/docs/content/rules/sort-interfaces.mdx b/docs/content/rules/sort-interfaces.mdx
index b62f3cb..2778809 100644
--- a/docs/content/rules/sort-interfaces.mdx
+++ b/docs/content/rules/sort-interfaces.mdx
@@ -142,6 +142,7 @@ Specifies the sorting method.
 - `'alphabetical'` — Sort items alphabetically (e.g., “a” < “b” < “c”) using [localeCompare](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare).
 - `'natural'` — Sort items in a [natural](https://github.com/yobacca/natural-orderby) order (e.g., “item2” < “item10”).
 - `'line-length'` — Sort items by the length of the code line (shorter lines first).
+- `'custom'` — Sort items using the alphabet entered in the [`alphabet`](#alphabet) option.
 
 ### order
 
@@ -152,6 +153,16 @@ Determines whether the sorted items should be in ascending or descending order.
 - `'asc'` — Sort items in ascending order (A to Z, 1 to 9).
 - `'desc'` — Sort items in descending order (Z to A, 9 to 1).
 
+### alphabet
+
+<sub>default: `''`</sub>
+
+Only used when the [`type`](#type) option is set to `'custom'`. Specifies the custom alphabet to use when sorting.
+
+Use the `Alphabet` utility class from `eslint-plugin-perfectionist/alphabet` to quickly generate a custom alphabet.
+
+Example: `0123456789abcdef...`
+
 ### ignoreCase
 
 <sub>default: `true`</sub>
@@ -273,10 +284,10 @@ interface User {
   firstName: string // unknown
   lastName: string  // unknown
   username: string  // unknown
-  job: {            // multiline
+  job: {            // multiline-member
     // Stuff about job
   }
-  localization: {   // multiline
+  localization: {   // multiline-member
     // Stuff about localization
   }
 }
@@ -289,7 +300,7 @@ interface User {
   groups: [
     'unknown',
     'method',
-    'multiline',
+    'multiline-member',
   ]
 }
 ```
@@ -395,7 +406,7 @@ Current API:
 <sub>
   type: `Array<CustomGroupDefinition | CustomGroupAnyOfDefinition>`
 </sub>
-<sub>default: `{}`</sub>
+<sub>default: `[]`</sub>
 
 You can define your own groups and use regexp patterns to match specific interface members.
 
@@ -418,7 +429,7 @@ interface User {
   age: number                // unknown
   isAdmin: boolean           // unknown
   lastUpdated_metadata: Date // bottom
-  localization?: {            // multiline
+  localization?: {            // optional-multiline-member
     // Stuff about localization
   }
   version_metadata: string   // bottom
@@ -432,7 +443,7 @@ interface User {
    groups: [
 +    'top',                           // [!code ++]
      'unknown',
-+    ['optional-multiline', 'bottom'] // [!code ++]
++    ['optional-multiline-member', 'bottom'] // [!code ++]
    ],
 +  customGroups: [                              // [!code ++]
 +    {                                          // [!code ++]
@@ -477,7 +488,7 @@ interface User {
                   newlinesBetween: 'ignore',
                   groupKind: 'mixed',
                   groups: [],
-                  customGroups: {},
+                  customGroups: [],
                 },
               ],
             },
@@ -508,7 +519,7 @@ interface User {
                 newlinesBetween: 'ignore',
                 groupKind: 'mixed',
                 groups: [],
-                customGroups: {},
+                customGroups: [],
               },
             ],
           },
diff --git a/docs/content/rules/sort-intersection-types.mdx b/docs/content/rules/sort-intersection-types.mdx
index 90f5b33..b56f6d7 100644
--- a/docs/content/rules/sort-intersection-types.mdx
+++ b/docs/content/rules/sort-intersection-types.mdx
@@ -83,6 +83,7 @@ Specifies the sorting method.
 - `'alphabetical'` — Sort items alphabetically (e.g., “a” < “b” < “c”) using [localeCompare](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare).
 - `'natural'` — Sort items in a [natural](https://github.com/yobacca/natural-orderby) order (e.g., “item2” < “item10”).
 - `'line-length'` — Sort items by the length of the code line (shorter lines first).
+- `'custom'` — Sort items using the alphabet entered in the [`alphabet`](#alphabet) option.
 
 ### order
 
@@ -93,6 +94,16 @@ Determines whether the sorted items should be in ascending or descending order.
 - `'asc'` — Sort items in ascending order (A to Z, 1 to 9).
 - `'desc'` — Sort items in descending order (Z to A, 9 to 1).
 
+### alphabet
+
+<sub>default: `''`</sub>
+
+Only used when the [`type`](#type) option is set to `'custom'`. Specifies the custom alphabet to use when sorting.
+
+Use the `Alphabet` utility class from `eslint-plugin-perfectionist/alphabet` to quickly generate a custom alphabet.
+
+Example: `0123456789abcdef...`
+
 ### ignoreCase
 
 <sub>default: `true`</sub>
diff --git a/docs/content/rules/sort-jsx-props.mdx b/docs/content/rules/sort-jsx-props.mdx
index 3b030dc..4fd7b6a 100644
--- a/docs/content/rules/sort-jsx-props.mdx
+++ b/docs/content/rules/sort-jsx-props.mdx
@@ -143,6 +143,7 @@ Specifies the sorting method.
 - `'alphabetical'` — Sort items alphabetically (e.g., “a” < “b” < “c”) using [localeCompare](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare).
 - `'natural'` — Sort items in a [natural](https://github.com/yobacca/natural-orderby) order (e.g., “item2” < “item10”).
 - `'line-length'` — Sort items by the length of the code line (shorter lines first).
+- `'custom'` — Sort items using the alphabet entered in the [`alphabet`](#alphabet) option.
 
 ### order
 
@@ -153,6 +154,16 @@ Determines whether the sorted items should be in ascending or descending order.
 - `'asc'` — Sort items in ascending order (A to Z, 1 to 9).
 - `'desc'` — Sort items in descending order (Z to A, 9 to 1).
 
+### alphabet
+
+<sub>default: `''`</sub>
+
+Only used when the [`type`](#type) option is set to `'custom'`. Specifies the custom alphabet to use when sorting.
+
+Use the `Alphabet` utility class from `eslint-plugin-perfectionist/alphabet` to quickly generate a custom alphabet.
+
+Example: `0123456789abcdef...`
+
 ### ignoreCase
 
 <sub>default: `true`</sub>
diff --git a/docs/content/rules/sort-maps.mdx b/docs/content/rules/sort-maps.mdx
index febb1e5..acc41f3 100644
--- a/docs/content/rules/sort-maps.mdx
+++ b/docs/content/rules/sort-maps.mdx
@@ -88,6 +88,7 @@ Specifies the sorting method.
 - `'alphabetical'` — Sort items alphabetically (e.g., “a” < “b” < “c”) using [localeCompare](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare).
 - `'natural'` — Sort items in a [natural](https://github.com/yobacca/natural-orderby) order (e.g., “item2” < “item10”).
 - `'line-length'` — Sort items by the length of the code line (shorter lines first).
+- `'custom'` — Sort items using the alphabet entered in the [`alphabet`](#alphabet) option.
 
 ### order
 
@@ -98,6 +99,16 @@ Determines whether the sorted items should be in ascending or descending order.
 - `'asc'` — Sort items in ascending order (A to Z, 1 to 9).
 - `'desc'` — Sort items in descending order (Z to A, 9 to 1).
 
+### alphabet
+
+<sub>default: `''`</sub>
+
+Only used when the [`type`](#type) option is set to `'custom'`. Specifies the custom alphabet to use when sorting.
+
+Use the `Alphabet` utility class from `eslint-plugin-perfectionist/alphabet` to quickly generate a custom alphabet.
+
+Example: `0123456789abcdef...`
+
 ### ignoreCase
 
 <sub>default: `true`</sub>
diff --git a/docs/content/rules/sort-modules.mdx b/docs/content/rules/sort-modules.mdx
index 2a57d71..1db7aa7 100644
--- a/docs/content/rules/sort-modules.mdx
+++ b/docs/content/rules/sort-modules.mdx
@@ -186,6 +186,7 @@ Specifies the sorting method.
 - `'alphabetical'` — Sort items alphabetically (e.g., “a” < “b” < “c”) using [localeCompare](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare).
 - `'natural'` — Sort items in a [natural](https://github.com/yobacca/natural-orderby) order (e.g., “item2” < “item10”).
 - `'line-length'` — Sort items by the length of the code line (shorter lines first).
+- `'custom'` — Sort items using the alphabet entered in the [`alphabet`](#alphabet) option.
 
 ### order
 
@@ -196,6 +197,16 @@ Determines whether the sorted items should be in ascending or descending order.
 - `'asc'` — Sort items in ascending order (A to Z, 1 to 9).
 - `'desc'` — Sort items in descending order (Z to A, 9 to 1).
 
+### alphabet
+
+<sub>default: `''`</sub>
+
+Only used when the [`type`](#type) option is set to `'custom'`. Specifies the custom alphabet to use when sorting.
+
+Use the `Alphabet` utility class from `eslint-plugin-perfectionist/alphabet` to quickly generate a custom alphabet.
+
+Example: `0123456789abcdef...`
+
 ### ignoreCase
 
 <sub>default: `true`</sub>
diff --git a/docs/content/rules/sort-named-exports.mdx b/docs/content/rules/sort-named-exports.mdx
index 6ba10b2..e96fdde 100644
--- a/docs/content/rules/sort-named-exports.mdx
+++ b/docs/content/rules/sort-named-exports.mdx
@@ -106,6 +106,7 @@ Specifies the sorting method.
 - `'alphabetical'` — Sort items alphabetically (e.g., “a” < “b” < “c”) using [localeCompare](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare).
 - `'natural'` — Sort items in a [natural](https://github.com/yobacca/natural-orderby) order (e.g., “item2” < “item10”).
 - `'line-length'` — Sort items by the length of the code line (shorter lines first).
+- `'custom'` — Sort items using the alphabet entered in the [`alphabet`](#alphabet) option.
 
 ### order
 
@@ -116,6 +117,16 @@ Determines whether the sorted items should be in ascending or descending order.
 - `'asc'` — Sort items in ascending order (A to Z, 1 to 9).
 - `'desc'` — Sort items in descending order (Z to A, 9 to 1).
 
+### alphabet
+
+<sub>default: `''`</sub>
+
+Only used when the [`type`](#type) option is set to `'custom'`. Specifies the custom alphabet to use when sorting.
+
+Use the `Alphabet` utility class from `eslint-plugin-perfectionist/alphabet` to quickly generate a custom alphabet.
+
+Example: `0123456789abcdef...`
+
 ### ignoreCase
 
 <sub>default: `true`</sub>
diff --git a/docs/content/rules/sort-named-imports.mdx b/docs/content/rules/sort-named-imports.mdx
index d7fb3a3..fddb8fd 100644
--- a/docs/content/rules/sort-named-imports.mdx
+++ b/docs/content/rules/sort-named-imports.mdx
@@ -107,6 +107,7 @@ Specifies the sorting method.
 - `'alphabetical'` — Sort items alphabetically (e.g., “a” < “b” < “c”) using [localeCompare](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare).
 - `'natural'` — Sort items in a [natural](https://github.com/yobacca/natural-orderby) order (e.g., “item2” < “item10”).
 - `'line-length'` — Sort items by the length of the code line (shorter lines first).
+- `'custom'` — Sort items using the alphabet entered in the [`alphabet`](#alphabet) option.
 
 ### order
 
@@ -117,6 +118,16 @@ Determines whether the sorted items should be in ascending or descending order.
 - `'asc'` — Sort items in ascending order (A to Z, 1 to 9).
 - `'desc'` — Sort items in descending order (Z to A, 9 to 1).
 
+### alphabet
+
+<sub>default: `''`</sub>
+
+Only used when the [`type`](#type) option is set to `'custom'`. Specifies the custom alphabet to use when sorting.
+
+Use the `Alphabet` utility class from `eslint-plugin-perfectionist/alphabet` to quickly generate a custom alphabet.
+
+Example: `0123456789abcdef...`
+
 ### ignoreCase
 
 <sub>default: `true`</sub>
diff --git a/docs/content/rules/sort-object-types.mdx b/docs/content/rules/sort-object-types.mdx
index afce158..6502ba4 100644
--- a/docs/content/rules/sort-object-types.mdx
+++ b/docs/content/rules/sort-object-types.mdx
@@ -104,6 +104,7 @@ Specifies the sorting method.
 - `'alphabetical'` — Sort items alphabetically (e.g., “a” < “b” < “c”) using [localeCompare](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare).
 - `'natural'` — Sort items in a [natural](https://github.com/yobacca/natural-orderby) order (e.g., “item2” < “item10”).
 - `'line-length'` — Sort items by the length of the code line (shorter lines first).
+- `'custom'` — Sort items using the alphabet entered in the [`alphabet`](#alphabet) option.
 
 ### order
 
@@ -114,6 +115,16 @@ Determines whether the sorted items should be in ascending or descending order.
 - `'asc'` — Sort items in ascending order (A to Z, 1 to 9).
 - `'desc'` — Sort items in descending order (Z to A, 9 to 1).
 
+### alphabet
+
+<sub>default: `''`</sub>
+
+Only used when the [`type`](#type) option is set to `'custom'`. Specifies the custom alphabet to use when sorting.
+
+Use the `Alphabet` utility class from `eslint-plugin-perfectionist/alphabet` to quickly generate a custom alphabet.
+
+Example: `0123456789abcdef...`
+
 ### ignoreCase
 
 <sub>default: `true`</sub>
@@ -238,10 +249,10 @@ type User = {
   firstName: string // unknown
   lastName: string  // unknown
   username: string  // unknown
-  job: {            // multiline
+  job: {            // multiline-member
     // Stuff about job
   }
-  localization: {   // multiline
+  localization: {   // multiline-member
     // Stuff about localization
   }
 }
@@ -360,7 +371,7 @@ Current API:
 <sub>
   type: `Array<CustomGroupDefinition | CustomGroupAnyOfDefinition>`
 </sub>
-<sub>default: `{}`</sub>
+<sub>default: `[]`</sub>
 
 You can define your own groups and use regexp pattern to match specific object type members.
 
@@ -383,7 +394,7 @@ type User = {
   age: number                // unknown
   isAdmin: boolean           // unknown
   lastUpdated_metadata: Date // bottom
-  localization?: {            // multiline
+  localization?: {            // optional-multiline-member
     // Stuff about localization
   }
   version_metadata: string   // bottom
@@ -397,7 +408,7 @@ type User = {
    groups: [
 +    'top',                           // [!code ++]
      'unknown',
-+    ['optional-multiline', 'bottom'] // [!code ++]
++    ['optional-multiline-member', 'bottom'] // [!code ++]
    ],
 +  customGroups: [                              // [!code ++]
 +    {                                          // [!code ++]
@@ -441,7 +452,7 @@ type User = {
                   partitionByNewLine: false,
                   newlinesBetween: 'ignore',
                   groups: [],
-                  customGroups: {},
+                  customGroups: [],
                 },
               ],
             },
@@ -471,7 +482,7 @@ type User = {
                 partitionByNewLine: false,
                 newlinesBetween: 'ignore',
                 groups: [],
-                customGroups: {},
+                customGroups: [],
               },
             ],
           },
diff --git a/docs/content/rules/sort-objects.mdx b/docs/content/rules/sort-objects.mdx
index b509d2f..2a82484 100644
--- a/docs/content/rules/sort-objects.mdx
+++ b/docs/content/rules/sort-objects.mdx
@@ -162,6 +162,7 @@ Specifies the sorting method.
 - `'alphabetical'` — Sort items alphabetically (e.g., “a” < “b” < “c”) using [localeCompare](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare).
 - `'natural'` — Sort items in a [natural](https://github.com/yobacca/natural-orderby) order (e.g., “item2” < “item10”).
 - `'line-length'` — Sort items by the length of the code line (shorter lines first).
+- `'custom'` — Sort items using the alphabet entered in the [`alphabet`](#alphabet) option.
 
 ### order
 
@@ -172,6 +173,16 @@ Determines whether the sorted items should be in ascending or descending order.
 - `'asc'` — Sort items in ascending order (A to Z, 1 to 9).
 - `'desc'` — Sort items in descending order (Z to A, 9 to 1).
 
+### alphabet
+
+<sub>default: `''`</sub>
+
+Only used when the [`type`](#type) option is set to `'custom'`. Specifies the custom alphabet to use when sorting.
+
+Use the `Alphabet` utility class from `eslint-plugin-perfectionist/alphabet` to quickly generate a custom alphabet.
+
+Example: `0123456789abcdef...`
+
 ### ignoreCase
 
 <sub>default: `true`</sub>
diff --git a/docs/content/rules/sort-sets.mdx b/docs/content/rules/sort-sets.mdx
index b20e93b..99bc8d8 100644
--- a/docs/content/rules/sort-sets.mdx
+++ b/docs/content/rules/sort-sets.mdx
@@ -134,6 +134,7 @@ Specifies the sorting method.
 - `'alphabetical'` — Sort items alphabetically (e.g., “a” < “b” < “c”) using [localeCompare](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare).
 - `'natural'` — Sort items in a [natural](https://github.com/yobacca/natural-orderby) order (e.g., “item2” < “item10”).
 - `'line-length'` — Sort items by the length of the code line (shorter lines first).
+- `'custom'` — Sort items using the alphabet entered in the [`alphabet`](#alphabet) option.
 
 ### order
 
@@ -144,6 +145,16 @@ Determines whether the sorted items should be in ascending or descending order.
 - `'asc'` — Sort items in ascending order (A to Z, 1 to 9).
 - `'desc'` — Sort items in descending order (Z to A, 9 to 1).
 
+### alphabet
+
+<sub>default: `''`</sub>
+
+Only used when the [`type`](#type) option is set to `'custom'`. Specifies the custom alphabet to use when sorting.
+
+Use the `Alphabet` utility class from `eslint-plugin-perfectionist/alphabet` to quickly generate a custom alphabet.
+
+Example: `0123456789abcdef...`
+
 ### ignoreCase
 
 <sub>default: `true`</sub>
diff --git a/docs/content/rules/sort-switch-case.mdx b/docs/content/rules/sort-switch-case.mdx
index 455618b..070f549 100644
--- a/docs/content/rules/sort-switch-case.mdx
+++ b/docs/content/rules/sort-switch-case.mdx
@@ -155,6 +155,7 @@ Specifies the sorting method.
 - `'alphabetical'` — Sort items alphabetically (e.g., “a” < “b” < “c”) using [localeCompare](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare).
 - `'natural'` — Sort items in a [natural](https://github.com/yobacca/natural-orderby) order (e.g., “item2” < “item10”).
 - `'line-length'` — Sort items by the length of the code line (shorter lines first).
+- `'custom'` — Sort items using the alphabet entered in the [`alphabet`](#alphabet) option.
 
 ### order
 
@@ -165,6 +166,16 @@ Determines whether the sorted items should be in ascending or descending order.
 - `'asc'` — Sort items in ascending order (A to Z, 1 to 9).
 - `'desc'` — Sort items in descending order (Z to A, 9 to 1).
 
+### alphabet
+
+<sub>default: `''`</sub>
+
+Only used when the [`type`](#type) option is set to `'custom'`. Specifies the custom alphabet to use when sorting.
+
+Use the `Alphabet` utility class from `eslint-plugin-perfectionist/alphabet` to quickly generate a custom alphabet.
+
+Example: `0123456789abcdef...`
+
 ### ignoreCase
 
 <sub>default: `true`</sub>
diff --git a/docs/content/rules/sort-union-types.mdx b/docs/content/rules/sort-union-types.mdx
index 162f441..f331603 100644
--- a/docs/content/rules/sort-union-types.mdx
+++ b/docs/content/rules/sort-union-types.mdx
@@ -103,6 +103,7 @@ Specifies the sorting method.
 - `'alphabetical'` — Sort items alphabetically (e.g., “a” < “b” < “c”) using [localeCompare](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare).
 - `'natural'` — Sort items in a [natural](https://github.com/yobacca/natural-orderby) order (e.g., “item2” < “item10”).
 - `'line-length'` — Sort items by the length of the code line (shorter lines first).
+- `'custom'` — Sort items using the alphabet entered in the [`alphabet`](#alphabet) option.
 
 ### order
 
@@ -113,6 +114,16 @@ Determines whether the sorted items should be in ascending or descending order.
 - `'asc'` — Sort items in ascending order (A to Z, 1 to 9).
 - `'desc'` — Sort items in descending order (Z to A, 9 to 1).
 
+### alphabet
+
+<sub>default: `''`</sub>
+
+Only used when the [`type`](#type) option is set to `'custom'`. Specifies the custom alphabet to use when sorting.
+
+Use the `Alphabet` utility class from `eslint-plugin-perfectionist/alphabet` to quickly generate a custom alphabet.
+
+Example: `0123456789abcdef...`
+
 ### ignoreCase
 
 <sub>default: `true`</sub>
diff --git a/docs/content/rules/sort-variable-declarations.mdx b/docs/content/rules/sort-variable-declarations.mdx
index ed2bc97..c930f91 100644
--- a/docs/content/rules/sort-variable-declarations.mdx
+++ b/docs/content/rules/sort-variable-declarations.mdx
@@ -87,6 +87,7 @@ Specifies the sorting method.
 - `'alphabetical'` — Sort items alphabetically (e.g., “a” < “b” < “c”) using [localeCompare](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare).
 - `'natural'` — Sort items in a [natural](https://github.com/yobacca/natural-orderby) order (e.g., “item2” < “item10”).
 - `'line-length'` — Sort items by the length of the code line (shorter lines first).
+- `'custom'` — Sort items using the alphabet entered in the [`alphabet`](#alphabet) option.
 
 ### order
 
@@ -97,6 +98,16 @@ Determines whether the sorted items should be in ascending or descending order.
 - `'asc'` — Sort items in ascending order (A to Z, 1 to 9).
 - `'desc'` — Sort items in descending order (Z to A, 9 to 1).
 
+### alphabet
+
+<sub>default: `''`</sub>
+
+Only used when the [`type`](#type) option is set to `'custom'`. Specifies the custom alphabet to use when sorting.
+
+Use the `Alphabet` utility class from `eslint-plugin-perfectionist/alphabet` to quickly generate a custom alphabet.
+
+Example: `0123456789abcdef...`
+
 ### ignoreCase
 
 <sub>default: `true`</sub>
diff --git a/docs/pages/configs/index.astro b/docs/pages/configs/index.astro
index ff6ec5c..9e545b6 100644
--- a/docs/pages/configs/index.astro
+++ b/docs/pages/configs/index.astro
@@ -19,6 +19,11 @@ let configs = [
     url: '/configs/recommended-line-length',
     name: 'recommended-line-length',
   },
+  {
+    description: 'All plugin rules with sorting by your own custom order',
+    url: '/configs/recommended-custom',
+    name: 'recommended-custom',
+  },
 ]
 ---
 
diff --git a/docs/utils/pages.ts b/docs/utils/pages.ts
index 5d1b851..a922d73 100644
--- a/docs/utils/pages.ts
+++ b/docs/utils/pages.ts
@@ -46,6 +46,10 @@ export let pages: ({ links: Page[] } & Page)[] = [
         url: '/configs/recommended-line-length',
         title: 'recommended-line-length',
       },
+      {
+        url: '/configs/recommended-custom',
+        title: 'recommended-custom',
+      },
     ],
     title: 'Configs',
     url: '/configs',
diff --git a/index.ts b/index.ts
index c69cf4a..0d47ebb 100644
--- a/index.ts
+++ b/index.ts
@@ -48,15 +48,17 @@ interface PluginConfig {
     'recommended-alphabetical-legacy': Linter.LegacyConfig
     'recommended-line-length-legacy': Linter.LegacyConfig
     'recommended-natural-legacy': Linter.LegacyConfig
+    'recommended-custom-legacy': Linter.LegacyConfig
     'recommended-alphabetical': Linter.Config
     'recommended-line-length': Linter.Config
     'recommended-natural': Linter.Config
+    'recommended-custom': Linter.Config
   }
   name: string
 }
 
 interface BaseOptions {
-  type: 'alphabetical' | 'line-length' | 'natural'
+  type: 'alphabetical' | 'line-length' | 'natural' | 'custom'
   order: 'desc' | 'asc'
 }
 
@@ -123,6 +125,10 @@ export default {
       type: 'natural',
       order: 'asc',
     }),
+    'recommended-custom-legacy': createLegacyConfig({
+      type: 'custom',
+      order: 'asc',
+    }),
     'recommended-alphabetical': createConfig({
       type: 'alphabetical',
       order: 'asc',
@@ -135,5 +141,9 @@ export default {
       type: 'natural',
       order: 'asc',
     }),
+    'recommended-custom': createConfig({
+      type: 'custom',
+      order: 'asc',
+    }),
   },
 } as PluginConfig
diff --git a/package.json b/package.json
index e46a6de..facb2d7 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,10 @@
       "types": "./dist/index.d.ts",
       "default": "./dist/index.js"
     },
+    "./alphabet": {
+      "types": "./dist/alphabet.d.ts",
+      "default": "./dist/utils/alphabet.js"
+    },
     "./package.json": "./package.json"
   },
   "files": [
diff --git a/readme.md b/readme.md
index 63dc618..713e42e 100644
--- a/readme.md
+++ b/readme.md
@@ -165,6 +165,7 @@ module.exports = {
 | [recommended-alphabetical](https://perfectionist.dev/configs/recommended-alphabetical) | All plugin rules with alphabetical sorting in ascending order    |
 | [recommended-natural](https://perfectionist.dev/configs/recommended-natural)           | All plugin rules with natural sorting in ascending order         |
 | [recommended-line-length](https://perfectionist.dev/configs/recommended-line-length)   | All plugin rules with sorting by line length in descending order |
+| [recommended-custom](https://perfectionist.dev/configs/recommended-custom)             | All plugin rules with sorting by your own custom order           |
 
 ## Rules
 
@@ -201,15 +202,15 @@ module.exports = {
 
 ### Can I automatically fix problems in the editor?
 
-Yes. To do this, you need to enable autofix in ESLint when you save the file in your editor. Instructions for your editor can be found [here](https://perfectionist.dev/guide/integrations).
+Yes. To do this, you need to enable autofix in ESLint when you save the file in your editor. You may find instructions for your editor can be found [here](https://perfectionist.dev/guide/integrations).
 
 ### Is it safe?
 
-On the whole, yes. We are very careful to make sure that the work of the plugin does not negatively affect the work of the code. For example, the plugin takes into account spread operators in JSX and objects, comments to the code. Safety is our priority. If you encounter any problem, you can create an [issue](https://github.com/azat-io/eslint-plugin-perfectionist/issues/new/choose).
+Overall, yes. We want to make sure that the work of the plugin does not negatively affect the behavior of the code. For example, the plugin takes into account spread operators in JSX and objects, comments to the code. Safety is our priority. If you encounter any problem, you can create an [issue](https://github.com/azat-io/eslint-plugin-perfectionist/issues/new/choose).
 
 ### Why not Prettier?
 
-I love Prettier. However, this is not his area of responsibility. Prettier is used for formatting, and ESLint is also used for styling. For example, changing the order of imports can affect how the code works (console.log calls, fetch, style loading). Prettier should not change the AST. There is a cool article about this: ["The Blurry Line Between Formatting and Style"](https://blog.joshuakgoldberg.com/the-blurry-line-between-formatting-and-style) by **@joshuakgoldberg**.
+I love Prettier. However, this is not its area of responsibility. Prettier is used for formatting, and ESLint is also used for styling. For example, changing the order of imports can affect how the code works (console.log calls, fetch, style loading). Prettier should not change the AST. There is a cool article about this: ["The Blurry Line Between Formatting and Style"](https://blog.joshuakgoldberg.com/the-blurry-line-between-formatting-and-style) by **@joshuakgoldberg**.
 
 ## Versioning Policy
 
diff --git a/rules/sort-array-includes.ts b/rules/sort-array-includes.ts
index eb30651..b7dbb57 100644
--- a/rules/sort-array-includes.ts
+++ b/rules/sort-array-includes.ts
@@ -8,6 +8,7 @@ import {
   partitionByCommentJsonSchema,
   specialCharactersJsonSchema,
   ignoreCaseJsonSchema,
+  alphabetJsonSchema,
   localesJsonSchema,
   orderJsonSchema,
   typeJsonSchema,
@@ -30,14 +31,15 @@ import { pairwise } from '../utils/pairwise'
 
 export type Options = [
   Partial<{
+    type: 'alphabetical' | 'line-length' | 'natural' | 'custom'
     groupKind: 'literals-first' | 'spreads-first' | 'mixed'
-    type: 'alphabetical' | 'line-length' | 'natural'
     partitionByComment: string[] | boolean | string
     specialCharacters: 'remove' | 'trim' | 'keep'
     locales: NonNullable<Intl.LocalesArgument>
     partitionByNewLine: boolean
     order: 'desc' | 'asc'
     ignoreCase: boolean
+    alphabet: string
   }>,
 ]
 
@@ -56,6 +58,7 @@ export let defaultOptions: Required<Options[0]> = {
   type: 'alphabetical',
   ignoreCase: true,
   locales: 'en-US',
+  alphabet: '',
   order: 'asc',
 }
 
@@ -78,6 +81,7 @@ export let jsonSchema: JSONSchema4 = {
     },
     specialCharacters: specialCharactersJsonSchema,
     ignoreCase: ignoreCaseJsonSchema,
+    alphabet: alphabetJsonSchema,
     locales: localesJsonSchema,
     order: orderJsonSchema,
     type: typeJsonSchema,
diff --git a/rules/sort-classes.ts b/rules/sort-classes.ts
index 31d1506..6fe28ed 100644
--- a/rules/sort-classes.ts
+++ b/rules/sort-classes.ts
@@ -15,6 +15,7 @@ import {
   specialCharactersJsonSchema,
   newlinesBetweenJsonSchema,
   ignoreCaseJsonSchema,
+  alphabetJsonSchema,
   localesJsonSchema,
   groupsJsonSchema,
   orderJsonSchema,
@@ -25,17 +26,18 @@ import {
   sortNodesByDependencies,
 } from '../utils/sort-nodes-by-dependencies'
 import { validateNewlinesAndPartitionConfiguration } from '../utils/validate-newlines-and-partition-configuration'
+import { validateGeneratedGroupsConfiguration } from '../utils/validate-generated-groups-configuration'
 import {
   singleCustomGroupJsonSchema,
   allModifiers,
   allSelectors,
 } from './sort-classes.types'
-import { validateGeneratedGroupsConfiguration } from './validate-generated-groups-configuration'
+import { validateCustomSortConfiguration } from '../utils/validate-custom-sort-configuration'
 import {
   getOverloadSignatureGroups,
   customGroupMatches,
 } from './sort-classes-utils'
-import { getCustomGroupsCompareOptions } from './get-custom-groups-compare-options'
+import { getCustomGroupsCompareOptions } from '../utils/get-custom-groups-compare-options'
 import { generatePredefinedGroups } from '../utils/generate-predefined-groups'
 import { getEslintDisabledLines } from '../utils/get-eslint-disabled-lines'
 import { isNodeEslintDisabled } from '../utils/is-node-eslint-disabled'
@@ -104,6 +106,7 @@ let defaultOptions: Required<SortClassesOptions[0]> = {
   ignoreCase: true,
   customGroups: [],
   locales: 'en-US',
+  alphabet: '',
   order: 'asc',
 }
 
@@ -116,6 +119,7 @@ export default createEslintRule<SortClassesOptions, MESSAGE_ID>({
 
       let settings = getSettings(context.settings)
       let options = complete(context.options.at(0), settings, defaultOptions)
+      validateCustomSortConfiguration(options)
       validateGeneratedGroupsConfiguration({
         customGroups: options.customGroups,
         modifiers: allModifiers,
@@ -123,6 +127,7 @@ export default createEslintRule<SortClassesOptions, MESSAGE_ID>({
         groups: options.groups,
       })
       validateNewlinesAndPartitionConfiguration(options)
+
       let sourceCode = getSourceCode(context)
       let eslintDisabledLines = getEslintDisabledLines({
         ruleName: context.id,
@@ -688,6 +693,7 @@ export default createEslintRule<SortClassesOptions, MESSAGE_ID>({
           specialCharacters: specialCharactersJsonSchema,
           newlinesBetween: newlinesBetweenJsonSchema,
           ignoreCase: ignoreCaseJsonSchema,
+          alphabet: alphabetJsonSchema,
           locales: localesJsonSchema,
           groups: groupsJsonSchema,
           order: orderJsonSchema,
diff --git a/rules/sort-classes.types.ts b/rules/sort-classes.types.ts
index 754865c..adde191 100644
--- a/rules/sort-classes.types.ts
+++ b/rules/sort-classes.types.ts
@@ -8,7 +8,7 @@ import {
 
 export type SortClassesOptions = [
   Partial<{
-    type: 'alphabetical' | 'line-length' | 'natural'
+    type: 'alphabetical' | 'line-length' | 'natural' | 'custom'
     partitionByComment: string[] | boolean | string
     newlinesBetween: 'ignore' | 'always' | 'never'
     specialCharacters: 'remove' | 'trim' | 'keep'
@@ -19,6 +19,7 @@ export type SortClassesOptions = [
     groups: (Group[] | Group)[]
     order: 'desc' | 'asc'
     ignoreCase: boolean
+    alphabet: string
   }>,
 ]
 export type SingleCustomGroup =
diff --git a/rules/sort-decorators.ts b/rules/sort-decorators.ts
index a87a960..26e5cea 100644
--- a/rules/sort-decorators.ts
+++ b/rules/sort-decorators.ts
@@ -8,11 +8,13 @@ import {
   specialCharactersJsonSchema,
   customGroupsJsonSchema,
   ignoreCaseJsonSchema,
+  alphabetJsonSchema,
   localesJsonSchema,
   groupsJsonSchema,
   orderJsonSchema,
   typeJsonSchema,
 } from '../utils/common-json-schemas'
+import { validateCustomSortConfiguration } from '../utils/validate-custom-sort-configuration'
 import { validateGroupsConfiguration } from '../utils/validate-groups-configuration'
 import { getEslintDisabledLines } from '../utils/get-eslint-disabled-lines'
 import { isNodeEslintDisabled } from '../utils/is-node-eslint-disabled'
@@ -35,8 +37,8 @@ import { pairwise } from '../utils/pairwise'
 
 export type Options<T extends string[]> = [
   Partial<{
+    type: 'alphabetical' | 'line-length' | 'natural' | 'custom'
     customGroups: Record<T[number], string[] | string>
-    type: 'alphabetical' | 'line-length' | 'natural'
     partitionByComment: string[] | boolean | string
     specialCharacters: 'remove' | 'trim' | 'keep'
     locales: NonNullable<Intl.LocalesArgument>
@@ -48,6 +50,7 @@ export type Options<T extends string[]> = [
     sortOnClasses: boolean
     order: 'desc' | 'asc'
     ignoreCase: boolean
+    alphabet: string
   }>,
 ]
 
@@ -69,6 +72,7 @@ let defaultOptions: Required<Options<string[]>[0]> = {
   ignoreCase: true,
   customGroups: {},
   locales: 'en-US',
+  alphabet: '',
   order: 'asc',
   groups: [],
 }
@@ -111,6 +115,7 @@ export default createEslintRule<Options<string[]>, MESSAGE_ID>({
           specialCharacters: specialCharactersJsonSchema,
           customGroups: customGroupsJsonSchema,
           ignoreCase: ignoreCaseJsonSchema,
+          alphabet: alphabetJsonSchema,
           locales: localesJsonSchema,
           groups: groupsJsonSchema,
           order: orderJsonSchema,
@@ -138,7 +143,7 @@ export default createEslintRule<Options<string[]>, MESSAGE_ID>({
     let settings = getSettings(context.settings)
 
     let options = complete(context.options.at(0), settings, defaultOptions)
-
+    validateCustomSortConfiguration(options)
     validateGroupsConfiguration(
       options.groups,
       ['unknown'],
diff --git a/rules/sort-enums.ts b/rules/sort-enums.ts
index 7699aae..8966ed3 100644
--- a/rules/sort-enums.ts
+++ b/rules/sort-enums.ts
@@ -8,6 +8,7 @@ import {
   partitionByNewLineJsonSchema,
   specialCharactersJsonSchema,
   ignoreCaseJsonSchema,
+  alphabetJsonSchema,
   localesJsonSchema,
   orderJsonSchema,
   typeJsonSchema,
@@ -16,6 +17,7 @@ import {
   getFirstUnorderedNodeDependentOn,
   sortNodesByDependencies,
 } from '../utils/sort-nodes-by-dependencies'
+import { validateCustomSortConfiguration } from '../utils/validate-custom-sort-configuration'
 import { getEslintDisabledLines } from '../utils/get-eslint-disabled-lines'
 import { isNodeEslintDisabled } from '../utils/is-node-eslint-disabled'
 import { hasPartitionComment } from '../utils/is-partition-comment'
@@ -35,7 +37,7 @@ import { pairwise } from '../utils/pairwise'
 
 export type Options = [
   Partial<{
-    type: 'alphabetical' | 'line-length' | 'natural'
+    type: 'alphabetical' | 'line-length' | 'natural' | 'custom'
     partitionByComment: string[] | boolean | string
     specialCharacters: 'remove' | 'trim' | 'keep'
     locales: NonNullable<Intl.LocalesArgument>
@@ -44,6 +46,7 @@ export type Options = [
     order: 'desc' | 'asc'
     sortByValue: boolean
     ignoreCase: boolean
+    alphabet: string
   }>,
 ]
 
@@ -63,6 +66,7 @@ let defaultOptions: Required<Options[0]> = {
   sortByValue: false,
   ignoreCase: true,
   locales: 'en-US',
+  alphabet: '',
   order: 'asc',
 }
 
@@ -79,6 +83,8 @@ export default createEslintRule<Options, MESSAGE_ID>({
 
       let settings = getSettings(context.settings)
       let options = complete(context.options.at(0), settings, defaultOptions)
+      validateCustomSortConfiguration(options)
+
       let sourceCode = getSourceCode(context)
       let eslintDisabledLines = getEslintDisabledLines({
         ruleName: context.id,
@@ -196,6 +202,7 @@ export default createEslintRule<Options, MESSAGE_ID>({
             : options.type,
         specialCharacters: options.specialCharacters,
         ignoreCase: options.ignoreCase,
+        alphabet: options.alphabet,
         locales: options.locales,
         order: options.order,
       }
@@ -274,6 +281,7 @@ export default createEslintRule<Options, MESSAGE_ID>({
           partitionByNewLine: partitionByNewLineJsonSchema,
           specialCharacters: specialCharactersJsonSchema,
           ignoreCase: ignoreCaseJsonSchema,
+          alphabet: alphabetJsonSchema,
           locales: localesJsonSchema,
           order: orderJsonSchema,
           type: typeJsonSchema,
diff --git a/rules/sort-exports.ts b/rules/sort-exports.ts
index f4ccee2..04624aa 100644
--- a/rules/sort-exports.ts
+++ b/rules/sort-exports.ts
@@ -7,10 +7,12 @@ import {
   partitionByNewLineJsonSchema,
   specialCharactersJsonSchema,
   ignoreCaseJsonSchema,
+  alphabetJsonSchema,
   localesJsonSchema,
   orderJsonSchema,
   typeJsonSchema,
 } from '../utils/common-json-schemas'
+import { validateCustomSortConfiguration } from '../utils/validate-custom-sort-configuration'
 import { getEslintDisabledLines } from '../utils/get-eslint-disabled-lines'
 import { isNodeEslintDisabled } from '../utils/is-node-eslint-disabled'
 import { hasPartitionComment } from '../utils/is-partition-comment'
@@ -27,14 +29,15 @@ import { pairwise } from '../utils/pairwise'
 
 type Options = [
   Partial<{
+    type: 'alphabetical' | 'line-length' | 'natural' | 'custom'
     groupKind: 'values-first' | 'types-first' | 'mixed'
-    type: 'alphabetical' | 'line-length' | 'natural'
     partitionByComment: string[] | boolean | string
     specialCharacters: 'remove' | 'trim' | 'keep'
     locales: NonNullable<Intl.LocalesArgument>
     partitionByNewLine: boolean
     order: 'desc' | 'asc'
     ignoreCase: boolean
+    alphabet: string
   }>,
 ]
 
@@ -55,6 +58,7 @@ let defaultOptions: Required<Options[0]> = {
   groupKind: 'mixed',
   ignoreCase: true,
   locales: 'en-US',
+  alphabet: '',
   order: 'asc',
 }
 
@@ -63,6 +67,7 @@ export default createEslintRule<Options, MESSAGE_ID>({
     let settings = getSettings(context.settings)
 
     let options = complete(context.options.at(0), settings, defaultOptions)
+    validateCustomSortConfiguration(options)
 
     let sourceCode = getSourceCode(context)
     let partitionComment = options.partitionByComment
@@ -191,6 +196,7 @@ export default createEslintRule<Options, MESSAGE_ID>({
           partitionByNewLine: partitionByNewLineJsonSchema,
           specialCharacters: specialCharactersJsonSchema,
           ignoreCase: ignoreCaseJsonSchema,
+          alphabet: alphabetJsonSchema,
           locales: localesJsonSchema,
           order: orderJsonSchema,
           type: typeJsonSchema,
diff --git a/rules/sort-heritage-clauses.ts b/rules/sort-heritage-clauses.ts
index 1737000..b3a5c30 100644
--- a/rules/sort-heritage-clauses.ts
+++ b/rules/sort-heritage-clauses.ts
@@ -7,11 +7,13 @@ import {
   specialCharactersJsonSchema,
   customGroupsJsonSchema,
   ignoreCaseJsonSchema,
+  alphabetJsonSchema,
   localesJsonSchema,
   groupsJsonSchema,
   orderJsonSchema,
   typeJsonSchema,
 } from '../utils/common-json-schemas'
+import { validateCustomSortConfiguration } from '../utils/validate-custom-sort-configuration'
 import { validateGroupsConfiguration } from '../utils/validate-groups-configuration'
 import { getEslintDisabledLines } from '../utils/get-eslint-disabled-lines'
 import { isNodeEslintDisabled } from '../utils/is-node-eslint-disabled'
@@ -30,13 +32,14 @@ import { pairwise } from '../utils/pairwise'
 
 export type Options<T extends string[]> = [
   Partial<{
+    type: 'alphabetical' | 'line-length' | 'natural' | 'custom'
     customGroups: Record<T[number], string[] | string>
-    type: 'alphabetical' | 'line-length' | 'natural'
     specialCharacters: 'remove' | 'trim' | 'keep'
     locales: NonNullable<Intl.LocalesArgument>
     groups: (Group<T>[] | Group<T>)[]
     order: 'desc' | 'asc'
     ignoreCase: boolean
+    alphabet: string
   }>,
 ]
 
@@ -52,6 +55,7 @@ let defaultOptions: Required<Options<string[]>[0]> = {
   ignoreCase: true,
   customGroups: {},
   locales: 'en-US',
+  alphabet: '',
   order: 'asc',
   groups: [],
 }
@@ -64,6 +68,7 @@ export default createEslintRule<Options<string[]>, MESSAGE_ID>({
           specialCharacters: specialCharactersJsonSchema,
           customGroups: customGroupsJsonSchema,
           ignoreCase: ignoreCaseJsonSchema,
+          alphabet: alphabetJsonSchema,
           locales: localesJsonSchema,
           groups: groupsJsonSchema,
           order: orderJsonSchema,
@@ -91,7 +96,7 @@ export default createEslintRule<Options<string[]>, MESSAGE_ID>({
     let settings = getSettings(context.settings)
 
     let options = complete(context.options.at(0), settings, defaultOptions)
-
+    validateCustomSortConfiguration(options)
     validateGroupsConfiguration(
       options.groups,
       ['unknown'],
diff --git a/rules/sort-imports.ts b/rules/sort-imports.ts
index 9c9f889..21bf981 100644
--- a/rules/sort-imports.ts
+++ b/rules/sort-imports.ts
@@ -10,12 +10,14 @@ import {
   specialCharactersJsonSchema,
   newlinesBetweenJsonSchema,
   ignoreCaseJsonSchema,
+  alphabetJsonSchema,
   localesJsonSchema,
   groupsJsonSchema,
   orderJsonSchema,
   typeJsonSchema,
 } from '../utils/common-json-schemas'
 import { validateNewlinesAndPartitionConfiguration } from '../utils/validate-newlines-and-partition-configuration'
+import { validateCustomSortConfiguration } from '../utils/validate-custom-sort-configuration'
 import { validateGroupsConfiguration } from '../utils/validate-groups-configuration'
 import { readClosestTsConfigByPath } from '../utils/read-closest-ts-config-by-path'
 import { getOptionsWithCleanGroups } from '../utils/get-options-with-clean-groups'
@@ -46,7 +48,7 @@ export type Options<T extends string[]> = [
       value?: Record<T[number], string[] | string>
       type?: Record<T[number], string[] | string>
     }
-    type: 'alphabetical' | 'line-length' | 'natural'
+    type: 'alphabetical' | 'line-length' | 'natural' | 'custom'
     partitionByComment: string[] | boolean | string
     newlinesBetween: 'ignore' | 'always' | 'never'
     specialCharacters: 'remove' | 'trim' | 'keep'
@@ -60,6 +62,7 @@ export type Options<T extends string[]> = [
     maxLineLength?: number
     order: 'desc' | 'asc'
     ignoreCase: boolean
+    alphabet: string
   }>,
 ]
 
@@ -122,6 +125,7 @@ export default createEslintRule<Options<string[]>, MESSAGE_ID>({
         environment: 'node',
         ignoreCase: true,
         locales: 'en-US',
+        alphabet: '',
         order: 'asc',
       } as const),
     )
@@ -153,6 +157,7 @@ export default createEslintRule<Options<string[]>, MESSAGE_ID>({
         ...Object.keys(options.customGroups.value ?? {}),
       ],
     )
+    validateCustomSortConfiguration(options)
     validateNewlinesAndPartitionConfiguration(options)
 
     let tsConfigOutput = options.tsconfigRootDir
@@ -629,6 +634,7 @@ export default createEslintRule<Options<string[]>, MESSAGE_ID>({
           specialCharacters: specialCharactersJsonSchema,
           newlinesBetween: newlinesBetweenJsonSchema,
           ignoreCase: ignoreCaseJsonSchema,
+          alphabet: alphabetJsonSchema,
           locales: localesJsonSchema,
           groups: groupsJsonSchema,
           order: orderJsonSchema,
@@ -710,6 +716,7 @@ export default createEslintRule<Options<string[]>, MESSAGE_ID>({
       environment: 'node',
       ignoreCase: true,
       locales: 'en-US',
+      alphabet: '',
       order: 'asc',
     },
   ],
diff --git a/rules/sort-interfaces.ts b/rules/sort-interfaces.ts
index 5eb6bf1..6267fcf 100644
--- a/rules/sort-interfaces.ts
+++ b/rules/sort-interfaces.ts
@@ -22,6 +22,7 @@ let defaultOptions: Required<Options[0]> = {
   ignoreCase: true,
   customGroups: {},
   locales: 'en-US',
+  alphabet: '',
   order: 'asc',
   groups: [],
 }
diff --git a/rules/sort-intersection-types.ts b/rules/sort-intersection-types.ts
index 130fc33..fe14c2f 100644
--- a/rules/sort-intersection-types.ts
+++ b/rules/sort-intersection-types.ts
@@ -6,12 +6,14 @@ import {
   specialCharactersJsonSchema,
   newlinesBetweenJsonSchema,
   ignoreCaseJsonSchema,
+  alphabetJsonSchema,
   localesJsonSchema,
   groupsJsonSchema,
   orderJsonSchema,
   typeJsonSchema,
 } from '../utils/common-json-schemas'
 import { validateNewlinesAndPartitionConfiguration } from '../utils/validate-newlines-and-partition-configuration'
+import { validateCustomSortConfiguration } from '../utils/validate-custom-sort-configuration'
 import { validateGroupsConfiguration } from '../utils/validate-groups-configuration'
 import { getEslintDisabledLines } from '../utils/get-eslint-disabled-lines'
 import { isNodeEslintDisabled } from '../utils/is-node-eslint-disabled'
@@ -34,7 +36,7 @@ import { pairwise } from '../utils/pairwise'
 
 type Options = [
   Partial<{
-    type: 'alphabetical' | 'line-length' | 'natural'
+    type: 'alphabetical' | 'line-length' | 'natural' | 'custom'
     partitionByComment: string[] | boolean | string
     newlinesBetween: 'ignore' | 'always' | 'never'
     specialCharacters: 'remove' | 'trim' | 'keep'
@@ -43,6 +45,7 @@ type Options = [
     partitionByNewLine: boolean
     order: 'desc' | 'asc'
     ignoreCase: boolean
+    alphabet: string
   }>,
 ]
 
@@ -75,6 +78,7 @@ let defaultOptions: Required<Options[0]> = {
   type: 'alphabetical',
   ignoreCase: true,
   locales: 'en-US',
+  alphabet: '',
   order: 'asc',
   groups: [],
 }
@@ -85,7 +89,7 @@ export default createEslintRule<Options, MESSAGE_ID>({
       let settings = getSettings(context.settings)
 
       let options = complete(context.options.at(0), settings, defaultOptions)
-
+      validateCustomSortConfiguration(options)
       validateGroupsConfiguration(
         options.groups,
         [
@@ -297,6 +301,7 @@ export default createEslintRule<Options, MESSAGE_ID>({
           specialCharacters: specialCharactersJsonSchema,
           newlinesBetween: newlinesBetweenJsonSchema,
           ignoreCase: ignoreCaseJsonSchema,
+          alphabet: alphabetJsonSchema,
           locales: localesJsonSchema,
           groups: groupsJsonSchema,
           order: orderJsonSchema,
diff --git a/rules/sort-jsx-props.ts b/rules/sort-jsx-props.ts
index 157b4b9..b667072 100644
--- a/rules/sort-jsx-props.ts
+++ b/rules/sort-jsx-props.ts
@@ -6,11 +6,13 @@ import {
   specialCharactersJsonSchema,
   customGroupsJsonSchema,
   ignoreCaseJsonSchema,
+  alphabetJsonSchema,
   localesJsonSchema,
   groupsJsonSchema,
   orderJsonSchema,
   typeJsonSchema,
 } from '../utils/common-json-schemas'
+import { validateCustomSortConfiguration } from '../utils/validate-custom-sort-configuration'
 import { validateGroupsConfiguration } from '../utils/validate-groups-configuration'
 import { getEslintDisabledLines } from '../utils/get-eslint-disabled-lines'
 import { isNodeEslintDisabled } from '../utils/is-node-eslint-disabled'
@@ -29,14 +31,15 @@ import { matches } from '../utils/matches'
 
 type Options<T extends string[]> = [
   Partial<{
+    type: 'alphabetical' | 'line-length' | 'natural' | 'custom'
     customGroups: Record<T[number], string[] | string>
-    type: 'alphabetical' | 'line-length' | 'natural'
     specialCharacters: 'remove' | 'trim' | 'keep'
     locales: NonNullable<Intl.LocalesArgument>
     groups: (Group<T>[] | Group<T>)[]
     ignorePattern: string[]
     order: 'desc' | 'asc'
     ignoreCase: boolean
+    alphabet: string
   }>,
 ]
 
@@ -55,6 +58,7 @@ let defaultOptions: Required<Options<string[]>[0]> = {
   ignoreCase: true,
   customGroups: {},
   locales: 'en-US',
+  alphabet: '',
   order: 'asc',
   groups: [],
 }
@@ -68,6 +72,7 @@ export default createEslintRule<Options<string[]>, MESSAGE_ID>({
 
       let settings = getSettings(context.settings)
       let options = complete(context.options.at(0), settings, defaultOptions)
+      validateCustomSortConfiguration(options)
       validateGroupsConfiguration(
         options.groups,
         ['multiline', 'shorthand', 'unknown'],
@@ -199,6 +204,7 @@ export default createEslintRule<Options<string[]>, MESSAGE_ID>({
           specialCharacters: specialCharactersJsonSchema,
           customGroups: customGroupsJsonSchema,
           ignoreCase: ignoreCaseJsonSchema,
+          alphabet: alphabetJsonSchema,
           locales: localesJsonSchema,
           groups: groupsJsonSchema,
           order: orderJsonSchema,
diff --git a/rules/sort-maps.ts b/rules/sort-maps.ts
index 5d24a02..8453211 100644
--- a/rules/sort-maps.ts
+++ b/rules/sort-maps.ts
@@ -7,10 +7,12 @@ import {
   partitionByNewLineJsonSchema,
   specialCharactersJsonSchema,
   ignoreCaseJsonSchema,
+  alphabetJsonSchema,
   localesJsonSchema,
   orderJsonSchema,
   typeJsonSchema,
 } from '../utils/common-json-schemas'
+import { validateCustomSortConfiguration } from '../utils/validate-custom-sort-configuration'
 import { getEslintDisabledLines } from '../utils/get-eslint-disabled-lines'
 import { isNodeEslintDisabled } from '../utils/is-node-eslint-disabled'
 import { hasPartitionComment } from '../utils/is-partition-comment'
@@ -29,13 +31,14 @@ import { pairwise } from '../utils/pairwise'
 
 type Options = [
   Partial<{
-    type: 'alphabetical' | 'line-length' | 'natural'
+    type: 'alphabetical' | 'line-length' | 'natural' | 'custom'
     partitionByComment: string[] | boolean | string
     specialCharacters: 'remove' | 'trim' | 'keep'
     locales: NonNullable<Intl.LocalesArgument>
     partitionByNewLine: boolean
     order: 'desc' | 'asc'
     ignoreCase: boolean
+    alphabet: string
   }>,
 ]
 
@@ -48,6 +51,7 @@ let defaultOptions: Required<Options[0]> = {
   type: 'alphabetical',
   ignoreCase: true,
   locales: 'en-US',
+  alphabet: '',
   order: 'asc',
 }
 
@@ -69,6 +73,8 @@ export default createEslintRule<Options, MESSAGE_ID>({
 
       let settings = getSettings(context.settings)
       let options = complete(context.options.at(0), settings, defaultOptions)
+      validateCustomSortConfiguration(options)
+
       let sourceCode = getSourceCode(context)
       let eslintDisabledLines = getEslintDisabledLines({
         ruleName: context.id,
@@ -192,6 +198,7 @@ export default createEslintRule<Options, MESSAGE_ID>({
           partitionByNewLine: partitionByNewLineJsonSchema,
           specialCharacters: specialCharactersJsonSchema,
           ignoreCase: ignoreCaseJsonSchema,
+          alphabet: alphabetJsonSchema,
           locales: localesJsonSchema,
           order: orderJsonSchema,
           type: typeJsonSchema,
diff --git a/rules/sort-modules.ts b/rules/sort-modules.ts
index 31501ab..d1c5113 100644
--- a/rules/sort-modules.ts
+++ b/rules/sort-modules.ts
@@ -17,6 +17,7 @@ import {
   specialCharactersJsonSchema,
   newlinesBetweenJsonSchema,
   ignoreCaseJsonSchema,
+  alphabetJsonSchema,
   localesJsonSchema,
   groupsJsonSchema,
   orderJsonSchema,
@@ -27,13 +28,14 @@ import {
   sortNodesByDependencies,
 } from '../utils/sort-nodes-by-dependencies'
 import { validateNewlinesAndPartitionConfiguration } from '../utils/validate-newlines-and-partition-configuration'
+import { validateGeneratedGroupsConfiguration } from '../utils/validate-generated-groups-configuration'
 import {
   singleCustomGroupJsonSchema,
   allModifiers,
   allSelectors,
 } from './sort-modules.types'
-import { validateGeneratedGroupsConfiguration } from './validate-generated-groups-configuration'
-import { getCustomGroupsCompareOptions } from './get-custom-groups-compare-options'
+import { validateCustomSortConfiguration } from '../utils/validate-custom-sort-configuration'
+import { getCustomGroupsCompareOptions } from '../utils/get-custom-groups-compare-options'
 import { generatePredefinedGroups } from '../utils/generate-predefined-groups'
 import { getEslintDisabledLines } from '../utils/get-eslint-disabled-lines'
 import { isNodeEslintDisabled } from '../utils/is-node-eslint-disabled'
@@ -93,6 +95,7 @@ let defaultOptions: Required<SortModulesOptions[0]> = {
   ignoreCase: true,
   customGroups: [],
   locales: 'en-US',
+  alphabet: '',
   order: 'asc',
 }
 
@@ -113,6 +116,7 @@ export default createEslintRule<SortModulesOptions, MESSAGE_ID>({
           specialCharacters: specialCharactersJsonSchema,
           newlinesBetween: newlinesBetweenJsonSchema,
           ignoreCase: ignoreCaseJsonSchema,
+          alphabet: alphabetJsonSchema,
           locales: localesJsonSchema,
           groups: groupsJsonSchema,
           order: orderJsonSchema,
@@ -144,6 +148,7 @@ export default createEslintRule<SortModulesOptions, MESSAGE_ID>({
   create: context => {
     let settings = getSettings(context.settings)
     let options = complete(context.options.at(0), settings, defaultOptions)
+    validateCustomSortConfiguration(options)
     validateGeneratedGroupsConfiguration({
       customGroups: options.customGroups,
       modifiers: allModifiers,
@@ -151,6 +156,7 @@ export default createEslintRule<SortModulesOptions, MESSAGE_ID>({
       groups: options.groups,
     })
     validateNewlinesAndPartitionConfiguration(options)
+
     let sourceCode = getSourceCode(context)
     let eslintDisabledLines = getEslintDisabledLines({
       ruleName: context.id,
diff --git a/rules/sort-modules.types.ts b/rules/sort-modules.types.ts
index 20b3370..838c2ef 100644
--- a/rules/sort-modules.types.ts
+++ b/rules/sort-modules.types.ts
@@ -8,7 +8,7 @@ import {
 
 export type SortModulesOptions = [
   Partial<{
-    type: 'alphabetical' | 'line-length' | 'natural'
+    type: 'alphabetical' | 'line-length' | 'natural' | 'custom'
     partitionByComment: string[] | boolean | string
     newlinesBetween: 'ignore' | 'always' | 'never'
     specialCharacters: 'remove' | 'trim' | 'keep'
@@ -18,6 +18,7 @@ export type SortModulesOptions = [
     partitionByNewLine: boolean
     order: 'desc' | 'asc'
     ignoreCase: boolean
+    alphabet: string
   }>,
 ]
 export type SingleCustomGroup = (
diff --git a/rules/sort-named-exports.ts b/rules/sort-named-exports.ts
index 8aafe7c..de58637 100644
--- a/rules/sort-named-exports.ts
+++ b/rules/sort-named-exports.ts
@@ -7,10 +7,12 @@ import {
   partitionByNewLineJsonSchema,
   specialCharactersJsonSchema,
   ignoreCaseJsonSchema,
+  alphabetJsonSchema,
   localesJsonSchema,
   orderJsonSchema,
   typeJsonSchema,
 } from '../utils/common-json-schemas'
+import { validateCustomSortConfiguration } from '../utils/validate-custom-sort-configuration'
 import { getEslintDisabledLines } from '../utils/get-eslint-disabled-lines'
 import { isNodeEslintDisabled } from '../utils/is-node-eslint-disabled'
 import { hasPartitionComment } from '../utils/is-partition-comment'
@@ -28,14 +30,15 @@ import { pairwise } from '../utils/pairwise'
 
 type Options = [
   Partial<{
+    type: 'alphabetical' | 'line-length' | 'natural' | 'custom'
     groupKind: 'values-first' | 'types-first' | 'mixed'
-    type: 'alphabetical' | 'line-length' | 'natural'
     partitionByComment: string[] | boolean | string
     specialCharacters: 'remove' | 'trim' | 'keep'
     locales: NonNullable<Intl.LocalesArgument>
     partitionByNewLine: boolean
     order: 'desc' | 'asc'
     ignoreCase: boolean
+    alphabet: string
   }>,
 ]
 
@@ -54,6 +57,7 @@ let defaultOptions: Required<Options[0]> = {
   groupKind: 'mixed',
   ignoreCase: true,
   locales: 'en-US',
+  alphabet: '',
   order: 'asc',
 }
 
@@ -66,6 +70,8 @@ export default createEslintRule<Options, MESSAGE_ID>({
 
       let settings = getSettings(context.settings)
       let options = complete(context.options.at(0), settings, defaultOptions)
+      validateCustomSortConfiguration(options)
+
       let sourceCode = getSourceCode(context)
       let eslintDisabledLines = getEslintDisabledLines({
         ruleName: context.id,
@@ -192,6 +198,7 @@ export default createEslintRule<Options, MESSAGE_ID>({
           partitionByNewLine: partitionByNewLineJsonSchema,
           specialCharacters: specialCharactersJsonSchema,
           ignoreCase: ignoreCaseJsonSchema,
+          alphabet: alphabetJsonSchema,
           locales: localesJsonSchema,
           order: orderJsonSchema,
           type: typeJsonSchema,
diff --git a/rules/sort-named-imports.ts b/rules/sort-named-imports.ts
index a259d3b..ec17f4f 100644
--- a/rules/sort-named-imports.ts
+++ b/rules/sort-named-imports.ts
@@ -7,10 +7,12 @@ import {
   partitionByNewLineJsonSchema,
   specialCharactersJsonSchema,
   ignoreCaseJsonSchema,
+  alphabetJsonSchema,
   localesJsonSchema,
   orderJsonSchema,
   typeJsonSchema,
 } from '../utils/common-json-schemas'
+import { validateCustomSortConfiguration } from '../utils/validate-custom-sort-configuration'
 import { getEslintDisabledLines } from '../utils/get-eslint-disabled-lines'
 import { isNodeEslintDisabled } from '../utils/is-node-eslint-disabled'
 import { hasPartitionComment } from '../utils/is-partition-comment'
@@ -28,8 +30,8 @@ import { pairwise } from '../utils/pairwise'
 
 type Options = [
   Partial<{
+    type: 'alphabetical' | 'line-length' | 'natural' | 'custom'
     groupKind: 'values-first' | 'types-first' | 'mixed'
-    type: 'alphabetical' | 'line-length' | 'natural'
     partitionByComment: string[] | boolean | string
     specialCharacters: 'remove' | 'trim' | 'keep'
     locales: NonNullable<Intl.LocalesArgument>
@@ -37,6 +39,7 @@ type Options = [
     order: 'desc' | 'asc'
     ignoreAlias: boolean
     ignoreCase: boolean
+    alphabet: string
   }>,
 ]
 
@@ -56,6 +59,7 @@ let defaultOptions: Required<Options[0]> = {
   groupKind: 'mixed',
   ignoreCase: true,
   locales: 'en-US',
+  alphabet: '',
   order: 'asc',
 }
 
@@ -71,6 +75,8 @@ export default createEslintRule<Options, MESSAGE_ID>({
 
       let settings = getSettings(context.settings)
       let options = complete(context.options.at(0), settings, defaultOptions)
+      validateCustomSortConfiguration(options)
+
       let sourceCode = getSourceCode(context)
       let eslintDisabledLines = getEslintDisabledLines({
         ruleName: context.id,
@@ -205,6 +211,7 @@ export default createEslintRule<Options, MESSAGE_ID>({
           partitionByNewLine: partitionByNewLineJsonSchema,
           specialCharacters: specialCharactersJsonSchema,
           ignoreCase: ignoreCaseJsonSchema,
+          alphabet: alphabetJsonSchema,
           locales: localesJsonSchema,
           order: orderJsonSchema,
           type: typeJsonSchema,
diff --git a/rules/sort-object-types.ts b/rules/sort-object-types.ts
index 95c6647..cbd5d69 100644
--- a/rules/sort-object-types.ts
+++ b/rules/sort-object-types.ts
@@ -13,14 +13,16 @@ import {
   newlinesBetweenJsonSchema,
   customGroupsJsonSchema,
   ignoreCaseJsonSchema,
+  alphabetJsonSchema,
   localesJsonSchema,
   groupsJsonSchema,
   orderJsonSchema,
   typeJsonSchema,
 } from '../utils/common-json-schemas'
 import { validateNewlinesAndPartitionConfiguration } from '../utils/validate-newlines-and-partition-configuration'
-import { validateGeneratedGroupsConfiguration } from './validate-generated-groups-configuration'
-import { getCustomGroupsCompareOptions } from './get-custom-groups-compare-options'
+import { validateGeneratedGroupsConfiguration } from '../utils/validate-generated-groups-configuration'
+import { validateCustomSortConfiguration } from '../utils/validate-custom-sort-configuration'
+import { getCustomGroupsCompareOptions } from '../utils/get-custom-groups-compare-options'
 import { generatePredefinedGroups } from '../utils/generate-predefined-groups'
 import { getEslintDisabledLines } from '../utils/get-eslint-disabled-lines'
 import { singleCustomGroupJsonSchema } from './sort-object-types.types'
@@ -74,6 +76,7 @@ let defaultOptions: Required<Options[0]> = {
   ignoreCase: true,
   customGroups: {},
   locales: 'en-US',
+  alphabet: '',
   order: 'asc',
   groups: [],
 }
@@ -108,6 +111,7 @@ export let jsonSchema: JSONSchema4 = {
     specialCharacters: specialCharactersJsonSchema,
     newlinesBetween: newlinesBetweenJsonSchema,
     ignoreCase: ignoreCaseJsonSchema,
+    alphabet: alphabetJsonSchema,
     locales: localesJsonSchema,
     groups: groupsJsonSchema,
     order: orderJsonSchema,
@@ -181,6 +185,7 @@ export let sortObjectTypeElements = <MessageIds extends string>({
 
   let settings = getSettings(context.settings)
   let options = complete(context.options.at(0), settings, defaultOptions)
+  validateCustomSortConfiguration(options)
   validateGeneratedGroupsConfiguration({
     customGroups: options.customGroups,
     selectors: allSelectors,
diff --git a/rules/sort-object-types.types.ts b/rules/sort-object-types.types.ts
index 443af3b..8ce16dd 100644
--- a/rules/sort-object-types.types.ts
+++ b/rules/sort-object-types.types.ts
@@ -9,11 +9,11 @@ import {
 export type Options = [
   Partial<{
     customGroups: Record<string, string[] | string> | CustomGroup[]
+    type: 'alphabetical' | 'line-length' | 'natural' | 'custom'
     /**
      * @deprecated for {@link `groups`}
      */
     groupKind: 'required-first' | 'optional-first' | 'mixed'
-    type: 'alphabetical' | 'line-length' | 'natural'
     partitionByComment: string[] | boolean | string
     newlinesBetween: 'ignore' | 'always' | 'never'
     specialCharacters: 'remove' | 'trim' | 'keep'
@@ -23,6 +23,7 @@ export type Options = [
     ignorePattern: string[]
     order: 'desc' | 'asc'
     ignoreCase: boolean
+    alphabet: string
   }>,
 ]
 
diff --git a/rules/sort-objects.ts b/rules/sort-objects.ts
index c79643e..82ea833 100644
--- a/rules/sort-objects.ts
+++ b/rules/sort-objects.ts
@@ -10,6 +10,7 @@ import {
   newlinesBetweenJsonSchema,
   customGroupsJsonSchema,
   ignoreCaseJsonSchema,
+  alphabetJsonSchema,
   localesJsonSchema,
   groupsJsonSchema,
   orderJsonSchema,
@@ -20,6 +21,7 @@ import {
   sortNodesByDependencies,
 } from '../utils/sort-nodes-by-dependencies'
 import { validateNewlinesAndPartitionConfiguration } from '../utils/validate-newlines-and-partition-configuration'
+import { validateCustomSortConfiguration } from '../utils/validate-custom-sort-configuration'
 import { validateGroupsConfiguration } from '../utils/validate-groups-configuration'
 import { getMatchingContextOptions } from '../utils/get-matching-context-options'
 import { getEslintDisabledLines } from '../utils/get-eslint-disabled-lines'
@@ -48,8 +50,8 @@ type Options = Partial<{
   useConfigurationIf: {
     allNamesMatchPattern?: string
   }
+  type: 'alphabetical' | 'line-length' | 'natural' | 'custom'
   destructuredObjects: { groups: boolean } | boolean
-  type: 'alphabetical' | 'line-length' | 'natural'
   customGroups: Record<string, string[] | string>
   partitionByComment: string[] | boolean | string
   newlinesBetween: 'ignore' | 'always' | 'never'
@@ -66,6 +68,7 @@ type Options = Partial<{
   ignorePattern: string[]
   order: 'desc' | 'asc'
   ignoreCase: boolean
+  alphabet: string
 }>[]
 
 type MESSAGE_ID =
@@ -92,6 +95,7 @@ let defaultOptions: Required<Options[0]> = {
   ignoreCase: true,
   customGroups: {},
   locales: 'en-US',
+  alphabet: '',
   order: 'asc',
   groups: [],
 }
@@ -114,6 +118,7 @@ export default createEslintRule<Options, MESSAGE_ID>({
         contextOptions: context.options,
       })
       let options = complete(matchedContextOptions, settings, defaultOptions)
+      validateCustomSortConfiguration(options)
       validateGroupsConfiguration(
         options.groups,
         ['multiline', 'method', 'unknown'],
@@ -541,6 +546,7 @@ export default createEslintRule<Options, MESSAGE_ID>({
           newlinesBetween: newlinesBetweenJsonSchema,
           customGroups: customGroupsJsonSchema,
           ignoreCase: ignoreCaseJsonSchema,
+          alphabet: alphabetJsonSchema,
           locales: localesJsonSchema,
           groups: groupsJsonSchema,
           order: orderJsonSchema,
diff --git a/rules/sort-switch-case.ts b/rules/sort-switch-case.ts
index edc1a7c..bf41408 100644
--- a/rules/sort-switch-case.ts
+++ b/rules/sort-switch-case.ts
@@ -6,10 +6,12 @@ import type { SortingNode } from '../typings'
 import {
   specialCharactersJsonSchema,
   ignoreCaseJsonSchema,
+  alphabetJsonSchema,
   localesJsonSchema,
   orderJsonSchema,
   typeJsonSchema,
 } from '../utils/common-json-schemas'
+import { validateCustomSortConfiguration } from '../utils/validate-custom-sort-configuration'
 import { makeCommentAfterFixes } from '../utils/make-comment-after-fixes'
 import { createEslintRule } from '../utils/create-eslint-rule'
 import { getSourceCode } from '../utils/get-source-code'
@@ -24,11 +26,12 @@ import { compare } from '../utils/compare'
 
 type Options = [
   Partial<{
-    type: 'alphabetical' | 'line-length' | 'natural'
+    type: 'alphabetical' | 'line-length' | 'natural' | 'custom'
     specialCharacters: 'remove' | 'trim' | 'keep'
     locales: NonNullable<Intl.LocalesArgument>
     order: 'desc' | 'asc'
     ignoreCase: boolean
+    alphabet: string
   }>,
 ]
 
@@ -43,6 +46,7 @@ let defaultOptions: Required<Options[0]> = {
   type: 'alphabetical',
   ignoreCase: true,
   locales: 'en-US',
+  alphabet: '',
   order: 'asc',
 }
 
@@ -56,9 +60,9 @@ export default createEslintRule<Options, MESSAGE_ID>({
       let settings = getSettings(context.settings)
 
       let options = complete(context.options.at(0), settings, defaultOptions)
+      validateCustomSortConfiguration(options)
 
       let sourceCode = getSourceCode(context)
-
       let isDiscriminantTrue =
         switchNode.discriminant.type === 'Literal' &&
         switchNode.discriminant.value === true
@@ -265,6 +269,7 @@ export default createEslintRule<Options, MESSAGE_ID>({
         properties: {
           specialCharacters: specialCharactersJsonSchema,
           ignoreCase: ignoreCaseJsonSchema,
+          alphabet: alphabetJsonSchema,
           locales: localesJsonSchema,
           order: orderJsonSchema,
           type: typeJsonSchema,
diff --git a/rules/sort-union-types.ts b/rules/sort-union-types.ts
index e8b4587..8fa1601 100644
--- a/rules/sort-union-types.ts
+++ b/rules/sort-union-types.ts
@@ -6,12 +6,14 @@ import {
   specialCharactersJsonSchema,
   newlinesBetweenJsonSchema,
   ignoreCaseJsonSchema,
+  alphabetJsonSchema,
   localesJsonSchema,
   groupsJsonSchema,
   orderJsonSchema,
   typeJsonSchema,
 } from '../utils/common-json-schemas'
 import { validateNewlinesAndPartitionConfiguration } from '../utils/validate-newlines-and-partition-configuration'
+import { validateCustomSortConfiguration } from '../utils/validate-custom-sort-configuration'
 import { validateGroupsConfiguration } from '../utils/validate-groups-configuration'
 import { getEslintDisabledLines } from '../utils/get-eslint-disabled-lines'
 import { isNodeEslintDisabled } from '../utils/is-node-eslint-disabled'
@@ -34,7 +36,7 @@ import { pairwise } from '../utils/pairwise'
 
 type Options = [
   Partial<{
-    type: 'alphabetical' | 'line-length' | 'natural'
+    type: 'alphabetical' | 'line-length' | 'natural' | 'custom'
     partitionByComment: string[] | boolean | string
     newlinesBetween: 'ignore' | 'always' | 'never'
     specialCharacters: 'remove' | 'trim' | 'keep'
@@ -43,6 +45,7 @@ type Options = [
     partitionByNewLine: boolean
     order: 'desc' | 'asc'
     ignoreCase: boolean
+    alphabet: string
   }>,
 ]
 
@@ -75,6 +78,7 @@ let defaultOptions: Required<Options[0]> = {
   type: 'alphabetical',
   ignoreCase: true,
   locales: 'en-US',
+  alphabet: '',
   order: 'asc',
   groups: [],
 }
@@ -85,7 +89,7 @@ export default createEslintRule<Options, MESSAGE_ID>({
       let settings = getSettings(context.settings)
 
       let options = complete(context.options.at(0), settings, defaultOptions)
-
+      validateCustomSortConfiguration(options)
       validateGroupsConfiguration(
         options.groups,
         [
@@ -296,6 +300,7 @@ export default createEslintRule<Options, MESSAGE_ID>({
           specialCharacters: specialCharactersJsonSchema,
           newlinesBetween: newlinesBetweenJsonSchema,
           ignoreCase: ignoreCaseJsonSchema,
+          alphabet: alphabetJsonSchema,
           locales: localesJsonSchema,
           groups: groupsJsonSchema,
           order: orderJsonSchema,
diff --git a/rules/sort-variable-declarations.ts b/rules/sort-variable-declarations.ts
index 25e4777..a1d04d4 100644
--- a/rules/sort-variable-declarations.ts
+++ b/rules/sort-variable-declarations.ts
@@ -7,6 +7,7 @@ import {
   partitionByNewLineJsonSchema,
   specialCharactersJsonSchema,
   ignoreCaseJsonSchema,
+  alphabetJsonSchema,
   localesJsonSchema,
   orderJsonSchema,
   typeJsonSchema,
@@ -15,6 +16,7 @@ import {
   getFirstUnorderedNodeDependentOn,
   sortNodesByDependencies,
 } from '../utils/sort-nodes-by-dependencies'
+import { validateCustomSortConfiguration } from '../utils/validate-custom-sort-configuration'
 import { getEslintDisabledLines } from '../utils/get-eslint-disabled-lines'
 import { isNodeEslintDisabled } from '../utils/is-node-eslint-disabled'
 import { hasPartitionComment } from '../utils/is-partition-comment'
@@ -33,13 +35,14 @@ import { pairwise } from '../utils/pairwise'
 
 type Options = [
   Partial<{
-    type: 'alphabetical' | 'line-length' | 'natural'
+    type: 'alphabetical' | 'line-length' | 'natural' | 'custom'
     partitionByComment: string[] | boolean | string
     specialCharacters: 'remove' | 'trim' | 'keep'
     locales: NonNullable<Intl.LocalesArgument>
     partitionByNewLine: boolean
     order: 'desc' | 'asc'
     ignoreCase: boolean
+    alphabet: string
   }>,
 ]
 
@@ -54,6 +57,7 @@ let defaultOptions: Required<Options[0]> = {
   type: 'alphabetical',
   ignoreCase: true,
   locales: 'en-US',
+  alphabet: '',
   order: 'asc',
 }
 
@@ -66,6 +70,8 @@ export default createEslintRule<Options, MESSAGE_ID>({
 
       let settings = getSettings(context.settings)
       let options = complete(context.options.at(0), settings, defaultOptions)
+      validateCustomSortConfiguration(options)
+
       let sourceCode = getSourceCode(context)
       let eslintDisabledLines = getEslintDisabledLines({
         ruleName: context.id,
@@ -282,6 +288,7 @@ export default createEslintRule<Options, MESSAGE_ID>({
           partitionByNewLine: partitionByNewLineJsonSchema,
           specialCharacters: specialCharactersJsonSchema,
           ignoreCase: ignoreCaseJsonSchema,
+          alphabet: alphabetJsonSchema,
           locales: localesJsonSchema,
           order: orderJsonSchema,
           type: typeJsonSchema,
diff --git a/utils/alphabet.ts b/utils/alphabet.ts
new file mode 100644
index 0000000..f53b238
--- /dev/null
+++ b/utils/alphabet.ts
@@ -0,0 +1,434 @@
+import { compare as createNaturalCompare } from 'natural-orderby'
+
+import { convertBooleanToSign } from './convert-boolean-to-sign'
+
+interface Character {
+  uppercaseCharacterCodePoint?: number
+  lowercaseCharacterCodePoint?: number
+  codePoint: number
+  value: string
+}
+
+/**
+ * Utility class to build alphabets
+ */
+export class Alphabet {
+  private _characters: Character[] = []
+
+  private constructor(characters: Character[]) {
+    this._characters = characters
+  }
+
+  /**
+   * Generates an alphabet from the given characters.
+   * @param {string|string[]} values - The characters to generate the alphabet from
+   * @returns {Alphabet} - The wrapped alphabet
+   */
+  public static generateFrom(values: string[] | string): Alphabet {
+    let arrayValues = typeof values === 'string' ? [...values] : values
+    if (arrayValues.length !== new Set(arrayValues).size) {
+      throw new Error('The alphabet must not contain repeated characters')
+    }
+    if (arrayValues.some(value => value.length !== 1)) {
+      throw new Error('The alphabet must contain single characters')
+    }
+    return new Alphabet(
+      arrayValues.map(value =>
+        Alphabet._convertCodepointToCharacter(value.codePointAt(0)!),
+      ),
+    )
+  }
+
+  /**
+   * Generates an alphabet containing relevant characters from the Unicode
+   * standard. Contains the Unicode planes 0 and 1.
+   * @returns {Alphabet} - The generated alphabet
+   */
+  public static generateRecommendedAlphabet(): Alphabet {
+    return Alphabet._generateAlphabetToRange(0x1ffff + 1)
+  }
+
+  /**
+   * Generates an alphabet containing all characters from the Unicode standard
+   * except for irrelevant Unicode planes.
+   * Contains the Unicode planes 0, 1, 2 and 3.
+   * @returns {Alphabet} - The generated alphabet
+   */
+  public static generateCompleteAlphabet(): Alphabet {
+    return Alphabet._generateAlphabetToRange(0x3ffff + 1)
+  }
+
+  private static _convertCodepointToCharacter(codePoint: number): Character {
+    let character = String.fromCodePoint(codePoint)
+    let lowercaseCharacter = character.toLowerCase()
+    let uppercaseCharacter = character.toUpperCase()
+    return {
+      value: character,
+      codePoint,
+      ...(lowercaseCharacter === character
+        ? null
+        : {
+            lowercaseCharacterCodePoint: lowercaseCharacter.codePointAt(0)!,
+          }),
+      ...(uppercaseCharacter === character
+        ? null
+        : {
+            uppercaseCharacterCodePoint: uppercaseCharacter.codePointAt(0)!,
+          }),
+    }
+  }
+
+  /**
+   * Generates an alphabet containing relevant characters from the Unicode
+   * standard.
+   * @param {number} maxCodePoint - The maximum code point to generate the
+   * alphabet to
+   * @returns {Alphabet} - The generated alphabet
+   */
+  private static _generateAlphabetToRange(maxCodePoint: number): Alphabet {
+    let totalCharacters: Character[] = Array.from(
+      { length: maxCodePoint },
+      (_, i) => Alphabet._convertCodepointToCharacter(i),
+    )
+    return new Alphabet(totalCharacters)
+  }
+
+  /**
+   * For each character with a lower and upper case, permutes the two cases
+   * so that the alphabet is ordered by the case priority entered.
+   * @param {string} casePriority - The case to prioritize
+   * @returns {Alphabet} - The same alphabet instance with the cases prioritized
+   * @example
+   * Alphabet.generateFrom('aAbBcdCD')
+   * .prioritizeCase('uppercase')
+   * // Returns 'AaBbCDcd'
+   */
+  public prioritizeCase(casePriority: 'lowercase' | 'uppercase'): this {
+    let charactersWithCase = this._getCharactersWithCase()
+    // Permutes each uppercase character with its lowercase one
+    let parsedIndexes = new Set<number>()
+    let indexByCodePoints = this._characters.reduce<
+      Record<number, undefined | number>
+    >((indexByCodePoint, character, index) => {
+      indexByCodePoint[character.codePoint] = index
+      return indexByCodePoint
+    }, {})
+    for (let { character, index } of charactersWithCase) {
+      if (parsedIndexes.has(index)) {
+        continue
+      }
+      parsedIndexes.add(index)
+      let otherCharacterIndex =
+        indexByCodePoints[
+          character.uppercaseCharacterCodePoint ??
+            character.lowercaseCharacterCodePoint!
+        ]
+      // eslint-disable-next-line no-undefined
+      if (otherCharacterIndex === undefined) {
+        continue
+      }
+      parsedIndexes.add(otherCharacterIndex)
+      let isCharacterUppercase = !character.uppercaseCharacterCodePoint
+      if (isCharacterUppercase) {
+        if (
+          (casePriority === 'uppercase' && index < otherCharacterIndex) ||
+          (casePriority === 'lowercase' && index > otherCharacterIndex)
+        ) {
+          continue
+        }
+      } else {
+        if (
+          (casePriority === 'uppercase' && index > otherCharacterIndex) ||
+          (casePriority === 'lowercase' && index < otherCharacterIndex)
+        ) {
+          continue
+        }
+      }
+      this._characters[index] = this._characters[otherCharacterIndex]
+      this._characters[otherCharacterIndex] = character
+    }
+    return this
+  }
+
+  /**
+   * Adds specific characters to the end of the alphabet.
+   * @param {string|string[]} values - The characters to push to the alphabet
+   * @returns {Alphabet} - The same alphabet instance without the specified
+   * characters
+   * @example
+   * Alphabet.generateFrom('ab')
+   * .pushCharacters('cd')
+   * // Returns 'abcd'
+   */
+  public pushCharacters(values: string[] | string): this {
+    let arrayValues = typeof values === 'string' ? [...values] : values
+    let valuesSet = new Set(arrayValues)
+    let valuesAlreadyExisting = this._characters.filter(({ value }) =>
+      valuesSet.has(value),
+    )
+    if (valuesAlreadyExisting.length > 0) {
+      throw new Error(
+        `The alphabet already contains the characters ${valuesAlreadyExisting
+          .slice(0, 5)
+          .map(({ value }) => value)
+          .join(', ')}`,
+      )
+    }
+    if (arrayValues.some(value => value.length !== 1)) {
+      throw new Error('Only single characters may be pushed')
+    }
+    this._characters.push(
+      ...[...valuesSet].map(value =>
+        Alphabet._convertCodepointToCharacter(value.codePointAt(0)!),
+      ),
+    )
+    return this
+  }
+
+  /**
+   * Permutes characters with cases so that all characters with the entered case
+   * are put before the other characters.
+   * @param {string} caseToComeFirst - The case to put before the other
+   * characters
+   * @returns {Alphabet} - The same alphabet instance with all characters with
+   * case before all the characters with the other case
+   */
+  public placeAllWithCaseBeforeAllWithOtherCase(
+    caseToComeFirst: 'uppercase' | 'lowercase',
+  ): this {
+    let charactersWithCase = this._getCharactersWithCase()
+    let orderedCharacters = [
+      ...charactersWithCase.filter(character =>
+        caseToComeFirst === 'uppercase'
+          ? !character.character.uppercaseCharacterCodePoint
+          : character.character.uppercaseCharacterCodePoint,
+      ),
+      ...charactersWithCase.filter(character =>
+        caseToComeFirst === 'uppercase'
+          ? character.character.uppercaseCharacterCodePoint
+          : !character.character.uppercaseCharacterCodePoint,
+      ),
+    ]
+    for (let [i, element] of charactersWithCase.entries()) {
+      this._characters[element.index] = orderedCharacters[i].character
+    }
+    return this
+  }
+
+  /**
+   * Places a specific character right before another character in the alphabet.
+   * @param {object} params - The parameters for the operation
+   * @param {string} params.characterBefore - The character to come before
+   * characterAfter
+   * @param {string} params.characterAfter - The target character
+   * @returns {Alphabet} - The same alphabet instance with the specific
+   * character prioritized
+   * @example
+   * Alphabet.generateFrom('ab-cd/')
+   * .placeCharacterBefore({ characterBefore: '/', characterAfter: '-' })
+   * // Returns 'ab/-cd'
+   */
+  public placeCharacterBefore({
+    characterBefore,
+    characterAfter,
+  }: {
+    characterBefore: string
+    characterAfter: string
+  }): this {
+    return this._placeCharacterBeforeOrAfter({
+      characterBefore,
+      characterAfter,
+      type: 'before',
+    })
+  }
+
+  /**
+   * Places a specific character right after another character in the alphabet.
+   * @param {object} params - The parameters for the operation
+   * @param {string} params.characterBefore - The target character
+   * @param {string} params.characterAfter - The character to come after
+   * characterBefore
+   * @returns {Alphabet} - The same alphabet instance with the specific
+   * character prioritized
+   * @example
+   * Alphabet.generateFrom('ab-cd/')
+   * .placeCharacterAfter({ characterBefore: '/', characterAfter: '-' })
+   * // Returns 'abcd/-'
+   */
+  public placeCharacterAfter({
+    characterBefore,
+    characterAfter,
+  }: {
+    characterBefore: string
+    characterAfter: string
+  }): this {
+    return this._placeCharacterBeforeOrAfter({
+      characterBefore,
+      characterAfter,
+      type: 'after',
+    })
+  }
+
+  /**
+   * Removes specific characters from the alphabet by their range.
+   * @param {object} range - The Unicode range to remove characters from
+   * @param {number} range.start - The starting Unicode codepoint
+   * @param {number} range.end - The ending Unicode codepoint
+   * @returns {Alphabet} - The same alphabet instance without the characters
+   * from the specified range
+   */
+  public removeUnicodeRange({
+    start,
+    end,
+  }: {
+    start: number
+    end: number
+  }): this {
+    this._characters = this._characters.filter(
+      ({ codePoint }) => codePoint < start || codePoint > end,
+    )
+    return this
+  }
+
+  /**
+   * Sorts the alphabet by the sorting function provided.
+   * @param {Function} sortingFunction - The sorting function to use
+   * @returns {Alphabet} - The same alphabet instance sorted by the sorting function provided
+   */
+  public sortBy(
+    sortingFunction: (characterA: string, characterB: string) => number,
+  ): this {
+    this._characters.sort((a, b) => sortingFunction(a.value, b.value))
+    return this
+  }
+
+  /**
+   * Removes specific characters from the alphabet.
+   * @param {string|string[]} values - The characters to remove from the
+   * alphabet
+   * @returns {Alphabet} - The same alphabet instance without the specified
+   * characters
+   * @example
+   * Alphabet.generateFrom('abcd')
+   * .removeCharacters('dcc')
+   * // Returns 'ab'
+   */
+  public removeCharacters(values: string[] | string): this {
+    this._characters = this._characters.filter(
+      ({ value }) => !values.includes(value),
+    )
+    return this
+  }
+
+  /**
+   * Sorts the alphabet by the natural order of the characters using
+   * `natural-orderby`.
+   * @param {string} locale - The locale to use for sorting
+   * @returns {Alphabet} - The same alphabet instance sorted by the natural
+   * order of the characters
+   */
+  public sortByNaturalSort(locale?: string): this {
+    let naturalCompare = createNaturalCompare({
+      locale,
+    })
+    return this.sortBy((a, b) => naturalCompare(a, b))
+  }
+
+  /**
+   * Sorts the alphabet by the character code point.
+   * @returns {Alphabet} - The same alphabet instance sorted by the character
+   * code point
+   */
+  public sortByCharCodeAt(): this {
+    return this.sortBy((a, b) =>
+      convertBooleanToSign(a.charCodeAt(0) > b.charCodeAt(0)),
+    )
+  }
+
+  /**
+   * Sorts the alphabet by the locale order of the characters.
+   * @param {Intl.LocalesArgument} locales - The locales to use for sorting
+   * @returns {Alphabet} - The same alphabet instance sorted by the locale
+   * order of the characters
+   */
+  public sortByLocaleCompare(locales?: Intl.LocalesArgument): this {
+    return this.sortBy((a, b) => a.localeCompare(b, locales))
+  }
+
+  /**
+   * Retrieves the characters from the alphabet.
+   * @returns {string} The characters from the alphabet
+   */
+  public getCharacters(): string {
+    return this._characters.map(({ value }) => value).join('')
+  }
+
+  /**
+   * Reverses the alphabet.
+   * @returns {Alphabet} - The same alphabet instance reversed
+   * @example
+   * Alphabet.generateFrom('ab')
+   * .reverse()
+   * // Returns 'ba'
+   */
+  public reverse(): this {
+    this._characters.reverse()
+    return this
+  }
+
+  private _placeCharacterBeforeOrAfter({
+    characterBefore,
+    characterAfter,
+    type,
+  }: {
+    type: 'before' | 'after'
+    characterBefore: string
+    characterAfter: string
+  }): this {
+    let indexOfCharacterAfter = this._characters.findIndex(
+      ({ value }) => value === characterAfter,
+    )
+    let indexOfCharacterBefore = this._characters.findIndex(
+      ({ value }) => value === characterBefore,
+    )
+    if (indexOfCharacterAfter === -1) {
+      throw new Error(`Character ${characterAfter} not found in alphabet`)
+    }
+    if (indexOfCharacterBefore === -1) {
+      throw new Error(`Character ${characterBefore} not found in alphabet`)
+    }
+    if (indexOfCharacterBefore <= indexOfCharacterAfter) {
+      return this
+    }
+
+    this._characters.splice(
+      type === 'before' ? indexOfCharacterAfter : indexOfCharacterBefore + 1,
+      0,
+      this._characters[
+        type === 'before' ? indexOfCharacterBefore : indexOfCharacterAfter
+      ],
+    )
+    this._characters.splice(
+      type === 'before' ? indexOfCharacterBefore + 1 : indexOfCharacterAfter,
+      1,
+    )
+    return this
+  }
+
+  private _getCharactersWithCase(): { character: Character; index: number }[] {
+    return this._characters
+      .map((character, index) => {
+        if (
+          !character.uppercaseCharacterCodePoint &&
+          !character.lowercaseCharacterCodePoint
+        ) {
+          return null
+        }
+        return {
+          character,
+          index,
+        }
+      })
+      .filter(element => element !== null)
+  }
+}
diff --git a/utils/common-json-schemas.ts b/utils/common-json-schemas.ts
index 29564ec..f3698bb 100644
--- a/utils/common-json-schemas.ts
+++ b/utils/common-json-schemas.ts
@@ -1,7 +1,7 @@
 import type { JSONSchema4 } from '@typescript-eslint/utils/json-schema'
 
 export let typeJsonSchema: JSONSchema4 = {
-  enum: ['alphabetical', 'natural', 'line-length'],
+  enum: ['alphabetical', 'natural', 'line-length', 'custom'],
   description: 'Specifies the sorting method.',
   type: 'string',
 }
@@ -13,6 +13,11 @@ export let orderJsonSchema: JSONSchema4 = {
   type: 'string',
 }
 
+export let alphabetJsonSchema: JSONSchema4 = {
+  description: 'Alphabet to use for the `custom` sort type.',
+  type: 'string',
+}
+
 export let localesJsonSchema: JSONSchema4 = {
   oneOf: [
     {
diff --git a/utils/compare.ts b/utils/compare.ts
index 38dcf0c..13aba43 100644
--- a/utils/compare.ts
+++ b/utils/compare.ts
@@ -2,10 +2,13 @@ import { compare as createNaturalCompare } from 'natural-orderby'
 
 import type { SortingNode } from '../typings'
 
+import { convertBooleanToSign } from './convert-boolean-to-sign'
+
 export type CompareOptions<T extends SortingNode> =
   | AlphabeticalCompareOptions<T>
   | LineLengthCompareOptions<T>
   | NaturalCompareOptions<T>
+  | CustomCompareOptions<T>
 
 interface BaseCompareOptions<T extends SortingNode> {
   /**
@@ -32,70 +35,135 @@ interface NaturalCompareOptions<T extends SortingNode>
   type: 'natural'
 }
 
+interface CustomCompareOptions<T extends SortingNode>
+  extends BaseCompareOptions<T> {
+  specialCharacters: 'remove' | 'trim' | 'keep'
+  ignoreCase: boolean
+  alphabet: string
+  type: 'custom'
+}
+
 interface LineLengthCompareOptions<T extends SortingNode>
   extends BaseCompareOptions<T> {
   maxLineLength?: number
   type: 'line-length'
 }
 
+type SortingFunction<T extends SortingNode> = (a: T, b: T) => number
+
+type IndexByCharacters = Map<string, number>
+let alphabetCache = new Map<string, IndexByCharacters>()
+
 export let compare = <T extends SortingNode>(
   a: T,
   b: T,
   options: CompareOptions<T>,
 ): number => {
-  let orderCoefficient = options.order === 'asc' ? 1 : -1
-  let sortingFunction: (a: T, b: T) => number
+  let sortingFunction: SortingFunction<T>
   let nodeValueGetter = options.nodeValueGetter ?? ((node: T) => node.name)
-  if (options.type === 'alphabetical') {
-    let formatString = getFormatStringFunction(
-      options.ignoreCase,
-      options.specialCharacters,
+
+  switch (options.type) {
+    case 'alphabetical':
+      sortingFunction = getAlphabeticalSortingFunction(options, nodeValueGetter)
+      break
+    case 'natural':
+      sortingFunction = getNaturalSortingFunction(options, nodeValueGetter)
+      break
+    case 'custom':
+      sortingFunction = getCustomSortingFunction(options, nodeValueGetter)
+      break
+    case 'line-length':
+      sortingFunction = getLineLengthSortingFunction(options, nodeValueGetter)
+  }
+
+  return convertBooleanToSign(options.order === 'asc') * sortingFunction(a, b)
+}
+
+let getAlphabeticalSortingFunction = <T extends SortingNode>(
+  { specialCharacters, ignoreCase, locales }: AlphabeticalCompareOptions<T>,
+  nodeValueGetter: (node: T) => string,
+): SortingFunction<T> => {
+  let formatString = getFormatStringFunction(ignoreCase, specialCharacters)
+  return (aNode: T, bNode: T) =>
+    formatString(nodeValueGetter(aNode)).localeCompare(
+      formatString(nodeValueGetter(bNode)),
+      locales,
     )
-    sortingFunction = (aNode, bNode) =>
-      formatString(nodeValueGetter(aNode)).localeCompare(
-        formatString(nodeValueGetter(bNode)),
-        options.locales,
-      )
-  } else if (options.type === 'natural') {
-    let naturalCompare = createNaturalCompare({
-      locale: options.locales.toString(),
-    })
-    let formatString = getFormatStringFunction(
-      options.ignoreCase,
-      options.specialCharacters,
+}
+
+let getNaturalSortingFunction = <T extends SortingNode>(
+  { specialCharacters, ignoreCase, locales }: NaturalCompareOptions<T>,
+  nodeValueGetter: (node: T) => string,
+): SortingFunction<T> => {
+  let naturalCompare = createNaturalCompare({
+    locale: locales.toString(),
+  })
+  let formatString = getFormatStringFunction(ignoreCase, specialCharacters)
+  return (aNode: T, bNode: T) =>
+    naturalCompare(
+      formatString(nodeValueGetter(aNode)),
+      formatString(nodeValueGetter(bNode)),
     )
-    sortingFunction = (aNode, bNode) =>
-      naturalCompare(
-        formatString(nodeValueGetter(aNode)),
-        formatString(nodeValueGetter(bNode)),
-      )
-  } else {
-    sortingFunction = (aNode, bNode) => {
-      let aSize = aNode.size
-      let bSize = bNode.size
-
-      let { maxLineLength } = options
-
-      if (maxLineLength) {
-        let isTooLong = (size: number, node: T): undefined | boolean =>
-          size > maxLineLength && node.hasMultipleImportDeclarations
-
-        if (isTooLong(aSize, aNode)) {
-          aSize = nodeValueGetter(aNode).length + 10
-        }
-
-        if (isTooLong(bSize, bNode)) {
-          bSize = nodeValueGetter(bNode).length + 10
-        }
-      }
+}
 
-      return aSize - bSize
+let getCustomSortingFunction = <T extends SortingNode>(
+  { specialCharacters, ignoreCase, alphabet }: CustomCompareOptions<T>,
+  nodeValueGetter: (node: T) => string,
+): SortingFunction<T> => {
+  let formatString = getFormatStringFunction(ignoreCase, specialCharacters)
+  let indexByCharacters = alphabetCache.get(alphabet)
+  if (!indexByCharacters) {
+    indexByCharacters = new Map()
+    for (let [index, character] of [...alphabet].entries()) {
+      indexByCharacters.set(character, index)
     }
+    alphabetCache.set(alphabet, indexByCharacters)
+  }
+  return (aNode: T, bNode: T) => {
+    let aValue = formatString(nodeValueGetter(aNode))
+    let bValue = formatString(nodeValueGetter(bNode))
+    // Iterate character by character
+    // eslint-disable-next-line unicorn/no-for-loop
+    for (let i = 0; i < aValue.length; i++) {
+      let aCharacter = aValue[i]
+      let bCharacter = bValue[i]
+      let indexOfA = indexByCharacters.get(aCharacter)
+      let indexOfB = indexByCharacters.get(bCharacter)
+      indexOfA ??= Infinity
+      indexOfB ??= Infinity
+      if (indexOfA !== indexOfB) {
+        return convertBooleanToSign(indexOfA - indexOfB > 0)
+      }
+    }
+    return 0
   }
-
-  return orderCoefficient * sortingFunction(a, b)
 }
 
+let getLineLengthSortingFunction =
+  <T extends SortingNode>(
+    { maxLineLength }: LineLengthCompareOptions<T>,
+    nodeValueGetter: (node: T) => string,
+  ): SortingFunction<T> =>
+  (aNode: T, bNode: T) => {
+    let aSize = aNode.size
+    let bSize = bNode.size
+
+    if (maxLineLength) {
+      let isTooLong = (size: number, node: T): undefined | boolean =>
+        size > maxLineLength && node.hasMultipleImportDeclarations
+
+      if (isTooLong(aSize, aNode)) {
+        aSize = nodeValueGetter(aNode).length + 10
+      }
+
+      if (isTooLong(bSize, bNode)) {
+        bSize = nodeValueGetter(bNode).length + 10
+      }
+    }
+
+    return aSize - bSize
+  }
+
 let getFormatStringFunction =
   (ignoreCase: boolean, specialCharacters: 'remove' | 'trim' | 'keep') =>
   (value: string) => {
diff --git a/utils/convert-boolean-to-sign.ts b/utils/convert-boolean-to-sign.ts
new file mode 100644
index 0000000..dea3c6a
--- /dev/null
+++ b/utils/convert-boolean-to-sign.ts
@@ -0,0 +1,1 @@
+export let convertBooleanToSign = (value: boolean): -1 | 1 => (value ? 1 : -1)
diff --git a/rules/get-custom-groups-compare-options.ts b/utils/get-custom-groups-compare-options.ts
similarity index 88%
rename from rules/get-custom-groups-compare-options.ts
rename to utils/get-custom-groups-compare-options.ts
index 13ea904..ad765ca 100644
--- a/rules/get-custom-groups-compare-options.ts
+++ b/utils/get-custom-groups-compare-options.ts
@@ -1,19 +1,20 @@
-import type { CompareOptions } from '../utils/compare'
+import type { CompareOptions } from './compare'
 import type { SortingNode } from '../typings'
 
 interface Options {
   customGroups: Record<string, string[] | string> | CustomGroup[]
-  type: 'alphabetical' | 'line-length' | 'natural'
+  type: 'alphabetical' | 'line-length' | 'natural' | 'custom'
   specialCharacters: 'remove' | 'trim' | 'keep'
   locales: NonNullable<Intl.LocalesArgument>
   groups: (string[] | string)[]
   order: 'desc' | 'asc'
   ignoreCase: boolean
+  alphabet: string
 }
 
 type CustomGroup = (
   | {
-      type?: 'alphabetical' | 'line-length' | 'natural'
+      type?: 'alphabetical' | 'line-length' | 'natural' | 'custom'
       order?: 'desc' | 'asc'
     }
   | {
@@ -59,6 +60,7 @@ export let getCustomGroupsCompareOptions = <T extends SortingNode>(
     specialCharacters: options.specialCharacters,
     type: customGroup?.type ?? options.type,
     ignoreCase: options.ignoreCase,
+    alphabet: options.alphabet,
     locales: options.locales,
   }
 }
diff --git a/utils/get-settings.ts b/utils/get-settings.ts
index ecae141..49cd0ae 100644
--- a/utils/get-settings.ts
+++ b/utils/get-settings.ts
@@ -1,7 +1,7 @@
 import type { TSESLint } from '@typescript-eslint/utils'
 
 export type Settings = Partial<{
-  type: 'alphabetical' | 'line-length' | 'natural'
+  type: 'alphabetical' | 'line-length' | 'natural' | 'custom'
   partitionByComment: string[] | boolean | string
   specialCharacters: 'remove' | 'trim' | 'keep'
   locales: NonNullable<Intl.LocalesArgument>
@@ -9,6 +9,7 @@ export type Settings = Partial<{
   ignorePattern: string[]
   order: 'desc' | 'asc'
   ignoreCase: boolean
+  alphabet: string
 }>
 
 export let getSettings = (
@@ -25,6 +26,7 @@ export let getSettings = (
       'specialCharacters',
       'ignorePattern',
       'ignoreCase',
+      'alphabet',
       'locales',
       'order',
       'type',
diff --git a/utils/validate-custom-sort-configuration.ts b/utils/validate-custom-sort-configuration.ts
new file mode 100644
index 0000000..98c8276
--- /dev/null
+++ b/utils/validate-custom-sort-configuration.ts
@@ -0,0 +1,16 @@
+interface Options {
+  type: 'alphabetical' | 'line-length' | 'natural' | 'custom'
+  alphabet: string
+}
+
+export let validateCustomSortConfiguration = ({
+  alphabet,
+  type,
+}: Options): void => {
+  if (type !== 'custom') {
+    return
+  }
+  if (!alphabet.length) {
+    throw new Error('`alphabet` option must not be empty')
+  }
+}
diff --git a/rules/validate-generated-groups-configuration.ts b/utils/validate-generated-groups-configuration.ts
similarity index 76%
rename from rules/validate-generated-groups-configuration.ts
rename to utils/validate-generated-groups-configuration.ts
index eba894d..0b05651 100644
--- a/rules/validate-generated-groups-configuration.ts
+++ b/utils/validate-generated-groups-configuration.ts
@@ -1,6 +1,4 @@
-import type { Modifier, Selector } from './sort-classes.types'
-
-import { validateNoDuplicatedGroups } from '../utils/validate-groups-configuration'
+import { validateNoDuplicatedGroups } from './validate-groups-configuration'
 
 interface Props {
   customGroups: Record<string, string[] | string> | BaseCustomGroup[]
@@ -50,18 +48,13 @@ let isPredefinedGroup = (
     return false
   }
   let twoWordsSelector = input.split('-').slice(-2).join('-')
-  let isTwoWordSelectorValid = allSelectors.includes(
-    twoWordsSelector as Selector,
-  )
-  if (
-    !allSelectors.includes(singleWordSelector as Selector) &&
-    !isTwoWordSelectorValid
-  ) {
+  let isTwoWordSelectorValid = allSelectors.includes(twoWordsSelector)
+  if (!allSelectors.includes(singleWordSelector) && !isTwoWordSelectorValid) {
     return false
   }
   let modifiers = input.split('-').slice(0, isTwoWordSelectorValid ? -2 : -1)
   return (
     new Set(modifiers).size === modifiers.length &&
-    modifiers.every(modifier => allModifiers.includes(modifier as Modifier))
+    modifiers.every(modifier => allModifiers.includes(modifier))
   )
 }
diff --git a/vite.config.ts b/vite.config.ts
index c41a934..040661b 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -76,8 +76,11 @@ export default defineConfig({
       external: (id: string) => !id.startsWith('.') && !path.isAbsolute(id),
     },
     lib: {
+      entry: [
+        path.resolve(__dirname, 'index.ts'),
+        path.resolve(__dirname, 'utils', 'alphabet.ts'),
+      ],
       fileName: (_format, entryName) => `${entryName}.js`,
-      entry: path.resolve(__dirname, 'index.ts'),
       name: 'eslint-plugin-perfectionist',
       formats: ['cjs'],
     },


Test patch:
diff --git a/test/alphabet.test.ts b/test/alphabet.test.ts
new file mode 100644
index 0000000..ecc1846
--- /dev/null
+++ b/test/alphabet.test.ts
@@ -0,0 +1,276 @@
+import { describe, expect, it } from 'vitest'
+
+import { Alphabet } from '../utils/alphabet'
+
+describe('alphabet', () => {
+  describe('generators', () => {
+    describe('generateFrom', () => {
+      it('allows to generate an alphabet from string', () => {
+        expect(() => Alphabet.generateFrom('abc')).not.toThrow()
+      })
+
+      it('allows to generate an alphabet from array', () => {
+        expect(() => Alphabet.generateFrom(['a', 'b', 'c'])).not.toThrow()
+      })
+
+      it('throws when a duplicate character is found in a string input', () => {
+        expect(() => Alphabet.generateFrom('aa')).toThrow(
+          'The alphabet must not contain repeated characters',
+        )
+      })
+
+      it('throws when a duplicate character is found in an array input', () => {
+        expect(() => Alphabet.generateFrom(['a', 'a'])).toThrow(
+          'The alphabet must not contain repeated characters',
+        )
+      })
+
+      it('throws when a non-single character is found in a array input', () => {
+        expect(() => Alphabet.generateFrom(['ab'])).toThrow(
+          'The alphabet must contain single characters',
+        )
+      })
+    })
+
+    describe('generateCompleteAlphabet', () => {
+      it('allows to generate a complete alphabet', () => {
+        expect(
+          Alphabet.generateCompleteAlphabet().getCharacters(),
+        ).toHaveLength(458752)
+      })
+    })
+
+    describe('generateRecommendedAlphabet', () => {
+      it('allows to generate the recommended alphabet', () => {
+        let charactersThatShouldBeIncluded = [
+          'a',
+          'A',
+          String(1),
+          '<',
+          '@',
+          'ê',
+          'Ê',
+          '{',
+          '[',
+          '_',
+          '$',
+          '🙂',
+        ]
+
+        let generatedCharacters =
+          Alphabet.generateRecommendedAlphabet().getCharacters()
+
+        expect(generatedCharacters).toHaveLength(196608)
+        for (let character of charactersThatShouldBeIncluded) {
+          expect(generatedCharacters).toContain(character)
+        }
+      })
+    })
+
+    describe('adding/removing characters', () => {
+      it('allows to push characters with string input', () => {
+        expect(
+          Alphabet.generateFrom('ab').pushCharacters('cdd').getCharacters(),
+        ).toBe('abcd')
+      })
+
+      it('allows to push characters with array input', () => {
+        expect(
+          Alphabet.generateFrom('ab')
+            .pushCharacters(['c', 'd', 'd'])
+            .getCharacters(),
+        ).toBe('abcd')
+      })
+
+      it('throws when a non-single character is found in a array input', () => {
+        expect(() => Alphabet.generateFrom('').pushCharacters(['ab'])).toThrow(
+          'Only single characters may be pushed',
+        )
+      })
+
+      it('throws when pushing a character that already exists in the alphabet', () => {
+        expect(() => Alphabet.generateFrom('ab').pushCharacters('ab')).toThrow(
+          'The alphabet already contains the characters a, b',
+        )
+      })
+
+      it('allows removing characters', () => {
+        expect(
+          Alphabet.generateFrom('ab').removeCharacters('aac').getCharacters(),
+        ).toBe('b')
+      })
+
+      it('allows removing a Unicode range', () => {
+        expect(
+          Alphabet.generateCompleteAlphabet()
+            .removeUnicodeRange({ start: 0, end: 9 })
+            .getCharacters(),
+        ).toHaveLength(458742)
+      })
+    })
+
+    describe('sorters', () => {
+      it('allows sorting by localeCompare', () => {
+        expect(
+          Alphabet.generateFrom('bac').sortByLocaleCompare().getCharacters(),
+        ).toBe('abc')
+      })
+
+      it('allows sorting by natural sort', () => {
+        expect(
+          Alphabet.generateFrom('1bac2').sortByNaturalSort().getCharacters(),
+        ).toBe('12abc')
+      })
+
+      it('allows sorting by charCodeAt', () => {
+        expect(
+          Alphabet.generateFrom('1bac2').sortByCharCodeAt().getCharacters(),
+        ).toBe('12abc')
+      })
+
+      it('allows sorting by custom sorting function', () => {
+        expect(
+          Alphabet.generateFrom('1bac2')
+            .sortBy((a, b) => {
+              if (a === 'a') {
+                return 1
+              }
+              if (b === 'a') {
+                return -1
+              }
+              return 0
+            })
+            .getCharacters(),
+        ).toBe('1bc2a')
+      })
+    })
+
+    describe('misc utilities', () => {
+      it('allows reversing the alphabet', () => {
+        expect(Alphabet.generateFrom('abc').reverse().getCharacters()).toBe(
+          'cba',
+        )
+      })
+
+      describe('prioritizeCase', () => {
+        it('allows to prioritize uppercase', () => {
+          expect(
+            Alphabet.generateFrom('aABbc!dCD')
+              .prioritizeCase('uppercase')
+              .getCharacters(),
+          ).toBe('AaBbC!Dcd')
+        })
+
+        it('allows to prioritize lowercase', () => {
+          expect(
+            Alphabet.generateFrom('aABbcdCDe')
+              .prioritizeCase('lowercase')
+              .getCharacters(),
+          ).toBe('aAbBcdCDe')
+        })
+      })
+
+      describe('placeCharacterBefore', () => {
+        it('allows to place a character before another', () => {
+          expect(
+            Alphabet.generateFrom('ab-cd/')
+              .placeCharacterBefore({
+                characterBefore: '/',
+                characterAfter: '-',
+              })
+              .getCharacters(),
+          ).toBe('ab/-cd')
+        })
+
+        it('does nothing if characterBefore is already before characterAfter', () => {
+          expect(
+            Alphabet.generateFrom('abcd')
+              .placeCharacterBefore({
+                characterBefore: 'b',
+                characterAfter: 'd',
+              })
+              .getCharacters(),
+          ).toBe('abcd')
+        })
+
+        it('throws when the characterBefore is not in the alphabet', () => {
+          expect(() =>
+            Alphabet.generateFrom('a').placeCharacterBefore({
+              characterBefore: 'b',
+              characterAfter: 'a',
+            }),
+          ).toThrow('Character b not found in alphabet')
+        })
+
+        it('throws when the characterAfter is not in the alphabet', () => {
+          expect(() =>
+            Alphabet.generateFrom('a').placeCharacterBefore({
+              characterBefore: 'a',
+              characterAfter: 'b',
+            }),
+          ).toThrow('Character b not found in alphabet')
+        })
+      })
+
+      describe('placeCharacterAfter', () => {
+        it('allows to place a character after another', () => {
+          expect(
+            Alphabet.generateFrom('ab-cd/')
+              .placeCharacterAfter({
+                characterBefore: '/',
+                characterAfter: '-',
+              })
+              .getCharacters(),
+          ).toBe('abcd/-')
+        })
+
+        it('does nothing if characterBefore is already after characterBefore', () => {
+          expect(
+            Alphabet.generateFrom('abcd')
+              .placeCharacterAfter({
+                characterBefore: 'b',
+                characterAfter: 'd',
+              })
+              .getCharacters(),
+          ).toBe('abcd')
+        })
+
+        it('throws when the characterBefore is not in the alphabet', () => {
+          expect(() =>
+            Alphabet.generateFrom('a').placeCharacterAfter({
+              characterBefore: 'b',
+              characterAfter: 'a',
+            }),
+          ).toThrow('Character b not found in alphabet')
+        })
+
+        it('throws when the characterAfter is not in the alphabet', () => {
+          expect(() =>
+            Alphabet.generateFrom('a').placeCharacterAfter({
+              characterBefore: 'a',
+              characterAfter: 'b',
+            }),
+          ).toThrow('Character b not found in alphabet')
+        })
+      })
+
+      describe('placeAllWithCaseBeforeAllWithOtherCase', () => {
+        it('allows to placing all uppercase before lowercase ones', () => {
+          expect(
+            Alphabet.generateFrom('aAbBcCdD')
+              .placeAllWithCaseBeforeAllWithOtherCase('uppercase')
+              .getCharacters(),
+          ).toBe('ABCDabcd')
+        })
+
+        it('allows to placing all lowercase before uppercase ones', () => {
+          expect(
+            Alphabet.generateFrom('aAbBcCdD')
+              .placeAllWithCaseBeforeAllWithOtherCase('lowercase')
+              .getCharacters(),
+          ).toBe('abcdABCD')
+        })
+      })
+    })
+  })
+})
diff --git a/test/compare.test.ts b/test/compare.test.ts
index 245118a..e2c9445 100644
--- a/test/compare.test.ts
+++ b/test/compare.test.ts
@@ -2,6 +2,7 @@ import { describe, expect, it } from 'vitest'
 
 import type { SortingNode } from '../typings'
 
+import { Alphabet } from '../utils/alphabet'
 import { compare } from '../utils/compare'
 
 describe('compare', () => {
@@ -133,8 +134,126 @@ describe('compare', () => {
     })
   })
 
+  describe('line-length', () => {
+    let compareOptions = {
+      specialCharacters: 'keep',
+      type: 'line-length',
+      ignoreCase: false,
+      locales: 'en-US',
+      order: 'desc',
+    } as const
+
+    it('sorts by order asc', () => {
+      expect(
+        compare(createTestNode({ name: 'b' }), createTestNode({ name: 'aa' }), {
+          ...compareOptions,
+          order: 'asc',
+        }),
+      ).toBe(-1)
+    })
+
+    it('sorts by order desc', () => {
+      expect(
+        compare(
+          createTestNode({ name: 'aa' }),
+          createTestNode({ name: 'b' }),
+          compareOptions,
+        ),
+      ).toBe(-1)
+    })
+  })
+
+  describe('custom', () => {
+    let compareOptions = {
+      alphabet: Alphabet.generateRecommendedAlphabet()
+        .sortByLocaleCompare('en-US')
+        .getCharacters(),
+      specialCharacters: 'keep',
+      ignoreCase: false,
+      locales: 'en-US',
+      type: 'custom',
+      order: 'asc',
+    } as const
+
+    it('sorts by order asc', () => {
+      expect(
+        compare(
+          createTestNode({ name: 'b' }),
+          createTestNode({ name: 'a' }),
+          compareOptions,
+        ),
+      ).toBe(1)
+    })
+
+    it('sorts by order desc', () => {
+      expect(
+        compare(createTestNode({ name: 'a' }), createTestNode({ name: 'b' }), {
+          ...compareOptions,
+          order: 'desc',
+        }),
+      ).toBe(1)
+    })
+
+    it('sorts ignoring case', () => {
+      expect(
+        compare(
+          createTestNode({ name: 'aB' }),
+          createTestNode({ name: 'Ab' }),
+          {
+            ...compareOptions,
+            ignoreCase: true,
+          },
+        ),
+      ).toBe(0)
+    })
+
+    it('sorts while trimming special characters', () => {
+      expect(
+        compare(createTestNode({ name: '_a' }), createTestNode({ name: 'a' }), {
+          ...compareOptions,
+          specialCharacters: 'trim',
+        }),
+      ).toBe(0)
+    })
+
+    it('sorts while removing special characters', () => {
+      expect(
+        compare(
+          createTestNode({ name: 'ab' }),
+          createTestNode({ name: 'a_b' }),
+          {
+            ...compareOptions,
+            specialCharacters: 'remove',
+          },
+        ),
+      ).toBe(0)
+    })
+
+    it('gives minimum priority to characters not in the alphabet', () => {
+      expect(
+        compare(createTestNode({ name: 'a' }), createTestNode({ name: 'b' }), {
+          ...compareOptions,
+          alphabet: 'b',
+        }),
+      ).toBe(1)
+      expect(
+        compare(createTestNode({ name: 'b' }), createTestNode({ name: 'a' }), {
+          ...compareOptions,
+          alphabet: 'b',
+        }),
+      ).toBe(-1)
+      expect(
+        compare(createTestNode({ name: 'b' }), createTestNode({ name: 'a' }), {
+          ...compareOptions,
+          alphabet: 'c',
+        }),
+      ).toBe(0)
+    })
+  })
+
   let createTestNode = ({ name }: { name: string }): SortingNode =>
     ({
+      size: name.length,
       name,
     }) as SortingNode
 })
diff --git a/test/get-settings.test.ts b/test/get-settings.test.ts
index 482c7a7..98a7a65 100644
--- a/test/get-settings.test.ts
+++ b/test/get-settings.test.ts
@@ -27,6 +27,7 @@ describe('get-settings', () => {
       ignorePattern: [],
       ignoreCase: true,
       locales: 'en-US',
+      alphabet: '',
       order: 'asc',
     }
     expect(() => {
diff --git a/test/sort-array-includes.test.ts b/test/sort-array-includes.test.ts
index b9bb5cd..0959acb 100644
--- a/test/sort-array-includes.test.ts
+++ b/test/sort-array-includes.test.ts
@@ -6,6 +6,7 @@ import { afterAll, describe, it } from 'vitest'
 import { dedent } from 'ts-dedent'
 
 import rule from '../rules/sort-array-includes'
+import { Alphabet } from '../utils/alphabet'
 
 let ruleName = 'sort-array-includes'
 
@@ -1329,6 +1330,65 @@ describe(ruleName, () => {
     })
   })
 
+  describe(`${ruleName}: sorts by custom alphabet`, () => {
+    let type = 'custom'
+
+    let alphabet = Alphabet.generateRecommendedAlphabet()
+      .sortByLocaleCompare('en-US')
+      .getCharacters()
+    let options = {
+      type: 'custom',
+      order: 'asc',
+      alphabet,
+    } as const
+
+    ruleTester.run(`${ruleName}(${type}): sorts arrays`, rule, {
+      invalid: [
+        {
+          errors: [
+            {
+              data: {
+                right: 'b',
+                left: 'c',
+              },
+              messageId: 'unexpectedArrayIncludesOrder',
+            },
+          ],
+          output: dedent`
+            [
+              'a',
+              'b',
+              'c',
+              'd',
+            ].includes(value)
+          `,
+          code: dedent`
+            [
+              'a',
+              'c',
+              'b',
+              'd',
+            ].includes(value)
+          `,
+          options: [options],
+        },
+      ],
+      valid: [
+        {
+          code: dedent`
+            [
+              'a',
+              'b',
+              'c',
+              'd',
+            ].includes(value)
+          `,
+          options: [options],
+        },
+      ],
+    })
+  })
+
   describe(`${ruleName}: misc`, () => {
     ruleTester.run(
       `${ruleName}: sets alphabetical asc sorting as default`,
diff --git a/test/sort-classes.test.ts b/test/sort-classes.test.ts
index a073135..f46766e 100644
--- a/test/sort-classes.test.ts
+++ b/test/sort-classes.test.ts
@@ -5,6 +5,7 @@ import { RuleTester as EslintRuleTester } from 'eslint'
 import { afterAll, describe, it } from 'vitest'
 import { dedent } from 'ts-dedent'
 
+import { Alphabet } from '../utils/alphabet'
 import rule from '../rules/sort-classes'
 
 let ruleName = 'sort-classes'
@@ -6100,6 +6101,173 @@ describe(ruleName, () => {
     })
   })
 
+  describe(`${ruleName}: sorts by custom alphabet`, () => {
+    let type = 'custom'
+
+    let alphabet = Alphabet.generateRecommendedAlphabet()
+      .sortByLocaleCompare('en-US')
+      .getCharacters()
+    let options = {
+      type: 'custom',
+      order: 'asc',
+      alphabet,
+    } as const
+
+    ruleTester.run(`${ruleName}(${type}): sorts class members`, rule, {
+      invalid: [
+        {
+          options: [
+            {
+              ...options,
+              groups: [
+                'static-property',
+                'protected-property',
+                'private-property',
+                'property',
+                'constructor',
+                'static-method',
+                'static-protected-method',
+                'protected-method',
+                'private-method',
+                'method',
+                'unknown',
+              ],
+            },
+          ],
+          errors: [
+            {
+              data: {
+                right: 'd',
+                left: 'e',
+              },
+              messageId: 'unexpectedClassesOrder',
+            },
+            {
+              data: {
+                leftGroup: 'static-method',
+                rightGroup: 'constructor',
+                right: 'constructor',
+                left: 'f',
+              },
+              messageId: 'unexpectedClassesGroupOrder',
+            },
+          ],
+          output: dedent`
+            class Class {
+              static a = 'a'
+
+              protected b = 'b'
+
+              private c = 'c'
+
+              d = 'd'
+
+              e = 'e'
+
+              constructor() {}
+
+              static f() {}
+
+              protected static g() {}
+
+              protected h() {}
+
+              private i() {}
+
+              j() {}
+
+              k() {}
+            }
+          `,
+          code: dedent`
+            class Class {
+              static a = 'a'
+
+              protected b = 'b'
+
+              private c = 'c'
+
+              e = 'e'
+
+              d = 'd'
+
+              static f() {}
+
+              constructor() {}
+
+              protected static g() {}
+
+              protected h() {}
+
+              private i() {}
+
+              j() {}
+
+              k() {}
+            }
+          `,
+        },
+      ],
+      valid: [
+        {
+          code: dedent`
+            class Class {
+              a
+            }
+          `,
+          options: [options],
+        },
+        {
+          options: [
+            {
+              ...options,
+              groups: [
+                'static-property',
+                'protected-property',
+                'private-property',
+                'property',
+                'constructor',
+                'static-method',
+                'static-protected-method',
+                'protected-method',
+                'private-method',
+                'method',
+                'unknown',
+              ],
+            },
+          ],
+          code: dedent`
+            class Class {
+              static a = 'a'
+
+              protected b = 'b'
+
+              private c = 'c'
+
+              d = 'd'
+
+              e = 'e'
+
+              constructor() {}
+
+              static f() {}
+
+              protected static g() {}
+
+              protected h() {}
+
+              private i() {}
+
+              j() {}
+
+              k() {}
+            }
+          `,
+        },
+      ],
+    })
+  })
+
   describe(`${ruleName}: sorting by line length`, () => {
     let type = 'line-length-order'
 
diff --git a/test/sort-decorators.test.ts b/test/sort-decorators.test.ts
index b96d506..2d66c17 100644
--- a/test/sort-decorators.test.ts
+++ b/test/sort-decorators.test.ts
@@ -6,6 +6,7 @@ import { RuleTester as EslintRuleTester } from 'eslint'
 import { afterAll, describe, it } from 'vitest'
 import { dedent } from 'ts-dedent'
 
+import { Alphabet } from '../utils/alphabet'
 import rule from '../rules/sort-decorators'
 
 let ruleName = 'sort-decorators'
@@ -2197,6 +2198,118 @@ describe(ruleName, () => {
     )
   })
 
+  describe(`${ruleName}: sorts by custom alphabet`, () => {
+    let type = 'custom'
+
+    let alphabet = Alphabet.generateRecommendedAlphabet()
+      .sortByLocaleCompare('en-US')
+      .getCharacters()
+    let options = {
+      type: 'custom',
+      order: 'asc',
+      alphabet,
+    } as const
+
+    ruleTester.run(`${ruleName}(${type}): sorts decorators`, rule, {
+      invalid: [
+        {
+          output: dedent`
+            @A @B() @C
+            class Class {
+
+              @A @B() @C
+              property
+
+              @A @B() @C
+              accessor field
+
+              @A @B() @C
+              method(
+                @A
+                @B()
+                @C
+                parameter) {}
+
+            }
+          `,
+          code: dedent`
+            @A @C @B()
+            class Class {
+
+              @A @C @B()
+              property
+
+              @A @C @B()
+              accessor field
+
+              @A @C @B()
+              method(
+                @A
+                @C
+                @B()
+                parameter) {}
+
+            }
+          `,
+          errors: duplicate5Times([
+            {
+              data: {
+                right: 'B',
+                left: 'C',
+              },
+              messageId: 'unexpectedDecoratorsOrder',
+            },
+          ]),
+          options: [options],
+        },
+      ],
+      valid: [
+        {
+          code: dedent`
+            @A
+            class Class {
+
+              @A
+              property
+
+              @A
+              accessor field
+
+              @A
+              method(
+                @A
+                parameter) {}
+
+            }
+          `,
+          options: [options],
+        },
+        {
+          code: dedent`
+            @A
+            @B()
+            @C
+            class Class {
+
+              @A @B() C
+              property
+
+              @A @B() C
+              accessor field
+
+              @A @B() C
+              method(
+                @A @B() @C
+                parameter) {}
+
+            }
+          `,
+          options: [options],
+        },
+      ],
+    })
+  })
+
   describe(`${ruleName}: sorting by line length`, () => {
     let type = 'line-length'
 
diff --git a/test/sort-enums.test.ts b/test/sort-enums.test.ts
index fd8628e..ca7ffc1 100644
--- a/test/sort-enums.test.ts
+++ b/test/sort-enums.test.ts
@@ -4,6 +4,7 @@ import { dedent } from 'ts-dedent'
 
 import type { Options } from '../rules/sort-enums'
 
+import { Alphabet } from '../utils/alphabet'
 import rule from '../rules/sort-enums'
 
 let ruleName = 'sort-enums'
@@ -1142,6 +1143,65 @@ describe(ruleName, () => {
     })
   })
 
+  describe(`${ruleName}: sorts by custom alphabet`, () => {
+    let type = 'custom'
+
+    let alphabet = Alphabet.generateRecommendedAlphabet()
+      .sortByLocaleCompare('en-US')
+      .getCharacters()
+    let options = {
+      type: 'custom',
+      order: 'asc',
+      alphabet,
+    } as const
+
+    ruleTester.run(`${ruleName}(${type}): sorts enum members`, rule, {
+      invalid: [
+        {
+          errors: [
+            {
+              data: {
+                right: 'bbb',
+                left: 'cc',
+              },
+              messageId: 'unexpectedEnumsOrder',
+            },
+          ],
+          output: dedent`
+            enum Enum {
+              aaaa = 'a',
+              bbb = 'b',
+              cc = 'c',
+              d = 'd',
+            }
+          `,
+          code: dedent`
+            enum Enum {
+              aaaa = 'a',
+              cc = 'c',
+              bbb = 'b',
+              d = 'd',
+            }
+          `,
+          options: [options],
+        },
+      ],
+      valid: [
+        {
+          code: dedent`
+            enum Enum {
+              aaaa = 'a',
+              bbb = 'b',
+              cc = 'c',
+              d = 'd',
+            }
+          `,
+          options: [options],
+        },
+      ],
+    })
+  })
+
   describe(`${ruleName}: sorting by line length`, () => {
     let type = 'line-length-order'
 
diff --git a/test/sort-exports.test.ts b/test/sort-exports.test.ts
index 9425f17..004600b 100644
--- a/test/sort-exports.test.ts
+++ b/test/sort-exports.test.ts
@@ -5,6 +5,7 @@ import { RuleTester as EslintRuleTester } from 'eslint'
 import { afterAll, describe, it } from 'vitest'
 import { dedent } from 'ts-dedent'
 
+import { Alphabet } from '../utils/alphabet'
 import rule from '../rules/sort-exports'
 
 let ruleName = 'sort-exports'
@@ -878,6 +879,66 @@ describe(ruleName, () => {
     })
   })
 
+  describe(`${ruleName}: sorts by custom alphabet`, () => {
+    let type = 'custom'
+
+    let alphabet = Alphabet.generateRecommendedAlphabet()
+      .sortByLocaleCompare('en-US')
+      .getCharacters()
+    let options = {
+      type: 'custom',
+      order: 'asc',
+      alphabet,
+    } as const
+
+    ruleTester.run(`${ruleName}(${type}): sorts exports`, rule, {
+      invalid: [
+        {
+          errors: [
+            {
+              data: {
+                right: 'a',
+                left: 'b',
+              },
+              messageId: 'unexpectedExportsOrder',
+            },
+            {
+              data: {
+                right: 'c',
+                left: 'd',
+              },
+              messageId: 'unexpectedExportsOrder',
+            },
+          ],
+          output: dedent`
+            export { a1 } from 'a'
+            export { b1, b2 } from 'b'
+            export { c1, c2, c3 } from 'c'
+            export { d1, d2 } from 'd'
+          `,
+          code: dedent`
+            export { b1, b2 } from 'b'
+            export { a1 } from 'a'
+            export { d1, d2 } from 'd'
+            export { c1, c2, c3 } from 'c'
+          `,
+          options: [options],
+        },
+      ],
+      valid: [
+        {
+          code: dedent`
+            export { a1 } from 'a'
+            export { b1, b2 } from 'b'
+            export { c1, c2, c3 } from 'c'
+            export { d1, d2 } from 'd'
+          `,
+          options: [options],
+        },
+      ],
+    })
+  })
+
   describe(`${ruleName}: sorting by line length`, () => {
     let type = 'line-length-order'
 
diff --git a/test/sort-heritage-clauses.test.ts b/test/sort-heritage-clauses.test.ts
index bd3e3d0..32659ca 100644
--- a/test/sort-heritage-clauses.test.ts
+++ b/test/sort-heritage-clauses.test.ts
@@ -6,6 +6,7 @@ import { afterAll, describe, it } from 'vitest'
 import { dedent } from 'ts-dedent'
 
 import rule from '../rules/sort-heritage-clauses'
+import { Alphabet } from '../utils/alphabet'
 
 let ruleName = 'sort-heritage-clauses'
 
@@ -699,6 +700,96 @@ describe(ruleName, () => {
     )
   })
 
+  describe(`${ruleName}: sorts by custom alphabet`, () => {
+    let type = 'custom'
+
+    let alphabet = Alphabet.generateRecommendedAlphabet()
+      .sortByLocaleCompare('en-US')
+      .getCharacters()
+    let options = {
+      type: 'custom',
+      order: 'asc',
+      alphabet,
+    } as const
+
+    ruleTester.run(`${ruleName}(${type}): sorts heritage clauses`, rule, {
+      invalid: [
+        {
+          errors: [
+            {
+              data: {
+                right: 'b',
+                left: 'c',
+              },
+              messageId: 'unexpectedHeritageClausesOrder',
+            },
+          ],
+          output: dedent`
+            interface Interface extends
+              a,
+              b,
+              c {
+            }
+          `,
+          code: dedent`
+            interface Interface extends
+              a,
+              c,
+              b {
+            }
+          `,
+          options: [options],
+        },
+        {
+          errors: [
+            {
+              data: {
+                right: 'b',
+                left: 'c',
+              },
+              messageId: 'unexpectedHeritageClausesOrder',
+            },
+          ],
+          output: dedent`
+            interface Interface extends
+              A.a,
+              B.b,
+              C.c {
+            }
+          `,
+          code: dedent`
+            interface Interface extends
+              A.a,
+              C.c,
+              B.b {
+            }
+          `,
+          options: [options],
+        },
+      ],
+      valid: [
+        {
+          code: dedent`
+            interface Interface extends
+              a,
+              b,
+              c {
+            }
+          `,
+          options: [options],
+        },
+        {
+          code: dedent`
+            interface Interface extends
+              a {
+            }
+          `,
+          options: [options],
+        },
+      ],
+    })
+  })
+
   describe(`${ruleName}: sorting by line length`, () => {
     let type = 'line-length-order'
 
diff --git a/test/sort-imports.test.ts b/test/sort-imports.test.ts
index e624f35..e2f672a 100644
--- a/test/sort-imports.test.ts
+++ b/test/sort-imports.test.ts
@@ -15,6 +15,7 @@ import type { MESSAGE_ID, Options } from '../rules/sort-imports'
 
 import * as readClosestTsConfigUtils from '../utils/read-closest-ts-config-by-path'
 import * as getTypescriptImportUtils from '../utils/get-typescript-import'
+import { Alphabet } from '../utils/alphabet'
 import rule from '../rules/sort-imports'
 
 let ruleName = 'sort-imports'
@@ -3924,6 +3925,53 @@ describe(ruleName, () => {
     )
   })
 
+  describe(`${ruleName}: sorts by custom alphabet`, () => {
+    let type = 'custom'
+
+    let alphabet = Alphabet.generateRecommendedAlphabet()
+      .sortByLocaleCompare('en-US')
+      .getCharacters()
+    let options = {
+      type: 'custom',
+      order: 'asc',
+      alphabet,
+    } as const
+
+    ruleTester.run(`${ruleName}(${type}): sorts imports`, rule, {
+      invalid: [
+        {
+          errors: [
+            {
+              data: {
+                right: 'a',
+                left: 'b',
+              },
+              messageId: 'unexpectedImportsOrder',
+            },
+          ],
+          output: dedent`
+            import { a1, a2 } from 'a'
+            import { b1 } from 'b'
+          `,
+          code: dedent`
+            import { b1 } from 'b'
+            import { a1, a2 } from 'a'
+          `,
+          options: [options],
+        },
+      ],
+      valid: [
+        {
+          code: dedent`
+            import { a1, a2 } from 'a'
+            import { b1 } from 'b'
+          `,
+          options: [options],
+        },
+      ],
+    })
+  })
+
   describe(`${ruleName}: sorting by line length`, () => {
     let type = 'line-length-order'
 
diff --git a/test/sort-interfaces.test.ts b/test/sort-interfaces.test.ts
index ec37d50..3c79520 100644
--- a/test/sort-interfaces.test.ts
+++ b/test/sort-interfaces.test.ts
@@ -4,6 +4,7 @@ import { dedent } from 'ts-dedent'
 
 import type { Options } from '../rules/sort-interfaces'
 
+import { Alphabet } from '../utils/alphabet'
 import rule from '../rules/sort-interfaces'
 
 let ruleName = 'sort-interfaces'
@@ -2790,6 +2791,70 @@ describe(ruleName, () => {
     })
   })
 
+  describe(`${ruleName}: sorts by custom alphabet`, () => {
+    let type = 'custom'
+
+    let alphabet = Alphabet.generateRecommendedAlphabet()
+      .sortByLocaleCompare('en-US')
+      .getCharacters()
+    let options = {
+      type: 'custom',
+      order: 'asc',
+      alphabet,
+    } as const
+
+    ruleTester.run(`${ruleName}(${type}): sorts interface properties`, rule, {
+      invalid: [
+        {
+          errors: [
+            {
+              data: {
+                right: 'b',
+                left: 'c',
+              },
+              messageId: 'unexpectedInterfacePropertiesOrder',
+            },
+          ],
+          output: dedent`
+            interface Interface {
+              a: string
+              b: 'b1' | 'b2',
+              c: string
+            }
+          `,
+          code: dedent`
+            interface Interface {
+              a: string
+              c: string
+              b: 'b1' | 'b2',
+            }
+          `,
+          options: [options],
+        },
+      ],
+      valid: [
+        {
+          code: dedent`
+            interface Interface {
+              a: string
+            }
+          `,
+          options: [options],
+        },
+        {
+          code: dedent`
+            interface Interface {
+              a: string
+              b: 'b1' | 'b2',
+              c: string
+            }
+          `,
+          options: [options],
+        },
+      ],
+    })
+  })
+
   describe(`${ruleName}: sorting by line length`, () => {
     let type = 'line-length-order'
 
diff --git a/test/sort-intersection-types.test.ts b/test/sort-intersection-types.test.ts
index cc61cf0..d1cadbb 100644
--- a/test/sort-intersection-types.test.ts
+++ b/test/sort-intersection-types.test.ts
@@ -3,6 +3,7 @@ import { afterAll, describe, it } from 'vitest'
 import { dedent } from 'ts-dedent'
 
 import rule from '../rules/sort-intersection-types'
+import { Alphabet } from '../utils/alphabet'
 
 let ruleName = 'sort-intersection-types'
 
@@ -1316,6 +1317,50 @@ describe(ruleName, () => {
     })
   })
 
+  describe(`${ruleName}: sorts by custom alphabet`, () => {
+    let type = 'custom'
+
+    let alphabet = Alphabet.generateRecommendedAlphabet()
+      .sortByLocaleCompare('en-US')
+      .getCharacters()
+    let options = {
+      type: 'custom',
+      order: 'asc',
+      alphabet,
+    } as const
+
+    ruleTester.run(`${ruleName}(${type}: sorts intersection types`, rule, {
+      invalid: [
+        {
+          errors: [
+            {
+              data: {
+                right: "{ label: 'bb' }",
+                left: "{ label: 'c' }",
+              },
+              messageId: 'unexpectedIntersectionTypesOrder',
+            },
+          ],
+          output: dedent`
+            type Type = { label: 'aaa' } & { label: 'bb' } & { label: 'c' }
+          `,
+          code: dedent`
+            type Type = { label: 'aaa' } & { label: 'c' } & { label: 'bb' }
+          `,
+          options: [options],
+        },
+      ],
+      valid: [
+        {
+          code: dedent`
+            type Type = { label: 'aaa' } & { label: 'bb' } & { label: 'c' }
+          `,
+          options: [options],
+        },
+      ],
+    })
+  })
+
   describe(`${ruleName}: sorting by line length`, () => {
     let type = 'line-length-order'
 
diff --git a/test/sort-jsx-props.test.ts b/test/sort-jsx-props.test.ts
index d84e960..675d994 100644
--- a/test/sort-jsx-props.test.ts
+++ b/test/sort-jsx-props.test.ts
@@ -4,6 +4,7 @@ import { afterAll, describe, it } from 'vitest'
 import { dedent } from 'ts-dedent'
 import path from 'node:path'
 
+import { Alphabet } from '../utils/alphabet'
 import rule from '../rules/sort-jsx-props'
 
 let ruleName = 'sort-jsx-props'
@@ -1057,6 +1058,74 @@ describe(ruleName, () => {
     })
   })
 
+  describe(`${ruleName}: sorts by custom alphabet`, () => {
+    let type = 'custom'
+
+    let alphabet = Alphabet.generateRecommendedAlphabet()
+      .sortByLocaleCompare('en-US')
+      .getCharacters()
+    let options = {
+      type: 'custom',
+      order: 'asc',
+      alphabet,
+    } as const
+
+    ruleTester.run(`${ruleName}(${type}): sorts jsx props`, rule, {
+      invalid: [
+        {
+          output: dedent`
+            let Component = () => (
+              <Element
+                a="aaa"
+                b="bb"
+                c="c"
+              >
+                Value
+              </Element>
+            )
+          `,
+          code: dedent`
+            let Component = () => (
+              <Element
+                a="aaa"
+                c="c"
+                b="bb"
+              >
+                Value
+              </Element>
+            )
+          `,
+          errors: [
+            {
+              data: {
+                right: 'b',
+                left: 'c',
+              },
+              messageId: 'unexpectedJSXPropsOrder',
+            },
+          ],
+          options: [options],
+        },
+      ],
+      valid: [
+        {
+          code: dedent`
+            let Component = () => (
+              <Element
+                a="aaa"
+                b="bb"
+                c="c"
+              >
+                Value
+              </Element>
+            )
+          `,
+          options: [options],
+        },
+      ],
+    })
+  })
+
   describe(`${ruleName}: sorting by line length`, () => {
     let type = 'line-length-order'
 
diff --git a/test/sort-maps.test.ts b/test/sort-maps.test.ts
index b0916d4..5d2ef75 100644
--- a/test/sort-maps.test.ts
+++ b/test/sort-maps.test.ts
@@ -5,6 +5,7 @@ import { RuleTester as EslintRuleTester } from 'eslint'
 import { afterAll, describe, it } from 'vitest'
 import { dedent } from 'ts-dedent'
 
+import { Alphabet } from '../utils/alphabet'
 import rule from '../rules/sort-maps'
 
 let ruleName = 'sort-maps'
@@ -768,6 +769,59 @@ describe(ruleName, () => {
     })
   })
 
+  describe(`${ruleName}: sorts by custom alphabet`, () => {
+    let type = 'custom'
+
+    let alphabet = Alphabet.generateRecommendedAlphabet()
+      .sortByLocaleCompare('en-US')
+      .getCharacters()
+    let options = {
+      type: 'custom',
+      order: 'asc',
+      alphabet,
+    } as const
+
+    ruleTester.run(`${ruleName}(${type}): works with variables as keys`, rule, {
+      invalid: [
+        {
+          errors: [
+            {
+              data: {
+                right: 'aa',
+                left: 'b',
+              },
+              messageId: 'unexpectedMapElementsOrder',
+            },
+          ],
+          output: dedent`
+              new Map([
+                [aa, aa],
+                [b, b],
+              ])
+            `,
+          code: dedent`
+              new Map([
+                [b, b],
+                [aa, aa],
+              ])
+            `,
+          options: [options],
+        },
+      ],
+      valid: [
+        {
+          code: dedent`
+              new Map([
+                [aa, aa],
+                [b, b],
+              ])
+            `,
+          options: [options],
+        },
+      ],
+    })
+  })
+
   describe(`${ruleName}: sorting by line length`, () => {
     let type = 'line-length-order'
 
diff --git a/test/sort-modules.test.ts b/test/sort-modules.test.ts
index 1517a86..caaf580 100644
--- a/test/sort-modules.test.ts
+++ b/test/sort-modules.test.ts
@@ -5,6 +5,7 @@ import { RuleTester as EslintRuleTester } from 'eslint'
 import { afterAll, describe, it } from 'vitest'
 import { dedent } from 'ts-dedent'
 
+import { Alphabet } from '../utils/alphabet'
 import rule from '../rules/sort-modules'
 
 let ruleName = 'sort-modules'
@@ -2571,6 +2572,150 @@ describe(ruleName, () => {
     })
   })
 
+  describe(`${ruleName}: sorts by custom alphabet`, () => {
+    let type = 'custom'
+
+    let alphabet = Alphabet.generateRecommendedAlphabet()
+      .sortByLocaleCompare('en-US')
+      .getCharacters()
+    let options = {
+      type: 'custom',
+      order: 'asc',
+      alphabet,
+    } as const
+
+    ruleTester.run(`${ruleName}(${type}): sorts modules`, rule, {
+      invalid: [
+        {
+          errors: [
+            {
+              data: {
+                leftGroup: 'export-interface',
+                left: 'FindUserInput',
+                right: 'CacheType',
+                rightGroup: 'enum',
+              },
+              messageId: 'unexpectedModulesGroupOrder',
+            },
+            {
+              data: {
+                rightGroup: 'export-function',
+                left: 'assertInputIsCorrect',
+                leftGroup: 'function',
+                right: 'findUser',
+              },
+              messageId: 'unexpectedModulesGroupOrder',
+            },
+            {
+              data: {
+                leftGroup: 'export-function',
+                right: 'FindAllUsersInput',
+                rightGroup: 'export-type',
+                left: 'findUser',
+              },
+              messageId: 'unexpectedModulesGroupOrder',
+            },
+            {
+              data: {
+                leftGroup: 'export-function',
+                left: 'findAllUsers',
+                rightGroup: 'class',
+                right: 'Cache',
+              },
+              messageId: 'unexpectedModulesGroupOrder',
+            },
+          ],
+          output: dedent`
+            enum CacheType {
+              ALWAYS = 'ALWAYS',
+              NEVER = 'NEVER',
+            }
+
+            export type FindAllUsersInput = {
+              ids: string[]
+              cache: CacheType
+            }
+
+            export type FindAllUsersOutput = FindUserOutput[]
+
+            export interface FindUserInput {
+              id: string
+              cache: CacheType
+            }
+
+            export type FindUserOutput = {
+              id: string
+              name: string
+              age: number
+            }
+
+            class Cache {
+              // Some logic
+            }
+
+            export function findAllUsers(input: FindAllUsersInput): FindAllUsersOutput {
+              assertInputIsCorrect(input)
+              return _findUserByIds(input.ids)
+            }
+
+            export function findUser(input: FindUserInput): FindUserOutput {
+              assertInputIsCorrect(input)
+              return _findUserByIds([input.id])[0]
+            }
+
+            function assertInputIsCorrect(input: FindUserInput | FindAllUsersInput): void {
+              // Some logic
+            }
+            `,
+          code: dedent`
+            export interface FindUserInput {
+              id: string
+              cache: CacheType
+            }
+
+            enum CacheType {
+              ALWAYS = 'ALWAYS',
+              NEVER = 'NEVER',
+            }
+
+            export type FindUserOutput = {
+              id: string
+              name: string
+              age: number
+            }
+
+            function assertInputIsCorrect(input: FindUserInput | FindAllUsersInput): void {
+              // Some logic
+            }
+
+            export function findUser(input: FindUserInput): FindUserOutput {
+              assertInputIsCorrect(input)
+              return _findUserByIds([input.id])[0]
+            }
+
+            export type FindAllUsersInput = {
+              ids: string[]
+              cache: CacheType
+            }
+
+            export type FindAllUsersOutput = FindUserOutput[]
+
+            export function findAllUsers(input: FindAllUsersInput): FindAllUsersOutput {
+              assertInputIsCorrect(input)
+              return _findUserByIds(input.ids)
+            }
+
+            class Cache {
+              // Some logic
+            }
+          `,
+          options: [options],
+        },
+      ],
+      valid: [],
+    })
+  })
+
   describe(`${ruleName}: sorting by line-length`, () => {
     let type = 'line-length'
 
diff --git a/test/sort-named-exports.test.ts b/test/sort-named-exports.test.ts
index 778d576..a80e609 100644
--- a/test/sort-named-exports.test.ts
+++ b/test/sort-named-exports.test.ts
@@ -6,6 +6,7 @@ import { afterAll, describe, it } from 'vitest'
 import { dedent } from 'ts-dedent'
 
 import rule from '../rules/sort-named-exports'
+import { Alphabet } from '../utils/alphabet'
 
 let ruleName = 'sort-named-exports'
 
@@ -668,6 +669,60 @@ describe(ruleName, () => {
     )
   })
 
+  describe(`${ruleName}: sorts by custom alphabet`, () => {
+    let type = 'custom'
+
+    let alphabet = Alphabet.generateRecommendedAlphabet()
+      .sortByLocaleCompare('en-US')
+      .getCharacters()
+    let options = {
+      type: 'custom',
+      order: 'asc',
+      alphabet,
+    } as const
+
+    ruleTester.run(`${ruleName}(${type}): sorts named exports`, rule, {
+      invalid: [
+        {
+          errors: [
+            {
+              data: {
+                right: 'bb',
+                left: 'c',
+              },
+              messageId: 'unexpectedNamedExportsOrder',
+            },
+          ],
+          output: dedent`
+            export {
+              aaa,
+              bb,
+              c
+            }
+          `,
+          code: dedent`
+            export {
+              aaa,
+              c,
+              bb
+            }
+          `,
+          options: [options],
+        },
+      ],
+      valid: [
+        {
+          code: 'export { a }',
+          options: [options],
+        },
+        {
+          code: 'export { aaa, bb, c }',
+          options: [options],
+        },
+      ],
+    })
+  })
+
   describe(`${ruleName}: sorting by line length`, () => {
     let type = 'line-length-order'
 
diff --git a/test/sort-named-imports.test.ts b/test/sort-named-imports.test.ts
index 55c81d6..a54c281 100644
--- a/test/sort-named-imports.test.ts
+++ b/test/sort-named-imports.test.ts
@@ -6,6 +6,7 @@ import { afterAll, describe, it } from 'vitest'
 import { dedent } from 'ts-dedent'
 
 import rule from '../rules/sort-named-imports'
+import { Alphabet } from '../utils/alphabet'
 
 let ruleName = 'sort-named-imports'
 
@@ -1145,6 +1146,50 @@ describe(ruleName, () => {
     })
   })
 
+  describe(`${ruleName}: sorts by custom alphabet`, () => {
+    let type = 'custom'
+
+    let alphabet = Alphabet.generateRecommendedAlphabet()
+      .sortByLocaleCompare('en-US')
+      .getCharacters()
+    let options = {
+      type: 'custom',
+      order: 'asc',
+      alphabet,
+    } as const
+
+    ruleTester.run(`${ruleName}(${type}): sorts named imports`, rule, {
+      invalid: [
+        {
+          errors: [
+            {
+              data: {
+                right: 'AAA',
+                left: 'BB',
+              },
+              messageId: 'unexpectedNamedImportsOrder',
+            },
+          ],
+          output: dedent`
+            import { AAA, BB, C } from 'module'
+          `,
+          code: dedent`
+            import { BB, AAA, C } from 'module'
+          `,
+          options: [options],
+        },
+      ],
+      valid: [
+        {
+          code: dedent`
+            import { AAA, BB, C } from 'module'
+          `,
+          options: [options],
+        },
+      ],
+    })
+  })
+
   describe(`${ruleName}: sorting by line length`, () => {
     let type = 'line-length-order'
 
diff --git a/test/sort-object-types.test.ts b/test/sort-object-types.test.ts
index 6fb5b8b..8c8709f 100644
--- a/test/sort-object-types.test.ts
+++ b/test/sort-object-types.test.ts
@@ -3,6 +3,7 @@ import { afterAll, describe, it } from 'vitest'
 import { dedent } from 'ts-dedent'
 
 import rule from '../rules/sort-object-types'
+import { Alphabet } from '../utils/alphabet'
 
 let ruleName = 'sort-object-types'
 
@@ -2412,6 +2413,62 @@ describe(ruleName, () => {
     })
   })
 
+  describe(`${ruleName}: sorts by custom alphabet`, () => {
+    let type = 'custom'
+
+    let alphabet = Alphabet.generateRecommendedAlphabet()
+      .sortByLocaleCompare('en-US')
+      .getCharacters()
+    let options = {
+      type: 'custom',
+      order: 'asc',
+      alphabet,
+    } as const
+
+    ruleTester.run(`${ruleName}(${type}): sorts type members`, rule, {
+      invalid: [
+        {
+          errors: [
+            {
+              data: {
+                right: 'b',
+                left: 'c',
+              },
+              messageId: 'unexpectedObjectTypesOrder',
+            },
+          ],
+          output: dedent`
+            type Type = {
+              a: 'aaa'
+              b: 'bb'
+              c: 'c'
+            }
+          `,
+          code: dedent`
+            type Type = {
+              a: 'aaa'
+              c: 'c'
+              b: 'bb'
+            }
+          `,
+          options: [options],
+        },
+      ],
+      valid: [
+        {
+          code: dedent`
+            type Type = {
+              a: 'aaa'
+              b: 'bb'
+              c: 'c'
+            }
+          `,
+          options: [options],
+        },
+      ],
+    })
+  })
+
   describe(`${ruleName}: sorting by line length`, () => {
     let type = 'line-length-order'
 
diff --git a/test/sort-objects.test.ts b/test/sort-objects.test.ts
index b82eff7..ca7c7ee 100644
--- a/test/sort-objects.test.ts
+++ b/test/sort-objects.test.ts
@@ -5,6 +5,7 @@ import { RuleTester as EslintRuleTester } from 'eslint'
 import { afterAll, describe, it } from 'vitest'
 import { dedent } from 'ts-dedent'
 
+import { Alphabet } from '../utils/alphabet'
 import rule from '../rules/sort-objects'
 
 let ruleName = 'sort-objects'
@@ -2777,6 +2778,69 @@ describe(ruleName, () => {
     )
   })
 
+  describe(`${ruleName}: sorts by custom alphabet`, () => {
+    let type = 'custom'
+
+    let alphabet = Alphabet.generateRecommendedAlphabet()
+      .sortByLocaleCompare('en-US')
+      .getCharacters()
+    let options = {
+      type: 'custom',
+      order: 'asc',
+      alphabet,
+    } as const
+
+    ruleTester.run(
+      `${ruleName}(${type}): sorts object with identifier and literal keys`,
+      rule,
+      {
+        invalid: [
+          {
+            errors: [
+              {
+                data: {
+                  right: 'b',
+                  left: 'c',
+                },
+                messageId: 'unexpectedObjectsOrder',
+              },
+            ],
+            output: dedent`
+              let Obj = {
+                a: 'aaaa',
+                b: 'bbb',
+                [c]: 'cc',
+                d: 'd',
+              }
+            `,
+            code: dedent`
+              let Obj = {
+                a: 'aaaa',
+                [c]: 'cc',
+                b: 'bbb',
+                d: 'd',
+              }
+            `,
+            options: [options],
+          },
+        ],
+        valid: [
+          {
+            code: dedent`
+              let Obj = {
+                a: 'aaaa',
+                b: 'bbb',
+                [c]: 'cc',
+                d: 'd',
+              }
+            `,
+            options: [options],
+          },
+        ],
+      },
+    )
+  })
+
   describe(`${ruleName}: sorting by line length`, () => {
     let type = 'line-length-order'
 
diff --git a/test/sort-sets.test.ts b/test/sort-sets.test.ts
index ab3480a..460f26e 100644
--- a/test/sort-sets.test.ts
+++ b/test/sort-sets.test.ts
@@ -5,6 +5,7 @@ import { RuleTester as EslintRuleTester } from 'eslint'
 import { afterAll, describe, it } from 'vitest'
 import { dedent } from 'ts-dedent'
 
+import { Alphabet } from '../utils/alphabet'
 import rule from '../rules/sort-sets'
 
 let ruleName = 'sort-sets'
@@ -1005,6 +1006,65 @@ describe(ruleName, () => {
     })
   })
 
+  describe(`${ruleName}: sorts by custom alphabet`, () => {
+    let type = 'custom'
+
+    let alphabet = Alphabet.generateRecommendedAlphabet()
+      .sortByLocaleCompare('en-US')
+      .getCharacters()
+    let options = {
+      type: 'custom',
+      order: 'asc',
+      alphabet,
+    } as const
+
+    ruleTester.run(`${ruleName}(${type}): sorts sets`, rule, {
+      invalid: [
+        {
+          errors: [
+            {
+              data: {
+                right: 'b',
+                left: 'c',
+              },
+              messageId: 'unexpectedSetsOrder',
+            },
+          ],
+          output: dedent`
+            new Set([
+              'a',
+              'b',
+              'c',
+              'd',
+            ])
+          `,
+          code: dedent`
+            new Set([
+              'a',
+              'c',
+              'b',
+              'd',
+            ])
+          `,
+          options: [options],
+        },
+      ],
+      valid: [
+        {
+          code: dedent`
+            new Set(
+              'a',
+              'b',
+              'c',
+              'd',
+            ).includes(value)
+          `,
+          options: [options],
+        },
+      ],
+    })
+  })
+
   describe(`${ruleName}: sorting by line length`, () => {
     let type = 'line-length-order'
 
diff --git a/test/sort-switch-case.test.ts b/test/sort-switch-case.test.ts
index a5657ba..61d6343 100644
--- a/test/sort-switch-case.test.ts
+++ b/test/sort-switch-case.test.ts
@@ -6,6 +6,7 @@ import { afterAll, describe, it } from 'vitest'
 import { dedent } from 'ts-dedent'
 
 import rule from '../rules/sort-switch-case'
+import { Alphabet } from '../utils/alphabet'
 
 let ruleName = 'sort-switch-case'
 
@@ -1857,6 +1858,114 @@ describe(ruleName, () => {
     )
   })
 
+  describe(`${ruleName}: sorts by custom alphabet`, () => {
+    let type = 'custom'
+
+    let alphabet = Alphabet.generateRecommendedAlphabet()
+      .sortByLocaleCompare('en-US')
+      .getCharacters()
+    let options = {
+      type: 'custom',
+      order: 'asc',
+      alphabet,
+    } as const
+
+    ruleTester.run(`${ruleName}(${type}): works with grouped cases`, rule, {
+      invalid: [
+        {
+          output: [
+            dedent`
+              switch (value) {
+                case 'aaaaaa':
+                  return 'primary'
+                case 'cccc':
+                case 'ee':
+                case 'f':
+                  return 'tertiary'
+                case 'bbbbb':
+                case 'ddd':
+                  return 'secondary'
+                case 'x':
+                default:
+                  return 'unknown'
+              }
+            `,
+            dedent`
+              switch (value) {
+                case 'aaaaaa':
+                  return 'primary'
+                case 'bbbbb':
+                case 'ddd':
+                  return 'secondary'
+                case 'cccc':
+                case 'ee':
+                case 'f':
+                  return 'tertiary'
+                case 'x':
+                default:
+                  return 'unknown'
+              }
+            `,
+          ],
+          code: dedent`
+            switch (value) {
+              case 'aaaaaa':
+                return 'primary'
+              case 'ee':
+              case 'cccc':
+              case 'f':
+                return 'tertiary'
+              case 'bbbbb':
+              case 'ddd':
+                return 'secondary'
+              case 'x':
+              default:
+                return 'unknown'
+            }
+          `,
+          errors: [
+            {
+              data: {
+                right: 'cccc',
+                left: 'ee',
+              },
+              messageId: 'unexpectedSwitchCaseOrder',
+            },
+            {
+              data: {
+                right: 'bbbbb',
+                left: 'f',
+              },
+              messageId: 'unexpectedSwitchCaseOrder',
+            },
+          ],
+          options: [options],
+        },
+      ],
+      valid: [
+        {
+          code: dedent`
+            switch (value) {
+              case 'aaaaaa':
+                return 'primary'
+              case 'bbbbb':
+              case 'ddd':
+                return 'secondary'
+              case 'cccc':
+              case 'ee':
+              case 'f':
+                return 'tertiary'
+              case 'x':
+              default:
+                return 'unknown'
+            }
+          `,
+          options: [options],
+        },
+      ],
+    })
+  })
+
   describe(`${ruleName}: sorting by line length`, () => {
     let type = 'line-length-order'
 
diff --git a/test/sort-union-types.test.ts b/test/sort-union-types.test.ts
index 3f7e063..49129ad 100644
--- a/test/sort-union-types.test.ts
+++ b/test/sort-union-types.test.ts
@@ -3,6 +3,7 @@ import { afterAll, describe, it } from 'vitest'
 import { dedent } from 'ts-dedent'
 
 import rule from '../rules/sort-union-types'
+import { Alphabet } from '../utils/alphabet'
 
 let ruleName = 'sort-union-types'
 
@@ -1335,6 +1336,50 @@ describe(ruleName, () => {
     })
   })
 
+  describe(`${ruleName}: sorts by custom alphabet`, () => {
+    let type = 'custom'
+
+    let alphabet = Alphabet.generateRecommendedAlphabet()
+      .sortByLocaleCompare('en-US')
+      .getCharacters()
+    let options = {
+      type: 'custom',
+      order: 'asc',
+      alphabet,
+    } as const
+
+    ruleTester.run(`${ruleName}(${type}: sorts union types`, rule, {
+      invalid: [
+        {
+          errors: [
+            {
+              data: {
+                right: "'bbb'",
+                left: "'cc'",
+              },
+              messageId: 'unexpectedUnionTypesOrder',
+            },
+          ],
+          output: dedent`
+            type Type = 'aaaa' | 'bbb' | 'cc' | 'd'
+          `,
+          code: dedent`
+            type Type = 'aaaa' | 'cc' | 'bbb' | 'd'
+          `,
+          options: [options],
+        },
+      ],
+      valid: [
+        {
+          code: dedent`
+            type Type = 'aaaa' | 'bbb' | 'cc' | 'd'
+          `,
+          options: [options],
+        },
+      ],
+    })
+  })
+
   describe(`${ruleName}: sorting by line length`, () => {
     let type = 'line-length-order'
 
diff --git a/test/sort-variable-declarations.test.ts b/test/sort-variable-declarations.test.ts
index 9fcbbd4..686726d 100644
--- a/test/sort-variable-declarations.test.ts
+++ b/test/sort-variable-declarations.test.ts
@@ -6,6 +6,7 @@ import { afterAll, describe, it } from 'vitest'
 import { dedent } from 'ts-dedent'
 
 import rule from '../rules/sort-variable-declarations'
+import { Alphabet } from '../utils/alphabet'
 
 let ruleName = 'sort-variable-declarations'
 
@@ -1287,6 +1288,47 @@ describe(ruleName, () => {
     )
   })
 
+  describe(`${ruleName}: sorts by custom alphabet`, () => {
+    let type = 'custom'
+
+    let alphabet = Alphabet.generateRecommendedAlphabet()
+      .sortByLocaleCompare('en-US')
+      .getCharacters()
+    let options = {
+      type: 'custom',
+      order: 'asc',
+      alphabet,
+    } as const
+
+    ruleTester.run(`${ruleName}(${type}): sorts variables declarations`, rule, {
+      invalid: [
+        {
+          errors: [
+            {
+              messageId: 'unexpectedVariableDeclarationsOrder',
+              data: { right: 'aaa', left: 'bb' },
+            },
+          ],
+          output: dedent`
+              const aaa, bb, c
+            `,
+          code: dedent`
+              const bb, aaa, c
+            `,
+          options: [options],
+        },
+      ],
+      valid: [
+        {
+          code: dedent`
+              const aaa, bb, c
+            `,
+          options: [options],
+        },
+      ],
+    })
+  })
+
   describe(`${ruleName}: sorting by line length`, () => {
     let type = 'line-length-order'
 
diff --git a/test/validate-custom-sort-configuration.test.ts b/test/validate-custom-sort-configuration.test.ts
new file mode 100644
index 0000000..aa2587c
--- /dev/null
+++ b/test/validate-custom-sort-configuration.test.ts
@@ -0,0 +1,25 @@
+import { describe, expect, it } from 'vitest'
+
+import { validateCustomSortConfiguration } from '../utils/validate-custom-sort-configuration'
+
+describe('validate-custom-sort-configuration', () => {
+  it('accepts empty alphabet when type is not `custom`', () => {
+    for (let type of ['alphabetical', 'line-length', 'natural'] as const) {
+      expect(() =>
+        validateCustomSortConfiguration({
+          alphabet: '',
+          type,
+        }),
+      ).not.toThrow()
+    }
+  })
+
+  it('throws when an empty alphabet is entered while type is `custom`', () => {
+    expect(() =>
+      validateCustomSortConfiguration({
+        type: 'custom',
+        alphabet: '',
+      }),
+    ).toThrow('alphabet` option must not be empty')
+  })
+})
diff --git a/test/validate-generated-groups-configuration.test.ts b/test/validate-generated-groups-configuration.test.ts
index 11a2284..e4d38c2 100644
--- a/test/validate-generated-groups-configuration.test.ts
+++ b/test/validate-generated-groups-configuration.test.ts
@@ -1,6 +1,6 @@
 import { describe, expect, it } from 'vitest'
 
-import { validateGeneratedGroupsConfiguration } from '../rules/validate-generated-groups-configuration'
+import { validateGeneratedGroupsConfiguration } from '../utils/validate-generated-groups-configuration'
 import { allModifiers, allSelectors } from '../rules/sort-classes.types'
 import { getArrayCombinations } from '../utils/get-array-combinations'