diff --git a/.changeset/fluffy-chicken-happen.md b/.changeset/fluffy-chicken-happen.md
new file mode 100644
index 000000000..7120e2628
--- /dev/null
+++ b/.changeset/fluffy-chicken-happen.md
@@ -0,0 +1,5 @@
+---
+"eslint-plugin-svelte": minor
+---
+
+feat: add `require-store-callbacks-use-set-param` rule
diff --git a/README.md b/README.md
index 4f19f5e35..bc6fc8f8c 100644
--- a/README.md
+++ b/README.md
@@ -271,6 +271,7 @@ These rules relate to possible syntax or logic errors in Svelte code:
| [svelte/no-shorthand-style-property-overrides](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-shorthand-style-property-overrides/) | disallow shorthand style properties that override related longhand properties | :star: |
| [svelte/no-store-async](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-store-async/) | disallow using async/await inside svelte stores because it causes issues with the auto-unsubscribing features | |
| [svelte/no-unknown-style-directive-property](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-unknown-style-directive-property/) | disallow unknown `style:property` | :star: |
+| [svelte/require-store-callbacks-use-set-param](https://ota-meshi.github.io/eslint-plugin-svelte/rules/require-store-callbacks-use-set-param/) | (no description) | |
| [svelte/valid-compile](https://ota-meshi.github.io/eslint-plugin-svelte/rules/valid-compile/) | disallow warnings when compiling. | :star: |
## Security Vulnerability
diff --git a/docs/rules.md b/docs/rules.md
index 9601f4517..946972a4e 100644
--- a/docs/rules.md
+++ b/docs/rules.md
@@ -24,6 +24,7 @@ These rules relate to possible syntax or logic errors in Svelte code:
| [svelte/no-shorthand-style-property-overrides](./rules/no-shorthand-style-property-overrides.md) | disallow shorthand style properties that override related longhand properties | :star: |
| [svelte/no-store-async](./rules/no-store-async.md) | disallow using async/await inside svelte stores because it causes issues with the auto-unsubscribing features | |
| [svelte/no-unknown-style-directive-property](./rules/no-unknown-style-directive-property.md) | disallow unknown `style:property` | :star: |
+| [svelte/require-store-callbacks-use-set-param](./rules/require-store-callbacks-use-set-param.md) | (no description) | |
| [svelte/valid-compile](./rules/valid-compile.md) | disallow warnings when compiling. | :star: |
## Security Vulnerability
diff --git a/docs/rules/require-store-callbacks-use-set-param.md b/docs/rules/require-store-callbacks-use-set-param.md
new file mode 100644
index 000000000..e7300daa5
--- /dev/null
+++ b/docs/rules/require-store-callbacks-use-set-param.md
@@ -0,0 +1,76 @@
+---
+pageClass: "rule-details"
+sidebarDepth: 0
+title: "svelte/require-store-callbacks-use-set-param"
+description: "store callbacks must use `set` param"
+---
+
+# svelte/require-store-callbacks-use-set-param
+
+> Store callbacks must use `set` param.
+
+## :book: Rule Details
+
+This rule disallows if `readable` / `writable` store's setter function doesn't use `set` parameter.
+This rule doesn't check `derived` store. Therefore if you set a updated value asynchronously, please don't forget to use `set` function.
+
+
+
+
+
+```svelte
+
+```
+
+
+
+## :wrench: Options
+
+Nothing.
+
+## :books: Further Reading
+
+- [Svelte - Docs > RUN TIME > svelte/store](https://svelte.dev/docs#run-time-svelte-store)
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/src/rules/require-store-callbacks-use-set-param.ts)
+- [Test source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/tests/src/rules/require-store-callbacks-use-set-param.ts)
diff --git a/package.json b/package.json
index 09bf15212..0cc69de87 100644
--- a/package.json
+++ b/package.json
@@ -174,7 +174,7 @@
"access": "public"
},
"typeCoverage": {
- "atLeast": 98.71,
+ "atLeast": 98.72,
"cache": true,
"detail": true,
"ignoreAsAssertion": true,
diff --git a/src/rules/require-store-callbacks-use-set-param.ts b/src/rules/require-store-callbacks-use-set-param.ts
new file mode 100644
index 000000000..005b186e5
--- /dev/null
+++ b/src/rules/require-store-callbacks-use-set-param.ts
@@ -0,0 +1,44 @@
+import { createRule } from "../utils"
+import { extractStoreReferences } from "./reference-helpers/svelte-store"
+
+export default createRule("require-store-callbacks-use-set-param", {
+ meta: {
+ docs: {
+ description: "store callbacks must use `set` param",
+ category: "Possible Errors",
+ recommended: false,
+ },
+ schema: [],
+ messages: {
+ unexpected: "Store callbacks must use `set` param.",
+ },
+ type: "suggestion",
+ },
+ create(context) {
+ return {
+ Program() {
+ for (const { node } of extractStoreReferences(context, [
+ "readable",
+ "writable",
+ ])) {
+ const [_, fn] = node.arguments
+ if (
+ !fn ||
+ (fn.type !== "ArrowFunctionExpression" &&
+ fn.type !== "FunctionExpression")
+ ) {
+ continue
+ }
+ const param = fn.params[0]
+ if (!param || (param.type === "Identifier" && param.name !== "set")) {
+ context.report({
+ node: fn,
+ loc: fn.loc!,
+ messageId: "unexpected",
+ })
+ }
+ }
+ },
+ }
+ },
+})
diff --git a/src/utils/rules.ts b/src/utils/rules.ts
index d4157c2e4..f4bc55b2b 100644
--- a/src/utils/rules.ts
+++ b/src/utils/rules.ts
@@ -33,6 +33,7 @@ import preferClassDirective from "../rules/prefer-class-directive"
import preferDestructuredStoreProps from "../rules/prefer-destructured-store-props"
import preferStyleDirective from "../rules/prefer-style-directive"
import requireOptimizedStyleAttribute from "../rules/require-optimized-style-attribute"
+import requireStoreCallbacksUseSetParam from "../rules/require-store-callbacks-use-set-param"
import requireStoresInit from "../rules/require-stores-init"
import shorthandAttribute from "../rules/shorthand-attribute"
import shorthandDirective from "../rules/shorthand-directive"
@@ -76,6 +77,7 @@ export const rules = [
preferDestructuredStoreProps,
preferStyleDirective,
requireOptimizedStyleAttribute,
+ requireStoreCallbacksUseSetParam,
requireStoresInit,
shorthandAttribute,
shorthandDirective,
diff --git a/tests/fixtures/rules/html-self-closing/invalid/presets/html/preset-html-errors.yaml b/tests/fixtures/rules/html-self-closing/invalid/presets/html/preset-html-errors.yaml
new file mode 100644
index 000000000..b22c0655f
--- /dev/null
+++ b/tests/fixtures/rules/html-self-closing/invalid/presets/html/preset-html-errors.yaml
@@ -0,0 +1,16 @@
+- message: Disallow self-closing on HTML elements.
+ line: 3
+ column: 3
+ suggestions: null
+- message: Require self-closing on HTML void elements.
+ line: 4
+ column: 3
+ suggestions: null
+- message: Disallow self-closing on Svelte custom components.
+ line: 5
+ column: 3
+ suggestions: null
+- message: Require self-closing on Svelte special elements.
+ line: 8
+ column: 1
+ suggestions: null
diff --git a/tests/fixtures/rules/html-self-closing/invalid/presets/none/preset-none-errors.yaml b/tests/fixtures/rules/html-self-closing/invalid/presets/none/preset-none-errors.yaml
new file mode 100644
index 000000000..cde3eff78
--- /dev/null
+++ b/tests/fixtures/rules/html-self-closing/invalid/presets/none/preset-none-errors.yaml
@@ -0,0 +1,16 @@
+- message: Disallow self-closing on HTML elements.
+ line: 3
+ column: 3
+ suggestions: null
+- message: Disallow self-closing on Svelte custom components.
+ line: 4
+ column: 3
+ suggestions: null
+- message: Disallow self-closing on HTML void elements.
+ line: 5
+ column: 3
+ suggestions: null
+- message: Disallow self-closing on Svelte special elements.
+ line: 8
+ column: 1
+ suggestions: null
diff --git a/tests/fixtures/rules/require-store-callbacks-use-set-param/invalid/test01-errors.yaml b/tests/fixtures/rules/require-store-callbacks-use-set-param/invalid/test01-errors.yaml
new file mode 100644
index 000000000..89483b9f1
--- /dev/null
+++ b/tests/fixtures/rules/require-store-callbacks-use-set-param/invalid/test01-errors.yaml
@@ -0,0 +1,16 @@
+- message: Store callbacks must use `set` param.
+ line: 4
+ column: 19
+ suggestions: null
+- message: Store callbacks must use `set` param.
+ line: 5
+ column: 19
+ suggestions: null
+- message: Store callbacks must use `set` param.
+ line: 7
+ column: 19
+ suggestions: null
+- message: Store callbacks must use `set` param.
+ line: 8
+ column: 19
+ suggestions: null
diff --git a/tests/fixtures/rules/require-store-callbacks-use-set-param/invalid/test01-input.svelte b/tests/fixtures/rules/require-store-callbacks-use-set-param/invalid/test01-input.svelte
new file mode 100644
index 000000000..ddc584cec
--- /dev/null
+++ b/tests/fixtures/rules/require-store-callbacks-use-set-param/invalid/test01-input.svelte
@@ -0,0 +1,9 @@
+
diff --git a/tests/fixtures/rules/require-store-callbacks-use-set-param/invalid/test02-errors.yaml b/tests/fixtures/rules/require-store-callbacks-use-set-param/invalid/test02-errors.yaml
new file mode 100644
index 000000000..89483b9f1
--- /dev/null
+++ b/tests/fixtures/rules/require-store-callbacks-use-set-param/invalid/test02-errors.yaml
@@ -0,0 +1,16 @@
+- message: Store callbacks must use `set` param.
+ line: 4
+ column: 19
+ suggestions: null
+- message: Store callbacks must use `set` param.
+ line: 5
+ column: 19
+ suggestions: null
+- message: Store callbacks must use `set` param.
+ line: 7
+ column: 19
+ suggestions: null
+- message: Store callbacks must use `set` param.
+ line: 8
+ column: 19
+ suggestions: null
diff --git a/tests/fixtures/rules/require-store-callbacks-use-set-param/invalid/test02-input.svelte b/tests/fixtures/rules/require-store-callbacks-use-set-param/invalid/test02-input.svelte
new file mode 100644
index 000000000..2f789ab7f
--- /dev/null
+++ b/tests/fixtures/rules/require-store-callbacks-use-set-param/invalid/test02-input.svelte
@@ -0,0 +1,9 @@
+
diff --git a/tests/fixtures/rules/require-store-callbacks-use-set-param/valid/test01-input.svelte b/tests/fixtures/rules/require-store-callbacks-use-set-param/valid/test01-input.svelte
new file mode 100644
index 000000000..bd88ad43e
--- /dev/null
+++ b/tests/fixtures/rules/require-store-callbacks-use-set-param/valid/test01-input.svelte
@@ -0,0 +1,24 @@
+
diff --git a/tests/fixtures/rules/require-store-callbacks-use-set-param/valid/test02-input.svelte b/tests/fixtures/rules/require-store-callbacks-use-set-param/valid/test02-input.svelte
new file mode 100644
index 000000000..160e18ae9
--- /dev/null
+++ b/tests/fixtures/rules/require-store-callbacks-use-set-param/valid/test02-input.svelte
@@ -0,0 +1,32 @@
+
diff --git a/tests/src/rules/require-store-callbacks-use-set-param.ts b/tests/src/rules/require-store-callbacks-use-set-param.ts
new file mode 100644
index 000000000..f2d3b3ab2
--- /dev/null
+++ b/tests/src/rules/require-store-callbacks-use-set-param.ts
@@ -0,0 +1,16 @@
+import { RuleTester } from "eslint"
+import rule from "../../../src/rules/require-store-callbacks-use-set-param"
+import { loadTestCases } from "../../utils/utils"
+
+const tester = new RuleTester({
+ parserOptions: {
+ ecmaVersion: 2020,
+ sourceType: "module",
+ },
+})
+
+tester.run(
+ "require-store-callbacks-use-set-param",
+ rule as any,
+ loadTestCases("require-store-callbacks-use-set-param"),
+)