import fs from 'fs/promises'; import path from 'path'; /** * File synchronization utility functions */ /** * Get existing IDs from files in a directory * @param {string} dir - Directory path * @param {Object} options - Configuration options * @param {string} options.prefix - File prefix to filter (e.g., 'category_') * @param {string} options.suffix - File suffix to filter (e.g., '.json') * @param {RegExp} options.pattern - Custom regex pattern to extract ID * @returns {Promise} - Array of numeric IDs */ export async function getExistingIds(dir, options = {}) { const { prefix = '', suffix = '', pattern = null } = options; let existingFiles = []; try { existingFiles = await fs.readdir(dir); } catch (err) { // Directory might be empty or new return []; } if (pattern) { return existingFiles .map(f => { const match = f.match(pattern); return match ? parseInt(match[1]) : null; }) .filter(id => id !== null && !isNaN(id)); } return existingFiles .filter(f => { if (prefix && !f.startsWith(prefix)) return false; if (suffix && !f.endsWith(suffix)) return false; return true; }) .map(f => { let id = f; if (prefix) id = id.replace(prefix, ''); if (suffix) id = id.replace(suffix, ''); return parseInt(id); }) .filter(id => !isNaN(id)); } /** * Delete obsolete files based on valid IDs * @param {string} dir - Directory path * @param {number[]} existingIds - IDs of existing files * @param {Set} validIds - Set of valid IDs to keep * @param {Function} filenameFn - Function to generate filename from ID * @returns {Promise} - Number of files deleted */ export async function deleteObsoleteFiles(dir, existingIds, validIds, filenameFn) { const toDelete = existingIds.filter(id => !validIds.has(id)); for (const id of toDelete) { const filePath = path.join(dir, filenameFn(id)); await fs.unlink(filePath); } if (toDelete.length > 0) { console.log(`🗑️ Deleted ${toDelete.length} obsolete files.`); } return toDelete.length; } /** * Write JSON to file only if content has changed * @param {string} filePath - Full path to file * @param {*} data - Data to write (will be JSON.stringify'd) * @param {number} indent - JSON indentation (default: 2) * @returns {Promise} - True if file was written, false if unchanged */ export async function writeJsonIfChanged(filePath, data, indent = 2) { const newContent = JSON.stringify(data, null, indent); let oldContent = ''; try { oldContent = await fs.readFile(filePath, 'utf-8'); } catch (e) { // File doesn't exist yet } if (oldContent !== newContent) { await fs.writeFile(filePath, newContent); return true; } return false; } /** * Ensure directory exists, create if it doesn't * @param {string} dir - Directory path * @returns {Promise} */ export async function ensureDir(dir) { await fs.mkdir(dir, { recursive: true }); } /** * Read JSON file safely * @param {string} filePath - Full path to file * @returns {Promise<*|null>} - Parsed JSON or null if file doesn't exist */ export async function readJsonFile(filePath) { try { const content = await fs.readFile(filePath, 'utf-8'); return JSON.parse(content); } catch (err) { return null; } } /** * Read text file safely * @param {string} filePath - Full path to file * @returns {Promise} - File content or null if file doesn't exist */ export async function readTextFile(filePath) { try { return await fs.readFile(filePath, 'utf-8'); } catch (err) { return null; } }