Repository: sveltejs/eslint-plugin-svelte
Pull request #963 (2024-12-14 10:12:29)

feat: support Svelte5 of `valid-prop-names-in-kit-pages` rule

changeset-bot[bot]: ###  ⚠️  No Changeset found

Latest commit: b00c09edb685e2ef5f45b909494f6df9993c88c5

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. **If these changes should result in a version bump, you need to add a changeset.**

<details><summary>This PR includes no changesets</summary>

  When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

</details>

[Click here to learn what changesets are, and how to add one](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add a changeset to this PR](https://github.com/sveltejs/eslint-plugin-svelte/new/feat/svelte5-valid-prop-names-in-kit-pages?filename=.changeset/mighty-geese-rule.md&value=---%0A%22eslint-plugin-svelte%22%3A%20patch%0A---%0A%0Afeat%3A%20support%20Svelte5%20of%20%60valid-prop-names-in-kit-pages%60%20rule%0A)



Patch:
diff --git a/.changeset/pre.json b/.changeset/pre.json
index 7ae8544..672a612 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -8,6 +8,7 @@
   "changesets": [
     "cool-rockets-allow",
     "fast-coins-fly",
+    "hungry-grapes-shave",
     "lazy-eyes-wait",
     "olive-wolves-travel",
     "quiet-toys-burn",
diff --git a/.changeset/sixty-news-look.md b/.changeset/sixty-news-look.md
new file mode 100644
index 0000000..e70b9cd
--- /dev/null
+++ b/.changeset/sixty-news-look.md
@@ -0,0 +1,5 @@
+---
+'eslint-plugin-svelte': minor
+---
+
+feat: support Svelte5 of `valid-prop-names-in-kit-pages` rule
diff --git a/README.md b/README.md
index a959eda..2601875 100644
--- a/README.md
+++ b/README.md
@@ -10,8 +10,6 @@ You can check on the [Online DEMO](https://eslint-online-playground.netlify.app/
 > For example, <https://github.com/sveltejs/eslint-plugin-svelte/blob/eslint-plugin-svelte%402.46.0/README.md>
 > and <https://github.com/sveltejs/eslint-plugin-svelte/blob/eslint-plugin-svelte%402.46.0/docs>
 
-**_We are working on experimental support for Svelte v5, but may break with new versions of Svelte v5._**
-
 [![NPM license](https://img.shields.io/npm/l/eslint-plugin-svelte.svg)](https://www.npmjs.com/package/eslint-plugin-svelte)
 [![NPM version](https://img.shields.io/npm/v/eslint-plugin-svelte.svg)](https://www.npmjs.com/package/eslint-plugin-svelte)
 [![NPM downloads](https://img.shields.io/badge/dynamic/json.svg?label=downloads&colorB=green&suffix=/day&query=$.downloads&uri=https://api.npmjs.org//downloads/point/last-day/eslint-plugin-svelte&maxAge=3600)](http://www.npmtrends.com/eslint-plugin-svelte)
@@ -46,6 +44,13 @@ We are working on support for Svelte v5, but it is still an experimental feature
 
 <!--DOCS_IGNORE_START-->
 
+## Versioning policy
+
+This plugin follows [Semantic Versioning](https://semver.org/).  
+However, unlike [ESLint’s Semantic Versioning Policy](https://github.com/eslint/eslint#semantic-versioning-policy), this plugin adds new rules to its configs even in minor releases. For example, if you are using the recommended config, a minor update may add new rules, which could cause new lint errors in your project.  
+While [ESLint’s Semantic Versioning Policy](https://github.com/eslint/eslint#semantic-versioning-policy) only adds new rules to configs in major releases, most users (myself included) don’t regularly monitor new rules. This makes it challenging to manually add them to projects whenever they are introduced.  
+By adding new rules to configs in minor releases, this plugin ensures users can adopt them more easily. If any new rules cause issues, you can simply disable them. I believe this approach helps maintain and improve code quality with minimal effort.
+
 ## Migration Guide
 
 To migrate from `eslint-plugin-svelte` v1, or [`@ota-meshi/eslint-plugin-svelte`](https://www.npmjs.com/package/@ota-meshi/eslint-plugin-svelte), please refer to the [migration guide](https://sveltejs.github.io/eslint-plugin-svelte/migration/).
diff --git a/docs/README.md b/docs/README.md
index 38f8187..e7bea45 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -14,8 +14,6 @@ You can check on the [Online DEMO](https://eslint-online-playground.netlify.app/
 > For example, <https://github.com/sveltejs/eslint-plugin-svelte/blob/eslint-plugin-svelte%402.46.0/README.md>
 > and <https://github.com/sveltejs/eslint-plugin-svelte/blob/eslint-plugin-svelte%402.46.0/docs>
 
-**_We are working on experimental support for Svelte v5, but may break with new versions of Svelte v5._**
-
 [![NPM license](https://img.shields.io/npm/l/eslint-plugin-svelte.svg)](https://www.npmjs.com/package/eslint-plugin-svelte)
 [![NPM version](https://img.shields.io/npm/v/eslint-plugin-svelte.svg)](https://www.npmjs.com/package/eslint-plugin-svelte)
 [![NPM downloads](https://img.shields.io/badge/dynamic/json.svg?label=downloads&colorB=green&suffix=/day&query=$.downloads&uri=https://api.npmjs.org//downloads/point/last-day/eslint-plugin-svelte&maxAge=3600)](http://www.npmtrends.com/eslint-plugin-svelte)
diff --git a/package.json b/package.json
index b14d8cd..d0cd91b 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,7 @@
   "private": true,
   "license": "MIT",
   "funding": "https://github.com/sponsors/ota-meshi",
-  "packageManager": "pnpm@9.15.0",
+  "packageManager": "pnpm@9.15.2",
   "scripts": {
     "prerelease": "cd packages/eslint-plugin-svelte && pnpm clean && pnpm run build && cd ../.. && cp README.md packages/eslint-plugin-svelte",
     "release": "changeset publish",
diff --git a/packages/eslint-plugin-svelte/CHANGELOG.md b/packages/eslint-plugin-svelte/CHANGELOG.md
index 07ae720..e8067a2 100644
--- a/packages/eslint-plugin-svelte/CHANGELOG.md
+++ b/packages/eslint-plugin-svelte/CHANGELOG.md
@@ -1,5 +1,11 @@
 # eslint-plugin-svelte
 
+## 3.0.0-next.5
+
+### Patch Changes
+
+- [#956](https://github.com/sveltejs/eslint-plugin-svelte/pull/956) [`157ee1f`](https://github.com/sveltejs/eslint-plugin-svelte/commit/157ee1fade79aab88f01d125b0b01cf375da4cd1) Thanks [@baseballyama](https://github.com/baseballyama)! - chore: update `svelte-eslint-parser` to v1.0.0-next.4
+
 ## 3.0.0-next.4
 
 ### Major Changes
diff --git a/packages/eslint-plugin-svelte/package.json b/packages/eslint-plugin-svelte/package.json
index 047cc2e..7dccad6 100644
--- a/packages/eslint-plugin-svelte/package.json
+++ b/packages/eslint-plugin-svelte/package.json
@@ -1,6 +1,6 @@
 {
   "name": "eslint-plugin-svelte",
-  "version": "3.0.0-next.4",
+  "version": "3.0.0-next.5",
   "description": "ESLint plugin for Svelte using AST",
   "repository": "git+https://github.com/sveltejs/eslint-plugin-svelte.git",
   "homepage": "https://sveltejs.github.io/eslint-plugin-svelte",
@@ -30,7 +30,7 @@
     "cover": "c8 --reporter=lcov pnpm run test",
     "debug": "pnpm run mocha \"tests/src/**/*.ts\" --reporter dot --timeout 60000",
     "lint": "run-p lint:*",
-    "lint-fix": "pnpm run lint:es --fix && pnpm run lint:style --fix",
+    "lint-fix": "pnpm run lint:es --fix",
     "lint:es": "eslint --cache .",
     "mocha": "pnpm run ts ./node_modules/mocha/bin/mocha.js",
     "new": "pnpm run ts ./tools/new-rule.ts",
diff --git a/packages/eslint-plugin-svelte/src/meta.ts b/packages/eslint-plugin-svelte/src/meta.ts
index 81b3a6c..f2a2a6d 100644
--- a/packages/eslint-plugin-svelte/src/meta.ts
+++ b/packages/eslint-plugin-svelte/src/meta.ts
@@ -2,4 +2,4 @@
 // This file has been automatically generated,
 // in order to update its content execute "pnpm run update"
 export const name = 'eslint-plugin-svelte';
-export const version = '3.0.0-next.4';
+export const version = '3.0.0-next.5';
diff --git a/packages/eslint-plugin-svelte/src/rules/valid-prop-names-in-kit-pages.ts b/packages/eslint-plugin-svelte/src/rules/valid-prop-names-in-kit-pages.ts
index 77b3e98..ff12df7 100644
--- a/packages/eslint-plugin-svelte/src/rules/valid-prop-names-in-kit-pages.ts
+++ b/packages/eslint-plugin-svelte/src/rules/valid-prop-names-in-kit-pages.ts
@@ -2,9 +2,27 @@ import type { AST } from 'svelte-eslint-parser';
 import type { TSESTree } from '@typescript-eslint/types';
 import { createRule } from '../utils/index.js';
 import { isKitPageComponent } from '../utils/svelte-kit.js';
+import type { RuleContext } from '../types.js';
 
 const EXPECTED_PROP_NAMES = ['data', 'errors', 'form', 'snapshot'];
 
+function checkProp(node: TSESTree.VariableDeclarator, context: RuleContext) {
+	if (node.id.type !== 'ObjectPattern') return;
+	for (const p of node.id.properties) {
+		if (
+			p.type === 'Property' &&
+			p.value.type === 'Identifier' &&
+			!EXPECTED_PROP_NAMES.includes(p.value.name)
+		) {
+			context.report({
+				node: p.value,
+				loc: p.value.loc,
+				messageId: 'unexpected'
+			});
+		}
+	}
+}
+
 export default createRule('valid-prop-names-in-kit-pages', {
 	meta: {
 		docs: {
@@ -39,6 +57,7 @@ export default createRule('valid-prop-names-in-kit-pages', {
 				isScript = false;
 			},
 
+			// Svelte3,4
 			'ExportNamedDeclaration > VariableDeclaration > VariableDeclarator': (
 				node: TSESTree.VariableDeclarator
 			) => {
@@ -57,20 +76,22 @@ export default createRule('valid-prop-names-in-kit-pages', {
 				}
 
 				// export let { xxx, yyy } = zzz
-				if (node.id.type !== 'ObjectPattern') return;
-				for (const p of node.id.properties) {
-					if (
-						p.type === 'Property' &&
-						p.value.type === 'Identifier' &&
-						!EXPECTED_PROP_NAMES.includes(p.value.name)
-					) {
-						context.report({
-							node: p.value,
-							loc: p.value.loc,
-							messageId: 'unexpected'
-						});
-					}
+				checkProp(node, context);
+			},
+
+			// Svelte5
+			// let { foo, bar } = $props();
+			'VariableDeclaration > VariableDeclarator': (node: TSESTree.VariableDeclarator) => {
+				if (!isScript) return;
+				if (
+					node.init?.type !== 'CallExpression' ||
+					node.init.callee?.type !== 'Identifier' ||
+					node.init.callee?.name !== '$props'
+				) {
+					return;
 				}
+
+				checkProp(node, context);
 			}
 		};
 	}


Test patch:
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte5/+svelte5-test001-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte5/+svelte5-test001-errors.yaml
new file mode 100644
index 0000000..a5dcdf6
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte5/+svelte5-test001-errors.yaml
@@ -0,0 +1,8 @@
+- message: disallow props other than data or errors in SvelteKit page components.
+  line: 2
+  column: 8
+  suggestions: null
+- message: disallow props other than data or errors in SvelteKit page components.
+  line: 2
+  column: 13
+  suggestions: null
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte5/+svelte5-test001-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte5/+svelte5-test001-input.svelte
new file mode 100644
index 0000000..33f7c90
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte5/+svelte5-test001-input.svelte
@@ -0,0 +1,5 @@
+<script>
+	let { foo, bar } = $props();
+</script>
+
+{foo}, {bar}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte5/+svelte5-test001-requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte5/+svelte5-test001-requirements.json
new file mode 100644
index 0000000..0192b10
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte5/+svelte5-test001-requirements.json
@@ -0,0 +1,3 @@
+{
+	"svelte": ">=5.0.0-0"
+}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte5/_config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte5/_config.json
new file mode 100644
index 0000000..7a71ebe
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte5/_config.json
@@ -0,0 +1,13 @@
+{
+	"languageOptions": {
+		"parserOptions": {
+			"svelteConfig": {
+				"kit": {
+					"files": {
+						"routes": "tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte5"
+					}
+				}
+			}
+		}
+	}
+}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/not-page/svelte5/test01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/not-page/svelte5/test01-input.svelte
new file mode 100644
index 0000000..b042de2
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/not-page/svelte5/test01-input.svelte
@@ -0,0 +1,5 @@
+<script>
+	let { data, errors, foo, bar } = $props();
+</script>
+
+{data}, {errors}, {foo}, {bar}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/not-page/svelte5/test01-requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/not-page/svelte5/test01-requirements.json
new file mode 100644
index 0000000..0192b10
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/not-page/svelte5/test01-requirements.json
@@ -0,0 +1,3 @@
+{
+	"svelte": ">=5.0.0-0"
+}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/svelte5/+test001-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/svelte5/+test001-input.svelte
new file mode 100644
index 0000000..6ed2875
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/svelte5/+test001-input.svelte
@@ -0,0 +1,21 @@
+<script>
+	let { data, errors, form } = $props();
+
+	let comment = '';
+
+	export const snapshot = {
+		capture: () => comment,
+		restore: (value) => (comment = value)
+	};
+</script>
+
+{data}, {errors}
+
+{#if form?.success}
+	<p>Successfully logged in! Welcome back, {data.user.name}</p>
+{/if}
+
+<form method="POST">
+	<textarea bind:value={comment} />
+	<button>Post comment</button>
+</form>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/svelte5/+test001-requirements.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/svelte5/+test001-requirements.json
new file mode 100644
index 0000000..0192b10
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/svelte5/+test001-requirements.json
@@ -0,0 +1,3 @@
+{
+	"svelte": ">=5.0.0-0"
+}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/svelte5/_config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/svelte5/_config.json
new file mode 100644
index 0000000..cd38d8e
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/svelte5/_config.json
@@ -0,0 +1,13 @@
+{
+	"languageOptions": {
+		"parserOptions": {
+			"svelteConfig": {
+				"kit": {
+					"files": {
+						"routes": "tests/fixtures/rules/valid-prop-names-in-kit-pages/valid/svelte5"
+					}
+				}
+			}
+		}
+	}
+}