Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ These rules relate to style guidelines, and are therefore quite subjective:
| [svelte/prefer-style-directive](https://ota-meshi.github.io/eslint-plugin-svelte/rules/prefer-style-directive/) | require style directives instead of style attribute | :wrench: |
| [svelte/shorthand-attribute](https://ota-meshi.github.io/eslint-plugin-svelte/rules/shorthand-attribute/) | enforce use of shorthand syntax in attribute | :wrench: |
| [svelte/shorthand-directive](https://ota-meshi.github.io/eslint-plugin-svelte/rules/shorthand-directive/) | enforce use of shorthand syntax in directives | :wrench: |
| [svelte/sort-attributes](https://ota-meshi.github.io/eslint-plugin-svelte/rules/sort-attributes/) | enforce order of attributes | :wrench: |
| [svelte/spaced-html-comment](https://ota-meshi.github.io/eslint-plugin-svelte/rules/spaced-html-comment/) | enforce consistent spacing after the `<!--` and before the `-->` in a HTML comment | :wrench: |

## Extension Rules
Expand Down
1 change: 1 addition & 0 deletions docs-svelte-kit/tools/highlight.mts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const EXTENSION_MAPPINGS: Record<string, string | undefined> = {
styl: "stylus",
kt: "kotlin",
rs: "rust",
jsonc: "json5",
}

export default (str: string, lang: string): string => {
Expand Down
1 change: 1 addition & 0 deletions docs/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ These rules relate to style guidelines, and are therefore quite subjective:
| [svelte/prefer-style-directive](./rules/prefer-style-directive.md) | require style directives instead of style attribute | :wrench: |
| [svelte/shorthand-attribute](./rules/shorthand-attribute.md) | enforce use of shorthand syntax in attribute | :wrench: |
| [svelte/shorthand-directive](./rules/shorthand-directive.md) | enforce use of shorthand syntax in directives | :wrench: |
| [svelte/sort-attributes](./rules/sort-attributes.md) | enforce order of attributes | :wrench: |
| [svelte/spaced-html-comment](./rules/spaced-html-comment.md) | enforce consistent spacing after the `<!--` and before the `-->` in a HTML comment | :wrench: |

## Extension Rules
Expand Down
275 changes: 275 additions & 0 deletions docs/rules/sort-attributes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
---
pageClass: "rule-details"
sidebarDepth: 0
title: "svelte/sort-attributes"
description: "enforce order of attributes"
---

# svelte/sort-attributes

> enforce order of attributes

- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> **_This rule has not been released yet._** </badge>
- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.

## :book: Rule Details

This rule aims to enforce ordering of attributes.
The default order is:

- `this` property.
- `bind:this` directive.
- `id` attribute.
- `name` attribute.
- `slot` attribute.
- `--style-props` (Alphabetical order within the same group.)
- `style` attribute, and `style:` directives.
- `class` attribute.
- `class:` directives. (Alphabetical order within the same group.)
- other attributes. (Alphabetical order within the same group.)
- `bind:` directives (other then `bind:this`), and `on:` directives.
- `use:` directives. (Alphabetical order within the same group.)
- `transition:` directive.
- `in:` directive.
- `out:` directive.
- `animate:` directive.
- `let:` directives. (Alphabetical order within the same group.)

<ESLintCodeBlock fix>

<!-- prettier-ignore-start -->
<!--eslint-skip-->

```svelte
<script>
/* eslint svelte/sort-attributes: "error" */
</script>

<!-- ✓ GOOD -->
<svelte:component
this={component}
--style-props={color}
bind:value={componentValue}
on:changeValue={handleChange}
bind:metaData
/>
<input
bind:this={foo}
id="foo"
style="width: 150px;"
style:color
class="my-input"
class:disable
class:enable={!disable}
bind:value={inputValue}
use:action={parameters}
transition:fn
in:fn
out:fn
animate:name
/>
<slot name="content" {abc} {def} />

<!-- ✗ BAD -->
<svelte:component
bind:value={componentValue}
this={component}
on:changeValue={handleChange}
{def}
data-foo
{abc}
bind:metaData
--style-props={color}
/>
<input
id="foo"
bind:this={foo}
style:color
style="width: 150px;"
class="my-input"
class:enable={!disable}
class:disable
animate:name
use:action
transition:fn
bind:value={inputValue}
in:fn
out:fn
/>
<slot name="content" {def} {abc} data-foo />
```

<!-- prettier-ignore-end -->

</ESLintCodeBlock>

If there is a spread attribute between the attributes, it will not be reported as changing the order can change the behavior.

<ESLintCodeBlock fix>

<!--eslint-skip-->

```svelte
<script>
/* eslint svelte/sort-attributes: "error" */
</script>

<!-- ✓ GOOD -->
<div c d {...attrs} a b />

<!-- ✗ BAD -->
<div d c {...attrs} b a />
```

</ESLintCodeBlock>

## :wrench: Options

```jsonc
{
"svelte/sort-attributes": [
"error",
{
"order": [
// `this` property.
"this",
// `bind:this` directive.
"bind:this",
// `id` attribute.
"id",
// `name` attribute.
"name",
// `slot` attribute.
"slot",
// `--style-props` (Alphabetical order within the same group.)
{ "match": "/^--/u", "sort": "alphabetical" },
// `style` attribute, and `style:` directives.
["style", "/^style:/u"],
// `class` attribute.
"class",
// `class:` directives. (Alphabetical order within the same group.)
{ "match": "/^class:/u", "sort": "alphabetical" },
// other attributes. (Alphabetical order within the same group.)
{
"match": ["!/:/u", "!/^(?:this|id|name|style|class)$/u", "!/^--/u"],
"sort": "alphabetical"
},
// `bind:` directives (other then `bind:this`), and `on:` directives.
["/^bind:/u", "!bind:this", "/^on:/u"],
// `use:` directives. (Alphabetical order within the same group.)
{ "match": "/^use:/u", "sort": "alphabetical" },
// `transition:` directive.
{ "match": "/^transition:/u", "sort": "alphabetical" },
// `in:` directive.
{ "match": "/^in:/u", "sort": "alphabetical" },
// `out:` directive.
{ "match": "/^out:/u", "sort": "alphabetical" },
// `animate:` directive.
{ "match": "/^animate:/u", "sort": "alphabetical" },
// `let:` directives. (Alphabetical order within the same group.)
{ "match": "/^let:/u", "sort": "alphabetical" }
]
}
]
}
```

- `order` ... Specify an array of your preferred attribute order. Array elements accept strings, string arrays, and objects.
- String ... Specify the name or pattern of the attribute.
- String array ... Specifies an array of the names or patterns of the attributes to be grouped. It will not be sorted within this same group.
- Object ... Specifies an object with a definition for sorting within the same group.
- `match` ... Specifies an array or string of the name or pattern of the attributes to be grouped.
- `sort` ... Specify the sorting method. Currently, only `"alphabetical"` is supported.
- `"alphabetical"` ... Sorts the attributes of the same group in alphabetical order.
- `"ignore"` ... Attributes in the same group are not sorted.

Note that the behavior may change depending on how you specify the `order` setting.
For example, `bind:value` and `on:input={() => console.log(value)}` behave differently depending on the order. See <https://svelte.dev/docs#template-syntax-element-directives-bind-property> for details.
By default it is designed to be sorted safely.

You can use the following formats for names or patterns:

- `"foo"` ... Matches only the `foo` attribute name.
- `"/foo/"` ... Matches attribute names that match the `/foo/` regex. That is, it matches the attribute name including `foo`.
- `"!foo"` ... Exclude `foo` attribute from the matched attribute names. When used first in the array or alone, matches other than the `foo` attribute name.
- `"!/foo/"` ... Excludes attributes that match the `/foo/` regex from the matched attribute names. When used first in the array or alone, matches an attribute name that does not match the `/foo/` regex.
- `["style", "/^style:/u"]` ... Matches the `style` attribute or the attribute name that matches the `/^style:/u` regex.
- `["/^bind:/u", "!bind:this", "/^on:/u"]` ... Matches an attribute name that matches `/^bind:/u` and other than `bind:this`, or an attribute name that matches `/^on:/u`.

### `{ order: [ /*See below*/ ] }`

<ESLintCodeBlock fix>

<!--eslint-skip-->

```svelte
<script>
/* eslint svelte/sort-attributes: ["error", {
"order": [
"id",
"class",
"/^class:/u",
"value",
"src",
"/^data-/u",
"style",
"/^style:/u",
"/^on:/u",
"/^use:/u",
"/^animate:/u",
"/^transition:/u",
"/^in:/u",
"/^out:/u",
"bind:this",
["/^bind:/u", "!bind:this"],
{
"match": ["!/:/u", "!/^(?:id|class|value|src|style)$/u", "!/^data-/u"],
"sort": "alphabetical"
},
]
}] */
</script>

<!-- ✓ GOOD -->
<MyComponent data-foo bind:this={comp} bind:data {abc} {def} />
<input
id="foo"
class="my-block"
class:bar
value="abc"
data-value="x"
style="width: 30px;"
style:color
animate:name
transition:fn
in:fn
out:fn
bind:this={foo}
/>
<img id="bar" {src} alt="bar" />

<!-- ✗ BAD -->
<MyComponent bind:data bind:this={comp} {abc} {def} data-foo />
<input
class:bar
class="my-block"
id="foo"
bind:this={foo}
value="abc"
style:color
style="width: 30px;"
data-value="x"
animate:name
in:fn
out:fn
transition:fn
/>
<img alt="bar" {src} id="bar" />
```

</ESLintCodeBlock>

## :mag: Implementation

- [Rule source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/src/rules/sort-attributes.ts)
- [Test source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/tests/src/rules/sort-attributes.ts)
Loading