135 lines
3.8 KiB
JavaScript
135 lines
3.8 KiB
JavaScript
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<number[]>} - 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<number>} validIds - Set of valid IDs to keep
|
|
* @param {Function} filenameFn - Function to generate filename from ID
|
|
* @returns {Promise<number>} - 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<boolean>} - 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<void>}
|
|
*/
|
|
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<string|null>} - 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;
|
|
}
|
|
}
|