From 86b50dcb705f33746215fdca9e3d3ed41e2e6bf3 Mon Sep 17 00:00:00 2001 From: sudacode Date: Sat, 28 Mar 2026 10:42:21 -0700 Subject: [PATCH] refactor: deduplicate ensureDir into shared/fs-utils 5 copies of mkdir-p-if-not-exists consolidated into one shared module with ensureDir (directory path) and ensureDirForFile (file path) variants. --- src/core/services/anilist/anilist-token-store.ts | 11 ++--------- src/core/services/anilist/anilist-update-queue.ts | 11 ++--------- src/core/services/jellyfin-token-store.ts | 11 ++--------- src/main/character-dictionary-runtime/fs-utils.ts | 7 +------ src/main/runtime/character-dictionary-auto-sync.ts | 7 +------ src/shared/fs-utils.ts | 14 ++++++++++++++ 6 files changed, 22 insertions(+), 39 deletions(-) create mode 100644 src/shared/fs-utils.ts diff --git a/src/core/services/anilist/anilist-token-store.ts b/src/core/services/anilist/anilist-token-store.ts index a1739da..b34ce03 100644 --- a/src/core/services/anilist/anilist-token-store.ts +++ b/src/core/services/anilist/anilist-token-store.ts @@ -1,6 +1,6 @@ import * as fs from 'fs'; -import * as path from 'path'; import * as electron from 'electron'; +import { ensureDirForFile } from '../../../shared/fs-utils'; interface PersistedTokenPayload { encryptedToken?: string; @@ -21,15 +21,8 @@ export interface SafeStorageLike { getSelectedStorageBackend?: () => string; } -function ensureDirectory(filePath: string): void { - const dir = path.dirname(filePath); - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); - } -} - function writePayload(filePath: string, payload: PersistedTokenPayload): void { - ensureDirectory(filePath); + ensureDirForFile(filePath); fs.writeFileSync(filePath, JSON.stringify(payload, null, 2), 'utf-8'); } diff --git a/src/core/services/anilist/anilist-update-queue.ts b/src/core/services/anilist/anilist-update-queue.ts index 71e1339..d51d358 100644 --- a/src/core/services/anilist/anilist-update-queue.ts +++ b/src/core/services/anilist/anilist-update-queue.ts @@ -1,5 +1,5 @@ import * as fs from 'fs'; -import * as path from 'path'; +import { ensureDirForFile } from '../../../shared/fs-utils'; const INITIAL_BACKOFF_MS = 30_000; const MAX_BACKOFF_MS = 6 * 60 * 60 * 1000; @@ -35,13 +35,6 @@ export interface AnilistUpdateQueue { getSnapshot: (nowMs?: number) => AnilistRetryQueueSnapshot; } -function ensureDir(filePath: string): void { - const dir = path.dirname(filePath); - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); - } -} - function clampBackoffMs(attemptCount: number): number { const computed = INITIAL_BACKOFF_MS * Math.pow(2, Math.max(0, attemptCount - 1)); return Math.min(MAX_BACKOFF_MS, computed); @@ -60,7 +53,7 @@ export function createAnilistUpdateQueue( const persist = () => { try { - ensureDir(filePath); + ensureDirForFile(filePath); const payload: AnilistRetryQueuePayload = { pending, deadLetter }; fs.writeFileSync(filePath, JSON.stringify(payload, null, 2), 'utf-8'); } catch (error) { diff --git a/src/core/services/jellyfin-token-store.ts b/src/core/services/jellyfin-token-store.ts index 179abf3..e125c23 100644 --- a/src/core/services/jellyfin-token-store.ts +++ b/src/core/services/jellyfin-token-store.ts @@ -1,6 +1,6 @@ import * as fs from 'fs'; -import * as path from 'path'; import electron from 'electron'; +import { ensureDirForFile } from '../../shared/fs-utils'; const { safeStorage } = electron; @@ -27,15 +27,8 @@ export interface JellyfinTokenStore { clearSession: () => void; } -function ensureDirectory(filePath: string): void { - const dir = path.dirname(filePath); - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); - } -} - function writePayload(filePath: string, payload: PersistedSessionPayload): void { - ensureDirectory(filePath); + ensureDirForFile(filePath); fs.writeFileSync(filePath, JSON.stringify(payload, null, 2), 'utf-8'); } diff --git a/src/main/character-dictionary-runtime/fs-utils.ts b/src/main/character-dictionary-runtime/fs-utils.ts index 8c6e225..f53af46 100644 --- a/src/main/character-dictionary-runtime/fs-utils.ts +++ b/src/main/character-dictionary-runtime/fs-utils.ts @@ -1,6 +1 @@ -import * as fs from 'fs'; - -export function ensureDir(dirPath: string): void { - if (fs.existsSync(dirPath)) return; - fs.mkdirSync(dirPath, { recursive: true }); -} +export { ensureDir } from '../../shared/fs-utils'; diff --git a/src/main/runtime/character-dictionary-auto-sync.ts b/src/main/runtime/character-dictionary-auto-sync.ts index c9b78a1..d65e1d6 100644 --- a/src/main/runtime/character-dictionary-auto-sync.ts +++ b/src/main/runtime/character-dictionary-auto-sync.ts @@ -1,5 +1,6 @@ import * as fs from 'fs'; import * as path from 'path'; +import { ensureDir } from '../../shared/fs-utils'; import type { AnilistCharacterDictionaryProfileScope } from '../../types'; import type { CharacterDictionarySnapshotProgressCallbacks, @@ -63,12 +64,6 @@ export interface CharacterDictionaryAutoSyncRuntimeDeps { onSyncComplete?: (result: { mediaId: number; mediaTitle: string; changed: boolean }) => void; } -function ensureDir(dirPath: string): void { - if (!fs.existsSync(dirPath)) { - fs.mkdirSync(dirPath, { recursive: true }); - } -} - function normalizeMediaId(rawMediaId: number): number | null { const mediaId = Math.max(1, Math.floor(rawMediaId)); return Number.isFinite(mediaId) ? mediaId : null; diff --git a/src/shared/fs-utils.ts b/src/shared/fs-utils.ts new file mode 100644 index 0000000..21c1889 --- /dev/null +++ b/src/shared/fs-utils.ts @@ -0,0 +1,14 @@ +import fs from 'node:fs'; +import path from 'node:path'; + +export function ensureDir(dirPath: string): void { + if (fs.existsSync(dirPath)) return; + fs.mkdirSync(dirPath, { recursive: true }); +} + +export function ensureDirForFile(filePath: string): void { + const dir = path.dirname(filePath); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } +}