Repository: sveltejs/eslint-plugin-svelte (ts)

Issue:
Old-styled svelte-ignores are ignored with Svelte 5
### Before You File a Bug Report Please Confirm You Have Done The Following...

- [X] I have tried restarting my IDE and the issue persists.
- [X] I have updated to the latest version of the packages.

### What version of ESLint are you using?

9.3.0

### What version of `eslint-plugin-svelte` are you using?

2.39.0

### What did you do?

<details>
<summary>Configuration</summary>

I use default configuration provided by `npm create svelte@latest`
```
import js from '@eslint/js';
import svelte from 'eslint-plugin-svelte';
import globals from 'globals';

/** @type {import('eslint').Linter.FlatConfig[]} */
export default [
	js.configs.recommended,
	...svelte.configs['flat/recommended'],
	{
		languageOptions: {
			globals: {
				...globals.browser,
				...globals.node
			}
		}
	},
	{
		ignores: ['build/', '.svelte-kit/', 'dist/']
	}
];

```
</details>

```svelte
<!-- svelte-ignore a11y-click-events-have-key-events a11y-no-static-element-interactions -->
<my-tag on:click={() => {}}>
</my-tag>

<div />

```


### What did you expect to happen?

Like when Svelte 4.2.17 is used, ` a11y-click-events-have-key-events` and `a11y-no-static-element-interactions` should not be reported.

Also `element_invalid_self_closing_tag` was also not reported with Svelte 4, so IMO should not be reported in legacy mode with Svelte 5.

### What actually happened?

```
eslint-plugin-svelte-repro/src/routes/+page.svelte
  1:20  error  svelte-ignore comment is used, but not warned                                                                                                                                                                                                                                                                                                                svelte/no-unused-svelte-ignore
  1:54  error  svelte-ignore comment is used, but not warned                                                                                                                                                                                                                                                                                                                svelte/no-unused-svelte-ignore
  2:1   error  Visible, non-interactive elements with a click event must be accompanied by a keyboard event handler. Consider whether an interactive element such as `<button type="button">` or `<a>` might be more appropriate. See https://svelte.dev/docs/accessibility-warnings#a11y-click-events-have-key-events for more details(a11y_click_events_have_key_events)  svelte/valid-compile
  2:1   error  `<my-tag>` with a click handler must have an ARIA role(a11y_no_static_element_interactions)                                                                                                                                                                                                                                                                  svelte/valid-compile
  5:1   error  Self-closing HTML tags for non-void elements are ambiguous — use `<div ...></div>` rather than `<div ... />`(element_invalid_self_closing_tag)                                                                                                                                                                                                               svelte/valid-compile

✖ 5 problems (5 errors, 0 warnings)
```

### Link to **GitHub Repo** with Minimal Reproducible Example

https://github.com/shadow-identity/eslint-plugin-svelte-repro

### Additional comments

I think the reason of the mess is that svelte 5 uses underscores in warning names. But according to Svelte developers, old-styled components (which does not use runes) should keep working: https://github.com/sveltejs/svelte/issues/11666

After https://github.com/sveltejs/svelte/issues/11666 was fixed, `svelte-check` does not report this markup.

I also put `<div />` case, it was also not reported with Svelte 4 (it is reported with `svelte-kit` tho).

Pull request description:
fix: false positive for kebab-case with svelte v5 in `svelte/no-unused-svelte-ignore`


fixes #765

Patch:
diff --git a/.changeset/ninety-poets-divide.md b/.changeset/ninety-poets-divide.md
new file mode 100644
index 0000000..89dc487
--- /dev/null
+++ b/.changeset/ninety-poets-divide.md
@@ -0,0 +1,5 @@
+---
+"eslint-plugin-svelte": patch
+---
+
+fix: false positive for kebab-case with svelte v5 in `svelte/no-unused-svelte-ignore`
diff --git a/packages/eslint-plugin-svelte/src/shared/svelte-compile-warns/ignore-comment.ts b/packages/eslint-plugin-svelte/src/shared/svelte-compile-warns/ignore-comment.ts
index 4d3fada..623909a 100644
--- a/packages/eslint-plugin-svelte/src/shared/svelte-compile-warns/ignore-comment.ts
+++ b/packages/eslint-plugin-svelte/src/shared/svelte-compile-warns/ignore-comment.ts
@@ -4,6 +4,22 @@ import { getSourceCode } from '../../utils/compat';
 
 const SVELTE_IGNORE_PATTERN = /^\s*svelte-ignore/m;
 
+/**
+ * Map of legacy code -> new code
+ * See https://github.com/sveltejs/svelte/blob/c9202a889612df3c2fcb369096a5573668be99d6/packages/svelte/src/compiler/utils/extract_svelte_ignore.js#L6
+ */
+const V5_REPLACEMENTS: Record<string, string | undefined> = {
+	'non-top-level-reactive-declaration': 'reactive_declaration_invalid_placement',
+	'module-script-reactive-declaration': 'reactive_declaration_module_script',
+	'empty-block': 'block_empty',
+	'avoid-is': 'attribute_avoid_is',
+	'invalid-html-attribute': 'attribute_invalid_property_name',
+	'a11y-structure': 'a11y_figcaption_parent',
+	'illegal-attribute-character': 'attribute_illegal_colon',
+	'invalid-rest-eachblock-binding': 'bind_invalid_each_rest',
+	'unused-export-let': 'export_let_unused'
+};
+
 export type IgnoreItemWithoutCode = {
 	range: [number, number];
 	code: null;
@@ -12,6 +28,7 @@ export type IgnoreItemWithoutCode = {
 export type IgnoreItem = {
 	range: [number, number];
 	code: string;
+	codeForV5: string; // Code targeting Svelte v5.
 	token: AST.Token | AST.Comment;
 };
 
@@ -75,6 +92,7 @@ function extractSvelteIgnore(
 		if (trimmed) {
 			results.push({
 				code: trimmed,
+				codeForV5: V5_REPLACEMENTS[trimmed] || trimmed.replace(/-/gu, '_'),
 				range: [start, end],
 				token
 			});
diff --git a/packages/eslint-plugin-svelte/src/shared/svelte-compile-warns/index.ts b/packages/eslint-plugin-svelte/src/shared/svelte-compile-warns/index.ts
index 20e1845..d5e3bca 100644
--- a/packages/eslint-plugin-svelte/src/shared/svelte-compile-warns/index.ts
+++ b/packages/eslint-plugin-svelte/src/shared/svelte-compile-warns/index.ts
@@ -45,6 +45,7 @@ const STYLE_TRANSFORMS: Record<string, typeof transformWithPostCSS | undefined>
 
 const CSS_WARN_CODES = new Set([
 	'css-unused-selector',
+	'css_unused_selector',
 	'css-invalid-global',
 	'css-invalid-global-selector'
 ]);
@@ -481,7 +482,9 @@ function processIgnore(
 		while (node) {
 			for (const comment of extractLeadingComments(context, node).reverse()) {
 				const ignoreItem = ignoreComments.find(
-					(item) => item.token === comment && item.code === warning.code
+					(item) =>
+						item.token === comment &&
+						(item.code === warning.code || item.codeForV5 === warning.code)
 				);
 				if (ignoreItem) {
 					unusedIgnores.delete(ignoreItem);
@@ -497,7 +500,9 @@ function processIgnore(
 	for (const node of stripStyleElements) {
 		for (const comment of extractLeadingComments(context, node).reverse()) {
 			const ignoreItem = ignoreComments.find(
-				(item) => item.token === comment && CSS_WARN_CODES.has(item.code)
+				(item) =>
+					item.token === comment &&
+					(CSS_WARN_CODES.has(item.code) || CSS_WARN_CODES.has(item.codeForV5))
 			);
 			if (ignoreItem) {
 				unusedIgnores.delete(ignoreItem);


Test patch:
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/invalid/html-comment-svelte4-requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/invalid/html-comment-svelte4-requirements.json
deleted file mode 100644
index b650cc2..0000000
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/invalid/html-comment-svelte4-requirements.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-	"svelte": "^3.0.0 || ^4.0.0"
-}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/invalid/invalid-svelte-ignore01-svelte4-requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/invalid/invalid-svelte-ignore01-svelte4-requirements.json
deleted file mode 100644
index b650cc2..0000000
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/invalid/invalid-svelte-ignore01-svelte4-requirements.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-	"svelte": "^3.0.0 || ^4.0.0"
-}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/invalid/invalid-svelte-ignore02-svelte4-requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/invalid/invalid-svelte-ignore02-svelte4-requirements.json
deleted file mode 100644
index b650cc2..0000000
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/invalid/invalid-svelte-ignore02-svelte4-requirements.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-	"svelte": "^3.0.0 || ^4.0.0"
-}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/invalid/invalid-svelte-ignore03-svelte4-requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/invalid/invalid-svelte-ignore03-svelte4-requirements.json
deleted file mode 100644
index b650cc2..0000000
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/invalid/invalid-svelte-ignore03-svelte4-requirements.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-	"svelte": "^3.0.0 || ^4.0.0"
-}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/invalid/script-comment01-svelte4-requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/invalid/script-comment01-svelte4-requirements.json
deleted file mode 100644
index b650cc2..0000000
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/invalid/script-comment01-svelte4-requirements.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-	"svelte": "^3.0.0 || ^4.0.0"
-}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/invalid/script-comment02-svelte4-requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/invalid/script-comment02-svelte4-requirements.json
deleted file mode 100644
index b650cc2..0000000
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/invalid/script-comment02-svelte4-requirements.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-	"svelte": "^3.0.0 || ^4.0.0"
-}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/invalid/transform-test-svelte4-requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/invalid/transform-test-svelte4-requirements.json
deleted file mode 100644
index b650cc2..0000000
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/invalid/transform-test-svelte4-requirements.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-	"svelte": "^3.0.0 || ^4.0.0"
-}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/element-ignore01-svelte4-requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/element-ignore01-svelte4-requirements.json
deleted file mode 100644
index b650cc2..0000000
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/element-ignore01-svelte4-requirements.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-	"svelte": "^3.0.0 || ^4.0.0"
-}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/has-error-svelte4-requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/has-error-svelte4-requirements.json
deleted file mode 100644
index b650cc2..0000000
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/has-error-svelte4-requirements.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-	"svelte": "^3.0.0 || ^4.0.0"
-}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/html-comment-svelte4-requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/html-comment-svelte4-requirements.json
deleted file mode 100644
index b650cc2..0000000
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/html-comment-svelte4-requirements.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-	"svelte": "^3.0.0 || ^4.0.0"
-}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/kebab-ignore-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/kebab-ignore-input.svelte
new file mode 100644
index 0000000..4f0cb24
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/kebab-ignore-input.svelte
@@ -0,0 +1,4 @@
+<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
+<span tabindex="0">
+	<span class="element"></span>
+</span>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/script-comment-svelte4-requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/script-comment-svelte4-requirements.json
deleted file mode 100644
index b650cc2..0000000
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/script-comment-svelte4-requirements.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-	"svelte": "^3.0.0 || ^4.0.0"
-}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore01-svelte4-requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore01-svelte4-requirements.json
deleted file mode 100644
index b650cc2..0000000
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore01-svelte4-requirements.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-	"svelte": "^3.0.0 || ^4.0.0"
-}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore02-svelte4-requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore02-svelte4-requirements.json
deleted file mode 100644
index b650cc2..0000000
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore02-svelte4-requirements.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-	"svelte": "^3.0.0 || ^4.0.0"
-}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore03-svelte4-requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore03-svelte4-requirements.json
deleted file mode 100644
index b650cc2..0000000
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore03-svelte4-requirements.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-	"svelte": "^3.0.0 || ^4.0.0"
-}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore04-svelte4-requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore04-svelte4-requirements.json
deleted file mode 100644
index b650cc2..0000000
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore04-svelte4-requirements.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-	"svelte": "^3.0.0 || ^4.0.0"
-}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore05-svelte4-requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore05-svelte4-requirements.json
deleted file mode 100644
index b650cc2..0000000
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore05-svelte4-requirements.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-	"svelte": "^3.0.0 || ^4.0.0"
-}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore06-svelte4-requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore06-svelte4-requirements.json
deleted file mode 100644
index b650cc2..0000000
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore06-svelte4-requirements.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-	"svelte": "^3.0.0 || ^4.0.0"
-}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore07-svelte4-requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore07-svelte4-requirements.json
deleted file mode 100644
index b650cc2..0000000
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore07-svelte4-requirements.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-	"svelte": "^3.0.0 || ^4.0.0"
-}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/ts-lang01-svelte4-requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/ts-lang01-svelte4-requirements.json
deleted file mode 100644
index b650cc2..0000000
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-svelte-ignore/valid/ts-lang01-svelte4-requirements.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-	"svelte": "^3.0.0 || ^4.0.0"
-}