Gathering detailed insights and metrics for eslint-plugin-pinia-getters-no-array-mutations
Gathering detailed insights and metrics for eslint-plugin-pinia-getters-no-array-mutations
npm install eslint-plugin-pinia-getters-no-array-mutations
Typescript
Module System
Node Version
NPM Version
74.5
Supply Chain
99
Quality
78.5
Maintenance
100
Vulnerability
100
License
Total Downloads
1,835
Last Day
1
Last Week
15
Last Month
25
Last Year
1,835
Minified
Minified + Gzipped
Latest Version
1.2.0
Package Id
eslint-plugin-pinia-getters-no-array-mutations@1.2.0
Unpacked Size
67.17 kB
Size
19.34 kB
File Count
14
NPM Version
10.2.4
Node Version
20.11.0
Publised On
15 Feb 2024
Cumulative downloads
Total Downloads
Last day
0%
1
Compared to previous day
Last week
0%
15
Compared to previous week
Last month
78.6%
25
Compared to previous month
Last year
0%
1,835
Compared to previous year
4
This ESLint plugin introduces a rule for Pinia, the intuitive, type-safe, and flexible store pattern for Vue.js applications. By ensuring that getters in Pinia stores avoid in-place array mutations on the store's state, it prevents side effects, hard-to-track reactivity issues and unexpected infinite recursive behavior, ensuring a smoother development experience and less frustrating bug hunts. :)
If you haven't installed eslint in your project yet, follow its installation guide.
Then, install this plugin:
1npm install eslint-plugin-pinia-getters-no-array-mutations --save-dev
To include the rule in your ESLint checks, add it to your .eslintrc
configuration like this:
1{ 2 "plugins": [ 3 // ... other plugins 4 "pinia-getters-no-array-mutations" 5 ], 6 "rules": { 7 // ... other rules 8 "pinia-getters-no-array-mutations/no-array-mutations-on-state": "error", 9 } 10}
The rule offers an automatic fix in the form of creating a clone of the original structure via structuredClone
.
Be aware that this changes the semantics of your code, therefore it is disabled by default. If you are not sure what
structuredClone
does or if it's the right approach for you, please read the section
Understanding the Fix.
You can enable the automatic fix in you .eslintrc
configuration like this:
1"rules": { 2 // ... 3 "pinia-getters-no-array-mutations/no-array-mutations-on-state": ["error", { "enableFix": true }] 4 // ... 5}
Executing the fix will wrap the affected property in a structuredClone()
call:
1// Before: 2sortedDifferently: (state) => state.someArray.sort(sortFn) 3 4// After: 5sortedDifferently: (state) => structuredClone(state.someArray).sort(sortFn)
no-array-mutations-on-state
This rule checks for and reports any usage of array methods that mutate the store's state directly within Pinia getters.
The rule will flag any use of mutating array methods that are directly applied to this
, state
, or any nested
properties within a Pinia getter function. These can modify your state in place and lead to infinite recursive behavior.
Here are some examples that will be flagged by the rule:
1const store = { 2 getters: { 3 // Using .reverse() directly on this.someArray 4 reversedArray: (state) => this.someArray.reverse(), 5 6 // Mutating a nested array on this 7 sortedNestedArray: (state) => this.nested.someArray.sort(sortFn), 8 9 // Same idea for state 10 sortedStateArray: (state) => state.someArray.sort(sortFn), 11 12 // Mutating a nested array on state 13 sortedStateNestedArray: (state) => state.nested.someArray.sort(sortFn), 14 } 15};
The following code examples use non-mutating patterns and will not be flagged by the rule:
1const store = { 2 getters: { 3 // Making a shallow copy of an array on this before sorting - may still be dangerous if you have nested arrays 4 sortedArray: (state) => [...this.someArray].sort(sortFn), 5 sortedNestedArray: (state) => [...this.nested.someArray].sort(sortFn), 6 7 // Same for state 8 sortedStateArray: (state) => [...state.someArray].sort(sortFn), 9 sortedStateNestedArray: (state) => [...state.nested.someArray].sort(sortFn), 10 11 // Using structuredClone to deeply clone an array on this before sorting 12 sortedDeepCloneArray: (state) => structuredClone(this.someArray).sort(sortFn), 13 sortedStateDeepCloneNestedArray: (state) => structuredClone(state.nested.someArray).sort(sortFn), 14 } 15};
this
, state
or any member like state.someArray
this.deeply.nested.property
is followed by a call to a mutating array method.push
, pop
, shift
, unshift
, splice
, sort
, reverse
, fill
, and copyWithin
on arrays that are
part of the store's reactive state.The rule currently does not cover cases where other stores' getters or properties are mutated. For example:
1getters: { 2 sortedDifferently: () => { 3 const otherStore = useOtherStore(); 4 otherStore.someArray.sort(sortFn); // Not currently flagged by this rule, but still dangerous 5 } 6}
Since the rule does not flag the mutation of other properties, e.g. from other stores, you may still end up with methods that mutate arrays in place. This is a trade-off because the alternative would be to make the rule very broad, which could end up causing a lot of false positives, e.g. flagging operations on non-reactive arrays. If you have a good approach to improve this, please open a pull request or raise an issue :)
If you build complex getters, for example by using Maps
, Sets
, Object.values
or similar to get a subset of your state, and then use
methods that mutate this subset, it might affect the underlying reactive state. This rule is not made for complex
use cases like that.
The rule does not check the contents of your array. If you create a shallow copy of a deeply nested array, you may still end up mutating properties of the original state if you mutate it. Example:
1getters: { 2 sortedDifferently: () => { 3 const otherStore = useOtherStore(); 4 otherStore.someArray.sort(sortFn); // Not currently flagged by this rule, but still dangerous 5 } 6}
To avoid this, use a deep cloning
method like structuredClone
or any library of your choosing, popular choices being lodash's
_.cloneDeep or klona.
structuredClone
Deep CloningThis plugin offers an automatic fix to prevent array mutations by cloning the target array before applying methods like
.sort()
. The cloning is done using the structuredClone function, a recent addition to the JavaScript language that
creates a deep clone of a given object, preserving the structure and data of the original without any reference to it.
structuredClone
:structuredClone
will recursively copy all properties, leading to
a completely new array while maintaining nested data. This is useful for preventing side effects in reactive state
management but be mindful of any performance implications.structuredClone
is supported in modern browsers and Node.js environments; however, if you are
targeting older environments, a polyfill or alternative cloning method may be necessary.
Check a compatibility table like caniuse for up-to-date support information.[...state.shallowArray].sort(sortFn)
. This creates a shallow copy of your array.1import { cloneDeep } from 'lodash'; 2 3// ... 4getters: { 5 sortedArray: (state) => cloneDeep(state).sort(sortFn), // valid 6} 7// ...
The repo provides a demonstration of the rule.
npm install
npm run lint
/demo/README.md
.After one too many hard-to-track reactivity issues, maximum call stack size exceeded
errors and infinite recursion
loops, I am no longer willing to spend time endlessly debugging code just to remember at some point that .sort
modifies arrays in place.
Contributions are welcome! If you find any issues or have suggestions for improvements, please open an issue or submit a pull request.
License: ISC
© Fabian Mohr, 2024. All rights reserved.
No vulnerabilities found.
No security vulnerabilities found.