# sort-export-attributes

Enforce sorted export attributes.

This rule keeps attributes inside `export ... with { ... }` consistently ordered. It improves readability, makes diffs smaller, and keeps attribute groups tidy in larger files.

## Try it out

**Initial**

```tsx
export { data } from 'lib' with {
  mode: 'no-cors',
  type: 'json',
  integrity: 'sha256-...',
}
```

**Sorted alphabetically**

```tsx
export { data } from 'lib' with {
  integrity: 'sha256-...',
  mode: 'no-cors',
  type: 'json',
}
```

**Sorted by line length**

```tsx
export { data } from 'lib' with {
  integrity: 'sha256-...',
  mode: 'no-cors',
  type: 'json',
}
```

## Options

This rule accepts an options object with the following properties:

### type

default: `'alphabetical'`

Specifies the sorting method.

- `'alphabetical'` — Sort attributes alphabetically using [localeCompare](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare).
- `'natural'` — Sort by [natural](https://github.com/yobacca/natural-orderby) order (e.g., `link2` \< `link10`).
- `'line-length'` — Sort by attribute name length (shorter first by default).
- `'custom'` — Sort using a custom alphabet specified in [`alphabet`](#alphabet).
- `'unsorted'` — Do not sort items. [`grouping`](#groups) and [`newlines behavior`](#newlinesbetween) are still enforced.

### order

default: `'asc'`

Sort direction.

- `'asc'` — Ascending (A→Z, short→long).
- `'desc'` — Descending (Z→A, long→short).

### fallbackSort

type:

```ts
{
  type:
    | 'alphabetical'
    | 'natural'
    | 'line-length'
    | 'custom'
    | 'subgroup-order'
    | 'unsorted'
  order?: 'asc' | 'desc'
}
```

default: `{ type: 'unsorted' }`

Specifies fallback sort options for elements that are equal according to the primary sort [`type`](#type).

You can also sort by subgroup order (nested groups in the [`groups`](#groups) option) using `subgroup-order`.

Example: enforce alphabetical sort between two elements with the same length.

```ts
{
  type: 'line-length',
  order: 'desc',
  fallbackSort: { type: 'alphabetical', order: 'asc' }
}
```

### alphabet

default: `''`

Used only when [`type`](#type) is `'custom'`. Defines the custom character order.

### ignoreCase

default: `true`

Whether to perform case-insensitive comparison for string-based sorts.

### specialCharacters

default: `'keep'`

How to handle special characters before comparison.

- `'keep'` — Keep as-is.
- `'trim'` — Trim leading special characters.
- `'remove'` — Remove all special characters.

### locales

default: `'en-US'`

Locales passed to `localeCompare` for alphabetical/natural sorts.

### partitionByComment

default: `false`

Use comments to split attributes into independent partitions that are sorted separately.

- `true` — Any non-ESLint comment creates a partition.
- `false` — Ignore comments for partitioning.
- `RegExpPattern = string | { pattern: string; flags?: string }` — Pattern for matching comments.
- `RegExpPattern[]` — List of patterns.
- `{ block: boolean | RegExpPattern | RegExpPattern[]; line: boolean | RegExpPattern | RegExpPattern[] }` — Separate settings for block/line comments.

### partitionByNewLine

default: `false`

When `true`, an empty line between attributes creates a partition. Each partition is sorted independently.

### newlinesBetween

type: `number | 'ignore'`

default: `'ignore'`

Specifies how to handle newlines between groups.

- `'ignore'` — Do not report errors related to newlines.
- `0` — No newlines are allowed.
- Any other number — Enforce this number of newlines between each group.

You can also enforce the newline behavior between two specific groups through the [`groups`](#newlines-between-groups)
option.

This option is only applicable when [`partitionByNewLine`](#partitionbynewline) is `false`.

### newlinesInside

type: `number | 'ignore' | 'newlinesBetween'`

default: `'newlinesBetween'`

Specifies how to handle newlines inside groups.

- `'ignore'` — Do not report errors related to newlines.
- `'newlinesBetween'` — \[DEPRECATED] If [`newlinesBetween`](#newlinesbetween) is `'ignore'`, then `'ignore'`, otherwise `0`.
- `0` — No newlines are allowed.
- Any other number — Enforce this number of newlines between each element of the same group.

You can also enforce the newline behavior inside a given group through the [`groups`](#group-with-overridden-settings)
or [`customGroups`](#customgroups) options.

This option is only applicable when [`partitionByNewLine`](#partitionbynewline) is `false`.

### useConfigurationIf

type:

```ts
{
  allNamesMatchPattern?:
    | string
    | string[]
    | { pattern: string; flags: string }
    | { pattern: string; flags: string }[]
  matchesAstSelector?: string
}
```

default: `{}`

Specifies filters to match a particular options configuration for a given export.

The first matching options configuration will be used. If no configuration matches, the default options configuration will be used.

- `allNamesMatchPattern` — A regexp pattern that all export attributes must match.

Example configuration:

```ts
{
  'perfectionist/sort-export-attributes': [
    'error',
    {
      groups: ['r', 'g', 'b'], // Sort colors by RGB
      customGroups: [
        {
          elementNamePattern: '^r$',
          groupName: 'r',
        },
        {
          elementNamePattern: '^g$',
          groupName: 'g',
        },
        {
          elementNamePattern: '^b$',
          groupName: 'b',
        },
      ],
      useConfigurationIf: {
        allNamesMatchPattern: '^[rgb]$',
      },
    },
    {
      type: 'alphabetical' // Fallback configuration
    }
  ],
}
```

- `matchesAstSelector` — An [AST selector](https://eslint.org/docs/latest/extend/selectors) matching an `ExportNamedDeclaration` node.
  To avoid unexpected behavior, do not use `:exit` or `:enter` pseudo-selectors.

### groups

type:

```ts
  Array<
    | string
    | string[]
    | { newlinesBetween: number | 'ignore' }
    | {
        group: string | string[];
        type?: 'alphabetical' | 'natural' | 'line-length' | 'custom' | 'unsorted';
        order?: 'asc' | 'desc';
        fallbackSort?: { type: string; order?: 'asc' | 'desc' };
        newlinesInside?: number | 'ignore';
      }
  >
```

default: `[]`

Defines the order of attribute groups. Unknown attributes are placed after the last group.

You can mix predefined (none for this rule) and custom groups. Typical usage is with custom groups declared via `elementNamePattern`.

Example: put any attribute starting with `type` before others, and require a newline between groups.

```ts
{
  groups: ['type-attrs', 'unknown'],
  customGroups: [
    { groupName: 'type-attrs', elementNamePattern: '^type' },
  ],
  newlinesBetween: 1
}
```

#### Group with overridden settings

You may directly override options for a specific group by using an object with the `group` property and other option overrides.

- `type` — Overrides the [`type`](#type) option for that group.
- `order` — Overrides the [`order`](#order) option for that group.
- `fallbackSort` — Overrides the [`fallbackSort`](#fallbacksort) option for that group.
- `newlinesInside` — Overrides the [`newlinesInside`](#newlinesinside) option for that group.

```ts
{
  groups: [
    'myCustomGroup1',
    { group: 'myCustomGroup2', type: 'unsorted' }, // Elements from this group will not be sorted
  ]
}
```

#### Newlines between groups

You may place `newlinesBetween` objects between your groups to enforce the newline behavior between two specific groups.

See the [`newlinesBetween`](#newlinesbetween) option.

This feature is only applicable when [`partitionByNewLine`](#partitionbynewline) is `false`.

```ts
{
  newlinesBetween: 1,
  groups: [
    'a',
    { newlinesBetween: 0 }, // Overrides the global newlinesBetween option
    'b',
  ]
}
```

### customGroups

Define custom attribute groups with optional per-group sort overrides.

```ts
type CustomGroup = {
  groupName: string
  // Match by attribute name
  elementNamePattern?: string | string[] | { pattern: string; flags?: string } | { pattern: string; flags?: string }[]

  // Optional per-group overrides:
  type?: 'alphabetical' | 'natural' | 'line-length' | 'custom' | 'unsorted'
  order?: 'asc' | 'desc'
  fallbackSort?: { type: string; order?: 'asc' | 'desc' }
  newlinesInside?: number | 'ignore'
}[]
```

An attribute matches a custom group when its name satisfies `elementNamePattern`. The first matching definition wins.

## Usage

**Flat Config**

```tsx
// eslint.config.js
import perfectionist from 'eslint-plugin-perfectionist'

export default [
  {
    plugins: { perfectionist },
    rules: {
      'perfectionist/sort-export-attributes': [
        'error',
        {
          type: 'alphabetical',
          order: 'asc',
          fallbackSort: { type: 'unsorted' },
          ignoreCase: true,
          specialCharacters: 'keep',
          locales: 'en-US',
          alphabet: '',
          partitionByComment: false,
          partitionByNewLine: false,
          newlinesBetween: 'ignore',
          newlinesInside: 'ignore',
          useConfigurationIf: {},
          groups: [],
          customGroups: [],
        },
      ],
    },
  },
]
```

**Legacy Config**

```tsx
// .eslintrc.js
module.exports = {
  plugins: ['perfectionist'],
  rules: {
    'perfectionist/sort-export-attributes': [
      'error',
      {
        type: 'alphabetical',
        order: 'asc',
        fallbackSort: { type: 'unsorted' },
        ignoreCase: true,
        specialCharacters: 'keep',
        locales: 'en-US',
        alphabet: '',
        partitionByComment: false,
        partitionByNewLine: false,
        newlinesBetween: 'ignore',
        newlinesInside: 'ignore',
        useConfigurationIf: {},
        groups: [],
        customGroups: [],
      },
    ],
  },
}
```

## Version

This rule was introduced in [v5.0.0](https://github.com/azat-io/eslint-plugin-perfectionist/releases/tag/v5.0.0).

## Resources

- [Rule source](https://github.com/azat-io/eslint-plugin-perfectionist/blob/main/rules/sort-export-attributes.ts)
- [Test source](https://github.com/azat-io/eslint-plugin-perfectionist/blob/main/test/rules/sort-export-attributes.test.ts)
