Skip to content

Commit 119e4af

Browse files
authored
Add svelte/require-stores-init rule (#211)
* Add `svelte/require-stores-init` rule * Update doc * revert tests/utils.ts * fix lint errors * fix test case
1 parent 6f8ec65 commit 119e4af

File tree

22 files changed

+217
-11
lines changed

22 files changed

+217
-11
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ These rules relate to better ways of doing things to help you avoid problems:
287287
| [svelte/no-unused-svelte-ignore](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-unused-svelte-ignore/) | disallow unused svelte-ignore comments | :star: |
288288
| [svelte/no-useless-mustaches](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-useless-mustaches/) | disallow unnecessary mustache interpolations | :wrench: |
289289
| [svelte/require-optimized-style-attribute](https://ota-meshi.github.io/eslint-plugin-svelte/rules/require-optimized-style-attribute/) | require style attributes that can be optimized | |
290+
| [svelte/require-stores-init](https://ota-meshi.github.io/eslint-plugin-svelte/rules/require-stores-init/) | require initial value in store | |
290291

291292
## Stylistic Issues
292293

docs-svelte-kit/src/lib/components/ESLintCodeBlock.svelte

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@
1212
let code = ""
1313
export let rules = {}
1414
export let fix = false
15+
export let language = "html"
1516
let time = ""
16-
let options = {
17-
filename: "example.svelte",
17+
$: options = {
18+
filename: language === "html" ? "example.svelte" : "example.js",
1819
preprocess,
1920
postprocess,
2021
}
@@ -43,7 +44,7 @@
4344
{linter}
4445
bind:code
4546
config={{
46-
parser: "svelte-eslint-parser",
47+
parser: language === "html" ? "svelte-eslint-parser" : undefined,
4748
parserOptions: {
4849
ecmaVersion: 2020,
4950
sourceType: "module",
@@ -54,6 +55,7 @@
5455
es2021: true,
5556
},
5657
}}
58+
{language}
5759
{options}
5860
on:result={onLintedResult}
5961
showDiff={showDiff && fix}

docs-svelte-kit/src/lib/eslint/ESLintEditor.svelte

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
export let options = {}
1212
export let fix = true
1313
export let showDiff = true
14+
export let language = "html"
1415
1516
let fixedValue = code
1617
let leftMarkers = []
@@ -221,7 +222,7 @@
221222
bind:this={editor}
222223
bind:code
223224
bind:rightCode={fixedValue}
224-
language="html"
225+
{language}
225226
diffEditor={fix && showDiff}
226227
markers={leftMarkers}
227228
{rightMarkers}

docs/rules.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ These rules relate to better ways of doing things to help you avoid problems:
4747
| [svelte/no-unused-svelte-ignore](./rules/no-unused-svelte-ignore.md) | disallow unused svelte-ignore comments | :star: |
4848
| [svelte/no-useless-mustaches](./rules/no-useless-mustaches.md) | disallow unnecessary mustache interpolations | :wrench: |
4949
| [svelte/require-optimized-style-attribute](./rules/require-optimized-style-attribute.md) | require style attributes that can be optimized | |
50+
| [svelte/require-stores-init](./rules/require-stores-init.md) | require initial value in store | |
5051

5152
## Stylistic Issues
5253

docs/rules/require-stores-init.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
---
2+
pageClass: "rule-details"
3+
sidebarDepth: 0
4+
title: "svelte/require-stores-init"
5+
description: "require initial value in store"
6+
---
7+
8+
# svelte/require-stores-init
9+
10+
> require initial value in store
11+
12+
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> **_This rule has not been released yet._** </badge>
13+
14+
## :book: Rule Details
15+
16+
This rule is aimed to enforce initial values when initializing the Svelte stores.
17+
18+
<ESLintCodeBlock language="javascript">
19+
20+
<!--eslint-skip-->
21+
22+
```js
23+
/* eslint svelte/require-stores-init: "error" */
24+
25+
import { writable, readable, derived } from "svelte/store"
26+
27+
/* ✓ GOOD */
28+
export const w1 = writable(false)
29+
export const r1 = readable({})
30+
export const d1 = derived([a, b], () => {}, false)
31+
32+
/* ✗ BAD */
33+
export const w2 = writable()
34+
export const r2 = readable()
35+
export const d2 = derived([a, b], () => {})
36+
```
37+
38+
</ESLintCodeBlock>
39+
40+
## :wrench: Options
41+
42+
Nothing.
43+
44+
## :heart: Compatibility
45+
46+
This rule was taken from [@tivac/eslint-plugin-svelte].
47+
This rule is compatible with `@tivac/svelte/stores-initial-value` rule.
48+
49+
[@tivac/eslint-plugin-svelte]: https://github.com/tivac/eslint-plugin-svelte/
50+
51+
## :mag: Implementation
52+
53+
- [Rule source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/src/rules/require-stores-init.ts)
54+
- [Test source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/tests/src/rules/require-stores-init.ts)

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
"@types/escape-html": "^1.0.2",
8484
"@types/eslint": "^8.0.0",
8585
"@types/eslint-scope": "^3.7.0",
86+
"@types/eslint-utils": "^3.0.1",
8687
"@types/eslint-visitor-keys": "^1.0.0",
8788
"@types/estree": "^1.0.0",
8889
"@types/less": "^3.0.3",

src/rules/require-stores-init.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { createRule } from "../utils"
2+
import type * as ESTree from "estree"
3+
import { ReferenceTracker } from "eslint-utils"
4+
5+
export default createRule("require-stores-init", {
6+
meta: {
7+
docs: {
8+
description: "require initial value in store",
9+
category: "Best Practices",
10+
recommended: false,
11+
},
12+
schema: [],
13+
messages: {
14+
storeDefaultValue: `Always set a default value for svelte stores.`,
15+
},
16+
type: "suggestion",
17+
},
18+
create(context) {
19+
/** Extract 'svelte/store' references */
20+
function* extractStoreReferences() {
21+
const referenceTracker = new ReferenceTracker(context.getScope())
22+
for (const { node, path } of referenceTracker.iterateEsmReferences({
23+
"svelte/store": {
24+
[ReferenceTracker.ESM]: true,
25+
writable: {
26+
[ReferenceTracker.CALL]: true,
27+
},
28+
readable: {
29+
[ReferenceTracker.CALL]: true,
30+
},
31+
derived: {
32+
[ReferenceTracker.CALL]: true,
33+
},
34+
},
35+
})) {
36+
yield {
37+
node: node as ESTree.CallExpression,
38+
name: path[path.length - 1],
39+
}
40+
}
41+
}
42+
43+
return {
44+
Program() {
45+
for (const { node, name } of extractStoreReferences()) {
46+
const minArgs =
47+
name === "writable" || name === "readable"
48+
? 1
49+
: name === "derived"
50+
? 3
51+
: 0
52+
53+
if (
54+
node.arguments.length >= minArgs ||
55+
node.arguments.some((arg) => arg.type === "SpreadElement")
56+
) {
57+
continue
58+
}
59+
context.report({
60+
node,
61+
messageId: "storeDefaultValue",
62+
})
63+
}
64+
},
65+
}
66+
},
67+
})

src/utils/rules.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import noUselessMustaches from "../rules/no-useless-mustaches"
2828
import preferClassDirective from "../rules/prefer-class-directive"
2929
import preferStyleDirective from "../rules/prefer-style-directive"
3030
import requireOptimizedStyleAttribute from "../rules/require-optimized-style-attribute"
31+
import requireStoresInit from "../rules/require-stores-init"
3132
import shorthandAttribute from "../rules/shorthand-attribute"
3233
import shorthandDirective from "../rules/shorthand-directive"
3334
import sortAttributes from "../rules/sort-attributes"
@@ -65,6 +66,7 @@ export const rules = [
6566
preferClassDirective,
6667
preferStyleDirective,
6768
requireOptimizedStyleAttribute,
69+
requireStoresInit,
6870
shorthandAttribute,
6971
shorthandDirective,
7072
sortAttributes,

tests/fixtures/rules/.eslintrc.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
"use strict"
2-
31
module.exports = {
2+
parserOptions: {
3+
sourceType: "module",
4+
},
45
overrides: [
56
{
67
files: ["*output.svelte"],
@@ -17,5 +18,6 @@ module.exports = {
1718
"no-empty-function": "off",
1819
"one-var": "off",
1920
"func-style": "off",
21+
"node/no-unsupported-features/es-syntax": "off",
2022
},
2123
}

tests/fixtures/rules/indent/invalid/.eslintrc.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
"use strict"
2-
31
module.exports = {
42
rules: {
53
"no-sparse-arrays": "off",

0 commit comments

Comments
 (0)