tiny-essentials
Version:
Collection of small, essential scripts designed to be used across various projects. These simple utilities are crafted for speed, ease of use, and versatility.
273 lines (242 loc) • 7.46 kB
JavaScript
;
var fs = require('fs');
var promises = require('fs/promises');
var path = require('path');
var normalFuncs = require('./normalFuncs.cjs');
/*========================*
* JSON Operations
*========================*/
/**
* Reads and parses a JSON file.
* Throws an error if the file content is not valid JSON.
* @param {string} filePath
* @returns {Promise<any>}
*/
async function readJsonFileAsync(filePath) {
if (!fs.existsSync(filePath)) throw new Error(`File not found: ${filePath}`);
const content = await promises.readFile(filePath, 'utf-8');
return JSON.parse(content);
}
/**
* Saves an object as JSON to a file.
* Automatically creates the directory if it does not exist.
* @param {string} filePath
* @param {any} data
* @param {number} [spaces=2]
* @returns {Promise<void>}
*/
function writeJsonFileAsync(filePath, data, spaces = 2) {
const json = JSON.stringify(data, null, spaces);
return promises.writeFile(filePath, json, 'utf-8');
}
/*========================*
* Directory Management
*========================*/
/**
* Clears all contents inside a directory but keeps the directory.
* @param {string} dirPath
*/
async function clearDirectoryAsync(dirPath) {
if (!fs.existsSync(dirPath)) return;
const files = await promises.readdir(dirPath);
/** @type {Record<string, import('fs').Stats>} */
const dataList = {};
const promises$1 = [];
for (const file of files) {
const fullPath = path.join(dirPath, file);
const lsResult = promises.lstat(fullPath);
lsResult.then((statData) => {
dataList[fullPath] = statData;
return statData;
});
promises$1.push(lsResult);
}
await Promise.all(promises$1);
const promises2 = [];
for (const fullPath in dataList) {
const statData = dataList[fullPath];
if (statData.isDirectory()) {
promises2.push(promises.rm(fullPath, { recursive: true, force: true }));
} else {
promises2.push(promises.unlink(fullPath));
}
}
await Promise.all(promises2);
}
/*========================*
* File Checks
*========================*/
/**
* Checks whether a directory is empty.
* @param {string} dirPath
* @returns {Promise<boolean>}
*/
async function isDirEmptyAsync(dirPath) {
const data = await promises.readdir(dirPath);
return data.length === 0;
}
/*========================*
* File Operations
*========================*/
/**
* Copies a file to a destination.
* @param {string} src
* @param {string} dest
* @param {number} [mode]
* @returns {Promise<void>}
*/
function ensureCopyFileAsync(src, dest, mode) {
normalFuncs.ensureDirectory(path.dirname(dest));
return promises.copyFile(src, dest, mode);
}
/**
* Deletes a file (If the file exists).
* @param {string} filePath
* @returns {Promise<boolean>}
*/
async function tryDeleteFileAsync(filePath) {
if (normalFuncs.fileExists(filePath)) {
await promises.unlink(filePath);
return true;
}
return false;
}
/*========================*
* Text Operations
*========================*/
/**
* Writes text to a file (Ensures that the directory exists, creating it recursively if needed).
* @param {string} filePath
* @param {string} content
* @param {import('fs').WriteFileOptions} [ops='utf-8']
* @returns {Promise<void>}
*/
function writeTextFileAsync(filePath, content, ops = 'utf-8') {
const dir = path.dirname(filePath);
normalFuncs.ensureDirectory(dir);
return promises.writeFile(filePath, content, ops);
}
/*========================*
* Directory Listings
*========================*/
/**
* Lists all files and dirs in a directory (optionally recursive).
* @param {string} dirPath
* @param {boolean} [recursive=false]
* @returns {Promise<{ files: string[]; dirs: string[] }>}
*/
async function listFilesAsync(dirPath, recursive = false) {
/** @type {{ files: string[]; dirs: string[] }} */
const results = { files: [], dirs: [] };
if (!normalFuncs.dirExists(dirPath)) return results;
const entries = await promises.readdir(dirPath);
for (const entry of entries) {
const fullPath = path.join(dirPath, entry);
const statData = await promises.lstat(fullPath);
if (statData.isDirectory()) {
results.dirs.push(fullPath);
if (recursive) {
const results2 = await listFilesAsync(fullPath, true);
results.files.push(...results2.files);
results.dirs.push(...results2.dirs);
}
} else {
results.files.push(fullPath);
}
}
return results;
}
/**
* Lists all directories in a directory (optionally recursive).
* @param {string} dirPath
* @param {boolean} [recursive=false]
* @returns {Promise<string[]>}
*/
async function listDirsAsync(dirPath, recursive = false) {
/** @type {string[]} */
const results = [];
if (!normalFuncs.dirExists(dirPath)) return results;
const entries = await promises.readdir(dirPath);
for (const entry of entries) {
const fullPath = path.join(dirPath, entry);
const statData = await promises.lstat(fullPath);
if (statData.isDirectory()) {
results.push(fullPath);
if (recursive) {
results.push(...(await listDirsAsync(fullPath, true)));
}
}
}
return results;
}
/*========================*
* File Info
*========================*/
/**
* Returns the size of a file in bytes.
* @param {string} filePath
* @returns {Promise<number>}
*/
async function fileSizeAsync(filePath) {
if (!normalFuncs.fileExists(filePath)) return 0;
const stats = await promises.stat(filePath);
return stats.size;
}
/**
* Returns the total size of a directory in bytes.
* @param {string} dirPath
* @returns {Promise<number>}
*/
async function dirSizeAsync(dirPath) {
let total = 0;
const { files } = await listFilesAsync(dirPath, true);
const promises = [];
for (const file of files) {
const result = fileSizeAsync(file);
result.then((item) => {
total += item;
return item;
});
promises.push(result);
}
await Promise.all(promises);
return total;
}
/*========================*
* Backup Utilities
*========================*/
/**
* Restores the most recent backup of a file.
* @param {string} filePath
* @param {string} [ext='bak']
* @returns {Promise<void>}
*/
function restoreLatestBackupAsync(filePath, ext = 'bak') {
const latestBackup = normalFuncs.getLatestBackupPath(filePath, ext);
return ensureCopyFileAsync(latestBackup, filePath);
}
/**
* Creates a backup copy of a file with .bak timestamp suffix.
* @param {string} filePath
* @param {string} [ext='bak']
* @returns {Promise<void>}
*/
async function backupFileAsync(filePath, ext = 'bak') {
if (!normalFuncs.fileExists(filePath)) return;
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const backupPath = `${filePath}.${ext}.${timestamp}`;
return ensureCopyFileAsync(filePath, backupPath);
}
exports.backupFileAsync = backupFileAsync;
exports.clearDirectoryAsync = clearDirectoryAsync;
exports.dirSizeAsync = dirSizeAsync;
exports.ensureCopyFileAsync = ensureCopyFileAsync;
exports.fileSizeAsync = fileSizeAsync;
exports.isDirEmptyAsync = isDirEmptyAsync;
exports.listDirsAsync = listDirsAsync;
exports.listFilesAsync = listFilesAsync;
exports.readJsonFileAsync = readJsonFileAsync;
exports.restoreLatestBackupAsync = restoreLatestBackupAsync;
exports.tryDeleteFileAsync = tryDeleteFileAsync;
exports.writeJsonFileAsync = writeJsonFileAsync;
exports.writeTextFileAsync = writeTextFileAsync;