Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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/neat-dots-grin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'eslint-plugin-svelte': minor
---

Adds a suggestion to the `derived-has-same-inputs-outputs` rule which renames the outputs.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ These rules relate to style guidelines, and are therefore quite subjective:
| Rule ID | Description | |
|:--------|:------------|:---|
| [svelte/consistent-selector-style](https://sveltejs.github.io/eslint-plugin-svelte/rules/consistent-selector-style/) | enforce a consistent style for CSS selectors | |
| [svelte/derived-has-same-inputs-outputs](https://sveltejs.github.io/eslint-plugin-svelte/rules/derived-has-same-inputs-outputs/) | derived store should use same variable names between values and callback | |
| [svelte/derived-has-same-inputs-outputs](https://sveltejs.github.io/eslint-plugin-svelte/rules/derived-has-same-inputs-outputs/) | derived store should use same variable names between values and callback | :bulb: |
| [svelte/first-attribute-linebreak](https://sveltejs.github.io/eslint-plugin-svelte/rules/first-attribute-linebreak/) | enforce the location of first attribute | :wrench: |
| [svelte/html-closing-bracket-new-line](https://sveltejs.github.io/eslint-plugin-svelte/rules/html-closing-bracket-new-line/) | Require or disallow a line break before tag's closing brackets | :wrench: |
| [svelte/html-closing-bracket-spacing](https://sveltejs.github.io/eslint-plugin-svelte/rules/html-closing-bracket-spacing/) | require or disallow a space before tag's closing brackets | :wrench: |
Expand Down
2 changes: 1 addition & 1 deletion docs/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ These rules relate to style guidelines, and are therefore quite subjective:
| Rule ID | Description | |
| :------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------- | :------- |
| [svelte/consistent-selector-style](./rules/consistent-selector-style.md) | enforce a consistent style for CSS selectors | |
| [svelte/derived-has-same-inputs-outputs](./rules/derived-has-same-inputs-outputs.md) | derived store should use same variable names between values and callback | |
| [svelte/derived-has-same-inputs-outputs](./rules/derived-has-same-inputs-outputs.md) | derived store should use same variable names between values and callback | :bulb: |
| [svelte/first-attribute-linebreak](./rules/first-attribute-linebreak.md) | enforce the location of first attribute | :wrench: |
| [svelte/html-closing-bracket-new-line](./rules/html-closing-bracket-new-line.md) | Require or disallow a line break before tag's closing brackets | :wrench: |
| [svelte/html-closing-bracket-spacing](./rules/html-closing-bracket-spacing.md) | require or disallow a space before tag's closing brackets | :wrench: |
Expand Down
2 changes: 2 additions & 0 deletions docs/rules/derived-has-same-inputs-outputs.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ since: 'v2.8.0'

> derived store should use same variable names between values and callback

- :bulb: Some problems reported by this rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).

## :book: Rule Details

This rule reports where variable names and callback function's argument names are different.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,56 @@
import type { TSESTree } from '@typescript-eslint/types';
import type { Variable } from '@typescript-eslint/scope-manager';
import { createRule } from '../utils/index.js';
import type { RuleContext } from '../types.js';
import type { RuleContext, RuleFixer } from '../types.js';
import { extractStoreReferences } from './reference-helpers/svelte-store.js';
import { getScope } from '../utils/ast-utils.js';

function findVariableForName(
context: RuleContext,
node: TSESTree.Node,
name: string,
expectedName: string
): { hasConflict: boolean; variable: Variable | null } {
const scope = getScope(context, node);
let hasConflict = false;
let variable: Variable | null = null;

for (const ref of scope.references) {
if (ref.identifier.name === expectedName) {
hasConflict = true;
break;
}
}

if (!hasConflict) {
for (const v of scope.variables) {
if (hasConflict && variable) {
break;
}
if (v.name === expectedName) {
hasConflict = true;
continue;
}
if (v.name === name) {
variable = v;
}
}
}

return { hasConflict, variable };
}

function createFixer(node: TSESTree.Node, variable: Variable | null, name: string) {
return function* fix(fixer: RuleFixer) {
yield fixer.replaceText(node, name);

if (variable) {
for (const ref of variable.references) {
yield fixer.replaceText(ref.identifier, name);
}
}
};
}

export default createRule('derived-has-same-inputs-outputs', {
meta: {
Expand All @@ -11,9 +60,11 @@ export default createRule('derived-has-same-inputs-outputs', {
recommended: false,
conflictWithPrettier: false
},
hasSuggestions: true,
schema: [],
messages: {
unexpected: "The argument name should be '{{name}}'."
unexpected: "The argument name should be '{{name}}'.",
renameParam: 'Rename the parameter from {{oldName}} to {{newName}}.'
},
type: 'suggestion'
},
Expand Down Expand Up @@ -49,11 +100,27 @@ export default createRule('derived-has-same-inputs-outputs', {
if (fnParam.type !== 'Identifier') return;
const expectedName = `$${args.name}`;
if (expectedName !== fnParam.name) {
const { hasConflict, variable } = findVariableForName(
context,
fn.body,
fnParam.name,
expectedName
);

context.report({
node: fn,
loc: fnParam.loc,
messageId: 'unexpected',
data: { name: expectedName }
data: { name: expectedName },
suggest: hasConflict
? undefined
: [
{
messageId: 'renameParam',
data: { oldName: fnParam.name, newName: expectedName },
fix: createFixer(fnParam, variable, expectedName)
}
]
});
}
}
Expand All @@ -77,11 +144,27 @@ export default createRule('derived-has-same-inputs-outputs', {
if (element && element.type === 'Identifier' && argName) {
const expectedName = `$${argName}`;
if (expectedName !== element.name) {
const { hasConflict, variable } = findVariableForName(
context,
fn.body,
element.name,
expectedName
);

context.report({
node: fn,
loc: element.loc,
messageId: 'unexpected',
data: { name: expectedName }
data: { name: expectedName },
suggest: hasConflict
? undefined
: [
{
messageId: 'renameParam',
data: { oldName: element.name, newName: expectedName },
fix: createFixer(element, variable, expectedName)
}
]
});
}
}
Expand Down
Loading
Loading