-
-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implement
derived-has-same-inputs-outputs
(#249)
* feat: implement derived-has-same-inputs-outputs rule * chore: add changeset * chore: update according to review comments * chore: add more tests
- Loading branch information
1 parent
1690371
commit 6d0b89f
Showing
12 changed files
with
265 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"eslint-plugin-svelte": minor | ||
--- | ||
|
||
feat: add `svelte/derived-has-same-inputs-outputs` rule |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
--- | ||
pageClass: "rule-details" | ||
sidebarDepth: 0 | ||
title: "svelte/derived-has-same-inputs-outputs" | ||
description: "derived store should use same variable names between values and callback" | ||
--- | ||
|
||
# svelte/derived-has-same-inputs-outputs | ||
|
||
> derived store should use same variable names between values and callback | ||
## :book: Rule Details | ||
|
||
This rule reports where variable names and callback function's argument names are different. | ||
This is mainly a recommended rule to avoid implementation confusion. | ||
|
||
<ESLintCodeBlock language="javascript"> | ||
|
||
<!--eslint-skip--> | ||
|
||
```js | ||
/* eslint svelte/derived-has-same-inputs-outputs: "error" */ | ||
|
||
import { derived } from "svelte/store" | ||
|
||
/* ✓ GOOD */ | ||
derived(a, ($a) => {}); | ||
derived(a, ($a, set) => {}) | ||
derived([ a, b ], ([ $a, $b ]) => {}) | ||
|
||
/* ✗ BAD */ | ||
derived(a, (b) => {}); | ||
derived(a, (b, set) => {}); | ||
derived([ a, b ], ([ one, two ]) => {}) | ||
``` | ||
|
||
</ESLintCodeBlock> | ||
|
||
## :wrench: Options | ||
|
||
Nothing. | ||
|
||
|
||
## :books: Further Reading | ||
|
||
- [Svelte - Docs > RUN TIME > svelte/store > derived](https://svelte.dev/docs#run-time-svelte-store-derived) | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
import type * as ESTree from "estree" | ||
import { createRule } from "../utils" | ||
import type { RuleContext } from "../types" | ||
import { extractStoreReferences } from "./reference-helpers/svelte-store" | ||
|
||
export default createRule("derived-has-same-inputs-outputs", { | ||
meta: { | ||
docs: { | ||
description: | ||
"derived store should use same variable names between values and callback", | ||
category: "Stylistic Issues", | ||
recommended: false, | ||
conflictWithPrettier: false, | ||
}, | ||
schema: [], | ||
messages: { | ||
unexpected: "The argument name should be '{{name}}'.", | ||
}, | ||
type: "suggestion", | ||
}, | ||
create(context) { | ||
/** check node type */ | ||
function isIdentifierOrArrayExpression( | ||
node: ESTree.SpreadElement | ESTree.Expression, | ||
): node is ESTree.Identifier | ESTree.ArrayExpression { | ||
return ["Identifier", "ArrayExpression"].includes(node.type) | ||
} | ||
|
||
type ArrowFunctionExpressionOrFunctionExpression = | ||
| ESTree.ArrowFunctionExpression | ||
| ESTree.FunctionExpression | ||
|
||
/** check node type */ | ||
function isFunctionExpression( | ||
node: ESTree.SpreadElement | ESTree.Expression, | ||
): node is ArrowFunctionExpressionOrFunctionExpression { | ||
return ["ArrowFunctionExpression", "FunctionExpression"].includes( | ||
node.type, | ||
) | ||
} | ||
|
||
/** | ||
* Check for identifier type. | ||
* e.g. derived(a, ($a) => {}); | ||
*/ | ||
function checkIdentifier( | ||
context: RuleContext, | ||
args: ESTree.Identifier, | ||
fn: ArrowFunctionExpressionOrFunctionExpression, | ||
) { | ||
const fnParam = fn.params[0] | ||
if (fnParam.type !== "Identifier") return | ||
const expectedName = `$${args.name}` | ||
if (expectedName !== fnParam.name) { | ||
context.report({ | ||
node: fn, | ||
loc: fnParam.loc!, | ||
messageId: "unexpected", | ||
data: { name: expectedName }, | ||
}) | ||
} | ||
} | ||
|
||
/** | ||
* Check for array type. | ||
* e.g. derived([ a, b ], ([ $a, $b ]) => {}) | ||
*/ | ||
function checkArrayExpression( | ||
context: RuleContext, | ||
args: ESTree.ArrayExpression, | ||
fn: ArrowFunctionExpressionOrFunctionExpression, | ||
) { | ||
const fnParam = fn.params[0] | ||
if (fnParam.type !== "ArrayPattern") return | ||
const argNames = args.elements.map((element) => { | ||
return element && element.type === "Identifier" ? element.name : null | ||
}) | ||
fnParam.elements.forEach((element, index) => { | ||
const argName = argNames[index] | ||
if (element && element.type === "Identifier" && argName) { | ||
const expectedName = `$${argName}` | ||
if (expectedName !== element.name) { | ||
context.report({ | ||
node: fn, | ||
loc: element.loc!, | ||
messageId: "unexpected", | ||
data: { name: expectedName }, | ||
}) | ||
} | ||
} | ||
}) | ||
} | ||
|
||
return { | ||
Program() { | ||
for (const { node } of extractStoreReferences(context, ["derived"])) { | ||
const [args, fn] = node.arguments | ||
if (!args || !isIdentifierOrArrayExpression(args)) continue | ||
if (!fn || !isFunctionExpression(fn)) continue | ||
if (!fn.params || fn.params.length === 0) continue | ||
if (args.type === "Identifier") checkIdentifier(context, args, fn) | ||
else checkArrayExpression(context, args, fn) | ||
} | ||
}, | ||
} | ||
}, | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
32 changes: 32 additions & 0 deletions
32
tests/fixtures/rules/derived-has-same-inputs-outputs/invalid/test01-errors.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
- message: The argument name should be '$a'. | ||
line: 3 | ||
column: 13 | ||
suggestions: null | ||
- message: The argument name should be '$c'. | ||
line: 6 | ||
column: 13 | ||
suggestions: null | ||
- message: The argument name should be '$e'. | ||
line: 9 | ||
column: 19 | ||
suggestions: null | ||
- message: The argument name should be '$f'. | ||
line: 9 | ||
column: 22 | ||
suggestions: null | ||
- message: The argument name should be '$i'. | ||
line: 12 | ||
column: 19 | ||
suggestions: null | ||
- message: The argument name should be '$j'. | ||
line: 12 | ||
column: 22 | ||
suggestions: null | ||
- message: The argument name should be '$l'. | ||
line: 15 | ||
column: 26 | ||
suggestions: null | ||
- message: The argument name should be '$o'. | ||
line: 18 | ||
column: 22 | ||
suggestions: null |
20 changes: 20 additions & 0 deletions
20
tests/fixtures/rules/derived-has-same-inputs-outputs/invalid/test01-input.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { derived } from "svelte/store" | ||
|
||
derived(a, (b) => { | ||
/** do nothing */ | ||
}) | ||
derived(c, (d, set) => { | ||
/** do nothing */ | ||
}) | ||
derived([e, f], ([g, h]) => { | ||
/** do nothing */ | ||
}) | ||
derived([i, j], ([k, l], set) => { | ||
/** do nothing */ | ||
}) | ||
derived([null, l], ([$m, $n]) => { | ||
/** do nothing */ | ||
}) | ||
derived([o, null], ([$p, $q]) => { | ||
/** do nothing */ | ||
}) |
26 changes: 26 additions & 0 deletions
26
tests/fixtures/rules/derived-has-same-inputs-outputs/valid/test01-input.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { derived } from "svelte/store" | ||
|
||
derived(a, ($a) => { | ||
/** do nothing */ | ||
}) | ||
derived(c, ($c, set) => { | ||
/** do nothing */ | ||
}) | ||
derived([e, f], ([$e, $f]) => { | ||
/** do nothing */ | ||
}) | ||
derived([i, j], ([$i, $j], set) => { | ||
/** do nothing */ | ||
}) | ||
derived(null, ($null, set) => { | ||
/** do nothing */ | ||
}) | ||
derived(null, ($k, set) => { | ||
/** do nothing */ | ||
}) | ||
derived([null, l], ([$m, $l]) => { | ||
/** do nothing */ | ||
}) | ||
derived([n, null], ([$n, $o]) => { | ||
/** do nothing */ | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { RuleTester } from "eslint" | ||
import rule from "../../../src/rules/derived-has-same-inputs-outputs" | ||
import { loadTestCases } from "../../utils/utils" | ||
|
||
const tester = new RuleTester({ | ||
parserOptions: { | ||
ecmaVersion: 2020, | ||
sourceType: "module", | ||
}, | ||
}) | ||
|
||
tester.run( | ||
"derived-has-same-inputs-outputs", | ||
rule as any, | ||
loadTestCases("derived-has-same-inputs-outputs"), | ||
) |