fix: replace mutating sort/reverse with toSorted/toReversed (ES2023)

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.
This commit is contained in:
jevb
2026-04-01 15:44:06 +02:00
parent e203be5638
commit 57ba535673
7 changed files with 9 additions and 11 deletions
+1 -3
View File
@@ -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"]
}
@@ -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),
);
@@ -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;
@@ -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;
+1 -1
View File
@@ -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 });
@@ -90,7 +90,7 @@ async function rotateOldFiles(): Promise<void> {
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<string> {
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) {
@@ -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];