From d7137bda65cfe20e3ba6f2ab344dbf7c872b95df Mon Sep 17 00:00:00 2001 From: ota-meshi Date: Fri, 8 Sep 2023 19:48:58 +0900 Subject: [PATCH 1/3] Fix false positives for mutable member in `svelte/no-immutable-reactive-statements` rule --- src/rules/no-immutable-reactive-statements.ts | 73 ++++++++++++++----- .../valid/mutable-member-input.svelte | 10 +++ 2 files changed, 64 insertions(+), 19 deletions(-) create mode 100644 tests/fixtures/rules/no-immutable-reactive-statements/valid/mutable-member-input.svelte diff --git a/src/rules/no-immutable-reactive-statements.ts b/src/rules/no-immutable-reactive-statements.ts index 4a0845184..f132de8b4 100644 --- a/src/rules/no-immutable-reactive-statements.ts +++ b/src/rules/no-immutable-reactive-statements.ts @@ -1,6 +1,7 @@ import type { AST } from 'svelte-eslint-parser'; import { createRule } from '../utils'; import type { Scope, Variable, Reference, Definition } from '@typescript-eslint/scope-manager'; +import type { TSESTree } from '@typescript-eslint/types'; export default createRule('no-immutable-reactive-statements', { meta: { @@ -55,45 +56,79 @@ export default createRule('no-immutable-reactive-statements', { // Global variables are assumed to be immutable. return true; } - const isMutable = variable.defs.some((def) => { - if (def.type === 'Variable') { - const parent = def.parent; - if (parent.kind === 'const') { - return false; - } - const pp = parent.parent; - if (pp && pp.type === 'ExportNamedDeclaration' && pp.declaration === parent) { - // Props - return true; - } - return hasWrite(variable); - } + const isMutableDefine = variable.defs.some((def) => { if (def.type === 'ImportBinding') { return false; } - if (def.node.type === 'AssignmentExpression') { // Reactive values return true; } + if (def.type === 'Variable') { + const parent = def.parent; + if (parent.kind === 'const') { + if ( + def.node.init && + (def.node.init.type === 'FunctionExpression' || + def.node.init.type === 'ArrowFunctionExpression' || + def.node.init.type === 'Literal') + ) { + return false; + } + } else { + const pp = parent.parent; + if (pp && pp.type === 'ExportNamedDeclaration' && pp.declaration === parent) { + // Props + return true; + } + } + return hasWrite(variable); + } return false; }); - cacheMutableVariable.set(variable, isMutable); - return isMutable; + cacheMutableVariable.set(variable, isMutableDefine); + return isMutableDefine; } /** Checks whether the given variable has a write or reactive store reference or not. */ function hasWrite(variable: Variable) { const defIds = variable.defs.map((def: Definition) => def.name); - return variable.references.some( - (reference) => + for (const reference of variable.references) { + if ( reference.isWrite() && !defIds.some( (defId) => defId.range[0] <= reference.identifier.range[0] && reference.identifier.range[1] <= defId.range[1] ) - ); + ) { + return true; + } + if (isMutableMember(reference.identifier)) { + return true; + } + } + return false; + + function isMutableMember( + expr: TSESTree.Identifier | TSESTree.JSXIdentifier | TSESTree.MemberExpression + ): boolean { + if (expr.type === 'JSXIdentifier') return false; + const parent = expr.parent; + if (parent.type === 'AssignmentExpression') { + return parent.left === expr; + } + if (parent.type === 'UpdateExpression') { + return parent.argument === expr; + } + if (parent.type === 'UnaryExpression') { + return parent.operator === 'delete' && parent.argument === expr; + } + if (parent.type === 'MemberExpression') { + return parent.object === expr && isMutableMember(parent); + } + return false; + } } /** diff --git a/tests/fixtures/rules/no-immutable-reactive-statements/valid/mutable-member-input.svelte b/tests/fixtures/rules/no-immutable-reactive-statements/valid/mutable-member-input.svelte new file mode 100644 index 000000000..c26692ec4 --- /dev/null +++ b/tests/fixtures/rules/no-immutable-reactive-statements/valid/mutable-member-input.svelte @@ -0,0 +1,10 @@ + + +{a}{b} + + From ffb8701afe18dee70b39eb96adbc5e8ded1214ca Mon Sep 17 00:00:00 2001 From: Yosuke Ota Date: Fri, 8 Sep 2023 19:50:12 +0900 Subject: [PATCH 2/3] Create hip-donkeys-collect.md --- .changeset/hip-donkeys-collect.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/hip-donkeys-collect.md diff --git a/.changeset/hip-donkeys-collect.md b/.changeset/hip-donkeys-collect.md new file mode 100644 index 000000000..2a15a016c --- /dev/null +++ b/.changeset/hip-donkeys-collect.md @@ -0,0 +1,5 @@ +--- +"eslint-plugin-svelte": patch +--- + +fix: false positives for mutable member in `svelte/no-immutable-reactive-statements` rule From cce972fe8037f0791322b87e757d63bebbbe41f9 Mon Sep 17 00:00:00 2001 From: ota-meshi Date: Fri, 8 Sep 2023 19:56:03 +0900 Subject: [PATCH 3/3] chore: .prettierignore --- .prettierignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.prettierignore b/.prettierignore index d88e23015..265f6e137 100644 --- a/.prettierignore +++ b/.prettierignore @@ -4,3 +4,4 @@ build /lib .npmrc /tests/fixtures/rules/indent/valid/ +.changeset