@catbee/utils
Version:
A modular, production-grade utility toolkit for Node.js and TypeScript, designed for robust, scalable applications (including Express-based services). All utilities are tree-shakable and can be imported independently.
330 lines • 9.97 kB
JavaScript
import fs from "fs/promises";
import { createReadStream, createWriteStream } from "fs";
import { tmpdir } from "os";
import path from "path";
import { pipeline } from "stream/promises";
import { randomUUID } from "crypto";
/**
* Checks whether a file or directory exists at the given path.
*
* @param {string} path - The file or directory path to check.
* @returns {Promise<boolean>} Resolves to `true` if the path exists, `false` otherwise.
*/
export async function fileExists(path) {
try {
await fs.access(path);
return true;
}
catch (_a) {
return false;
}
}
/**
* Reads and parses a JSON file from the specified path.
*
* @typeParam T - The expected type of the parsed JSON object.
* @param {string} path - The path to the JSON file.
* @returns {Promise<T | null>} The parsed object, or `null` if reading or parsing fails.
*/
export async function readJsonFile(path) {
try {
const data = await fs.readFile(path, "utf-8");
return JSON.parse(data);
}
catch (_a) {
return null;
}
}
/**
* Writes a JavaScript object to a file as formatted JSON.
*
* @param {string} path - The destination file path.
* @param {any} data - The object to serialize and write.
* @param {number} [space=2] - Number of spaces for JSON formatting.
* @returns {Promise<void>} Resolves when writing is complete.
*/
export async function writeJsonFile(path, data, space = 2) {
const json = JSON.stringify(data, null, space);
await fs.writeFile(path, json, "utf-8");
}
/**
* Deletes a file if it exists.
*
* @param {string} path - The file path to delete.
* @returns {Promise<boolean>} Resolves to `true` if the file was deleted or didn't exist, `false` if deletion failed.
*/
export async function deleteFileIfExists(path) {
try {
await fs.unlink(path);
return true;
}
catch (err) {
if (err.code === "ENOENT")
return true;
return false;
}
}
/**
* Reads a text file from the specified path.
*
* @param {string} path - The path to the text file.
* @param {BufferEncoding} [encoding="utf-8"] - The encoding to use.
* @returns {Promise<string | null>} The file contents, or `null` if reading fails.
*/
export async function readTextFile(path, encoding = "utf-8") {
try {
return await fs.readFile(path, encoding);
}
catch (_a) {
return null;
}
}
/**
* Writes text content to a file.
*
* @param {string} path - The destination file path.
* @param {string} content - The text content to write.
* @param {BufferEncoding} [encoding="utf-8"] - The encoding to use.
* @returns {Promise<boolean>} Resolves to `true` if successful, `false` otherwise.
*/
export async function writeTextFile(path, content, encoding = "utf-8") {
try {
await fs.writeFile(path, content, encoding);
return true;
}
catch (_a) {
return false;
}
}
/**
* Appends text content to a file.
*
* @param {string} path - The file path to append to.
* @param {string} content - The text content to append.
* @param {BufferEncoding} [encoding="utf-8"] - The encoding to use.
* @returns {Promise<boolean>} Resolves to `true` if successful, `false` otherwise.
*/
export async function appendTextFile(path, content, encoding = "utf-8") {
try {
await fs.appendFile(path, content, encoding);
return true;
}
catch (_a) {
return false;
}
}
/**
* Copies a file from source to destination.
*
* @param {string} source - Source file path.
* @param {string} destination - Destination file path.
* @param {boolean} [overwrite=false] - Whether to overwrite if destination exists.
* @returns {Promise<boolean>} Resolves to `true` if successful, `false` otherwise.
*/
export async function copyFile(source, destination, overwrite = false) {
try {
const flags = overwrite ? 0 : fs.constants.COPYFILE_EXCL;
await fs.copyFile(source, destination, flags);
return true;
}
catch (_a) {
return false;
}
}
/**
* Renames/moves a file.
*
* @param {string} oldPath - Current file path.
* @param {string} newPath - New file path.
* @returns {Promise<boolean>} Resolves to `true` if successful, `false` otherwise.
*/
export async function moveFile(oldPath, newPath) {
try {
await fs.rename(oldPath, newPath);
return true;
}
catch (_a) {
// If rename fails (e.g., across devices), try copy+delete
try {
await fs.copyFile(oldPath, newPath);
await fs.unlink(oldPath);
return true;
}
catch (_b) {
return false;
}
}
}
/**
* Gets file stats if the file exists.
*
* @param {string} path - Path to the file.
* @returns {Promise<fs.Stats | null>} File stats object or null if file doesn't exist.
*/
export async function getFileStats(path) {
try {
return await fs.stat(path);
}
catch (_a) {
return null;
}
}
/**
* Creates a temporary file with optional content.
*
* @param {object} [options] - Options for creating the temp file.
* @param {string} [options.prefix="tmp-"] - Filename prefix.
* @param {string} [options.extension=""] - File extension.
* @param {string} [options.dir] - Directory to create the file in (defaults to OS temp dir).
* @param {string | Buffer} [options.content] - Optional content to write to the file.
* @returns {Promise<string>} Path to the created temporary file.
*/
export async function createTempFile({ prefix = "tmp-", extension = "", dir = tmpdir(), content, } = {}) {
const ext = extension
? extension.startsWith(".")
? extension
: `.${extension}`
: "";
const filename = `${prefix}${randomUUID()}${ext}`;
const filepath = path.join(dir, filename);
if (content) {
await fs.writeFile(filepath, content);
}
else {
await fs.writeFile(filepath, "");
}
return filepath;
}
/**
* Streams a file from source to destination.
* Useful for large files to avoid loading the entire file into memory.
*
* @param {string} source - Source file path.
* @param {string} destination - Destination file path.
* @returns {Promise<void>} Resolves when streaming completes.
* @throws {Error} If streaming fails.
*/
export async function streamFile(source, destination) {
const readStream = createReadStream(source);
const writeStream = createWriteStream(destination);
try {
await pipeline(readStream, writeStream);
}
catch (error) {
// Ensure both streams are closed in case of error
readStream.destroy();
writeStream.destroy();
throw error;
}
}
/**
* Reads a directory and returns file names.
*
* @param {string} dirPath - Path to the directory.
* @param {object} [options] - Options for reading the directory.
* @param {boolean} [options.fullPaths=false] - Whether to return full paths.
* @param {RegExp} [options.filter] - Optional regex to filter files.
* @returns {Promise<string[]>} Array of file names or paths.
* @throws {Error} If directory cannot be read.
*/
export async function readDirectory(dirPath, options = {}) {
const files = await fs.readdir(dirPath);
let result = files;
if (options.filter) {
result = result.filter((file) => options.filter.test(file));
}
if (options.fullPaths) {
result = result.map((file) => path.join(dirPath, file));
}
return result;
}
/**
* Creates a directory if it doesn't exist.
*
* @param {string} dirPath - Path to the directory.
* @param {boolean} [recursive=true] - Whether to create parent directories.
* @returns {Promise<boolean>} Resolves to `true` if successful, `false` otherwise.
*/
export async function createDirectory(dirPath, recursive = true) {
try {
await fs.mkdir(dirPath, { recursive });
return true;
}
catch (err) {
// Consider it success if directory already exists
return err.code === "EEXIST";
}
}
/**
* Safely reads and parses a JSON file with error details.
*
* @typeParam T - The expected type of the parsed JSON object.
* @param {string} path - The path to the JSON file.
* @returns {Promise<{ data: T | null; error: Error | null }>} Object with data and error properties.
*/
export async function safeReadJsonFile(path) {
try {
const content = await fs.readFile(path, "utf-8");
try {
const data = JSON.parse(content);
return { data, error: null };
}
catch (error) {
return {
data: null,
error: new Error(`Invalid JSON in file ${path}: ${error.message}`),
};
}
}
catch (error) {
return {
data: null,
error: new Error(`Failed to read file ${path}: ${error.message}`),
};
}
}
/**
* Checks if a path points to a file (not a directory).
*
* @param {string} path - Path to check.
* @returns {Promise<boolean>} True if the path is a file, false otherwise.
*/
export async function isFile(path) {
try {
const stat = await fs.stat(path);
return stat.isFile();
}
catch (_a) {
return false;
}
}
/**
* Gets the size of a file in bytes.
*
* @param {string} path - Path to the file.
* @returns {Promise<number>} Size in bytes or -1 if file doesn't exist.
*/
export async function getFileSize(path) {
try {
const stat = await fs.stat(path);
return stat.size;
}
catch (_a) {
return -1;
}
}
/**
* Reads a file as a Buffer.
*
* @param {string} path - Path to the file.
* @returns {Promise<Buffer | null>} File contents as Buffer or null if reading fails.
*/
export async function readFileBuffer(path) {
try {
return await fs.readFile(path);
}
catch (_a) {
return null;
}
}
//# sourceMappingURL=fs.utils.js.map