Repository: sveltejs/eslint-plugin-svelte
Pull request #1221 (2025-05-15 12:37:30)

feat(sort-attributes): support `{@attach}`
close: https://github.com/sveltejs/eslint-plugin-svelte/issues/1220

changeset-bot[bot]: ###  🦋  Changeset detected

Latest commit: deeff734acea30a108129b95f19aa1c9659dc5b1

**The changes in this PR will be included in the next version bump.**

<details><summary>This PR includes changesets to release 1 package</summary>

  | Name                 | Type  |
| -------------------- | ----- |
| eslint-plugin-svelte | Minor |

</details>

Not sure what this means? [Click here  to learn what changesets are](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add another changeset to this PR](https://github.com/sveltejs/eslint-plugin-svelte/new/fix/1220?filename=.changeset/honest-lemons-change.md&value=---%0A%22eslint-plugin-svelte%22%3A%20patch%0A%22docs%22%3A%20patch%0A---%0A%0Afeat(sort-attributes)%3A%20support%20%60%7B%40Attach%7D%60%0A)



Patch:
diff --git a/.changeset/dry-facts-eat.md b/.changeset/dry-facts-eat.md
new file mode 100644
index 0000000..04e4211
--- /dev/null
+++ b/.changeset/dry-facts-eat.md
@@ -0,0 +1,5 @@
+---
+'eslint-plugin-svelte': minor
+---
+
+feat(sort-attributes): support `{@attach}`
diff --git a/docs-svelte-kit/package.json b/docs-svelte-kit/package.json
index 6137c39..cc6caaf 100644
--- a/docs-svelte-kit/package.json
+++ b/docs-svelte-kit/package.json
@@ -56,7 +56,7 @@
     "sass": "^1.81.0",
     "source-map-js": "^1.2.1",
     "stylus": "^0.64.0",
-    "svelte": "^5.2.10",
+    "svelte": "^5.30.1",
     "svelte-adapter-ghpages": "0.2.2",
     "twoslash-eslint": "^0.3.0",
     "twoslash-protocol": "^0.3.0",
diff --git a/packages/eslint-plugin-svelte/package.json b/packages/eslint-plugin-svelte/package.json
index 7fbaf0d..14759b1 100644
--- a/packages/eslint-plugin-svelte/package.json
+++ b/packages/eslint-plugin-svelte/package.json
@@ -64,7 +64,7 @@
     "postcss-load-config": "^3.1.4",
     "postcss-safe-parser": "^7.0.0",
     "semver": "^7.6.3",
-    "svelte-eslint-parser": "^1.1.1"
+    "svelte-eslint-parser": "^1.2.0"
   },
   "devDependencies": {
     "@babel/core": "^7.26.0",
@@ -95,7 +95,7 @@
     "sass": "^1.81.0",
     "source-map-js": "^1.2.1",
     "stylus": "^0.64.0",
-    "svelte": "^5.2.9",
+    "svelte": "^5.30.1",
     "svelte-i18n": "^4.0.1",
     "tsx": "^4.19.2",
     "type-coverage": "^2.29.7",
diff --git a/packages/eslint-plugin-svelte/src/meta.ts b/packages/eslint-plugin-svelte/src/meta.ts
index 65a367b..d34775e 100644
--- a/packages/eslint-plugin-svelte/src/meta.ts
+++ b/packages/eslint-plugin-svelte/src/meta.ts
@@ -1,5 +1,5 @@
 // IMPORTANT!
 // This file has been automatically generated,
 // in order to update its content execute "pnpm run update"
-export const name = 'eslint-plugin-svelte' as const;
-export const version = '3.6.0' as const;
+export const name = 'eslint-plugin-svelte';
+export const version = '3.6.0';
diff --git a/packages/eslint-plugin-svelte/src/utils/ast-utils.ts b/packages/eslint-plugin-svelte/src/utils/ast-utils.ts
index dd426e1..bff6c09 100644
--- a/packages/eslint-plugin-svelte/src/utils/ast-utils.ts
+++ b/packages/eslint-plugin-svelte/src/utils/ast-utils.ts
@@ -462,7 +462,8 @@ export function getAttributeKeyText(
 		| SvAST.SvelteStyleDirective
 		| SvAST.SvelteDirective
 		| SvAST.SvelteSpecialDirective
-		| SvAST.SvelteGenericsDirective,
+		| SvAST.SvelteGenericsDirective
+		| SvAST.SvelteAttachTag,
 	context: RuleContext
 ): string {
 	switch (node.type) {
@@ -480,6 +481,8 @@ export function getAttributeKeyText(
 				node.key.modifiers.length ? `|${node.key.modifiers.join('|')}` : ''
 			}`;
 		}
+		case 'SvelteAttachTag':
+			return '@attach';
 		default:
 			throw new Error(
 				`Unknown node type: ${
@@ -556,6 +559,7 @@ export function findClassesInAttribute(
 		| SvAST.SvelteStyleDirective
 		| SvAST.SvelteSpecialDirective
 		| SvAST.SvelteGenericsDirective
+		| SvAST.SvelteAttachTag
 ): string[] {
 	if (attribute.type === 'SvelteAttribute' && attribute.key.name === 'class') {
 		return attribute.value.flatMap((value) =>


Test patch:
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/sort-attributes/invalid/attach-tag-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/sort-attributes/invalid/attach-tag-errors.yaml
new file mode 100644
index 0000000..5972a09
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/sort-attributes/invalid/attach-tag-errors.yaml
@@ -0,0 +1,4 @@
+- message: Attribute '@attach' should go before 'foo'.
+  line: 14
+  column: 10
+  suggestions: null
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/sort-attributes/invalid/attach-tag-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/sort-attributes/invalid/attach-tag-input.svelte
new file mode 100644
index 0000000..5adcffb
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/sort-attributes/invalid/attach-tag-input.svelte
@@ -0,0 +1,14 @@
+<script lang="ts">
+	import type { Attachment } from 'svelte/attachments';
+
+
+	const myAttachment: Attachment = (element) => {
+		console.log(element.nodeName); // 'DIV'
+
+		return () => {
+			console.log('cleaning up');
+		};
+	};
+</script>
+
+<div foo {@attach myAttachment}>...</div>
\ No newline at end of file
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/sort-attributes/invalid/attach-tag-output.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/sort-attributes/invalid/attach-tag-output.svelte
new file mode 100644
index 0000000..02c44ab
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/sort-attributes/invalid/attach-tag-output.svelte
@@ -0,0 +1,14 @@
+<script lang="ts">
+	import type { Attachment } from 'svelte/attachments';
+
+
+	const myAttachment: Attachment = (element) => {
+		console.log(element.nodeName); // 'DIV'
+
+		return () => {
+			console.log('cleaning up');
+		};
+	};
+</script>
+
+<div {@attach myAttachment} foo>...</div>
\ No newline at end of file
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/sort-attributes/valid/attach-tag-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/sort-attributes/valid/attach-tag-input.svelte
new file mode 100644
index 0000000..e1bc8a5
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/sort-attributes/valid/attach-tag-input.svelte
@@ -0,0 +1,14 @@
+<script lang="ts">
+	import type { Attachment } from 'svelte/attachments';
+
+
+	const myAttachment: Attachment = (element) => {
+		console.log(element.nodeName); // 'DIV'
+
+		return () => {
+			console.log('cleaning up');
+		};
+	};
+</script>
+
+<div {@attach myAttachment}>...</div>
\ No newline at end of file