diff --git a/.changeset/tidy-pugs-draw.md b/.changeset/tidy-pugs-draw.md new file mode 100644 index 000000000..dcbe6bd68 --- /dev/null +++ b/.changeset/tidy-pugs-draw.md @@ -0,0 +1,5 @@ +--- +"eslint-plugin-svelte": minor +--- + +feat: add `svelte/no-reactive-reassign` rule diff --git a/README.md b/README.md index 0a9910f12..51735373b 100644 --- a/README.md +++ b/README.md @@ -319,6 +319,7 @@ These rules relate to possible syntax or logic errors in Svelte code: | [svelte/no-export-load-in-svelte-module-in-kit-pages](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-export-load-in-svelte-module-in-kit-pages/) | disallow exporting load functions in `*.svelte` module in Svelte Kit page components. | | | [svelte/no-not-function-handler](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-not-function-handler/) | disallow use of not function in event handler | :star: | | [svelte/no-object-in-text-mustaches](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-object-in-text-mustaches/) | disallow objects in text mustache interpolation | :star: | +| [svelte/no-reactive-reassign](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-reactive-reassign/) | disallow reassigning reactive values | | | [svelte/no-shorthand-style-property-overrides](https://sveltejs.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://sveltejs.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://sveltejs.github.io/eslint-plugin-svelte/rules/no-unknown-style-directive-property/) | disallow unknown `style:property` | :star: | diff --git a/docs/rules.md b/docs/rules.md index b4543b474..ecef4f018 100644 --- a/docs/rules.md +++ b/docs/rules.md @@ -26,6 +26,7 @@ These rules relate to possible syntax or logic errors in Svelte code: | [svelte/no-export-load-in-svelte-module-in-kit-pages](./rules/no-export-load-in-svelte-module-in-kit-pages.md) | disallow exporting load functions in `*.svelte` module in Svelte Kit page components. | | | [svelte/no-not-function-handler](./rules/no-not-function-handler.md) | disallow use of not function in event handler | :star: | | [svelte/no-object-in-text-mustaches](./rules/no-object-in-text-mustaches.md) | disallow objects in text mustache interpolation | :star: | +| [svelte/no-reactive-reassign](./rules/no-reactive-reassign.md) | disallow reassigning reactive values | | | [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: | diff --git a/docs/rules/no-reactive-reassign.md b/docs/rules/no-reactive-reassign.md new file mode 100644 index 000000000..194a21a85 --- /dev/null +++ b/docs/rules/no-reactive-reassign.md @@ -0,0 +1,122 @@ +--- +pageClass: "rule-details" +sidebarDepth: 0 +title: "svelte/no-reactive-reassign" +description: "disallow reassigning reactive values" +--- + +# svelte/no-reactive-reassign + +> disallow reassigning reactive values + +- :exclamation: **_This rule has not been released yet._** + +## :book: Rule Details + +This rule aims to prevent unintended behavior caused by modification or reassignment of reactive values. + + + + + +```svelte + + + + + + +``` + + + +## :wrench: Options + +```json +{ + "svelte/no-reactive-reassign": ["error", { + "props": true + }] +} +``` + +- `props` ... If set to `true`, this rule warns against the modification of reactive value properties. Default is `true`. + +### `{ "props": true }` + + + + + +```svelte + + + + + + + +``` + + + +### `{ "props": false }` + + + + + +```svelte + + + + + + + + +``` + + + +## :mag: Implementation + +- [Rule source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/src/rules/no-reactive-reassign.ts) +- [Test source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/tests/src/rules/no-reactive-reassign.ts) diff --git a/src/rules/no-reactive-reassign.ts b/src/rules/no-reactive-reassign.ts new file mode 100644 index 000000000..214a6e60f --- /dev/null +++ b/src/rules/no-reactive-reassign.ts @@ -0,0 +1,296 @@ +import { TSESTree } from "@typescript-eslint/types" +import type { AST } from "svelte-eslint-parser" +import { createRule } from "../utils" +import { getPropertyName } from "@eslint-community/eslint-utils" + +export default createRule("no-reactive-reassign", { + meta: { + docs: { + description: "disallow reassigning reactive values", + category: "Possible Errors", + // TODO Switch to recommended in the major version. + recommended: false, + }, + schema: [ + { + type: "object", + properties: { + props: { + type: "boolean", + }, + }, + additionalProperties: false, + }, + ], + messages: { + assignmentToReactiveValue: "Assignment to reactive value '{{name}}'.", + assignmentToReactiveValueProp: + "Assignment to property of reactive value '{{name}}'.", + }, + type: "problem", + }, + create(context) { + const props = context.options[0]?.props !== false // default true + const sourceCode = context.getSourceCode() + const scopeManager = sourceCode.scopeManager + const globalScope = scopeManager.globalScope + const toplevelScope = + globalScope?.childScopes.find((scope) => scope.type === "module") || + globalScope + if (!globalScope || !toplevelScope) { + return {} + } + + type CheckContext

= { + node: TSESTree.Expression + parent: P + pathNodes: TSESTree.MemberExpression[] + } + const CHECK_REASSIGN: { + [key in TSESTree.Node["type"] | "SvelteDirective"]?: ( + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Ignore + ctx: CheckContext, + ) => + | null // The given expression does not reassign. + // The given expression will reassign. + | { + type: "reassign" + node: TSESTree.Node | AST.SvelteNode + pathNodes?: TSESTree.MemberExpression[] + } + // The context to check next. + | { + type: "check" + node: TSESTree.Expression + pathNodes?: TSESTree.MemberExpression[] + } + } = { + [TSESTree.AST_NODE_TYPES.UpdateExpression]: + // e.g. foo ++, foo -- + ({ parent }) => ({ type: "reassign", node: parent }), + [TSESTree.AST_NODE_TYPES.UnaryExpression]: ({ + parent, + }: CheckContext) => { + if (parent.operator === "delete") { + // e.g. delete foo.prop + return { type: "reassign", node: parent } + } + return null + }, + [TSESTree.AST_NODE_TYPES.AssignmentExpression]: ({ + node, + parent, + }: CheckContext) => { + if (parent.left === node) { + // e.g. foo = 42, foo += 42, foo -= 42 + return { type: "reassign", node: parent } + } + return null + }, + [TSESTree.AST_NODE_TYPES.ForInStatement]: ({ + node, + parent, + }: CheckContext) => { + if (parent.left === node) { + // e.g. for (foo in itr) + return { type: "reassign", node: parent } + } + return null + }, + [TSESTree.AST_NODE_TYPES.ForOfStatement]: ({ + node, + parent, + }: CheckContext) => { + if (parent.left === node) { + // e.g. for (foo of itr) + return { type: "reassign", node: parent } + } + return null + }, + [TSESTree.AST_NODE_TYPES.CallExpression]: ({ + node, + parent, + pathNodes, + }: CheckContext) => { + if (pathNodes.length > 0 && parent.callee === node) { + const mem = pathNodes[pathNodes.length - 1] + const callName = getPropertyName(mem) + if ( + callName && + /^(?:push|pop|shift|unshift|reverse|splice|sort|copyWithin|fill)$/u.test( + callName, + ) + ) { + // e.g. foo.push() + return { + type: "reassign", + node: parent, + pathNodes: pathNodes.slice(0, -1), + } + } + } + return null + }, + [TSESTree.AST_NODE_TYPES.MemberExpression]: ({ + node, + parent, + pathNodes, + }: CheckContext) => { + if (parent.object === node) { + // The context to check next. + return { + type: "check", + node: parent, + pathNodes: [...pathNodes, parent], + } + } + return null + }, + [TSESTree.AST_NODE_TYPES.ChainExpression]: ({ + parent, + }: CheckContext) => { + // e.g. `foo?.prop` + // The context to check next. + return { type: "check", node: parent } + }, + [TSESTree.AST_NODE_TYPES.ConditionalExpression]: ({ + node, + parent, + }: CheckContext) => { + if (parent.test === node) { + return null + } + // The context to check next for `(test ? foo : bar).prop`. + return { type: "check", node: parent } + }, + [TSESTree.AST_NODE_TYPES.Property]: ({ + node, + parent, + }: CheckContext) => { + if ( + parent.value === node && + parent.parent && + parent.parent.type === "ObjectPattern" + ) { + // The context to check next for `({a: foo} = obj)`. + return { type: "check", node: parent.parent } + } + return null + }, + [TSESTree.AST_NODE_TYPES.ArrayPattern]: ({ + node, + parent, + }: CheckContext) => { + if (parent.elements.includes(node as TSESTree.DestructuringPattern)) { + // The context to check next for `([foo] = obj)`. + return { type: "check", node: parent } + } + return null + }, + [TSESTree.AST_NODE_TYPES.RestElement]: ({ + node, + parent, + }: CheckContext) => { + if (parent.argument === node && parent.parent) { + // The context to check next for `({...foo} = obj)`. + return { + type: "check", + node: parent.parent as + | TSESTree.ArrayPattern + | TSESTree.ObjectPattern, + } + } + return null + }, + SvelteDirective: ({ + node, + parent, + }: CheckContext) => { + if (parent.kind !== "Binding") { + return null + } + if (parent.shorthand || parent.expression === node) { + return { + type: "reassign", + node: parent, + } + } + return null + }, + } + + /** + * Returns the reassign information for the given expression node if it has a reassign. + */ + function getReassignData(expr: TSESTree.Expression) { + let pathNodes: TSESTree.MemberExpression[] = [] + let node: TSESTree.Expression = expr + let parent + while ((parent = node.parent)) { + const check = CHECK_REASSIGN[parent.type] + if (!check) { + return null + } + const result = check({ node, parent, pathNodes }) + if (!result) { + return null + } + pathNodes = result.pathNodes || pathNodes + if (result.type === "reassign") { + return { + node: result.node, + pathNodes, + } + } + node = result.node + } + return null + } + + return { + SvelteReactiveStatement(node: AST.SvelteReactiveStatement) { + if ( + node.body.type !== "ExpressionStatement" || + node.body.expression.type !== "AssignmentExpression" || + node.body.expression.operator !== "=" + ) { + return + } + const assignment = node.body.expression + for (const variable of toplevelScope.variables) { + if (!variable.defs.some((def) => def.node === assignment)) { + continue + } + for (const reference of variable.references) { + const id = reference.identifier + if ( + (assignment.left.range[0] <= id.range[0] && + id.range[1] <= assignment.left.range[1]) || + id.type === "JSXIdentifier" + ) { + continue + } + const reassign = getReassignData(id) + if (!reassign) { + continue + } + + // Suppresses reporting if the props option is set to `false` and reassigned to properties. + if (!props && reassign.pathNodes.length > 0) continue + + context.report({ + node: reassign.node, + messageId: + reassign.pathNodes.length === 0 + ? "assignmentToReactiveValue" + : "assignmentToReactiveValueProp", + data: { + name: id.name, + }, + }) + } + } + }, + } + }, +}) diff --git a/src/utils/rules.ts b/src/utils/rules.ts index b720d98b7..0f560ad86 100644 --- a/src/utils/rules.ts +++ b/src/utils/rules.ts @@ -29,6 +29,7 @@ import noNotFunctionHandler from "../rules/no-not-function-handler" import noObjectInTextMustaches from "../rules/no-object-in-text-mustaches" import noReactiveFunctions from "../rules/no-reactive-functions" import noReactiveLiterals from "../rules/no-reactive-literals" +import noReactiveReassign from "../rules/no-reactive-reassign" import noShorthandStylePropertyOverrides from "../rules/no-shorthand-style-property-overrides" import noSpacesAroundEqualSignsInAttribute from "../rules/no-spaces-around-equal-signs-in-attribute" import noStoreAsync from "../rules/no-store-async" @@ -84,6 +85,7 @@ export const rules = [ noObjectInTextMustaches, noReactiveFunctions, noReactiveLiterals, + noReactiveReassign, noShorthandStylePropertyOverrides, noSpacesAroundEqualSignsInAttribute, noStoreAsync, diff --git a/tests/fixtures/rules/no-reactive-reassign/invalid/array01-errors.yaml b/tests/fixtures/rules/no-reactive-reassign/invalid/array01-errors.yaml new file mode 100644 index 000000000..87c0b9b91 --- /dev/null +++ b/tests/fixtures/rules/no-reactive-reassign/invalid/array01-errors.yaml @@ -0,0 +1,36 @@ +- message: Assignment to reactive value 'array1'. + line: 15 + column: 5 + suggestions: null +- message: Assignment to reactive value 'array2'. + line: 16 + column: 5 + suggestions: null +- message: Assignment to reactive value 'array3'. + line: 17 + column: 5 + suggestions: null +- message: Assignment to reactive value 'array4'. + line: 18 + column: 5 + suggestions: null +- message: Assignment to reactive value 'array5'. + line: 19 + column: 5 + suggestions: null +- message: Assignment to reactive value 'array6'. + line: 20 + column: 5 + suggestions: null +- message: Assignment to reactive value 'array7'. + line: 21 + column: 5 + suggestions: null +- message: Assignment to reactive value 'array8'. + line: 22 + column: 5 + suggestions: null +- message: Assignment to reactive value 'array9'. + line: 23 + column: 5 + suggestions: null diff --git a/tests/fixtures/rules/no-reactive-reassign/invalid/array01-input.svelte b/tests/fixtures/rules/no-reactive-reassign/invalid/array01-input.svelte new file mode 100644 index 000000000..c50246253 --- /dev/null +++ b/tests/fixtures/rules/no-reactive-reassign/invalid/array01-input.svelte @@ -0,0 +1,41 @@ + + + + + + +{JSON.stringify(array1) + + JSON.stringify(array2) + + JSON.stringify(array3) + + JSON.stringify(array4) + + JSON.stringify(array5) + + JSON.stringify(array6) + + JSON.stringify(array7) + + JSON.stringify(array8) + + JSON.stringify(array9) + + rerender} diff --git a/tests/fixtures/rules/no-reactive-reassign/invalid/bind-dir01-errors.yaml b/tests/fixtures/rules/no-reactive-reassign/invalid/bind-dir01-errors.yaml new file mode 100644 index 000000000..97bf6731b --- /dev/null +++ b/tests/fixtures/rules/no-reactive-reassign/invalid/bind-dir01-errors.yaml @@ -0,0 +1,4 @@ +- message: Assignment to reactive value 'reactiveValue'. + line: 6 + column: 22 + suggestions: null diff --git a/tests/fixtures/rules/no-reactive-reassign/invalid/bind-dir01-input.svelte b/tests/fixtures/rules/no-reactive-reassign/invalid/bind-dir01-input.svelte new file mode 100644 index 000000000..ebcd157a8 --- /dev/null +++ b/tests/fixtures/rules/no-reactive-reassign/invalid/bind-dir01-input.svelte @@ -0,0 +1,6 @@ + + + diff --git a/tests/fixtures/rules/no-reactive-reassign/invalid/conditional01-errors.yaml b/tests/fixtures/rules/no-reactive-reassign/invalid/conditional01-errors.yaml new file mode 100644 index 000000000..55c536ee2 --- /dev/null +++ b/tests/fixtures/rules/no-reactive-reassign/invalid/conditional01-errors.yaml @@ -0,0 +1,4 @@ +- message: Assignment to property of reactive value 'reactiveValue'. + line: 7 + column: 6 + suggestions: null diff --git a/tests/fixtures/rules/no-reactive-reassign/invalid/conditional01-input.svelte b/tests/fixtures/rules/no-reactive-reassign/invalid/conditional01-input.svelte new file mode 100644 index 000000000..38df90d69 --- /dev/null +++ b/tests/fixtures/rules/no-reactive-reassign/invalid/conditional01-input.svelte @@ -0,0 +1,13 @@ + + +{reactiveValue} diff --git a/tests/fixtures/rules/no-reactive-reassign/invalid/delete01-errors.yaml b/tests/fixtures/rules/no-reactive-reassign/invalid/delete01-errors.yaml new file mode 100644 index 000000000..ec9c1100a --- /dev/null +++ b/tests/fixtures/rules/no-reactive-reassign/invalid/delete01-errors.yaml @@ -0,0 +1,4 @@ +- message: Assignment to property of reactive value 'reactiveValue'. + line: 7 + column: 5 + suggestions: null diff --git a/tests/fixtures/rules/no-reactive-reassign/invalid/delete01-input.svelte b/tests/fixtures/rules/no-reactive-reassign/invalid/delete01-input.svelte new file mode 100644 index 000000000..25838e82b --- /dev/null +++ b/tests/fixtures/rules/no-reactive-reassign/invalid/delete01-input.svelte @@ -0,0 +1,11 @@ + + +{reactiveValue} diff --git a/tests/fixtures/rules/no-reactive-reassign/invalid/destructure01-errors.yaml b/tests/fixtures/rules/no-reactive-reassign/invalid/destructure01-errors.yaml new file mode 100644 index 000000000..362330ace --- /dev/null +++ b/tests/fixtures/rules/no-reactive-reassign/invalid/destructure01-errors.yaml @@ -0,0 +1,16 @@ +- message: Assignment to reactive value 'reactiveValue'. + line: 9 + column: 7 + suggestions: null +- message: Assignment to reactive value 'reactiveObject'. + line: 10 + column: 7 + suggestions: null +- message: Assignment to reactive value 'reactiveValue'. + line: 12 + column: 6 + suggestions: null +- message: Assignment to reactive value 'reactiveArray'. + line: 13 + column: 6 + suggestions: null diff --git a/tests/fixtures/rules/no-reactive-reassign/invalid/destructure01-input.svelte b/tests/fixtures/rules/no-reactive-reassign/invalid/destructure01-input.svelte new file mode 100644 index 000000000..58e060cff --- /dev/null +++ b/tests/fixtures/rules/no-reactive-reassign/invalid/destructure01-input.svelte @@ -0,0 +1,19 @@ + + +{reactiveValue} +{JSON.stringify(reactiveObject)} +{JSON.stringify(reactiveArray)} diff --git a/tests/fixtures/rules/no-reactive-reassign/invalid/for-in01-errors.yaml b/tests/fixtures/rules/no-reactive-reassign/invalid/for-in01-errors.yaml new file mode 100644 index 000000000..eff4f0b2e --- /dev/null +++ b/tests/fixtures/rules/no-reactive-reassign/invalid/for-in01-errors.yaml @@ -0,0 +1,4 @@ +- message: Assignment to property of reactive value 'reactiveValue'. + line: 8 + column: 5 + suggestions: null diff --git a/tests/fixtures/rules/no-reactive-reassign/invalid/for-in01-input.svelte b/tests/fixtures/rules/no-reactive-reassign/invalid/for-in01-input.svelte new file mode 100644 index 000000000..a0bc9962a --- /dev/null +++ b/tests/fixtures/rules/no-reactive-reassign/invalid/for-in01-input.svelte @@ -0,0 +1,19 @@ + + + + + + +{reactiveValue.key + rerender} diff --git a/tests/fixtures/rules/no-reactive-reassign/invalid/for-of01-errors.yaml b/tests/fixtures/rules/no-reactive-reassign/invalid/for-of01-errors.yaml new file mode 100644 index 000000000..eff4f0b2e --- /dev/null +++ b/tests/fixtures/rules/no-reactive-reassign/invalid/for-of01-errors.yaml @@ -0,0 +1,4 @@ +- message: Assignment to property of reactive value 'reactiveValue'. + line: 8 + column: 5 + suggestions: null diff --git a/tests/fixtures/rules/no-reactive-reassign/invalid/for-of01-input.svelte b/tests/fixtures/rules/no-reactive-reassign/invalid/for-of01-input.svelte new file mode 100644 index 000000000..ba9c313b3 --- /dev/null +++ b/tests/fixtures/rules/no-reactive-reassign/invalid/for-of01-input.svelte @@ -0,0 +1,19 @@ + + + + + + +{reactiveValue.key + rerender} diff --git a/tests/fixtures/rules/no-reactive-reassign/invalid/member01-errors.yaml b/tests/fixtures/rules/no-reactive-reassign/invalid/member01-errors.yaml new file mode 100644 index 000000000..af20c5021 --- /dev/null +++ b/tests/fixtures/rules/no-reactive-reassign/invalid/member01-errors.yaml @@ -0,0 +1,8 @@ +- message: Assignment to property of reactive value 'reactiveValue'. + line: 6 + column: 5 + suggestions: null +- message: Assignment to property of reactive value 'reactiveValue'. + line: 8 + column: 6 + suggestions: null diff --git a/tests/fixtures/rules/no-reactive-reassign/invalid/member01-input.svelte b/tests/fixtures/rules/no-reactive-reassign/invalid/member01-input.svelte new file mode 100644 index 000000000..3861dfba5 --- /dev/null +++ b/tests/fixtures/rules/no-reactive-reassign/invalid/member01-input.svelte @@ -0,0 +1,12 @@ + + +{reactiveValue} diff --git a/tests/fixtures/rules/no-reactive-reassign/invalid/props-false/_config.json b/tests/fixtures/rules/no-reactive-reassign/invalid/props-false/_config.json new file mode 100644 index 000000000..cda6d1be9 --- /dev/null +++ b/tests/fixtures/rules/no-reactive-reassign/invalid/props-false/_config.json @@ -0,0 +1,7 @@ +{ + "options": [ + { + "props": false + } + ] +} diff --git a/tests/fixtures/rules/no-reactive-reassign/invalid/props-false/test01-errors.yaml b/tests/fixtures/rules/no-reactive-reassign/invalid/props-false/test01-errors.yaml new file mode 100644 index 000000000..62f301cdf --- /dev/null +++ b/tests/fixtures/rules/no-reactive-reassign/invalid/props-false/test01-errors.yaml @@ -0,0 +1,8 @@ +- message: Assignment to reactive value 'reactiveValue'. + line: 7 + column: 5 + suggestions: null +- message: Assignment to reactive value 'reactiveValue'. + line: 15 + column: 22 + suggestions: null diff --git a/tests/fixtures/rules/no-reactive-reassign/invalid/props-false/test01-input.svelte b/tests/fixtures/rules/no-reactive-reassign/invalid/props-false/test01-input.svelte new file mode 100644 index 000000000..ec3fbaf0c --- /dev/null +++ b/tests/fixtures/rules/no-reactive-reassign/invalid/props-false/test01-input.svelte @@ -0,0 +1,17 @@ + + + + + + + + +{reactiveValue} diff --git a/tests/fixtures/rules/no-reactive-reassign/invalid/props-true01-errors.yaml b/tests/fixtures/rules/no-reactive-reassign/invalid/props-true01-errors.yaml new file mode 100644 index 000000000..659cac015 --- /dev/null +++ b/tests/fixtures/rules/no-reactive-reassign/invalid/props-true01-errors.yaml @@ -0,0 +1,8 @@ +- message: Assignment to property of reactive value 'reactiveValue'. + line: 9 + column: 5 + suggestions: null +- message: Assignment to property of reactive value 'reactiveValue'. + line: 16 + column: 22 + suggestions: null diff --git a/tests/fixtures/rules/no-reactive-reassign/invalid/props-true01-input.svelte b/tests/fixtures/rules/no-reactive-reassign/invalid/props-true01-input.svelte new file mode 100644 index 000000000..53d38b183 --- /dev/null +++ b/tests/fixtures/rules/no-reactive-reassign/invalid/props-true01-input.svelte @@ -0,0 +1,16 @@ + + + + + + diff --git a/tests/fixtures/rules/no-reactive-reassign/invalid/test01-errors.yaml b/tests/fixtures/rules/no-reactive-reassign/invalid/test01-errors.yaml new file mode 100644 index 000000000..8889ad39a --- /dev/null +++ b/tests/fixtures/rules/no-reactive-reassign/invalid/test01-errors.yaml @@ -0,0 +1,12 @@ +- message: Assignment to reactive value 'reactiveValue'. + line: 7 + column: 5 + suggestions: null +- message: Assignment to reactive value 'reactiveValue'. + line: 8 + column: 5 + suggestions: null +- message: Assignment to reactive value 'reactiveValue'. + line: 16 + column: 22 + suggestions: null diff --git a/tests/fixtures/rules/no-reactive-reassign/invalid/test01-input.svelte b/tests/fixtures/rules/no-reactive-reassign/invalid/test01-input.svelte new file mode 100644 index 000000000..9e4d6240b --- /dev/null +++ b/tests/fixtures/rules/no-reactive-reassign/invalid/test01-input.svelte @@ -0,0 +1,18 @@ + + + + + + + + +{reactiveValue} diff --git a/tests/fixtures/rules/no-reactive-reassign/valid/array01-input.svelte b/tests/fixtures/rules/no-reactive-reassign/valid/array01-input.svelte new file mode 100644 index 000000000..215eb285c --- /dev/null +++ b/tests/fixtures/rules/no-reactive-reassign/valid/array01-input.svelte @@ -0,0 +1,41 @@ + + + + + + +{JSON.stringify(array1) + + JSON.stringify(array2) + + JSON.stringify(array3) + + JSON.stringify(array4) + + JSON.stringify(array5) + + JSON.stringify(array6) + + JSON.stringify(array7) + + JSON.stringify(array8) + + JSON.stringify(array9) + + rerender} diff --git a/tests/fixtures/rules/no-reactive-reassign/valid/assign-right01-input.svelte b/tests/fixtures/rules/no-reactive-reassign/valid/assign-right01-input.svelte new file mode 100644 index 000000000..ba41cb86a --- /dev/null +++ b/tests/fixtures/rules/no-reactive-reassign/valid/assign-right01-input.svelte @@ -0,0 +1,14 @@ + + +{reactiveValue} diff --git a/tests/fixtures/rules/no-reactive-reassign/valid/destructure01-input.svelte b/tests/fixtures/rules/no-reactive-reassign/valid/destructure01-input.svelte new file mode 100644 index 000000000..a253277c8 --- /dev/null +++ b/tests/fixtures/rules/no-reactive-reassign/valid/destructure01-input.svelte @@ -0,0 +1,12 @@ + + +{reactiveValue} diff --git a/tests/fixtures/rules/no-reactive-reassign/valid/for-in01-input.svelte b/tests/fixtures/rules/no-reactive-reassign/valid/for-in01-input.svelte new file mode 100644 index 000000000..a4caf704e --- /dev/null +++ b/tests/fixtures/rules/no-reactive-reassign/valid/for-in01-input.svelte @@ -0,0 +1,19 @@ + + + + + + +{reactiveValue.key + rerender} diff --git a/tests/fixtures/rules/no-reactive-reassign/valid/for-of01-input.svelte b/tests/fixtures/rules/no-reactive-reassign/valid/for-of01-input.svelte new file mode 100644 index 000000000..b6c07b2d3 --- /dev/null +++ b/tests/fixtures/rules/no-reactive-reassign/valid/for-of01-input.svelte @@ -0,0 +1,19 @@ + + + + + + +{reactiveValue[0] + rerender} diff --git a/tests/fixtures/rules/no-reactive-reassign/valid/member-key01-input.svelte b/tests/fixtures/rules/no-reactive-reassign/valid/member-key01-input.svelte new file mode 100644 index 000000000..fe8d886e4 --- /dev/null +++ b/tests/fixtures/rules/no-reactive-reassign/valid/member-key01-input.svelte @@ -0,0 +1,10 @@ + + +{reactiveValue} diff --git a/tests/fixtures/rules/no-reactive-reassign/valid/on-dir01-input.svelte b/tests/fixtures/rules/no-reactive-reassign/valid/on-dir01-input.svelte new file mode 100644 index 000000000..2e0a7a1ed --- /dev/null +++ b/tests/fixtures/rules/no-reactive-reassign/valid/on-dir01-input.svelte @@ -0,0 +1,8 @@ + + + + + +{reactiveLikeValue} diff --git a/tests/fixtures/rules/no-reactive-reassign/valid/reactive-like02-input.svelte b/tests/fixtures/rules/no-reactive-reassign/valid/reactive-like02-input.svelte new file mode 100644 index 000000000..3e73fb527 --- /dev/null +++ b/tests/fixtures/rules/no-reactive-reassign/valid/reactive-like02-input.svelte @@ -0,0 +1,16 @@ + + + + + +{reactiveLikeValue} diff --git a/tests/fixtures/rules/no-reactive-reassign/valid/test01-input.svelte b/tests/fixtures/rules/no-reactive-reassign/valid/test01-input.svelte new file mode 100644 index 000000000..1098b14f3 --- /dev/null +++ b/tests/fixtures/rules/no-reactive-reassign/valid/test01-input.svelte @@ -0,0 +1,14 @@ + + + + + +{reactiveValue} diff --git a/tests/fixtures/rules/no-reactive-reassign/valid/typeof01-input.svelte b/tests/fixtures/rules/no-reactive-reassign/valid/typeof01-input.svelte new file mode 100644 index 000000000..91c0be5ea --- /dev/null +++ b/tests/fixtures/rules/no-reactive-reassign/valid/typeof01-input.svelte @@ -0,0 +1,10 @@ + + +{reactiveValue} diff --git a/tests/src/rules/no-reactive-reassign.ts b/tests/src/rules/no-reactive-reassign.ts new file mode 100644 index 000000000..ebf34c6bf --- /dev/null +++ b/tests/src/rules/no-reactive-reassign.ts @@ -0,0 +1,16 @@ +import { RuleTester } from "eslint" +import rule from "../../../src/rules/no-reactive-reassign" +import { loadTestCases } from "../../utils/utils" + +const tester = new RuleTester({ + parserOptions: { + ecmaVersion: 2020, + sourceType: "module", + }, +}) + +tester.run( + "no-reactive-reassign", + rule as any, + loadTestCases("no-reactive-reassign"), +)