Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fluffy-chicken-happen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"eslint-plugin-svelte": minor
---

feat: add `require-store-callbacks-use-set-param` rule
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions docs/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
66 changes: 66 additions & 0 deletions docs/rules/require-store-callbacks-use-set-param.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
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.

<ESLintCodeBlock>

<!--eslint-skip-->

```svelte
<script>
/* eslint svelte/require-store-callbacks-use-set-param: "error" */
import { readable, writable, derived } from "svelte/store"

/** ✓ GOOD */
readable(false, (set) => set(true))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I know it should return the stop function.
A rule doesn't have to check for it, but a GOOD example would be better if it was more correct.

Suggested change
readable(false, (set) => set(true))
readable(false, (set) => {
set(true)
return () => {/* stop */}
})

// `set` is unused but this rule doesn't report.
// For that, please use `no-unused-vars` rule.
// refer: https://eslint.org/docs/latest/rules/no-unused-vars
readable(false, (set) => true)

writable(false, (set) => set(true))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
writable(false, (set) => set(true))
writable(false, (set) => {
set(true)
return () => {/* stop */}
})

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated.
a6686e4

And I added description about derived store.

writable(false, (set) => true)

derived(a, ($a) => $a * 2)
derived(
a,
($a, set) => {
setTimeout(() => set($a), 1000)
},
"one moment...",
)

/** ✗ BAD */
readable(false, () => true)
readable(false, (foo) => true)

writable(false, () => true)
writable(false, (foo) => true)
</script>
```

</ESLintCodeBlock>

## :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)
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@
"access": "public"
},
"typeCoverage": {
"atLeast": 98.71,
"atLeast": 98.72,
"cache": true,
"detail": true,
"ignoreAsAssertion": true,
Expand Down
40 changes: 40 additions & 0 deletions src/rules/require-store-callbacks-use-set-param.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
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") {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be even better if we also check FunctionExpression.

Suggested change
if (!fn || fn.type !== "ArrowFunctionExpression") {
if (!fn || (fn.type !== "ArrowFunctionExpression" && fn.type !== "FunctionExpression")) {

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops! Sorry I fixed it.
ec2d47d

continue
}
const param = fn.params[0]
if (!param || (param.type === "Identifier" && param.name !== "set")) {
context.report({
node: fn,
loc: fn.loc!,
messageId: "unexpected",
})
}
}
},
}
},
})
2 changes: 2 additions & 0 deletions src/utils/rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -76,6 +77,7 @@ export const rules = [
preferDestructuredStoreProps,
preferStyleDirective,
requireOptimizedStyleAttribute,
requireStoreCallbacksUseSetParam,
requireStoresInit,
shorthandAttribute,
shorthandDirective,
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<script>
import { readable, writable } from "svelte/store"

readable(false, () => true)
readable(false, (foo) => true)

writable(false, () => true)
writable(false, (foo) => true)
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<script>
import { readable, writable, derived } from "svelte/store"

readable(false, (set) => set(true))
readable(false, (set) => true)

writable(false, (set) => set(true))
writable(false, (set) => true)

derived(a, ($a) => $a * 2)
derived(
a,
($a, set) => {
setTimeout(() => set($a), 1000)
},
"one moment...",
)
</script>
16 changes: 16 additions & 0 deletions tests/src/rules/require-store-callbacks-use-set-param.ts
Original file line number Diff line number Diff line change
@@ -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"),
)