add eslint rule to prevent direct store state mutation

This commit is contained in:
pa
2026-03-08 21:34:42 +09:00
parent ddee396376
commit 8c21ecd9f0
3 changed files with 42 additions and 22 deletions

View File

@@ -69,6 +69,25 @@ export default defineConfig([
'no-unused-vars': 'warn',
'no-case-declarations': 'off',
'no-control-regex': 'warn',
// Store boundary rule:
// 1) Disallow `xxxStore.xxx = ...`
// 2) Disallow `xxxStore.xxx++ / --`
// Reason: prevent direct cross-store mutation and enforce owner-store actions.
'no-restricted-syntax': [
'error',
{
selector:
"AssignmentExpression[left.type='MemberExpression'][left.object.type='Identifier'][left.object.name=/Store$/]",
message:
'Do not mutate store state directly via *Store.* assignment. Use owner-store actions.'
},
{
selector:
"UpdateExpression[argument.type='MemberExpression'][argument.object.type='Identifier'][argument.object.name=/Store$/]",
message:
'Do not mutate store state directly via *Store.* update operators. Use owner-store actions.'
}
],
'vue/no-mutating-props': 'warn',
'vue/multi-word-component-names': 'off',

View File

@@ -117,28 +117,7 @@ export const useAvatarStore = defineStore('Avatar', () => {
favoriteStore.applyFavorite('avatar', ref.id);
if (favoriteStore.localAvatarFavoritesList.includes(ref.id)) {
const avatarRef = ref;
for (
let i = 0;
i < favoriteStore.localAvatarFavoriteGroups.length;
++i
) {
const groupName = favoriteStore.localAvatarFavoriteGroups[i];
if (!favoriteStore.localAvatarFavorites[groupName]) {
continue;
}
for (
let j = 0;
j < favoriteStore.localAvatarFavorites[groupName].length;
++j
) {
const favoriteRef =
favoriteStore.localAvatarFavorites[groupName][j];
if (favoriteRef.id === avatarRef.id) {
favoriteStore.localAvatarFavorites[groupName][j] =
avatarRef;
}
}
}
favoriteStore.syncLocalAvatarFavoriteRef(avatarRef);
// update db cache
database.addAvatarToCache(avatarRef);

View File

@@ -970,6 +970,27 @@ export const useFavoriteStore = defineStore('Favorite', () => {
friendImportDialogInput.value = value;
}
/**
* @param {object} avatarRef
*/
function syncLocalAvatarFavoriteRef(avatarRef) {
if (!avatarRef?.id) {
return;
}
for (let i = 0; i < localAvatarFavoriteGroups.value.length; ++i) {
const groupName = localAvatarFavoriteGroups.value[i];
const group = localAvatarFavorites[groupName];
if (!group) {
continue;
}
for (let j = 0; j < group.length; ++j) {
if (group[j]?.id === avatarRef.id) {
group[j] = avatarRef;
}
}
}
}
/**
*
* @param {string} worldId
@@ -1892,6 +1913,7 @@ export const useFavoriteStore = defineStore('Favorite', () => {
setAvatarImportDialogInput,
setWorldImportDialogInput,
setFriendImportDialogInput,
syncLocalAvatarFavoriteRef,
addLocalWorldFavorite,
hasLocalWorldFavorite,
hasLocalAvatarFavorite,