From 57ba5356736bba6b1992c4b91e975e88d44d54e0 Mon Sep 17 00:00:00 2001 From: jevb Date: Wed, 1 Apr 2026 15:44:06 +0200 Subject: [PATCH] fix: replace mutating sort/reverse with toSorted/toReversed (ES2023) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace .sort() with .toSorted() and .reverse() with .toReversed() to avoid in-place mutation (consistent with project immutability rules). Only disable no-map-spread rule — new Map(existingMap) is the correct immutable copy pattern, not a perf issue worth flagging. Oxlint: 0 warnings, 0 errors with all rules enabled except no-map-spread. --- Client/tauri-client/.oxlintrc.json | 4 +--- Client/tauri-client/src/components/DmSidebar.ts | 2 +- Client/tauri-client/src/components/MemberList.ts | 2 +- Client/tauri-client/src/components/QuickSwitcher.ts | 2 +- Client/tauri-client/src/lib/dispatcher.ts | 2 +- Client/tauri-client/src/lib/logPersistence.ts | 4 ++-- Client/tauri-client/src/stores/messages.store.ts | 4 ++-- 7 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Client/tauri-client/.oxlintrc.json b/Client/tauri-client/.oxlintrc.json index 51203feb..fe9b7a7e 100644 --- a/Client/tauri-client/.oxlintrc.json +++ b/Client/tauri-client/.oxlintrc.json @@ -6,9 +6,7 @@ "perf": "warn" }, "rules": { - "no-map-spread": "off", - "no-array-sort": "off", - "no-array-reverse": "off" + "no-map-spread": "off" }, "ignorePatterns": ["dist", "node_modules", "public"] } diff --git a/Client/tauri-client/src/components/DmSidebar.ts b/Client/tauri-client/src/components/DmSidebar.ts index 75819595..5beedaf5 100644 --- a/Client/tauri-client/src/components/DmSidebar.ts +++ b/Client/tauri-client/src/components/DmSidebar.ts @@ -190,7 +190,7 @@ export function createDmSidebar(options: DmSidebarOptions): MountableComponent { sectionLabel.appendChild(addBtn); // Conversation list - const sorted = [...options.conversations].sort( + const sorted = [...options.conversations].toSorted( (a, b) => (b.unread ? 1 : 0) - (a.unread ? 1 : 0), ); diff --git a/Client/tauri-client/src/components/MemberList.ts b/Client/tauri-client/src/components/MemberList.ts index df2189e3..b9b7bc1d 100644 --- a/Client/tauri-client/src/components/MemberList.ts +++ b/Client/tauri-client/src/components/MemberList.ts @@ -174,7 +174,7 @@ function renderList(root: HTMLDivElement, opts: MemberListOptions, signal: Abort for (const group of ROLE_GROUPS) { const groupMembers = allMembers .filter((m) => m.role.toLowerCase() === group.role) - .sort((a, b) => statusPriority(a.status) - statusPriority(b.status)); + .toSorted((a, b) => statusPriority(a.status) - statusPriority(b.status)); if (groupMembers.length === 0) continue; diff --git a/Client/tauri-client/src/components/QuickSwitcher.ts b/Client/tauri-client/src/components/QuickSwitcher.ts index 01670e07..a57e8ff3 100644 --- a/Client/tauri-client/src/components/QuickSwitcher.ts +++ b/Client/tauri-client/src/components/QuickSwitcher.ts @@ -30,7 +30,7 @@ export function createQuickSwitcher(options: QuickSwitcherOptions): MountableCom function getFilteredChannels(query: string): readonly Channel[] { const state = channelsStore.getState(); const all = Array.from(state.channels.values()); - const sorted = [...all].sort((a, b) => a.position - b.position); + const sorted = [...all].toSorted((a, b) => a.position - b.position); if (query.length === 0) return sorted; diff --git a/Client/tauri-client/src/lib/dispatcher.ts b/Client/tauri-client/src/lib/dispatcher.ts index 4ef40a59..1be2e3c7 100644 --- a/Client/tauri-client/src/lib/dispatcher.ts +++ b/Client/tauri-client/src/lib/dispatcher.ts @@ -279,7 +279,7 @@ export function wireDispatcher(ws: WsClient): DispatcherCleanup { const remaining = channelsStore.select((s) => s.channels); const sorted = [...remaining.values()] .filter((ch) => ch.type === "text") - .sort((a, b) => a.position - b.position); + .toSorted((a, b) => a.position - b.position); const firstTextId = sorted.length > 0 ? sorted[0]!.id : null; setActiveChannel(firstTextId); log.info("Active channel deleted, redirected", { deletedId: payload.id }); diff --git a/Client/tauri-client/src/lib/logPersistence.ts b/Client/tauri-client/src/lib/logPersistence.ts index 0b503840..c4ce78a3 100644 --- a/Client/tauri-client/src/lib/logPersistence.ts +++ b/Client/tauri-client/src/lib/logPersistence.ts @@ -90,7 +90,7 @@ async function rotateOldFiles(): Promise { const jsonlFiles = entries .filter((e) => e.name?.endsWith(".jsonl") && !e.isDirectory) .map((e) => e.name) - .sort((a, b) => a.localeCompare(b)); + .toSorted((a, b) => a.localeCompare(b)); if (jsonlFiles.length > MAX_LOG_FILES) { const toRemove = jsonlFiles.slice(0, jsonlFiles.length - MAX_LOG_FILES); @@ -183,7 +183,7 @@ export async function readAllPersistedLogs(): Promise { const jsonlFiles = entries .filter((e) => e.name?.endsWith(".jsonl") && !e.isDirectory) .map((e) => e.name) - .sort((a, b) => a.localeCompare(b)); + .toSorted((a, b) => a.localeCompare(b)); const parts: string[] = []; for (const file of jsonlFiles) { diff --git a/Client/tauri-client/src/stores/messages.store.ts b/Client/tauri-client/src/stores/messages.store.ts index fdb80708..8ecae7a2 100644 --- a/Client/tauri-client/src/stores/messages.store.ts +++ b/Client/tauri-client/src/stores/messages.store.ts @@ -134,7 +134,7 @@ export function setMessages( messages: readonly MessageResponse[], hasMore: boolean, ): void { - const converted = messages.map(messageResponseToMessage).reverse(); + const converted = messages.map(messageResponseToMessage).toReversed(); const trimmed = converted.length > MAX_MESSAGES_PER_CHANNEL ? converted.slice(converted.length - MAX_MESSAGES_PER_CHANNEL) @@ -165,7 +165,7 @@ export function prependMessages( messages: readonly MessageResponse[], hasMore: boolean, ): void { - const converted = messages.map(messageResponseToMessage).reverse(); + const converted = messages.map(messageResponseToMessage).toReversed(); messagesStore.setState((prev) => { const existing = prev.messagesByChannel.get(channelId) ?? []; let combined = [...converted, ...existing];