Skip to content

Commit ce7db8a

Browse files
ota-meshiJounQin
andauthored
Add svelte/sort-attributes rule (#196)
* Add `svelte/sort-attributes` rule * Update docs/rules/sort-attributes.md Co-authored-by: JounQin <[email protected]> * Update svelte-eslint-parser * Update doc * update * add test * add tests * update doc * format * update site * Update ast-utils.ts * add slot order Co-authored-by: JounQin <[email protected]>
1 parent d606390 commit ce7db8a

File tree

108 files changed

+2554
-18
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

108 files changed

+2554
-18
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ These rules relate to style guidelines, and are therefore quite subjective:
305305
| [svelte/prefer-style-directive](https://ota-meshi.github.io/eslint-plugin-svelte/rules/prefer-style-directive/) | require style directives instead of style attribute | :wrench: |
306306
| [svelte/shorthand-attribute](https://ota-meshi.github.io/eslint-plugin-svelte/rules/shorthand-attribute/) | enforce use of shorthand syntax in attribute | :wrench: |
307307
| [svelte/shorthand-directive](https://ota-meshi.github.io/eslint-plugin-svelte/rules/shorthand-directive/) | enforce use of shorthand syntax in directives | :wrench: |
308+
| [svelte/sort-attributes](https://ota-meshi.github.io/eslint-plugin-svelte/rules/sort-attributes/) | enforce order of attributes | :wrench: |
308309
| [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: |
309310

310311
## Extension Rules

docs-svelte-kit/tools/highlight.mts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const EXTENSION_MAPPINGS: Record<string, string | undefined> = {
2626
styl: "stylus",
2727
kt: "kotlin",
2828
rs: "rust",
29+
jsonc: "json5",
2930
}
3031

3132
export default (str: string, lang: string): string => {

docs/rules.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ These rules relate to style guidelines, and are therefore quite subjective:
6565
| [svelte/prefer-style-directive](./rules/prefer-style-directive.md) | require style directives instead of style attribute | :wrench: |
6666
| [svelte/shorthand-attribute](./rules/shorthand-attribute.md) | enforce use of shorthand syntax in attribute | :wrench: |
6767
| [svelte/shorthand-directive](./rules/shorthand-directive.md) | enforce use of shorthand syntax in directives | :wrench: |
68+
| [svelte/sort-attributes](./rules/sort-attributes.md) | enforce order of attributes | :wrench: |
6869
| [svelte/spaced-html-comment](./rules/spaced-html-comment.md) | enforce consistent spacing after the `<!--` and before the `-->` in a HTML comment | :wrench: |
6970

7071
## Extension Rules

docs/rules/sort-attributes.md

Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
---
2+
pageClass: "rule-details"
3+
sidebarDepth: 0
4+
title: "svelte/sort-attributes"
5+
description: "enforce order of attributes"
6+
---
7+
8+
# svelte/sort-attributes
9+
10+
> enforce order of attributes
11+
12+
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> **_This rule has not been released yet._** </badge>
13+
- :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.
14+
15+
## :book: Rule Details
16+
17+
This rule aims to enforce ordering of attributes.
18+
The default order is:
19+
20+
- `this` property.
21+
- `bind:this` directive.
22+
- `id` attribute.
23+
- `name` attribute.
24+
- `slot` attribute.
25+
- `--style-props` (Alphabetical order within the same group.)
26+
- `style` attribute, and `style:` directives.
27+
- `class` attribute.
28+
- `class:` directives. (Alphabetical order within the same group.)
29+
- other attributes. (Alphabetical order within the same group.)
30+
- `bind:` directives (other then `bind:this`), and `on:` directives.
31+
- `use:` directives. (Alphabetical order within the same group.)
32+
- `transition:` directive.
33+
- `in:` directive.
34+
- `out:` directive.
35+
- `animate:` directive.
36+
- `let:` directives. (Alphabetical order within the same group.)
37+
38+
<ESLintCodeBlock fix>
39+
40+
<!-- prettier-ignore-start -->
41+
<!--eslint-skip-->
42+
43+
```svelte
44+
<script>
45+
/* eslint svelte/sort-attributes: "error" */
46+
</script>
47+
48+
<!-- ✓ GOOD -->
49+
<svelte:component
50+
this={component}
51+
--style-props={color}
52+
bind:value={componentValue}
53+
on:changeValue={handleChange}
54+
bind:metaData
55+
/>
56+
<input
57+
bind:this={foo}
58+
id="foo"
59+
style="width: 150px;"
60+
style:color
61+
class="my-input"
62+
class:disable
63+
class:enable={!disable}
64+
bind:value={inputValue}
65+
use:action={parameters}
66+
transition:fn
67+
in:fn
68+
out:fn
69+
animate:name
70+
/>
71+
<slot name="content" {abc} {def} />
72+
73+
<!-- ✗ BAD -->
74+
<svelte:component
75+
bind:value={componentValue}
76+
this={component}
77+
on:changeValue={handleChange}
78+
{def}
79+
data-foo
80+
{abc}
81+
bind:metaData
82+
--style-props={color}
83+
/>
84+
<input
85+
id="foo"
86+
bind:this={foo}
87+
style:color
88+
style="width: 150px;"
89+
class="my-input"
90+
class:enable={!disable}
91+
class:disable
92+
animate:name
93+
use:action
94+
transition:fn
95+
bind:value={inputValue}
96+
in:fn
97+
out:fn
98+
/>
99+
<slot name="content" {def} {abc} data-foo />
100+
```
101+
102+
<!-- prettier-ignore-end -->
103+
104+
</ESLintCodeBlock>
105+
106+
If there is a spread attribute between the attributes, it will not be reported as changing the order can change the behavior.
107+
108+
<ESLintCodeBlock fix>
109+
110+
<!--eslint-skip-->
111+
112+
```svelte
113+
<script>
114+
/* eslint svelte/sort-attributes: "error" */
115+
</script>
116+
117+
<!-- ✓ GOOD -->
118+
<div c d {...attrs} a b />
119+
120+
<!-- ✗ BAD -->
121+
<div d c {...attrs} b a />
122+
```
123+
124+
</ESLintCodeBlock>
125+
126+
## :wrench: Options
127+
128+
```jsonc
129+
{
130+
"svelte/sort-attributes": [
131+
"error",
132+
{
133+
"order": [
134+
// `this` property.
135+
"this",
136+
// `bind:this` directive.
137+
"bind:this",
138+
// `id` attribute.
139+
"id",
140+
// `name` attribute.
141+
"name",
142+
// `slot` attribute.
143+
"slot",
144+
// `--style-props` (Alphabetical order within the same group.)
145+
{ "match": "/^--/u", "sort": "alphabetical" },
146+
// `style` attribute, and `style:` directives.
147+
["style", "/^style:/u"],
148+
// `class` attribute.
149+
"class",
150+
// `class:` directives. (Alphabetical order within the same group.)
151+
{ "match": "/^class:/u", "sort": "alphabetical" },
152+
// other attributes. (Alphabetical order within the same group.)
153+
{
154+
"match": ["!/:/u", "!/^(?:this|id|name|style|class)$/u", "!/^--/u"],
155+
"sort": "alphabetical"
156+
},
157+
// `bind:` directives (other then `bind:this`), and `on:` directives.
158+
["/^bind:/u", "!bind:this", "/^on:/u"],
159+
// `use:` directives. (Alphabetical order within the same group.)
160+
{ "match": "/^use:/u", "sort": "alphabetical" },
161+
// `transition:` directive.
162+
{ "match": "/^transition:/u", "sort": "alphabetical" },
163+
// `in:` directive.
164+
{ "match": "/^in:/u", "sort": "alphabetical" },
165+
// `out:` directive.
166+
{ "match": "/^out:/u", "sort": "alphabetical" },
167+
// `animate:` directive.
168+
{ "match": "/^animate:/u", "sort": "alphabetical" },
169+
// `let:` directives. (Alphabetical order within the same group.)
170+
{ "match": "/^let:/u", "sort": "alphabetical" }
171+
]
172+
}
173+
]
174+
}
175+
```
176+
177+
- `order` ... Specify an array of your preferred attribute order. Array elements accept strings, string arrays, and objects.
178+
- String ... Specify the name or pattern of the attribute.
179+
- 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.
180+
- Object ... Specifies an object with a definition for sorting within the same group.
181+
- `match` ... Specifies an array or string of the name or pattern of the attributes to be grouped.
182+
- `sort` ... Specify the sorting method. Currently, only `"alphabetical"` is supported.
183+
- `"alphabetical"` ... Sorts the attributes of the same group in alphabetical order.
184+
- `"ignore"` ... Attributes in the same group are not sorted.
185+
186+
Note that the behavior may change depending on how you specify the `order` setting.
187+
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.
188+
By default it is designed to be sorted safely.
189+
190+
You can use the following formats for names or patterns:
191+
192+
- `"foo"` ... Matches only the `foo` attribute name.
193+
- `"/foo/"` ... Matches attribute names that match the `/foo/` regex. That is, it matches the attribute name including `foo`.
194+
- `"!foo"` ... Exclude `foo` attribute from the matched attribute names. When used first in the array or alone, matches other than the `foo` attribute name.
195+
- `"!/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.
196+
- `["style", "/^style:/u"]` ... Matches the `style` attribute or the attribute name that matches the `/^style:/u` regex.
197+
- `["/^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`.
198+
199+
### `{ order: [ /*See below*/ ] }`
200+
201+
<ESLintCodeBlock fix>
202+
203+
<!--eslint-skip-->
204+
205+
```svelte
206+
<script>
207+
/* eslint svelte/sort-attributes: ["error", {
208+
"order": [
209+
"id",
210+
"class",
211+
"/^class:/u",
212+
"value",
213+
"src",
214+
"/^data-/u",
215+
"style",
216+
"/^style:/u",
217+
"/^on:/u",
218+
"/^use:/u",
219+
"/^animate:/u",
220+
"/^transition:/u",
221+
"/^in:/u",
222+
"/^out:/u",
223+
"bind:this",
224+
["/^bind:/u", "!bind:this"],
225+
{
226+
"match": ["!/:/u", "!/^(?:id|class|value|src|style)$/u", "!/^data-/u"],
227+
"sort": "alphabetical"
228+
},
229+
]
230+
}] */
231+
</script>
232+
233+
<!-- ✓ GOOD -->
234+
<MyComponent data-foo bind:this={comp} bind:data {abc} {def} />
235+
<input
236+
id="foo"
237+
class="my-block"
238+
class:bar
239+
value="abc"
240+
data-value="x"
241+
style="width: 30px;"
242+
style:color
243+
animate:name
244+
transition:fn
245+
in:fn
246+
out:fn
247+
bind:this={foo}
248+
/>
249+
<img id="bar" {src} alt="bar" />
250+
251+
<!-- ✗ BAD -->
252+
<MyComponent bind:data bind:this={comp} {abc} {def} data-foo />
253+
<input
254+
class:bar
255+
class="my-block"
256+
id="foo"
257+
bind:this={foo}
258+
value="abc"
259+
style:color
260+
style="width: 30px;"
261+
data-value="x"
262+
animate:name
263+
in:fn
264+
out:fn
265+
transition:fn
266+
/>
267+
<img alt="bar" {src} id="bar" />
268+
```
269+
270+
</ESLintCodeBlock>
271+
272+
## :mag: Implementation
273+
274+
- [Rule source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/src/rules/sort-attributes.ts)
275+
- [Test source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/tests/src/rules/sort-attributes.ts)

0 commit comments

Comments
 (0)